From 6313345fabc8393de304d8a50119ef05cb755405 Mon Sep 17 00:00:00 2001 From: arsam Date: Fri, 18 Sep 2026 23:11:11 +0330 Subject: [PATCH] Import source: src/features/auth/auth-screen Import the existing local project snapshot. This commit organizes existing files; it does not represent newly developed functionality. --- src/features/auth/auth-screen.tsx | 367 ++++++++++++++++++++++++++++++ 1 file changed, 367 insertions(+) create mode 100644 src/features/auth/auth-screen.tsx diff --git a/src/features/auth/auth-screen.tsx b/src/features/auth/auth-screen.tsx new file mode 100644 index 0000000..348e8cc --- /dev/null +++ b/src/features/auth/auth-screen.tsx @@ -0,0 +1,367 @@ +"use client"; + +import Image from "next/image"; +import Link from "next/link"; +import { useRouter } from "next/navigation"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { Eye, EyeOff, KeyRound, LockKeyhole, Server, ShieldCheck, Wifi } from "lucide-react"; +import { useState } from "react"; +import { useForm } from "react-hook-form"; +import { z } from "zod"; +import { Logo } from "@/components/common/logo"; +import { AmbientBackground } from "@/components/shell/ambient-background"; +import { Button } from "@/components/ui/button"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { Input } from "@/components/ui/input"; +import { Badge } from "@/components/ui/badge"; +import { panelService } from "@/api/services/panel-service"; + +const authHighlights = [ + { icon: ShieldCheck, label: "Secure authentication flows" }, + { icon: KeyRound, label: "Typed frontend API contracts" }, + { icon: Server, label: "Backend-ready service wiring" }, +]; + +const loginSchema = z.object({ + email: z.string().email("Enter a valid email address."), + password: z.string().min(8, "Password must be at least 8 characters."), + remember: z.boolean().optional(), +}); + +type LoginValues = z.infer; + +export type AuthMode = + | "login" + | "logout" + | "forgot-password" + | "reset-password" + | "two-factor" + | "recovery-code" + | "passkey" + | "device-verification" + | "session-expired" + | "account-locked" + | "invitation" + | "setup"; + +export function AuthScreen({ mode }: { mode: AuthMode }) { + const router = useRouter(); + const [showPassword, setShowPassword] = useState(false); + const [loading, setLoading] = useState(false); + const [mockError, setMockError] = useState(""); + const form = useForm({ + resolver: zodResolver(loginSchema), + defaultValues: { email: "owner@example.com", password: "", remember: true }, + }); + + async function onLogin(values: LoginValues) { + setLoading(true); + setMockError(""); + const response = await panelService.auth + .login(values.email, values.password) + .catch((error: unknown): Awaited> => { + const message = + error instanceof Error ? error.message : "The backend API is unreachable."; + return { + data: null, + meta: { requestId: "client_network_error" }, + error: { + code: "NETWORK_ERROR", + message, + fields: {}, + }, + }; + }); + setLoading(false); + if (response.error) { + setMockError(response.error.message); + for (const [field, message] of Object.entries(response.error.fields ?? {})) { + if (field === "email" || field === "password") { + form.setError(field, { message }); + } + } + return; + } + if (response.data?.challenge === "totp") { + router.push("/two-factor"); + return; + } + if (values.email.includes("locked")) { + router.push("/account-locked"); + return; + } + if (values.password.toLowerCase().includes("error")) { + setMockError("The supplied credentials were rejected."); + return; + } + router.push("/dashboard"); + } + + if (mode !== "login") return ; + + return ( + + + + + Sign in to CoaxysPanel + Server Hosting Control Center + + +
+
+ + srv-dal-01.coaxys.net + + Online +
+
+ Secure connection verified +
+
+
+ + +
+ + + Forgot password? + +
+ {mockError ? ( +
+ {mockError} +
+ ) : null} + +
+ + +
+
+
+
+
+ ); +} + +function SecondaryAuthScreen({ mode }: { mode: Exclude }) { + const content = authCopy[mode]; + return ( + + + + + {content.title} + {content.description} + + +
+
+ + {content.status} +
+
+ {content.fields.map((field) => ( + + ))} +
+
+
+ + +
+
+
+
+ ); +} + +function AuthLayout({ children }: { children: React.ReactNode }) { + return ( +
+ +
+ +
+
+ +

+ CoaxysPanel +

+

+ Dense, secure hosting administration for websites, domains, files, databases, mail, + automation, and server operations. +

+
+
+ {authHighlights.map(({ icon: Icon, label }) => ( +
+ + + + {label} +
+ ))} +
+
+
+ {children} +
+
+ ); +} + +const authCopy: Record< + Exclude, + { title: string; description: string; status: string; fields: string[]; action: string } +> = { + logout: { + title: "You are signed out", + description: "The mock session was closed locally.", + status: "Session token cleared in the frontend state.", + fields: [], + action: "Return to login", + }, + "forgot-password": { + title: "Forgot password", + description: "Request a reset link for your CoaxysPanel account.", + status: "A mock reset email will be queued.", + fields: ["Email address"], + action: "Send reset link", + }, + "reset-password": { + title: "Reset password", + description: "Choose a new password after validating the reset token.", + status: "Password policy and strength validation are active.", + fields: ["Reset token", "New password", "Confirm password"], + action: "Reset password", + }, + "two-factor": { + title: "Two-factor authentication", + description: "Enter the six-digit code from your authenticator app.", + status: "TOTP challenge required for this mock account.", + fields: ["Authentication code"], + action: "Verify code", + }, + "recovery-code": { + title: "Recovery code challenge", + description: "Use a saved recovery code when the authenticator is unavailable.", + status: "Recovery-code validation is simulated.", + fields: ["Recovery code"], + action: "Verify recovery code", + }, + passkey: { + title: "Passkey authentication", + description: "Use a passkey registered with this account.", + status: "Browser passkey ceremony is represented as a mock flow.", + fields: ["Account email"], + action: "Use passkey", + }, + "device-verification": { + title: "Device verification", + description: "Confirm this device before opening a privileged session.", + status: "Verification code sent to the mock contact method.", + fields: ["Verification code"], + action: "Trust device", + }, + "session-expired": { + title: "Session expired", + description: "Your session reached the configured timeout.", + status: "Re-authentication is required.", + fields: ["Password"], + action: "Resume session", + }, + "account-locked": { + title: "Account locked", + description: "Too many failed login attempts were detected.", + status: "A support operator can unlock the account after verification.", + fields: ["Unlock request note"], + action: "Request unlock", + }, + invitation: { + title: "Accept invitation", + description: "Join a CoaxysPanel team with the assigned role.", + status: "Invitation token and role preview are ready.", + fields: ["Invitation token", "Display name", "Password"], + action: "Accept invitation", + }, + setup: { + title: "First-run setup", + description: + "Configure the first server, account, security policy, branding, and notifications.", + status: "Setup wizard writes only to mock frontend state.", + fields: ["Server hostname", "Admin email", "Primary domain", "Timezone"], + action: "Finish setup", + }, +};