Files
MOPC-Portal/src/app/(admin)/admin/settings/page.tsx
Matt 5965f7889d
All checks were successful
Build and Push Docker Image / build (push) Successful in 8m4s
Platform-wide UX fixes: assignment dialog, invalidation, settings, dashboard
1. Assignment dialog overhaul: replace raw UUID inputs with searchable
   juror Combobox (shows name, email, capacity) and multi-select project
   checklist with bulk assignment support

2. Query invalidation sweep: fix missing invalidations in
   assignment-preview-sheet (roundAssignment.execute) and
   filtering-dashboard (filtering.finalizeResults) so data refreshes
   without page reload

3. Rename Submissions tab to Document Windows with descriptive
   header explaining upload window configuration

4. Connect 6 disconnected settings: storage_provider, local_storage_path,
   avatar_max_size_mb, allowed_image_types, whatsapp_enabled,
   whatsapp_provider - all now accessible in Settings UI

5. Admin dashboard redesign: branded Editorial Command Center with
   Dark Blue gradient header, colored border-l-4 stat cards, staggered
   animations, 2-column layout, action-required panel, activity timeline

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 16:05:25 +01:00

86 lines
2.7 KiB
TypeScript

import { Suspense } from 'react'
import { redirect } from 'next/navigation'
import { auth } from '@/lib/auth'
import { prisma } from '@/lib/prisma'
export const dynamic = 'force-dynamic'
import { Card, CardContent, CardHeader } from '@/components/ui/card'
import { Skeleton } from '@/components/ui/skeleton'
import { SettingsContent } from '@/components/settings/settings-content'
// Categories that only super admins can access
const SUPER_ADMIN_CATEGORIES = new Set(['AI', 'EMAIL', 'STORAGE', 'SECURITY', 'WHATSAPP'])
async function SettingsLoader({ isSuperAdmin }: { isSuperAdmin: boolean }) {
const settings = await prisma.systemSettings.findMany({
orderBy: [{ category: 'asc' }, { key: 'asc' }],
})
// Convert settings array to key-value map
// For secrets, pass a marker but not the actual value
// For non-super-admins, filter out infrastructure categories
const settingsMap: Record<string, string> = {}
settings.forEach((setting) => {
if (!isSuperAdmin && SUPER_ADMIN_CATEGORIES.has(setting.category)) {
return
}
if (setting.isSecret && setting.value) {
// Pass marker for UI to show "existing" state
settingsMap[setting.key] = '********'
} else {
settingsMap[setting.key] = setting.value
}
})
return <SettingsContent initialSettings={settingsMap} isSuperAdmin={isSuperAdmin} />
}
function SettingsSkeleton() {
return (
<div className="space-y-6">
<Skeleton className="h-10 w-full" />
<Card>
<CardHeader>
<Skeleton className="h-6 w-32" />
<Skeleton className="h-4 w-64" />
</CardHeader>
<CardContent>
<div className="space-y-4">
{[...Array(4)].map((_, i) => (
<Skeleton key={i} className="h-16 w-full" />
))}
</div>
</CardContent>
</Card>
</div>
)
}
export default async function SettingsPage() {
const session = await auth()
// Only admins (super admin + program admin) can access settings
if (session?.user?.role !== 'SUPER_ADMIN' && session?.user?.role !== 'PROGRAM_ADMIN') {
redirect('/admin')
}
const isSuperAdmin = session?.user?.role === 'SUPER_ADMIN'
return (
<div className="space-y-6">
{/* Header */}
<div>
<h1 className="text-2xl font-semibold tracking-tight">Settings</h1>
<p className="text-muted-foreground">
Configure platform settings and preferences
</p>
</div>
{/* Content */}
<Suspense fallback={<SettingsSkeleton />}>
<SettingsLoader isSuperAdmin={isSuperAdmin} />
</Suspense>
</div>
)
}