feat: multi-role jury fix, country flags, applicant deadline banner, timeline
Some checks failed
Build and Push Docker Image / build (push) Has been cancelled
Some checks failed
Build and Push Docker Image / build (push) Has been cancelled
- Fix project list returning empty for users with both SUPER_ADMIN and JURY_MEMBER roles (jury filter now skips admins) in project, assignment, and evaluation routers - Add CountryDisplay component showing flag emoji + name everywhere country is displayed (admin, observer, jury, mentor views — 17 files) - Add countdown deadline banner on applicant dashboard for INTAKE, SUBMISSION, and MENTORING rounds with live timer - Remove quick action buttons from applicant dashboard - Fix competition timeline sidebar: green dots/connectors only up to current round, yellow dot for current round, red connector into rejected round, grey after Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -9,6 +9,7 @@ import { Badge } from '@/components/ui/badge'
|
||||
import { Skeleton } from '@/components/ui/skeleton'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { AnimatedCard } from '@/components/shared/animated-container'
|
||||
import { CountryDisplay } from '@/components/shared/country-display'
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
@@ -200,7 +201,7 @@ export function FilteringPanel({ roundId }: { roundId: string }) {
|
||||
{r.project?.title ?? 'Unknown'}
|
||||
</Link>
|
||||
<p className="text-xs text-muted-foreground truncate">
|
||||
{formatCategory(r.project?.competitionCategory)} · {r.project?.country ?? ''}
|
||||
{formatCategory(r.project?.competitionCategory)} · {r.project?.country ? <CountryDisplay country={r.project.country} /> : ''}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 shrink-0">
|
||||
|
||||
@@ -7,6 +7,7 @@ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Skeleton } from '@/components/ui/skeleton'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { AnimatedCard } from '@/components/shared/animated-container'
|
||||
import { CountryDisplay } from '@/components/shared/country-display'
|
||||
import { Inbox, Globe, FolderOpen } from 'lucide-react'
|
||||
|
||||
function relativeTime(date: Date | string): string {
|
||||
@@ -87,11 +88,11 @@ export function IntakePanel({ roundId, programId }: { roundId: string; programId
|
||||
<div className="min-w-0 flex-1">
|
||||
<p className="text-sm font-medium truncate">{p.title}</p>
|
||||
<p className="text-xs text-muted-foreground truncate">
|
||||
{p.teamName ?? 'No team'} · {p.country ?? ''}
|
||||
{p.teamName ?? 'No team'} · {p.country ? <CountryDisplay country={p.country} /> : ''}
|
||||
</p>
|
||||
</div>
|
||||
<span className="text-[11px] tabular-nums text-muted-foreground shrink-0">
|
||||
{p.country ?? ''}
|
||||
{p.country ? <CountryDisplay country={p.country} /> : ''}
|
||||
</span>
|
||||
</Link>
|
||||
))}
|
||||
|
||||
@@ -6,6 +6,7 @@ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Skeleton } from '@/components/ui/skeleton'
|
||||
import { AnimatedCard } from '@/components/shared/animated-container'
|
||||
import { CountryDisplay } from '@/components/shared/country-display'
|
||||
import { ArrowDown, ChevronDown, ChevronUp, TrendingDown } from 'lucide-react'
|
||||
import { cn, formatCategory } from '@/lib/utils'
|
||||
|
||||
@@ -107,7 +108,7 @@ export function PreviousRoundSection({ currentRoundId }: { currentRoundId: strin
|
||||
<div className="grid grid-cols-2 gap-x-4 gap-y-1">
|
||||
{countryAttrition.map((c: any) => (
|
||||
<div key={c.country} className="flex items-center justify-between text-sm py-0.5">
|
||||
<span className="truncate">{c.country}</span>
|
||||
<span className="truncate"><CountryDisplay country={c.country} /></span>
|
||||
<Badge variant="destructive" className="tabular-nums text-xs">
|
||||
-{c.lost}
|
||||
</Badge>
|
||||
|
||||
@@ -7,6 +7,7 @@ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Skeleton } from '@/components/ui/skeleton'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { AnimatedCard } from '@/components/shared/animated-container'
|
||||
import { CountryDisplay } from '@/components/shared/country-display'
|
||||
import { FileText, Upload, Users } from 'lucide-react'
|
||||
|
||||
function relativeTime(date: Date | string): string {
|
||||
@@ -146,11 +147,11 @@ export function SubmissionPanel({ roundId, programId }: { roundId: string; progr
|
||||
<div className="min-w-0 flex-1">
|
||||
<p className="text-sm font-medium truncate">{p.title}</p>
|
||||
<p className="text-xs text-muted-foreground truncate">
|
||||
{p.teamName ?? 'No team'} · {p.country ?? ''}
|
||||
{p.teamName ?? 'No team'} · {p.country ? <CountryDisplay country={p.country} /> : ''}
|
||||
</p>
|
||||
</div>
|
||||
<Badge variant="outline" className="text-xs shrink-0">
|
||||
{p.country ?? '—'}
|
||||
{p.country ? <CountryDisplay country={p.country} /> : '—'}
|
||||
</Badge>
|
||||
</Link>
|
||||
))}
|
||||
|
||||
@@ -22,6 +22,7 @@ import { ProjectLogoWithUrl } from '@/components/shared/project-logo-with-url'
|
||||
import { UserAvatar } from '@/components/shared/user-avatar'
|
||||
import { StatusBadge } from '@/components/shared/status-badge'
|
||||
import { AnimatedCard } from '@/components/shared/animated-container'
|
||||
import { CountryDisplay } from '@/components/shared/country-display'
|
||||
import {
|
||||
AlertCircle,
|
||||
Users,
|
||||
@@ -174,7 +175,7 @@ export function ObserverProjectDetail({ projectId }: { projectId: string }) {
|
||||
{(project.country || project.geographicZone) && (
|
||||
<Badge variant="outline" className="gap-1">
|
||||
<MapPin className="h-3 w-3" />
|
||||
{project.country || project.geographicZone}
|
||||
{project.country ? <CountryDisplay country={project.country} /> : project.geographicZone}
|
||||
</Badge>
|
||||
)}
|
||||
{project.competitionCategory && (
|
||||
@@ -392,7 +393,7 @@ export function ObserverProjectDetail({ projectId }: { projectId: string }) {
|
||||
<MapPin className="h-4 w-4 text-muted-foreground mt-0.5" />
|
||||
<div>
|
||||
<p className="text-sm font-medium text-muted-foreground">Location</p>
|
||||
<p className="text-sm">{project.geographicZone || project.country}</p>
|
||||
<p className="text-sm">{project.geographicZone}{project.geographicZone && project.country ? ', ' : ''}{project.country ? <CountryDisplay country={project.country} /> : null}</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
CardDescription,
|
||||
} from '@/components/ui/card'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { CountryDisplay } from '@/components/shared/country-display'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Skeleton } from '@/components/ui/skeleton'
|
||||
@@ -394,7 +395,7 @@ export function ObserverProjectsContent() {
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell className="text-sm">
|
||||
{project.country ?? '-'}
|
||||
{project.country ? <CountryDisplay country={project.country} /> : '-'}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Badge variant="outline" className="text-xs whitespace-nowrap">
|
||||
|
||||
@@ -26,6 +26,7 @@ import { ChevronLeft, ChevronRight, ChevronDown, ChevronUp } from 'lucide-react'
|
||||
import { RoundTypeStatsCards } from '@/components/observer/round-type-stats'
|
||||
import { FilteringScreeningBar } from './filtering-screening-bar'
|
||||
import { ProjectPreviewDialog } from './project-preview-dialog'
|
||||
import { CountryDisplay } from '@/components/shared/country-display'
|
||||
|
||||
interface FilteringReportTabsProps {
|
||||
roundId: string
|
||||
@@ -176,7 +177,7 @@ export function FilteringReportTabs({ roundId }: FilteringReportTabsProps) {
|
||||
{formatCategory(r.project.competitionCategory) || '—'}
|
||||
</TableCell>
|
||||
<TableCell className="text-muted-foreground">
|
||||
{r.project.country ?? '—'}
|
||||
{r.project.country ? <CountryDisplay country={r.project.country} /> : '—'}
|
||||
</TableCell>
|
||||
<TableCell>{outcomeBadge(effectiveOutcome)}</TableCell>
|
||||
</TableRow>
|
||||
@@ -258,7 +259,7 @@ export function FilteringReportTabs({ roundId }: FilteringReportTabsProps) {
|
||||
</div>
|
||||
<div className="flex gap-3 text-xs text-muted-foreground mt-1">
|
||||
{r.project.competitionCategory && <span>{formatCategory(r.project.competitionCategory)}</span>}
|
||||
{r.project.country && <span>{r.project.country}</span>}
|
||||
{r.project.country && <span><CountryDisplay country={r.project.country} /></span>}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import { Button } from '@/components/ui/button'
|
||||
import { Skeleton } from '@/components/ui/skeleton'
|
||||
import { Separator } from '@/components/ui/separator'
|
||||
import { StatusBadge } from '@/components/shared/status-badge'
|
||||
import { CountryDisplay } from '@/components/shared/country-display'
|
||||
import { ExternalLink, MapPin, Waves, Users } from 'lucide-react'
|
||||
import Link from 'next/link'
|
||||
import type { Route } from 'next'
|
||||
@@ -78,7 +79,7 @@ export function ProjectPreviewDialog({ projectId, open, onOpenChange }: ProjectP
|
||||
{data.project.country && (
|
||||
<Badge variant="outline" className="gap-1">
|
||||
<MapPin className="h-3 w-3" />
|
||||
{data.project.country}
|
||||
<CountryDisplay country={data.project.country} />
|
||||
</Badge>
|
||||
)}
|
||||
{data.project.competitionCategory && (
|
||||
|
||||
Reference in New Issue
Block a user