diff --git a/js/src/app/router/guards/RequireAuth.tsx b/js/src/app/router/guards/RequireAuth.tsx
index 909fe70..db3e977 100644
--- a/js/src/app/router/guards/RequireAuth.tsx
+++ b/js/src/app/router/guards/RequireAuth.tsx
@@ -5,7 +5,7 @@ import { Navigate, Outlet } from "react-router-dom";
/**
* Route guard: render the nested routes only for an authenticated user.
* While the session is loading, show a spinner; if there is no session, redirect
- * to the public home (no /login page exists yet).
+ * to the public home.
*/
export function RequireAuth() {
const { data: session, isPending } = useSession();
diff --git a/js/src/app/router/router.tsx b/js/src/app/router/router.tsx
index 792873f..3eceb23 100644
--- a/js/src/app/router/router.tsx
+++ b/js/src/app/router/router.tsx
@@ -3,12 +3,17 @@ import { AppLayout } from "@/app/layouts/AppLayout";
import { PublicLayout } from "@/app/layouts/PublicLayout";
import { RequireAdmin } from "@/app/router/guards/RequireAdmin";
import { RequireAuth } from "@/app/router/guards/RequireAuth";
+import AdminPage from "@/features/admin/Admin.page";
+import AdminLoginPage from "@/features/admin/AdminLogin.page";
import HomePage from "@/features/home/Home.page";
import SamplePage from "@/features/sample/Sample.page";
import SampleAdminPage from "@/features/sample/SampleAdmin.page";
import { createBrowserRouter } from "react-router-dom";
export const router = createBrowserRouter([
+ // Public admin login: its page owns the full viewport.
+ { path: "admin/login", element: },
+
// Public: no guard, public chrome.
{
element: ,
@@ -28,7 +33,10 @@ export const router = createBrowserRouter([
children: [
{
element: ,
- children: [{ path: "sample/admin", element: }],
+ children: [
+ { path: "admin", element: },
+ { path: "sample/admin", element: },
+ ],
},
],
},
diff --git a/js/src/features/admin/Admin.page.tsx b/js/src/features/admin/Admin.page.tsx
new file mode 100644
index 0000000..e3c5a98
--- /dev/null
+++ b/js/src/features/admin/Admin.page.tsx
@@ -0,0 +1,10 @@
+import { Stack, Text, Title } from "@mantine/core";
+
+export default function AdminPage() {
+ return (
+
+ Admin home
+ Manage PatChats administrative workflows from here.
+
+ );
+}
diff --git a/js/src/features/admin/AdminLogin.page.tsx b/js/src/features/admin/AdminLogin.page.tsx
new file mode 100644
index 0000000..0b4abe8
--- /dev/null
+++ b/js/src/features/admin/AdminLogin.page.tsx
@@ -0,0 +1,85 @@
+import {
+ Box,
+ Button,
+ Flex,
+ PasswordInput,
+ Stack,
+ TextInput,
+ Title,
+} from "@mantine/core";
+import { useState, type FormEvent } from "react";
+
+export default function AdminLoginPage() {
+ const [email, setEmail] = useState("");
+ const [password, setPassword] = useState("");
+
+ const [errors, setErrors] = useState({
+ email: "",
+ password: "",
+ });
+
+ const handleSubmit = (event: FormEvent) => {
+ event.preventDefault();
+
+ const newErrors = {
+ email: "",
+ password: "",
+ };
+
+ if (!email.trim()) {
+ newErrors.email = "Email is required";
+ }
+
+ if (!password.trim()) {
+ newErrors.password = "Password is required";
+ }
+
+ setErrors(newErrors);
+
+ if (newErrors.email || newErrors.password) {
+ return;
+ }
+
+ // Login handler is not in scope for this ticket.
+ console.log("Admin login submitted", { email, password });
+ };
+
+ return (
+
+
+
+
+ PatChats
+
+
+
+
+
+
+ Admin Log in
+
+ setEmail(event.currentTarget.value)}
+ placeholder="Admin email"
+ radius="md"
+ size="sm"
+ value={email}
+ />
+ setPassword(event.currentTarget.value)}
+ placeholder="Password"
+ radius="md"
+ size="sm"
+ value={password}
+ />
+
+
+
+
+
+ );
+}