Files
landing_page/refer_landing_page/app/console/_components/AdminComponents.tsx
T
Godopu 78a66b7ad4 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)
2026-08-25 16:12:49 +09:00

121 lines
4.3 KiB
TypeScript

"use client";
import React, { useState } from "react";
import Link from "next/link";
import { useRouter } from "next/navigation";
import ThemeToggle from "@/components/ThemeToggle";
export function getClientToken(): string {
if (typeof window === "undefined") return "";
const match = document.cookie.match(new RegExp("(^| )admin_token=([^;]+)"));
if (match) return decodeURIComponent(match[2]);
return localStorage.getItem("admin_token") || "";
}
export function setClientToken(token: string) {
if (typeof window === "undefined") return;
document.cookie = `admin_token=${encodeURIComponent(token)}; path=/; max-age=604800; SameSite=Lax`;
localStorage.setItem("admin_token", token);
}
export function removeClientToken() {
if (typeof window === "undefined") return;
document.cookie = "admin_token=; path=/; max-age=0; SameSite=Lax";
localStorage.removeItem("admin_token");
}
export function AdminHeader({ title, description, action }: { title: string; description?: string; action?: React.ReactNode }) {
return (
<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>
<h1 className="text-2xl font-bold text-ink tracking-tight">{title}</h1>
{description && <p className="text-sm text-ink/70 mt-1">{description}</p>}
</div>
<div className="flex items-center gap-3 shrink-0">
{action}
<ThemeToggle />
</div>
</div>
);
}
export function AlertBanner({ type, message, onClose }: { type: "error" | "success" | "info"; message: string; onClose?: () => void }) {
const styles = {
error: "bg-red-50 dark:bg-red-950/40 border-red-200 dark:border-red-800 text-red-700 dark:text-red-300",
success: "bg-emerald-50 dark:bg-emerald-950/40 border-emerald-200 dark:border-emerald-800 text-emerald-700 dark:text-emerald-300",
info: "bg-blue-50 dark:bg-blue-950/40 border-blue-200 dark:border-blue-800 text-blue-700 dark:text-blue-300",
};
return (
<div className={`p-4 rounded-md border mb-4 flex items-center justify-between text-sm ${styles[type]}`}>
<div>{message}</div>
{onClose && (
<button onClick={onClose} className="text-xs font-semibold underline ml-4 hover:opacity-75">
Dismiss
</button>
)}
</div>
);
}
export function Modal({ isOpen, title, onClose, children }: { isOpen: boolean; title: string; onClose: () => void; children: React.ReactNode }) {
if (!isOpen) return null;
return (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-sm p-4">
<div className="bg-paper border border-line rounded-lg shadow-xl w-full max-w-2xl max-h-[90vh] overflow-y-auto">
<div className="flex items-center justify-between px-6 py-4 border-b border-line">
<h3 className="text-lg font-bold text-ink">{title}</h3>
<button onClick={onClose} className="text-ink/60 hover:text-ink text-xl font-bold">
&times;
</button>
</div>
<div className="p-6">{children}</div>
</div>
</div>
);
}
export function ConfirmDeleteModal({
isOpen,
title = "Confirm Deletion",
description = "Are you sure you want to delete this item? This action cannot be undone.",
onClose,
onConfirm,
isSubmitting,
}: {
isOpen: boolean;
title?: string;
description?: string;
onClose: () => void;
onConfirm: () => void;
isSubmitting?: boolean;
}) {
if (!isOpen) return null;
return (
<Modal isOpen={isOpen} title={title} onClose={onClose}>
<div className="space-y-4">
<p className="text-sm text-ink/80">{description}</p>
<div className="flex justify-end gap-3 pt-4 border-t border-line">
<button
type="button"
onClick={onClose}
className="px-4 py-2 text-sm rounded border border-line bg-paper hover:bg-ivory text-ink font-medium"
>
Cancel
</button>
<button
type="button"
onClick={onConfirm}
disabled={isSubmitting}
className="px-4 py-2 text-sm rounded bg-red-600 hover:bg-red-700 text-white font-medium disabled:opacity-50"
>
{isSubmitting ? "Deleting..." : "Delete Permanently"}
</button>
</div>
</div>
</Modal>
);
}