'use client' import { trpc } from '@/lib/trpc/client' import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from '@/components/ui/card' import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' import { Skeleton } from '@/components/ui/skeleton' import { Cog, Palette, Mail, HardDrive, Shield, Settings as SettingsIcon, Bell, Tags, ExternalLink, Newspaper, BarChart3, ShieldAlert, Webhook, MessageCircle, } from 'lucide-react' import Link from 'next/link' import { AnimatedCard } from '@/components/shared/animated-container' import { AISettingsForm } from './ai-settings-form' import { AIUsageCard } from './ai-usage-card' import { BrandingSettingsForm } from './branding-settings-form' import { EmailSettingsForm } from './email-settings-form' import { StorageSettingsForm } from './storage-settings-form' import { SecuritySettingsForm } from './security-settings-form' import { DefaultsSettingsForm } from './defaults-settings-form' import { NotificationSettingsForm } from './notification-settings-form' function SettingsSkeleton() { return (
{[...Array(4)].map((_, i) => ( ))}
) } interface SettingsContentProps { initialSettings: Record isSuperAdmin?: boolean } export function SettingsContent({ initialSettings, isSuperAdmin = true }: SettingsContentProps) { // We use the initial settings passed from the server // Forms will refetch on mutation success // Helper to get settings by prefix const getSettingsByKeys = (keys: string[]) => { const result: Record = {} keys.forEach((key) => { if (initialSettings[key] !== undefined) { result[key] = initialSettings[key] } }) return result } const aiSettings = getSettingsByKeys([ 'ai_enabled', 'ai_provider', 'ai_model', 'ai_send_descriptions', 'openai_api_key', 'openai_base_url', ]) const brandingSettings = getSettingsByKeys([ 'platform_name', 'primary_color', 'secondary_color', 'accent_color', ]) const emailSettings = getSettingsByKeys([ 'smtp_host', 'smtp_port', 'smtp_user', 'smtp_password', 'email_from', ]) const storageSettings = getSettingsByKeys([ 'storage_provider', 'local_storage_path', 'max_file_size_mb', 'avatar_max_size_mb', 'allowed_file_types', 'allowed_image_types', ]) const securitySettings = getSettingsByKeys([ 'session_duration_hours', 'magic_link_expiry_minutes', 'invite_link_expiry_hours', 'rate_limit_requests_per_minute', ]) const defaultsSettings = getSettingsByKeys([ 'default_timezone', 'default_page_size', 'autosave_interval_seconds', 'display_project_names_uppercase', 'learning_hub_external', 'learning_hub_external_url', 'support_email', ]) const digestSettings = getSettingsByKeys([ 'digest_enabled', 'digest_default_frequency', 'digest_send_hour', 'digest_include_evaluations', 'digest_include_assignments', 'digest_include_deadlines', 'digest_include_announcements', ]) const analyticsSettings = getSettingsByKeys([ 'analytics_observer_scores_tab', 'analytics_observer_progress_tab', 'analytics_observer_juror_tab', 'analytics_observer_comparison_tab', 'analytics_pdf_enabled', 'analytics_pdf_sections', ]) const auditSecuritySettings = getSettingsByKeys([ 'audit_retention_days', 'anomaly_detection_enabled', 'anomaly_rapid_actions_threshold', 'anomaly_off_hours_start', 'anomaly_off_hours_end', ]) const whatsappSettings = getSettingsByKeys([ 'whatsapp_enabled', 'whatsapp_provider', ]) return ( <> {/* Mobile: horizontal scrollable tabs */} Defaults Branding {isSuperAdmin && ( Email )} Notif. Digest {isSuperAdmin && ( WhatsApp )} {isSuperAdmin && ( Security )} Audit {isSuperAdmin && ( AI )} Tags Analytics {isSuperAdmin && ( Storage )} {isSuperAdmin && ( Webhooks )}
{/* Desktop: sidebar navigation */}
{/* Content area */}
{isSuperAdmin && ( AI Configuration Configure AI-powered features like smart jury assignment )} Platform Branding Customize the look and feel of your platform {isSuperAdmin && ( Email Configuration Configure email settings for notifications and magic links )} Notification Email Settings Configure which notification types should also send email notifications {isSuperAdmin && ( File Storage Configure file upload limits and allowed types )} {isSuperAdmin && ( Security Settings Configure security and access control settings )} Default Settings Configure default values for the platform Platform Features Configure Learning Hub, support contact, and other platform features Digest Configuration Configure automated digest emails sent to users Analytics & Reports Configure observer dashboard visibility and PDF report settings Audit & Security Configure audit log retention and anomaly detection {isSuperAdmin && ( WhatsApp Notifications Configure WhatsApp messaging for notifications )}
{/* end content area */}
{/* end lg:flex */}
) } export { SettingsSkeleton } // Inline settings sections for new tabs import { useState } from 'react' import { Switch } from '@/components/ui/switch' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { Checkbox } from '@/components/ui/checkbox' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select' import { Loader2 } from 'lucide-react' import { toast } from 'sonner' function useSettingsMutation() { const utils = trpc.useUtils() return trpc.settings.update.useMutation({ onSuccess: () => { utils.settings.invalidate() toast.success('Setting updated') }, onError: (e) => toast.error(e.message), }) } function SettingToggle({ label, description, settingKey, value, }: { label: string description?: string settingKey: string value: string }) { const mutation = useSettingsMutation() const isChecked = value === 'true' return (
{description && (

{description}

)}
mutation.mutate({ key: settingKey, value: String(checked) }) } />
) } function SettingInput({ label, description, settingKey, value, type = 'text', }: { label: string description?: string settingKey: string value: string type?: string }) { const [localValue, setLocalValue] = useState(value) const mutation = useSettingsMutation() const save = () => { if (localValue !== value) { mutation.mutate({ key: settingKey, value: localValue }) } } return (
{description && (

{description}

)}
setLocalValue(e.target.value)} onBlur={save} className="max-w-xs" /> {mutation.isPending && }
) } function SettingSelect({ label, description, settingKey, value, options, }: { label: string description?: string settingKey: string value: string options: Array<{ value: string; label: string }> }) { const mutation = useSettingsMutation() return (
{description && (

{description}

)}
) } function DigestSettingsSection({ settings }: { settings: Record }) { return (
) } function AnalyticsSettingsSection({ settings }: { settings: Record }) { return (

Choose which analytics tabs are visible to observers

) } function AuditSettingsSection({ settings }: { settings: Record }) { return (
) } function PlatformFeaturesSection({ settings }: { settings: Record }) { return (
) } function WhatsAppSettingsSection({ settings }: { settings: Record }) { return (
) }