From 3d666703e08b007cb6740a02c293289108d1e25b Mon Sep 17 00:00:00 2001 From: rajun Date: Fri, 9 Oct 2026 15:11:50 +0530 Subject: [PATCH 1/6] feat: add float and fade-up keyframe animations --- app/app.css | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/app.css b/app/app.css index da21390..e29413d 100644 --- a/app/app.css +++ b/app/app.css @@ -58,3 +58,13 @@ model-viewer { color: rgb(156 163 175); /* gray-400 */ opacity: 1; } + +@keyframes float { + 0%, 100% { transform: translateY(0); } + 50% { transform: translateY(-12px); } +} + +@keyframes fade-up { + from { opacity: 0; transform: translateY(12px); } + to { opacity: 1; transform: translateY(0); } +} From e45c16ef16dcaec35f86ac8b4f7040a207070981 Mon Sep 17 00:00:00 2001 From: rajun Date: Fri, 9 Oct 2026 15:11:50 +0530 Subject: [PATCH 2/6] feat: add reusable LogoMark component --- app/components/logo-mark.tsx | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 app/components/logo-mark.tsx diff --git a/app/components/logo-mark.tsx b/app/components/logo-mark.tsx new file mode 100644 index 0000000..1236eca --- /dev/null +++ b/app/components/logo-mark.tsx @@ -0,0 +1,9 @@ +import { cn } from '~/lib/utils'; + +export function LogoMark({ className }: { className?: string }) { + return ( +
+ t. +
+ ); +} From bcb0b5fc696d772f3f08240c7a066794f1978b30 Mon Sep 17 00:00:00 2001 From: rajun Date: Fri, 9 Oct 2026 15:11:51 +0530 Subject: [PATCH 3/6] feat: add armchair SVG for AR illustration --- app/components/auth/armchair.tsx | 43 ++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 app/components/auth/armchair.tsx diff --git a/app/components/auth/armchair.tsx b/app/components/auth/armchair.tsx new file mode 100644 index 0000000..cc8ec78 --- /dev/null +++ b/app/components/auth/armchair.tsx @@ -0,0 +1,43 @@ +// SVG armchair drawn in the coordinate space of ; relies on its gradient/filter defs. +export function Armchair() { + return ( + + {/* Contact shadow */} + + + {/* Back legs */} + + + + {/* Backrest */} + + + + {/* Tufting */} + + {[[184, 210], [200, 206], [216, 210], [192, 230], [208, 230]].map(([cx, cy]) => ( + + ))} + + + + + + {/* Seat base */} + + {/* Seat cushion */} + + + + {/* Armrests */} + + + + + + {/* Front legs */} + + + + ); +} From c9c36d5fa3e51c1a87958de008bb14b5106f8b04 Mon Sep 17 00:00:00 2001 From: rajun Date: Fri, 9 Oct 2026 15:11:51 +0530 Subject: [PATCH 4/6] feat: add AR phone illustration component --- app/components/auth/ar-illustration.tsx | 129 ++++++++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 app/components/auth/ar-illustration.tsx diff --git a/app/components/auth/ar-illustration.tsx b/app/components/auth/ar-illustration.tsx new file mode 100644 index 0000000..d038cf5 --- /dev/null +++ b/app/components/auth/ar-illustration.tsx @@ -0,0 +1,129 @@ +import { Armchair } from '~/components/auth/armchair'; + +export function ArIllustration() { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {/* Phone body */} + + + + + {/* Wall / floor horizon */} + + + {/* Perspective floor seen through the camera */} + + {[240, 262, 290, 326, 372].map((y) => ( + + ))} + {[-260, -170, -90, -30, 30, 90, 170, 260].map((dx) => ( + + ))} + + + + + {/* Placement reticle */} + + + + + + + + + + {/* Camera scan brackets */} + + + + + + + + {/* AR badge */} + + AR + + {/* Shutter */} + + + + + {/* Notch */} + + + {/* Floating particles */} + {[ + { cx: 70, cy: 120, r: 3, d: '3s' }, + { cx: 330, cy: 90, r: 2, d: '4s' }, + { cx: 345, cy: 260, r: 3, d: '3.6s' }, + { cx: 55, cy: 300, r: 2, d: '4.4s' }, + { cx: 95, cy: 210, r: 1.5, d: '2.8s' }, + ].map((p) => ( + + + + ))} + + ); +} From ed11c520bd4ea6e40d8fb25e8eddff4483a2cf48 Mon Sep 17 00:00:00 2001 From: rajun Date: Fri, 9 Oct 2026 15:11:51 +0530 Subject: [PATCH 5/6] feat: add auth brand panel with AR illustration --- app/components/auth/auth-brand-panel.tsx | 36 ++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 app/components/auth/auth-brand-panel.tsx diff --git a/app/components/auth/auth-brand-panel.tsx b/app/components/auth/auth-brand-panel.tsx new file mode 100644 index 0000000..5ca808a --- /dev/null +++ b/app/components/auth/auth-brand-panel.tsx @@ -0,0 +1,36 @@ +import { LogoMark } from '~/components/logo-mark'; +import { ArIllustration } from '~/components/auth/ar-illustration'; + +export function AuthBrandPanel() { + return ( + + ); +} From 8a5989bb4a9ad8124023c497afe80a84760da70b Mon Sep 17 00:00:00 2001 From: rajun Date: Fri, 9 Oct 2026 15:11:51 +0530 Subject: [PATCH 6/6] feat: redesign login page with split layout and improved form UX --- app/routes/admin/login.tsx | 229 +++++++++++++++++++++++-------------- 1 file changed, 146 insertions(+), 83 deletions(-) diff --git a/app/routes/admin/login.tsx b/app/routes/admin/login.tsx index 2063d3b..8184640 100644 --- a/app/routes/admin/login.tsx +++ b/app/routes/admin/login.tsx @@ -2,12 +2,13 @@ import { useState, useEffect } from 'react'; import { useSubmit, useNavigation, useActionData, useNavigate, redirect } from 'react-router'; import type { Route } from './+types/login'; import { pb } from '~/lib/pocketbase'; -import { Lock, Mail, Eye, EyeOff, Loader2, ArrowRight } from 'lucide-react'; +import { Lock, Mail, Eye, EyeOff, Loader2, ArrowRight, AlertCircle } from 'lucide-react'; import { Input } from '~/components/ui/input'; import { Button } from '~/components/ui/button'; import { Label } from '~/components/ui/label'; -import { Card } from '~/components/ui/card'; import { toast } from 'sonner'; +import { LogoMark } from '~/components/logo-mark'; +import { AuthBrandPanel } from '~/components/auth/auth-brand-panel'; export async function clientLoader() { if (pb.authStore.isValid) { @@ -33,6 +34,12 @@ export async function clientAction({ request }: Route.ClientActionArgs) { } } +const fieldLabel = 'normal-case tracking-normal text-sm font-medium text-neutral-300'; +const fieldIcon = + 'pointer-events-none absolute left-4 top-1/2 -translate-y-1/2 text-neutral-500 transition-colors group-focus-within:text-orange-500'; +const fieldInput = + 'h-12 rounded-xl border-neutral-800 bg-[#141414] pl-11 text-[15px] focus-visible:border-orange-500/70 focus-visible:ring-4 focus-visible:ring-orange-500/10'; + export default function Login() { const navigate = useNavigate(); const submit = useSubmit(); @@ -42,9 +49,11 @@ export default function Login() { const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const [showPassword, setShowPassword] = useState(false); + const [capsLock, setCapsLock] = useState(false); const [localError, setLocalError] = useState(''); const isLoading = navigation.state !== 'idle'; + const errorMessage = localError || actionData?.error; // Already-authenticated redirect fallback useEffect(() => { @@ -80,97 +89,151 @@ export default function Login() { submit(formData, { method: 'post' }); }; + const handleForgotPassword = () => { + toast.info('Please contact your administrator to reset your password.'); + }; + return ( -
-
+
+ -
-
-
- t. -
-

AR Management

-

- Authorized Access Only -

-
+
+ {/* Subtle dot texture */} +
+
- -
- {(localError || actionData?.error) && ( -
- {localError || actionData?.error} -
- )} +
+
+ -
- -
- - - - setEmail(e.target.value)} - /> -
+
+

Welcome back

+

Sign in to your account to continue.

-
- -
- - - - setPassword(e.target.value)} - /> - +
+
+ + { + setPassword(e.target.value); + if (localError) setLocalError(''); + }} + onKeyUp={(e) => setCapsLock(e.getModifierState('CapsLock'))} + onBlur={() => setCapsLock(false)} + /> + +
+ {capsLock && ( +

+ + Caps Lock is on +

+ )} +
+ + -
+ {isLoading ? ( + <> + + Signing in… + + ) : ( + <> + Sign in + + + )} + +
- - - - -
- SYSTEM IP MONITORED SECURE PORTAL +

+ + Access is restricted to authorized team members. +

+
-
+ + +
); }