Platform-wide visual overhaul, team invites, analytics improvements, and deployment hardening

UI overhaul applying jury dashboard design patterns across all pages:
- Stat cards with border-l-4 accent + icon pills on admin, observer, mentor, applicant dashboards and reports
- Card section headers with color-coded icon pills throughout
- Hover lift effects (translate-y + shadow) on cards and list items
- Gradient progress bars (brand-teal to brand-blue) platform-wide
- AnimatedCard stagger animations on all dashboard sections
- Auth pages with gradient accent strip and polished icon containers
- EmptyState component upgraded with rounded icon pill containers
- Replaced AI-looking icons (Brain/Sparkles/Bot/Wand2/Cpu) with descriptive alternatives across 12 files
- Removed gradient overlay from jury dashboard header
- Quick actions restyled as card links with group hover effects

Backend improvements:
- Team member invite emails with account setup flow and notification logging
- Analytics routers accept edition-wide queries (programId) in addition to roundId
- Round detail endpoint returns inline progress data (eliminates extra getProgress call)
- Award voting endpoints parallelized with Promise.all
- Bulk invite supports optional sendInvitation flag
- AwardVote composite index migration for query performance

Infrastructure:
- Docker entrypoint with migration retry loop (configurable retries/delay)
- docker-compose pull_policy: always for automatic image refresh
- Simplified deploy/update scripts using docker compose up -d --pull always
- Updated deployment documentation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-11 13:20:52 +01:00
parent 98f4a957cc
commit ce4069bf92
59 changed files with 1949 additions and 913 deletions

View File

@@ -60,7 +60,6 @@ import {
Search,
Trash2,
Loader2,
Sparkles,
Tags,
Clock,
CheckCircle2,
@@ -98,6 +97,7 @@ import {
ProjectFiltersBar,
type ProjectFilters,
} from './project-filters'
import { AnimatedCard } from '@/components/shared/animated-container'
const statusColors: Record<
string,
@@ -584,7 +584,7 @@ export default function ProjectsPage() {
</div>
<div className="flex flex-wrap gap-2">
<Button variant="outline" onClick={() => setAiTagDialogOpen(true)}>
<Sparkles className="mr-2 h-4 w-4" />
<Tags className="mr-2 h-4 w-4" />
AI Tags
</Button>
<Button variant="outline" asChild>
@@ -983,7 +983,7 @@ export default function ProjectsPage() {
/>
</div>
<Link href={`/admin/projects/${project.id}`} className="block">
<Card className="transition-colors hover:bg-muted/50">
<Card className="transition-all duration-200 hover:bg-muted/50 hover:-translate-y-0.5 hover:shadow-md">
<CardHeader className="pb-3">
<div className="flex items-start gap-3 pl-8">
<ProjectLogo project={project} size="md" fallback="initials" />
@@ -1051,7 +1051,7 @@ export default function ProjectsPage() {
/>
</div>
<Link href={`/admin/projects/${project.id}`} className="block">
<Card className={`transition-colors hover:bg-muted/50 h-full ${isEliminated ? 'opacity-60 bg-destructive/5' : ''}`}>
<Card className={`transition-all duration-200 hover:bg-muted/50 hover:-translate-y-0.5 hover:shadow-md h-full ${isEliminated ? 'opacity-60 bg-destructive/5' : ''}`}>
<CardHeader className="pb-3">
<div className="flex items-start gap-3 pl-7">
<ProjectLogo project={project} size="lg" fallback="initials" />
@@ -1483,7 +1483,7 @@ export default function ProjectsPage() {
<DialogHeader>
<DialogTitle className="flex items-center gap-2">
<div className="flex h-10 w-10 items-center justify-center rounded-full bg-gradient-to-br from-amber-400 to-orange-500">
<Sparkles className="h-5 w-5 text-white" />
<Tags className="h-5 w-5 text-white" />
</div>
<div>
<span>AI Tag Generator</span>
@@ -1723,7 +1723,7 @@ export default function ProjectsPage() {
{taggingInProgress ? (
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
) : (
<Sparkles className="mr-2 h-4 w-4" />
<Tags className="mr-2 h-4 w-4" />
)}
{taggingInProgress ? 'Processing...' : 'Generate Tags'}
</Button>