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:
2026-08-25 16:12:49 +09:00
parent 1410dcf89d
commit 78a66b7ad4
3 changed files with 16 additions and 4 deletions
@@ -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>
+5 -1
View File
@@ -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>