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,
|
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
|
|
|
Newspaper,
|
|
|
|
|
BarChart3,
|
|
|
|
|
ShieldAlert,
|
|
|
|
|
Globe,
|
|
|
|
|
Webhook,
|
|
|
|
|
LayoutTemplate,
|
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',
|
Improve projects UX, settings layout, uppercase names, per-page selector, and fix round deletion
- Fix round deletion FK constraint: add onDelete Cascade on Evaluation.form and SetNull on ProjectFile.round
- Add configurable per-page selector (10/20/50/100) to Pagination component, wired in projects page with URL sync
- Add display_project_names_uppercase setting in admin defaults, applied to project titles across desktop/mobile views
- Redesign admin settings page: vertical sidebar nav on desktop with grouped sections, horizontal scrollable tabs on mobile
- Polish projects page: responsive header with total count, search clear button with result count, status stats bar, submission date column, country display, mobile card file count
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-10 20:13:47 +01:00
|
|
|
'display_project_names_uppercase',
|
2026-01-30 13:41:32 +01:00
|
|
|
])
|
|
|
|
|
|
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
|
|
|
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',
|
|
|
|
|
])
|
|
|
|
|
|
2026-01-30 13:41:32 +01:00
|
|
|
return (
|
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
|
|
|
<>
|
Improve projects UX, settings layout, uppercase names, per-page selector, and fix round deletion
- Fix round deletion FK constraint: add onDelete Cascade on Evaluation.form and SetNull on ProjectFile.round
- Add configurable per-page selector (10/20/50/100) to Pagination component, wired in projects page with URL sync
- Add display_project_names_uppercase setting in admin defaults, applied to project titles across desktop/mobile views
- Redesign admin settings page: vertical sidebar nav on desktop with grouped sections, horizontal scrollable tabs on mobile
- Polish projects page: responsive header with total count, search clear button with result count, status stats bar, submission date column, country display, mobile card file count
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-10 20:13:47 +01:00
|
|
|
<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
|
2026-02-04 16:56:07 +01:00
|
|
|
</TabsTrigger>
|
Improve projects UX, settings layout, uppercase names, per-page selector, and fix round deletion
- Fix round deletion FK constraint: add onDelete Cascade on Evaluation.form and SetNull on ProjectFile.round
- Add configurable per-page selector (10/20/50/100) to Pagination component, wired in projects page with URL sync
- Add display_project_names_uppercase setting in admin defaults, applied to project titles across desktop/mobile views
- Redesign admin settings page: vertical sidebar nav on desktop with grouped sections, horizontal scrollable tabs on mobile
- Polish projects page: responsive header with total count, search clear button with result count, status stats bar, submission date column, country display, mobile card file count
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-10 20:13:47 +01:00
|
|
|
<TabsTrigger value="branding" className="gap-2 shrink-0">
|
2026-01-30 13:41:32 +01:00
|
|
|
<Palette className="h-4 w-4" />
|
Improve projects UX, settings layout, uppercase names, per-page selector, and fix round deletion
- Fix round deletion FK constraint: add onDelete Cascade on Evaluation.form and SetNull on ProjectFile.round
- Add configurable per-page selector (10/20/50/100) to Pagination component, wired in projects page with URL sync
- Add display_project_names_uppercase setting in admin defaults, applied to project titles across desktop/mobile views
- Redesign admin settings page: vertical sidebar nav on desktop with grouped sections, horizontal scrollable tabs on mobile
- Polish projects page: responsive header with total count, search clear button with result count, status stats bar, submission date column, country display, mobile card file count
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-10 20:13:47 +01:00
|
|
|
Branding
|
2026-01-30 13:41:32 +01:00
|
|
|
</TabsTrigger>
|
Improve projects UX, settings layout, uppercase names, per-page selector, and fix round deletion
- Fix round deletion FK constraint: add onDelete Cascade on Evaluation.form and SetNull on ProjectFile.round
- Add configurable per-page selector (10/20/50/100) to Pagination component, wired in projects page with URL sync
- Add display_project_names_uppercase setting in admin defaults, applied to project titles across desktop/mobile views
- Redesign admin settings page: vertical sidebar nav on desktop with grouped sections, horizontal scrollable tabs on mobile
- Polish projects page: responsive header with total count, search clear button with result count, status stats bar, submission date column, country display, mobile card file count
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-10 20:13:47 +01:00
|
|
|
<TabsTrigger value="localization" className="gap-2 shrink-0">
|
|
|
|
|
<Globe className="h-4 w-4" />
|
|
|
|
|
Locale
|
|
|
|
|
</TabsTrigger>
|
|
|
|
|
<TabsTrigger value="email" className="gap-2 shrink-0">
|
2026-01-30 13:41:32 +01:00
|
|
|
<Mail className="h-4 w-4" />
|
Improve projects UX, settings layout, uppercase names, per-page selector, and fix round deletion
- Fix round deletion FK constraint: add onDelete Cascade on Evaluation.form and SetNull on ProjectFile.round
- Add configurable per-page selector (10/20/50/100) to Pagination component, wired in projects page with URL sync
- Add display_project_names_uppercase setting in admin defaults, applied to project titles across desktop/mobile views
- Redesign admin settings page: vertical sidebar nav on desktop with grouped sections, horizontal scrollable tabs on mobile
- Polish projects page: responsive header with total count, search clear button with result count, status stats bar, submission date column, country display, mobile card file count
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-10 20:13:47 +01:00
|
|
|
Email
|
2026-01-30 13:41:32 +01:00
|
|
|
</TabsTrigger>
|
Improve projects UX, settings layout, uppercase names, per-page selector, and fix round deletion
- Fix round deletion FK constraint: add onDelete Cascade on Evaluation.form and SetNull on ProjectFile.round
- Add configurable per-page selector (10/20/50/100) to Pagination component, wired in projects page with URL sync
- Add display_project_names_uppercase setting in admin defaults, applied to project titles across desktop/mobile views
- Redesign admin settings page: vertical sidebar nav on desktop with grouped sections, horizontal scrollable tabs on mobile
- Polish projects page: responsive header with total count, search clear button with result count, status stats bar, submission date column, country display, mobile card file count
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-10 20:13:47 +01:00
|
|
|
<TabsTrigger value="notifications" className="gap-2 shrink-0">
|
2026-02-03 21:30:25 +01:00
|
|
|
<Bell className="h-4 w-4" />
|
Improve projects UX, settings layout, uppercase names, per-page selector, and fix round deletion
- Fix round deletion FK constraint: add onDelete Cascade on Evaluation.form and SetNull on ProjectFile.round
- Add configurable per-page selector (10/20/50/100) to Pagination component, wired in projects page with URL sync
- Add display_project_names_uppercase setting in admin defaults, applied to project titles across desktop/mobile views
- Redesign admin settings page: vertical sidebar nav on desktop with grouped sections, horizontal scrollable tabs on mobile
- Polish projects page: responsive header with total count, search clear button with result count, status stats bar, submission date column, country display, mobile card file count
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-10 20:13:47 +01:00
|
|
|
Notif.
|
2026-02-03 21:30:25 +01:00
|
|
|
</TabsTrigger>
|
Improve projects UX, settings layout, uppercase names, per-page selector, and fix round deletion
- Fix round deletion FK constraint: add onDelete Cascade on Evaluation.form and SetNull on ProjectFile.round
- Add configurable per-page selector (10/20/50/100) to Pagination component, wired in projects page with URL sync
- Add display_project_names_uppercase setting in admin defaults, applied to project titles across desktop/mobile views
- Redesign admin settings page: vertical sidebar nav on desktop with grouped sections, horizontal scrollable tabs on mobile
- Polish projects page: responsive header with total count, search clear button with result count, status stats bar, submission date column, country display, mobile card file count
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-10 20:13:47 +01:00
|
|
|
<TabsTrigger value="digest" className="gap-2 shrink-0">
|
|
|
|
|
<Newspaper className="h-4 w-4" />
|
|
|
|
|
Digest
|
2026-01-30 13:41:32 +01:00
|
|
|
</TabsTrigger>
|
Improve projects UX, settings layout, uppercase names, per-page selector, and fix round deletion
- Fix round deletion FK constraint: add onDelete Cascade on Evaluation.form and SetNull on ProjectFile.round
- Add configurable per-page selector (10/20/50/100) to Pagination component, wired in projects page with URL sync
- Add display_project_names_uppercase setting in admin defaults, applied to project titles across desktop/mobile views
- Redesign admin settings page: vertical sidebar nav on desktop with grouped sections, horizontal scrollable tabs on mobile
- Polish projects page: responsive header with total count, search clear button with result count, status stats bar, submission date column, country display, mobile card file count
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-10 20:13:47 +01:00
|
|
|
<TabsTrigger value="security" className="gap-2 shrink-0">
|
2026-01-30 13:41:32 +01:00
|
|
|
<Shield className="h-4 w-4" />
|
Improve projects UX, settings layout, uppercase names, per-page selector, and fix round deletion
- Fix round deletion FK constraint: add onDelete Cascade on Evaluation.form and SetNull on ProjectFile.round
- Add configurable per-page selector (10/20/50/100) to Pagination component, wired in projects page with URL sync
- Add display_project_names_uppercase setting in admin defaults, applied to project titles across desktop/mobile views
- Redesign admin settings page: vertical sidebar nav on desktop with grouped sections, horizontal scrollable tabs on mobile
- Polish projects page: responsive header with total count, search clear button with result count, status stats bar, submission date column, country display, mobile card file count
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-10 20:13:47 +01:00
|
|
|
Security
|
2026-01-30 13:41:32 +01:00
|
|
|
</TabsTrigger>
|
Improve projects UX, settings layout, uppercase names, per-page selector, and fix round deletion
- Fix round deletion FK constraint: add onDelete Cascade on Evaluation.form and SetNull on ProjectFile.round
- Add configurable per-page selector (10/20/50/100) to Pagination component, wired in projects page with URL sync
- Add display_project_names_uppercase setting in admin defaults, applied to project titles across desktop/mobile views
- Redesign admin settings page: vertical sidebar nav on desktop with grouped sections, horizontal scrollable tabs on mobile
- Polish projects page: responsive header with total count, search clear button with result count, status stats bar, submission date column, country display, mobile card file count
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-10 20:13:47 +01:00
|
|
|
<TabsTrigger value="audit" className="gap-2 shrink-0">
|
|
|
|
|
<ShieldAlert className="h-4 w-4" />
|
|
|
|
|
Audit
|
2026-01-30 13:41:32 +01:00
|
|
|
</TabsTrigger>
|
Improve projects UX, settings layout, uppercase names, per-page selector, and fix round deletion
- Fix round deletion FK constraint: add onDelete Cascade on Evaluation.form and SetNull on ProjectFile.round
- Add configurable per-page selector (10/20/50/100) to Pagination component, wired in projects page with URL sync
- Add display_project_names_uppercase setting in admin defaults, applied to project titles across desktop/mobile views
- Redesign admin settings page: vertical sidebar nav on desktop with grouped sections, horizontal scrollable tabs on mobile
- Polish projects page: responsive header with total count, search clear button with result count, status stats bar, submission date column, country display, mobile card file count
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-10 20:13:47 +01:00
|
|
|
<TabsTrigger value="ai" className="gap-2 shrink-0">
|
|
|
|
|
<Bot className="h-4 w-4" />
|
|
|
|
|
AI
|
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
|
|
|
</TabsTrigger>
|
Improve projects UX, settings layout, uppercase names, per-page selector, and fix round deletion
- Fix round deletion FK constraint: add onDelete Cascade on Evaluation.form and SetNull on ProjectFile.round
- Add configurable per-page selector (10/20/50/100) to Pagination component, wired in projects page with URL sync
- Add display_project_names_uppercase setting in admin defaults, applied to project titles across desktop/mobile views
- Redesign admin settings page: vertical sidebar nav on desktop with grouped sections, horizontal scrollable tabs on mobile
- Polish projects page: responsive header with total count, search clear button with result count, status stats bar, submission date column, country display, mobile card file count
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-10 20:13:47 +01:00
|
|
|
<TabsTrigger value="tags" className="gap-2 shrink-0">
|
|
|
|
|
<Tags className="h-4 w-4" />
|
|
|
|
|
Tags
|
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
|
|
|
</TabsTrigger>
|
Improve projects UX, settings layout, uppercase names, per-page selector, and fix round deletion
- Fix round deletion FK constraint: add onDelete Cascade on Evaluation.form and SetNull on ProjectFile.round
- Add configurable per-page selector (10/20/50/100) to Pagination component, wired in projects page with URL sync
- Add display_project_names_uppercase setting in admin defaults, applied to project titles across desktop/mobile views
- Redesign admin settings page: vertical sidebar nav on desktop with grouped sections, horizontal scrollable tabs on mobile
- Polish projects page: responsive header with total count, search clear button with result count, status stats bar, submission date column, country display, mobile card file count
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-10 20:13:47 +01:00
|
|
|
<TabsTrigger value="analytics" className="gap-2 shrink-0">
|
|
|
|
|
<BarChart3 className="h-4 w-4" />
|
|
|
|
|
Analytics
|
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
|
|
|
</TabsTrigger>
|
Improve projects UX, settings layout, uppercase names, per-page selector, and fix round deletion
- Fix round deletion FK constraint: add onDelete Cascade on Evaluation.form and SetNull on ProjectFile.round
- Add configurable per-page selector (10/20/50/100) to Pagination component, wired in projects page with URL sync
- Add display_project_names_uppercase setting in admin defaults, applied to project titles across desktop/mobile views
- Redesign admin settings page: vertical sidebar nav on desktop with grouped sections, horizontal scrollable tabs on mobile
- Polish projects page: responsive header with total count, search clear button with result count, status stats bar, submission date column, country display, mobile card file count
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-10 20:13:47 +01:00
|
|
|
<TabsTrigger value="storage" className="gap-2 shrink-0">
|
|
|
|
|
<HardDrive className="h-4 w-4" />
|
|
|
|
|
Storage
|
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
|
|
|
</TabsTrigger>
|
2026-01-30 13:41:32 +01:00
|
|
|
</TabsList>
|
|
|
|
|
|
Improve projects UX, settings layout, uppercase names, per-page selector, and fix round deletion
- Fix round deletion FK constraint: add onDelete Cascade on Evaluation.form and SetNull on ProjectFile.round
- Add configurable per-page selector (10/20/50/100) to Pagination component, wired in projects page with URL sync
- Add display_project_names_uppercase setting in admin defaults, applied to project titles across desktop/mobile views
- Redesign admin settings page: vertical sidebar nav on desktop with grouped sections, horizontal scrollable tabs on mobile
- Polish projects page: responsive header with total count, search clear button with result count, status stats bar, submission date column, country display, mobile card file count
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-10 20:13:47 +01:00
|
|
|
<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">
|
|
|
|
|
<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">
|
|
|
|
|
<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">
|
|
|
|
|
<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>
|
|
|
|
|
<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">
|
|
|
|
|
|
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>
|
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
|
|
|
|
|
|
|
|
<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>
|
Improve projects UX, settings layout, uppercase names, per-page selector, and fix round deletion
- Fix round deletion FK constraint: add onDelete Cascade on Evaluation.form and SetNull on ProjectFile.round
- Add configurable per-page selector (10/20/50/100) to Pagination component, wired in projects page with URL sync
- Add display_project_names_uppercase setting in admin defaults, applied to project titles across desktop/mobile views
- Redesign admin settings page: vertical sidebar nav on desktop with grouped sections, horizontal scrollable tabs on mobile
- Polish projects page: responsive header with total count, search clear button with result count, status stats bar, submission date column, country display, mobile card file count
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-10 20:13:47 +01:00
|
|
|
</div>{/* end content area */}
|
|
|
|
|
</div>{/* end lg:flex */}
|
2026-01-30 13:41:32 +01:00
|
|
|
</Tabs>
|
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
|
|
|
|
|
|
|
|
{/* 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>
|
|
|
|
|
|
|
|
|
|
<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>
|
|
|
|
|
</>
|
2026-01-30 13:41:32 +01:00
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export { SettingsSkeleton }
|
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
|
|
|
|
|
|
|
|
// 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ç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>
|
|
|
|
|
)
|
|
|
|
|
}
|