MOPC-App/src/components/settings/settings-content.tsx

904 lines
29 KiB
TypeScript

'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,
Bell,
Tags,
ExternalLink,
Newspaper,
BarChart3,
ShieldAlert,
Globe,
Webhook,
LayoutTemplate,
} from 'lucide-react'
import Link from 'next/link'
import { Button } from '@/components/ui/button'
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 (
<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>
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<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',
'display_project_names_uppercase',
])
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 localizationSettings = getSettingsByKeys([
'localization_enabled_locales',
'localization_default_locale',
])
return (
<>
<Tabs defaultValue="defaults" className="space-y-6">
{/* Mobile: horizontal scrollable tabs */}
<TabsList className="flex h-auto gap-1 overflow-x-auto whitespace-nowrap lg:hidden">
<TabsTrigger value="defaults" className="gap-2 shrink-0">
<SettingsIcon className="h-4 w-4" />
Defaults
</TabsTrigger>
<TabsTrigger value="branding" className="gap-2 shrink-0">
<Palette className="h-4 w-4" />
Branding
</TabsTrigger>
<TabsTrigger value="localization" className="gap-2 shrink-0">
<Globe className="h-4 w-4" />
Locale
</TabsTrigger>
{isSuperAdmin && (
<TabsTrigger value="email" className="gap-2 shrink-0">
<Mail className="h-4 w-4" />
Email
</TabsTrigger>
)}
<TabsTrigger value="notifications" className="gap-2 shrink-0">
<Bell className="h-4 w-4" />
Notif.
</TabsTrigger>
<TabsTrigger value="digest" className="gap-2 shrink-0">
<Newspaper className="h-4 w-4" />
Digest
</TabsTrigger>
{isSuperAdmin && (
<TabsTrigger value="security" className="gap-2 shrink-0">
<Shield className="h-4 w-4" />
Security
</TabsTrigger>
)}
<TabsTrigger value="audit" className="gap-2 shrink-0">
<ShieldAlert className="h-4 w-4" />
Audit
</TabsTrigger>
{isSuperAdmin && (
<TabsTrigger value="ai" className="gap-2 shrink-0">
<Bot className="h-4 w-4" />
AI
</TabsTrigger>
)}
<TabsTrigger value="tags" className="gap-2 shrink-0">
<Tags className="h-4 w-4" />
Tags
</TabsTrigger>
<TabsTrigger value="analytics" className="gap-2 shrink-0">
<BarChart3 className="h-4 w-4" />
Analytics
</TabsTrigger>
{isSuperAdmin && (
<TabsTrigger value="storage" className="gap-2 shrink-0">
<HardDrive className="h-4 w-4" />
Storage
</TabsTrigger>
)}
</TabsList>
<div className="lg:flex lg:gap-8">
{/* Desktop: sidebar navigation */}
<div className="hidden lg:block lg:w-56 lg:shrink-0">
<nav className="space-y-6">
<div>
<p className="mb-2 px-3 text-xs font-semibold uppercase tracking-wider text-muted-foreground">General</p>
<TabsList className="flex flex-col items-stretch h-auto w-full bg-transparent p-0 gap-0.5">
<TabsTrigger value="defaults" className="justify-start gap-2 w-full px-3 py-2 h-auto data-[state=active]:bg-muted">
<SettingsIcon className="h-4 w-4" />
Defaults
</TabsTrigger>
<TabsTrigger value="branding" className="justify-start gap-2 w-full px-3 py-2 h-auto data-[state=active]:bg-muted">
<Palette className="h-4 w-4" />
Branding
</TabsTrigger>
<TabsTrigger value="localization" className="justify-start gap-2 w-full px-3 py-2 h-auto data-[state=active]:bg-muted">
<Globe className="h-4 w-4" />
Locale
</TabsTrigger>
</TabsList>
</div>
<div>
<p className="mb-2 px-3 text-xs font-semibold uppercase tracking-wider text-muted-foreground">Communication</p>
<TabsList className="flex flex-col items-stretch h-auto w-full bg-transparent p-0 gap-0.5">
{isSuperAdmin && (
<TabsTrigger value="email" className="justify-start gap-2 w-full px-3 py-2 h-auto data-[state=active]:bg-muted">
<Mail className="h-4 w-4" />
Email
</TabsTrigger>
)}
<TabsTrigger value="notifications" className="justify-start gap-2 w-full px-3 py-2 h-auto data-[state=active]:bg-muted">
<Bell className="h-4 w-4" />
Notifications
</TabsTrigger>
<TabsTrigger value="digest" className="justify-start gap-2 w-full px-3 py-2 h-auto data-[state=active]:bg-muted">
<Newspaper className="h-4 w-4" />
Digest
</TabsTrigger>
</TabsList>
</div>
<div>
<p className="mb-2 px-3 text-xs font-semibold uppercase tracking-wider text-muted-foreground">Security</p>
<TabsList className="flex flex-col items-stretch h-auto w-full bg-transparent p-0 gap-0.5">
{isSuperAdmin && (
<TabsTrigger value="security" className="justify-start gap-2 w-full px-3 py-2 h-auto data-[state=active]:bg-muted">
<Shield className="h-4 w-4" />
Security
</TabsTrigger>
)}
<TabsTrigger value="audit" className="justify-start gap-2 w-full px-3 py-2 h-auto data-[state=active]:bg-muted">
<ShieldAlert className="h-4 w-4" />
Audit
</TabsTrigger>
</TabsList>
</div>
<div>
<p className="mb-2 px-3 text-xs font-semibold uppercase tracking-wider text-muted-foreground">Features</p>
<TabsList className="flex flex-col items-stretch h-auto w-full bg-transparent p-0 gap-0.5">
{isSuperAdmin && (
<TabsTrigger value="ai" className="justify-start gap-2 w-full px-3 py-2 h-auto data-[state=active]:bg-muted">
<Bot className="h-4 w-4" />
AI
</TabsTrigger>
)}
<TabsTrigger value="tags" className="justify-start gap-2 w-full px-3 py-2 h-auto data-[state=active]:bg-muted">
<Tags className="h-4 w-4" />
Tags
</TabsTrigger>
<TabsTrigger value="analytics" className="justify-start gap-2 w-full px-3 py-2 h-auto data-[state=active]:bg-muted">
<BarChart3 className="h-4 w-4" />
Analytics
</TabsTrigger>
</TabsList>
</div>
{isSuperAdmin && (
<div>
<p className="mb-2 px-3 text-xs font-semibold uppercase tracking-wider text-muted-foreground">Infrastructure</p>
<TabsList className="flex flex-col items-stretch h-auto w-full bg-transparent p-0 gap-0.5">
<TabsTrigger value="storage" className="justify-start gap-2 w-full px-3 py-2 h-auto data-[state=active]:bg-muted">
<HardDrive className="h-4 w-4" />
Storage
</TabsTrigger>
</TabsList>
</div>
)}
</nav>
</div>
{/* Content area */}
<div className="flex-1 min-w-0">
{isSuperAdmin && (
<TabsContent value="ai" className="space-y-6">
<Card>
<CardHeader>
<CardTitle>AI Configuration</CardTitle>
<CardDescription>
Configure AI-powered features like smart jury assignment
</CardDescription>
</CardHeader>
<CardContent>
<AISettingsForm settings={aiSettings} />
</CardContent>
</Card>
<AIUsageCard />
</TabsContent>
)}
<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>
<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>
{isSuperAdmin && (
<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>
)}
<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>
{isSuperAdmin && (
<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>
)}
{isSuperAdmin && (
<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>
<TabsContent value="digest" className="space-y-6">
<Card>
<CardHeader>
<CardTitle>Digest Configuration</CardTitle>
<CardDescription>
Configure automated digest emails sent to users
</CardDescription>
</CardHeader>
<CardContent>
<DigestSettingsSection settings={digestSettings} />
</CardContent>
</Card>
</TabsContent>
<TabsContent value="analytics" className="space-y-6">
<Card>
<CardHeader>
<CardTitle>Analytics & Reports</CardTitle>
<CardDescription>
Configure observer dashboard visibility and PDF report settings
</CardDescription>
</CardHeader>
<CardContent>
<AnalyticsSettingsSection settings={analyticsSettings} />
</CardContent>
</Card>
</TabsContent>
<TabsContent value="audit" className="space-y-6">
<Card>
<CardHeader>
<CardTitle>Audit & Security</CardTitle>
<CardDescription>
Configure audit log retention and anomaly detection
</CardDescription>
</CardHeader>
<CardContent>
<AuditSettingsSection settings={auditSecuritySettings} />
</CardContent>
</Card>
</TabsContent>
<TabsContent value="localization" className="space-y-6">
<Card>
<CardHeader>
<CardTitle>Localization</CardTitle>
<CardDescription>
Configure language and locale settings
</CardDescription>
</CardHeader>
<CardContent>
<LocalizationSettingsSection settings={localizationSettings} />
</CardContent>
</Card>
</TabsContent>
</div>{/* end content area */}
</div>{/* end lg:flex */}
</Tabs>
{/* Quick Links to sub-pages */}
<div className="grid gap-4 sm:grid-cols-2">
<Card>
<CardHeader>
<CardTitle className="text-base flex items-center gap-2">
<LayoutTemplate className="h-4 w-4" />
Round Templates
</CardTitle>
<CardDescription>
Create reusable round configuration templates
</CardDescription>
</CardHeader>
<CardContent>
<Button asChild>
<Link href="/admin/settings/templates">
<LayoutTemplate className="mr-2 h-4 w-4" />
Manage Templates
<ExternalLink className="ml-2 h-3 w-3" />
</Link>
</Button>
</CardContent>
</Card>
{isSuperAdmin && (
<Card>
<CardHeader>
<CardTitle className="text-base flex items-center gap-2">
<Webhook className="h-4 w-4" />
Webhooks
</CardTitle>
<CardDescription>
Configure webhook endpoints for platform events
</CardDescription>
</CardHeader>
<CardContent>
<Button asChild>
<Link href="/admin/settings/webhooks">
<Webhook className="mr-2 h-4 w-4" />
Manage Webhooks
<ExternalLink className="ml-2 h-3 w-3" />
</Link>
</Button>
</CardContent>
</Card>
)}
</div>
</>
)
}
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 (
<div className="flex items-center justify-between rounded-lg border p-3">
<div className="space-y-0.5">
<Label className="text-sm font-medium">{label}</Label>
{description && (
<p className="text-xs text-muted-foreground">{description}</p>
)}
</div>
<Switch
checked={isChecked}
disabled={mutation.isPending}
onCheckedChange={(checked) =>
mutation.mutate({ key: settingKey, value: String(checked) })
}
/>
</div>
)
}
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 (
<div className="space-y-2">
<Label className="text-sm font-medium">{label}</Label>
{description && (
<p className="text-xs text-muted-foreground">{description}</p>
)}
<div className="flex gap-2">
<Input
type={type}
value={localValue}
onChange={(e) => setLocalValue(e.target.value)}
onBlur={save}
className="max-w-xs"
/>
{mutation.isPending && <Loader2 className="h-4 w-4 animate-spin self-center" />}
</div>
</div>
)
}
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 (
<div className="space-y-2">
<Label className="text-sm font-medium">{label}</Label>
{description && (
<p className="text-xs text-muted-foreground">{description}</p>
)}
<Select
value={value || options[0]?.value}
onValueChange={(v) => mutation.mutate({ key: settingKey, value: v })}
disabled={mutation.isPending}
>
<SelectTrigger className="max-w-xs">
<SelectValue />
</SelectTrigger>
<SelectContent>
{options.map((opt) => (
<SelectItem key={opt.value} value={opt.value}>
{opt.label}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
)
}
function DigestSettingsSection({ settings }: { settings: Record<string, string> }) {
return (
<div className="space-y-4">
<SettingToggle
label="Enable Digest Emails"
description="Send periodic digest emails summarizing platform activity"
settingKey="digest_enabled"
value={settings.digest_enabled || 'false'}
/>
<SettingSelect
label="Default Frequency"
description="How often digests are sent to users by default"
settingKey="digest_default_frequency"
value={settings.digest_default_frequency || 'weekly'}
options={[
{ value: 'daily', label: 'Daily' },
{ value: 'weekly', label: 'Weekly' },
{ value: 'biweekly', label: 'Bi-weekly' },
{ value: 'monthly', label: 'Monthly' },
]}
/>
<SettingInput
label="Send Hour (UTC)"
description="Hour of day when digest emails are sent (0-23)"
settingKey="digest_send_hour"
value={settings.digest_send_hour || '8'}
type="number"
/>
<div className="border-t pt-4 space-y-3">
<Label className="text-sm font-medium">Digest Sections</Label>
<SettingToggle
label="Include Evaluations"
settingKey="digest_include_evaluations"
value={settings.digest_include_evaluations || 'true'}
/>
<SettingToggle
label="Include Assignments"
settingKey="digest_include_assignments"
value={settings.digest_include_assignments || 'true'}
/>
<SettingToggle
label="Include Deadlines"
settingKey="digest_include_deadlines"
value={settings.digest_include_deadlines || 'true'}
/>
<SettingToggle
label="Include Announcements"
settingKey="digest_include_announcements"
value={settings.digest_include_announcements || 'true'}
/>
</div>
</div>
)
}
function AnalyticsSettingsSection({ settings }: { settings: Record<string, string> }) {
return (
<div className="space-y-4">
<Label className="text-sm font-medium">Observer Tab Visibility</Label>
<p className="text-xs text-muted-foreground">
Choose which analytics tabs are visible to observers
</p>
<SettingToggle
label="Scores Tab"
settingKey="analytics_observer_scores_tab"
value={settings.analytics_observer_scores_tab || 'true'}
/>
<SettingToggle
label="Progress Tab"
settingKey="analytics_observer_progress_tab"
value={settings.analytics_observer_progress_tab || 'true'}
/>
<SettingToggle
label="Juror Stats Tab"
settingKey="analytics_observer_juror_tab"
value={settings.analytics_observer_juror_tab || 'true'}
/>
<SettingToggle
label="Comparison Tab"
settingKey="analytics_observer_comparison_tab"
value={settings.analytics_observer_comparison_tab || 'true'}
/>
<div className="border-t pt-4 space-y-3">
<Label className="text-sm font-medium">PDF Reports</Label>
<SettingToggle
label="Enable PDF Report Generation"
description="Allow admins and observers to generate PDF reports"
settingKey="analytics_pdf_enabled"
value={settings.analytics_pdf_enabled || 'true'}
/>
</div>
</div>
)
}
function AuditSettingsSection({ settings }: { settings: Record<string, string> }) {
return (
<div className="space-y-4">
<SettingInput
label="Retention Period (days)"
description="How long audit log entries are kept before automatic cleanup"
settingKey="audit_retention_days"
value={settings.audit_retention_days || '365'}
type="number"
/>
<div className="border-t pt-4 space-y-3">
<SettingToggle
label="Enable Anomaly Detection"
description="Detect suspicious patterns like rapid actions or off-hours access"
settingKey="anomaly_detection_enabled"
value={settings.anomaly_detection_enabled || 'false'}
/>
<SettingInput
label="Rapid Actions Threshold"
description="Maximum actions per minute before flagging as anomalous"
settingKey="anomaly_rapid_actions_threshold"
value={settings.anomaly_rapid_actions_threshold || '30'}
type="number"
/>
<SettingInput
label="Off-Hours Start (UTC)"
description="Start hour for off-hours monitoring (0-23)"
settingKey="anomaly_off_hours_start"
value={settings.anomaly_off_hours_start || '22'}
type="number"
/>
<SettingInput
label="Off-Hours End (UTC)"
description="End hour for off-hours monitoring (0-23)"
settingKey="anomaly_off_hours_end"
value={settings.anomaly_off_hours_end || '6'}
type="number"
/>
</div>
</div>
)
}
function LocalizationSettingsSection({ settings }: { settings: Record<string, string> }) {
const mutation = useSettingsMutation()
const enabledLocales = (settings.localization_enabled_locales || 'en').split(',')
const toggleLocale = (locale: string) => {
const current = new Set(enabledLocales)
if (current.has(locale)) {
if (current.size <= 1) {
toast.error('At least one locale must be enabled')
return
}
current.delete(locale)
} else {
current.add(locale)
}
mutation.mutate({
key: 'localization_enabled_locales',
value: Array.from(current).join(','),
})
}
return (
<div className="space-y-4">
<div className="space-y-3">
<Label className="text-sm font-medium">Enabled Languages</Label>
<div className="space-y-2">
<div className="flex items-center justify-between rounded-lg border p-3">
<div className="flex items-center gap-2">
<span className="font-medium text-sm">EN</span>
<span className="text-sm text-muted-foreground">English</span>
</div>
<Checkbox
checked={enabledLocales.includes('en')}
onCheckedChange={() => toggleLocale('en')}
disabled={mutation.isPending}
/>
</div>
<div className="flex items-center justify-between rounded-lg border p-3">
<div className="flex items-center gap-2">
<span className="font-medium text-sm">FR</span>
<span className="text-sm text-muted-foreground">Fran&ccedil;ais</span>
</div>
<Checkbox
checked={enabledLocales.includes('fr')}
onCheckedChange={() => toggleLocale('fr')}
disabled={mutation.isPending}
/>
</div>
</div>
</div>
<SettingSelect
label="Default Locale"
description="The default language for new users"
settingKey="localization_default_locale"
value={settings.localization_default_locale || 'en'}
options={[
{ value: 'en', label: 'English' },
{ value: 'fr', label: 'Fran\u00e7ais' },
]}
/>
</div>
)
}