Files
MOPC-Portal/src/components/shared/language-switcher.tsx

62 lines
1.9 KiB
TypeScript
Raw Normal View History

Implement 15 platform features: digest, availability, templates, comparison, live voting SSE, file versioning, mentorship, messaging, analytics, drafts, webhooks, peer review, audit enhancements, i18n Features implemented: - F1: Email digest notifications with cron endpoint and per-user frequency - F2: Jury availability windows and workload preferences in smart assignment - F3: Round templates with save-from-round and CRUD management - F4: Side-by-side project comparison view for jury members - F5: Real-time voting dashboard with Server-Sent Events (SSE) - F6: Live voting UX: QR codes, audience voting, tie-breaking, score animations - F7: File versioning, inline preview, bulk download with presigned URLs - F8: Mentor dashboard: milestones, private notes, activity tracking - F9: Communication hub with broadcasts, templates, and recipient targeting - F10: Advanced analytics: cross-round comparison, juror consistency, diversity metrics, PDF export - F11: Applicant draft saving with magic link resume and cron cleanup - F12: Webhook integration layer with HMAC signing, retry, and delivery logs - F13: Peer review discussions with anonymized scores and threaded comments - F14: Audit log enhancements: before/after diffs, session grouping, anomaly detection, retention - F15: i18n foundation with next-intl (EN/FR), cookie-based locale, language switcher Schema: 12 new models, field additions to User, Project, ProjectFile, LiveVotingSession, LiveVote, MentorAssignment, AuditLog, Program New routers: roundTemplate, message, webhook (registered in _app.ts) New services: email-digest, webhook-dispatcher New cron endpoints: /api/cron/digest, /api/cron/draft-cleanup, /api/cron/audit-cleanup New API routes: /api/live-voting/stream (SSE), /api/files/bulk-download All features are admin-configurable via SystemSettings or per-model settingsJson fields. Docker build verified successfully. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-05 23:31:41 +01:00
'use client'
import { useTransition } from 'react'
import { useLocale } from 'next-intl'
import { useRouter } from 'next/navigation'
import { Button } from '@/components/ui/button'
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu'
import { Globe, Check } from 'lucide-react'
const LANGUAGES = [
{ code: 'en', label: 'English', flag: 'EN' },
{ code: 'fr', label: 'Fran\u00e7ais', flag: 'FR' },
] as const
type LanguageCode = (typeof LANGUAGES)[number]['code']
export function LanguageSwitcher() {
const locale = useLocale() as LanguageCode
const router = useRouter()
const [isPending, startTransition] = useTransition()
const currentLang = LANGUAGES.find((l) => l.code === locale) ?? LANGUAGES[0]
const switchLanguage = (code: LanguageCode) => {
// Set cookie with 1 year expiry
document.cookie = `locale=${code};path=/;max-age=${365 * 24 * 60 * 60};samesite=lax`
// Refresh to re-run server components with new locale
startTransition(() => {
router.refresh()
})
}
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" size="sm" className="gap-2" disabled={isPending}>
<Globe className="h-4 w-4" />
<span className="font-medium">{currentLang.flag}</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
{LANGUAGES.map((lang) => (
<DropdownMenuItem
key={lang.code}
onClick={() => switchLanguage(lang.code)}
className="gap-2"
>
<span className="font-medium w-6">{lang.flag}</span>
<span>{lang.label}</span>
{locale === lang.code && <Check className="ml-auto h-4 w-4" />}
</DropdownMenuItem>
))}
</DropdownMenuContent>
</DropdownMenu>
)
}