feat(console): add theme toggle button to admin header, sidebar, and login form
- Embed ThemeToggle component directly into AdminHeader top-right area - Add ThemeToggle to AdminLayout fixed sidebar header next to Admin badge - Add ThemeToggle to AdminLoginForm for seamless theme switching prior to authentication - Cleanly passed all 12 E2E test gates (Exit Code 0)
This commit is contained in:
@@ -3,6 +3,7 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
|
import ThemeToggle from "@/components/ThemeToggle";
|
||||||
|
|
||||||
export function getClientToken(): string {
|
export function getClientToken(): string {
|
||||||
if (typeof window === "undefined") return "";
|
if (typeof window === "undefined") return "";
|
||||||
@@ -25,12 +26,15 @@ export function removeClientToken() {
|
|||||||
|
|
||||||
export function AdminHeader({ title, description, action }: { title: string; description?: string; action?: React.ReactNode }) {
|
export function AdminHeader({ title, description, action }: { title: string; description?: string; action?: React.ReactNode }) {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between pb-6 mb-6 border-b border-line">
|
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between pb-6 mb-6 border-b border-line gap-4">
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-2xl font-bold text-ink tracking-tight">{title}</h1>
|
<h1 className="text-2xl font-bold text-ink tracking-tight">{title}</h1>
|
||||||
{description && <p className="text-sm text-ink/70 mt-1">{description}</p>}
|
{description && <p className="text-sm text-ink/70 mt-1">{description}</p>}
|
||||||
</div>
|
</div>
|
||||||
{action && <div className="mt-4 sm:mt-0 flex gap-2">{action}</div>}
|
<div className="flex items-center gap-3 shrink-0">
|
||||||
|
{action}
|
||||||
|
<ThemeToggle />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import React, { useState } from "react";
|
|||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { setClientToken } from "./AdminComponents";
|
import { setClientToken } from "./AdminComponents";
|
||||||
import { adminVerifyToken } from "../../../lib/adminApi";
|
import { adminVerifyToken } from "../../../lib/adminApi";
|
||||||
|
import ThemeToggle from "@/components/ThemeToggle";
|
||||||
|
|
||||||
export function AdminLoginForm({ onLoginSuccess }: { onLoginSuccess?: () => void }) {
|
export function AdminLoginForm({ onLoginSuccess }: { onLoginSuccess?: () => void }) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@@ -41,7 +42,10 @@ export function AdminLoginForm({ onLoginSuccess }: { onLoginSuccess?: () => void
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full max-w-md bg-paper border border-line rounded-lg shadow-lg p-8">
|
<div className="w-full max-w-md bg-paper border border-line rounded-lg shadow-lg p-8 relative">
|
||||||
|
<div className="absolute top-4 right-4">
|
||||||
|
<ThemeToggle />
|
||||||
|
</div>
|
||||||
<div className="text-center mb-6">
|
<div className="text-center mb-6">
|
||||||
<h1 className="text-2xl font-bold text-ink">ANL Admin Console</h1>
|
<h1 className="text-2xl font-bold text-ink">ANL Admin Console</h1>
|
||||||
<p className="text-sm text-ink/70 mt-1">Enter your admin bearer token to manage laboratory datasets.</p>
|
<p className="text-sm text-ink/70 mt-1">Enter your admin bearer token to manage laboratory datasets.</p>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { usePathname, useRouter } from "next/navigation";
|
|||||||
import { getClientToken, removeClientToken } from "./_components/AdminComponents";
|
import { getClientToken, removeClientToken } from "./_components/AdminComponents";
|
||||||
import { AdminLoginForm } from "./_components/AdminLoginForm";
|
import { AdminLoginForm } from "./_components/AdminLoginForm";
|
||||||
import { adminVerifyToken } from "../../lib/adminApi";
|
import { adminVerifyToken } from "../../lib/adminApi";
|
||||||
|
import ThemeToggle from "@/components/ThemeToggle";
|
||||||
|
|
||||||
export default function AdminLayout({ children }: { children: React.ReactNode }) {
|
export default function AdminLayout({ children }: { children: React.ReactNode }) {
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
@@ -98,7 +99,10 @@ export default function AdminLayout({ children }: { children: React.ReactNode })
|
|||||||
<div className="p-6 border-b border-line shrink-0">
|
<div className="p-6 border-b border-line shrink-0">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<span className="font-bold text-lg text-cobalt tracking-tight">ANL Console</span>
|
<span className="font-bold text-lg text-cobalt tracking-tight">ANL Console</span>
|
||||||
<span className="text-xs bg-cobalt/10 text-cobalt font-medium px-2 py-0.5 rounded">Admin</span>
|
<div className="flex items-center gap-2">
|
||||||
|
<span className="text-xs bg-cobalt/10 text-cobalt font-medium px-2 py-0.5 rounded">Admin</span>
|
||||||
|
<ThemeToggle />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-xs text-ink/60 mt-1">Lab Data Management</p>
|
<p className="text-xs text-ink/60 mt-1">Lab Data Management</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user