2026-01-30 13:41:32 +01:00
|
|
|
'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 {
|
|
|
|
|
Bot,
|
|
|
|
|
Palette,
|
|
|
|
|
Mail,
|
|
|
|
|
HardDrive,
|
|
|
|
|
Shield,
|
|
|
|
|
Settings as SettingsIcon,
|
2026-02-03 21:30:25 +01:00
|
|
|
Bell,
|
2026-02-04 16:56:07 +01:00
|
|
|
Tags,
|
|
|
|
|
ExternalLink,
|
2026-01-30 13:41:32 +01:00
|
|
|
} from 'lucide-react'
|
2026-02-04 16:56:07 +01:00
|
|
|
import Link from 'next/link'
|
|
|
|
|
import { Button } from '@/components/ui/button'
|
2026-01-30 13:41:32 +01:00
|
|
|
import { AISettingsForm } from './ai-settings-form'
|
2026-02-03 11:58:12 +01:00
|
|
|
import { AIUsageCard } from './ai-usage-card'
|
2026-01-30 13:41:32 +01:00
|
|
|
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'
|
2026-02-03 21:30:25 +01:00
|
|
|
import { NotificationSettingsForm } from './notification-settings-form'
|
2026-01-30 13:41:32 +01:00
|
|
|
|
|
|
|
|
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>
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface SettingsContentProps {
|
|
|
|
|
initialSettings: Record<string, string>
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function SettingsContent({ initialSettings }: 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<string, string> = {}
|
|
|
|
|
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',
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
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([
|
|
|
|
|
'max_file_size_mb',
|
|
|
|
|
'allowed_file_types',
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
const securitySettings = getSettingsByKeys([
|
|
|
|
|
'session_duration_hours',
|
|
|
|
|
'magic_link_expiry_minutes',
|
|
|
|
|
'rate_limit_requests_per_minute',
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
const defaultsSettings = getSettingsByKeys([
|
|
|
|
|
'default_timezone',
|
|
|
|
|
'default_page_size',
|
|
|
|
|
'autosave_interval_seconds',
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<Tabs defaultValue="ai" className="space-y-6">
|
2026-02-04 16:56:07 +01:00
|
|
|
<TabsList className="grid w-full grid-cols-4 lg:grid-cols-8">
|
2026-01-30 13:41:32 +01:00
|
|
|
<TabsTrigger value="ai" className="gap-2">
|
|
|
|
|
<Bot className="h-4 w-4" />
|
|
|
|
|
<span className="hidden sm:inline">AI</span>
|
|
|
|
|
</TabsTrigger>
|
2026-02-04 16:56:07 +01:00
|
|
|
<TabsTrigger value="tags" className="gap-2">
|
|
|
|
|
<Tags className="h-4 w-4" />
|
|
|
|
|
<span className="hidden sm:inline">Tags</span>
|
|
|
|
|
</TabsTrigger>
|
2026-01-30 13:41:32 +01:00
|
|
|
<TabsTrigger value="branding" className="gap-2">
|
|
|
|
|
<Palette className="h-4 w-4" />
|
|
|
|
|
<span className="hidden sm:inline">Branding</span>
|
|
|
|
|
</TabsTrigger>
|
|
|
|
|
<TabsTrigger value="email" className="gap-2">
|
|
|
|
|
<Mail className="h-4 w-4" />
|
|
|
|
|
<span className="hidden sm:inline">Email</span>
|
|
|
|
|
</TabsTrigger>
|
2026-02-03 21:30:25 +01:00
|
|
|
<TabsTrigger value="notifications" className="gap-2">
|
|
|
|
|
<Bell className="h-4 w-4" />
|
|
|
|
|
<span className="hidden sm:inline">Notifications</span>
|
|
|
|
|
</TabsTrigger>
|
2026-01-30 13:41:32 +01:00
|
|
|
<TabsTrigger value="storage" className="gap-2">
|
|
|
|
|
<HardDrive className="h-4 w-4" />
|
|
|
|
|
<span className="hidden sm:inline">Storage</span>
|
|
|
|
|
</TabsTrigger>
|
|
|
|
|
<TabsTrigger value="security" className="gap-2">
|
|
|
|
|
<Shield className="h-4 w-4" />
|
|
|
|
|
<span className="hidden sm:inline">Security</span>
|
|
|
|
|
</TabsTrigger>
|
|
|
|
|
<TabsTrigger value="defaults" className="gap-2">
|
|
|
|
|
<SettingsIcon className="h-4 w-4" />
|
|
|
|
|
<span className="hidden sm:inline">Defaults</span>
|
|
|
|
|
</TabsTrigger>
|
|
|
|
|
</TabsList>
|
|
|
|
|
|
2026-02-03 11:58:12 +01:00
|
|
|
<TabsContent value="ai" className="space-y-6">
|
2026-01-30 13:41:32 +01:00
|
|
|
<Card>
|
|
|
|
|
<CardHeader>
|
|
|
|
|
<CardTitle>AI Configuration</CardTitle>
|
|
|
|
|
<CardDescription>
|
|
|
|
|
Configure AI-powered features like smart jury assignment
|
|
|
|
|
</CardDescription>
|
|
|
|
|
</CardHeader>
|
|
|
|
|
<CardContent>
|
|
|
|
|
<AISettingsForm settings={aiSettings} />
|
|
|
|
|
</CardContent>
|
|
|
|
|
</Card>
|
2026-02-03 11:58:12 +01:00
|
|
|
<AIUsageCard />
|
2026-01-30 13:41:32 +01:00
|
|
|
</TabsContent>
|
|
|
|
|
|
2026-02-04 16:56:07 +01:00
|
|
|
<TabsContent value="tags">
|
|
|
|
|
<Card>
|
|
|
|
|
<CardHeader>
|
|
|
|
|
<CardTitle className="flex items-center gap-2">
|
|
|
|
|
<Tags className="h-5 w-5" />
|
|
|
|
|
Expertise Tags
|
|
|
|
|
</CardTitle>
|
|
|
|
|
<CardDescription>
|
|
|
|
|
Manage tags used for jury expertise, project categorization, and AI-powered matching
|
|
|
|
|
</CardDescription>
|
|
|
|
|
</CardHeader>
|
|
|
|
|
<CardContent className="space-y-4">
|
|
|
|
|
<p className="text-sm text-muted-foreground">
|
|
|
|
|
Expertise tags are used across the platform to:
|
|
|
|
|
</p>
|
|
|
|
|
<ul className="text-sm text-muted-foreground list-disc list-inside space-y-1">
|
|
|
|
|
<li>Categorize jury members by their areas of expertise</li>
|
|
|
|
|
<li>Tag projects for better organization and filtering</li>
|
|
|
|
|
<li>Power AI-based project tagging</li>
|
|
|
|
|
<li>Enable smart jury-project matching</li>
|
|
|
|
|
</ul>
|
|
|
|
|
<Button asChild>
|
|
|
|
|
<Link href="/admin/settings/tags">
|
|
|
|
|
<Tags className="mr-2 h-4 w-4" />
|
|
|
|
|
Manage Expertise Tags
|
|
|
|
|
<ExternalLink className="ml-2 h-3 w-3" />
|
|
|
|
|
</Link>
|
|
|
|
|
</Button>
|
|
|
|
|
</CardContent>
|
|
|
|
|
</Card>
|
|
|
|
|
</TabsContent>
|
|
|
|
|
|
2026-01-30 13:41:32 +01:00
|
|
|
<TabsContent value="branding">
|
|
|
|
|
<Card>
|
|
|
|
|
<CardHeader>
|
|
|
|
|
<CardTitle>Platform Branding</CardTitle>
|
|
|
|
|
<CardDescription>
|
|
|
|
|
Customize the look and feel of your platform
|
|
|
|
|
</CardDescription>
|
|
|
|
|
</CardHeader>
|
|
|
|
|
<CardContent>
|
|
|
|
|
<BrandingSettingsForm settings={brandingSettings} />
|
|
|
|
|
</CardContent>
|
|
|
|
|
</Card>
|
|
|
|
|
</TabsContent>
|
|
|
|
|
|
|
|
|
|
<TabsContent value="email">
|
|
|
|
|
<Card>
|
|
|
|
|
<CardHeader>
|
|
|
|
|
<CardTitle>Email Configuration</CardTitle>
|
|
|
|
|
<CardDescription>
|
|
|
|
|
Configure email settings for notifications and magic links
|
|
|
|
|
</CardDescription>
|
|
|
|
|
</CardHeader>
|
|
|
|
|
<CardContent>
|
|
|
|
|
<EmailSettingsForm settings={emailSettings} />
|
|
|
|
|
</CardContent>
|
|
|
|
|
</Card>
|
|
|
|
|
</TabsContent>
|
|
|
|
|
|
2026-02-03 21:30:25 +01:00
|
|
|
<TabsContent value="notifications">
|
|
|
|
|
<Card>
|
|
|
|
|
<CardHeader>
|
|
|
|
|
<CardTitle>Notification Email Settings</CardTitle>
|
|
|
|
|
<CardDescription>
|
|
|
|
|
Configure which notification types should also send email notifications
|
|
|
|
|
</CardDescription>
|
|
|
|
|
</CardHeader>
|
|
|
|
|
<CardContent>
|
|
|
|
|
<NotificationSettingsForm />
|
|
|
|
|
</CardContent>
|
|
|
|
|
</Card>
|
|
|
|
|
</TabsContent>
|
|
|
|
|
|
2026-01-30 13:41:32 +01:00
|
|
|
<TabsContent value="storage">
|
|
|
|
|
<Card>
|
|
|
|
|
<CardHeader>
|
|
|
|
|
<CardTitle>File Storage</CardTitle>
|
|
|
|
|
<CardDescription>
|
|
|
|
|
Configure file upload limits and allowed types
|
|
|
|
|
</CardDescription>
|
|
|
|
|
</CardHeader>
|
|
|
|
|
<CardContent>
|
|
|
|
|
<StorageSettingsForm settings={storageSettings} />
|
|
|
|
|
</CardContent>
|
|
|
|
|
</Card>
|
|
|
|
|
</TabsContent>
|
|
|
|
|
|
|
|
|
|
<TabsContent value="security">
|
|
|
|
|
<Card>
|
|
|
|
|
<CardHeader>
|
|
|
|
|
<CardTitle>Security Settings</CardTitle>
|
|
|
|
|
<CardDescription>
|
|
|
|
|
Configure security and access control settings
|
|
|
|
|
</CardDescription>
|
|
|
|
|
</CardHeader>
|
|
|
|
|
<CardContent>
|
|
|
|
|
<SecuritySettingsForm settings={securitySettings} />
|
|
|
|
|
</CardContent>
|
|
|
|
|
</Card>
|
|
|
|
|
</TabsContent>
|
|
|
|
|
|
|
|
|
|
<TabsContent value="defaults">
|
|
|
|
|
<Card>
|
|
|
|
|
<CardHeader>
|
|
|
|
|
<CardTitle>Default Settings</CardTitle>
|
|
|
|
|
<CardDescription>
|
|
|
|
|
Configure default values for the platform
|
|
|
|
|
</CardDescription>
|
|
|
|
|
</CardHeader>
|
|
|
|
|
<CardContent>
|
|
|
|
|
<DefaultsSettingsForm settings={defaultsSettings} />
|
|
|
|
|
</CardContent>
|
|
|
|
|
</Card>
|
|
|
|
|
</TabsContent>
|
|
|
|
|
</Tabs>
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export { SettingsSkeleton }
|