Comprehensive platform audit: security, UX, performance, and visual polish

Phase 1: Security - status transition validation, Zod tightening, DB indexes, transactions

Phase 2: Admin UX - search/filter for awards, learning, partners pages

Phase 3: Dashboard - Recent Activity feed, Pending Actions card, quick actions

Phase 4: Jury - assignments progress/urgency, autosave indicator, divergence highlighting

Phase 5: Portals - observer charts, mentor search, login/onboarding polish

Phase 6: Messages preview dialog, CsvExportDialog with column selection

Phase 7: Performance - query optimizations, loading skeletons, useDebounce hook

Phase 8: Visual - AnimatedCard, hover effects, StatusBadge, empty state CTAs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-08 22:05:01 +01:00
parent e0e4cb2a32
commit e73a676412
33 changed files with 3193 additions and 977 deletions

View File

@@ -49,8 +49,12 @@ export default function LoginPage() {
// Use window.location for external redirects or callback URLs
window.location.href = callbackUrl
}
} catch {
setError('An unexpected error occurred. Please try again.')
} catch (err: unknown) {
if (err instanceof Error && err.message.includes('429')) {
setError('Too many attempts. Please wait a few minutes before trying again.')
} else {
setError('An unexpected error occurred. Please try again.')
}
} finally {
setIsLoading(false)
}
@@ -84,8 +88,12 @@ export default function LoginPage() {
} else {
setError('Failed to send magic link. Please try again.')
}
} catch {
setError('An unexpected error occurred. Please try again.')
} catch (err: unknown) {
if (err instanceof Error && err.message.includes('429')) {
setError('Too many attempts. Please wait a few minutes before trying again.')
} else {
setError('An unexpected error occurred. Please try again.')
}
} finally {
setIsLoading(false)
}
@@ -96,8 +104,8 @@ export default function LoginPage() {
return (
<Card className="w-full max-w-md">
<CardHeader className="text-center">
<div className="mx-auto mb-4 flex h-12 w-12 items-center justify-center rounded-full bg-green-100">
<CheckCircle2 className="h-6 w-6 text-green-600" />
<div className="mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-green-100 animate-in zoom-in-50 duration-300">
<Mail className="h-8 w-8 text-green-600" />
</div>
<CardTitle className="text-xl">Check your email</CardTitle>
<CardDescription className="text-base">
@@ -105,22 +113,27 @@ export default function LoginPage() {
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<p className="text-sm text-muted-foreground text-center">
Click the link in the email to sign in. The link will expire in 15
minutes.
</p>
<div className="border-t pt-4">
<div className="rounded-lg border bg-muted/50 p-4 text-sm text-muted-foreground space-y-2">
<p>Click the link in the email to sign in. The link will expire in 15 minutes.</p>
<p>If you don&apos;t see it, check your spam folder.</p>
</div>
<div className="border-t pt-4 space-y-2">
<Button
variant="ghost"
variant="outline"
className="w-full"
onClick={() => {
setIsSent(false)
setEmail('')
setPassword('')
setError(null)
}}
>
Use a different email
Send to a different email
</Button>
<p className="text-xs text-center text-muted-foreground">
Having trouble?{' '}
<a href="mailto:support@monaco-opc.com" className="text-primary hover:underline">
Contact support
</a>
</p>
</div>
</CardContent>
</Card>