Files
MOPC-Portal/src/components/shared/language-switcher.tsx
Matt 59436ed67a 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

62 lines
1.9 KiB
TypeScript

'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>
)
}