249 lines
8.8 KiB
TypeScript
249 lines
8.8 KiB
TypeScript
|
|
'use client'
|
||
|
|
|
||
|
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
||
|
|
import { Input } from '@/components/ui/input'
|
||
|
|
import { Label } from '@/components/ui/label'
|
||
|
|
import { Switch } from '@/components/ui/switch'
|
||
|
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
|
||
|
|
|
||
|
|
type RoundConfigFormProps = {
|
||
|
|
roundType: string
|
||
|
|
config: Record<string, unknown>
|
||
|
|
onChange: (config: Record<string, unknown>) => void
|
||
|
|
}
|
||
|
|
|
||
|
|
export function RoundConfigForm({ roundType, config, onChange }: RoundConfigFormProps) {
|
||
|
|
const updateConfig = (key: string, value: unknown) => {
|
||
|
|
onChange({ ...config, [key]: value })
|
||
|
|
}
|
||
|
|
|
||
|
|
if (roundType === 'INTAKE') {
|
||
|
|
return (
|
||
|
|
<Card>
|
||
|
|
<CardHeader>
|
||
|
|
<CardTitle className="text-base">Intake Configuration</CardTitle>
|
||
|
|
</CardHeader>
|
||
|
|
<CardContent className="space-y-4">
|
||
|
|
<div className="flex items-center justify-between">
|
||
|
|
<Label htmlFor="allowDrafts">Allow Drafts</Label>
|
||
|
|
<Switch
|
||
|
|
id="allowDrafts"
|
||
|
|
checked={(config.allowDrafts as boolean) ?? true}
|
||
|
|
onCheckedChange={(checked) => updateConfig('allowDrafts', checked)}
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div className="space-y-2">
|
||
|
|
<Label htmlFor="draftExpiryDays">Draft Expiry (days)</Label>
|
||
|
|
<Input
|
||
|
|
id="draftExpiryDays"
|
||
|
|
type="number"
|
||
|
|
min={1}
|
||
|
|
value={(config.draftExpiryDays as number) ?? 30}
|
||
|
|
onChange={(e) => updateConfig('draftExpiryDays', parseInt(e.target.value, 10))}
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div className="space-y-2">
|
||
|
|
<Label htmlFor="maxFileSizeMB">Max File Size (MB)</Label>
|
||
|
|
<Input
|
||
|
|
id="maxFileSizeMB"
|
||
|
|
type="number"
|
||
|
|
min={1}
|
||
|
|
value={(config.maxFileSizeMB as number) ?? 50}
|
||
|
|
onChange={(e) => updateConfig('maxFileSizeMB', parseInt(e.target.value, 10))}
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div className="flex items-center justify-between">
|
||
|
|
<Label htmlFor="publicFormEnabled">Public Form Enabled</Label>
|
||
|
|
<Switch
|
||
|
|
id="publicFormEnabled"
|
||
|
|
checked={(config.publicFormEnabled as boolean) ?? false}
|
||
|
|
onCheckedChange={(checked) => updateConfig('publicFormEnabled', checked)}
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
</CardContent>
|
||
|
|
</Card>
|
||
|
|
)
|
||
|
|
}
|
||
|
|
|
||
|
|
if (roundType === 'FILTERING') {
|
||
|
|
return (
|
||
|
|
<Card>
|
||
|
|
<CardHeader>
|
||
|
|
<CardTitle className="text-base">Filtering Configuration</CardTitle>
|
||
|
|
</CardHeader>
|
||
|
|
<CardContent className="space-y-4">
|
||
|
|
<div className="flex items-center justify-between">
|
||
|
|
<Label htmlFor="aiScreeningEnabled">AI Screening</Label>
|
||
|
|
<Switch
|
||
|
|
id="aiScreeningEnabled"
|
||
|
|
checked={(config.aiScreeningEnabled as boolean) ?? true}
|
||
|
|
onCheckedChange={(checked) => updateConfig('aiScreeningEnabled', checked)}
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div className="flex items-center justify-between">
|
||
|
|
<Label htmlFor="duplicateDetectionEnabled">Duplicate Detection</Label>
|
||
|
|
<Switch
|
||
|
|
id="duplicateDetectionEnabled"
|
||
|
|
checked={(config.duplicateDetectionEnabled as boolean) ?? true}
|
||
|
|
onCheckedChange={(checked) => updateConfig('duplicateDetectionEnabled', checked)}
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div className="flex items-center justify-between">
|
||
|
|
<Label htmlFor="manualReviewEnabled">Manual Review</Label>
|
||
|
|
<Switch
|
||
|
|
id="manualReviewEnabled"
|
||
|
|
checked={(config.manualReviewEnabled as boolean) ?? true}
|
||
|
|
onCheckedChange={(checked) => updateConfig('manualReviewEnabled', checked)}
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div className="space-y-2">
|
||
|
|
<Label htmlFor="batchSize">Batch Size</Label>
|
||
|
|
<Input
|
||
|
|
id="batchSize"
|
||
|
|
type="number"
|
||
|
|
min={1}
|
||
|
|
value={(config.batchSize as number) ?? 20}
|
||
|
|
onChange={(e) => updateConfig('batchSize', parseInt(e.target.value, 10))}
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
</CardContent>
|
||
|
|
</Card>
|
||
|
|
)
|
||
|
|
}
|
||
|
|
|
||
|
|
if (roundType === 'EVALUATION') {
|
||
|
|
return (
|
||
|
|
<Card>
|
||
|
|
<CardHeader>
|
||
|
|
<CardTitle className="text-base">Evaluation Configuration</CardTitle>
|
||
|
|
</CardHeader>
|
||
|
|
<CardContent className="space-y-4">
|
||
|
|
<div className="space-y-2">
|
||
|
|
<Label htmlFor="requiredReviews">Required Reviews per Project</Label>
|
||
|
|
<Input
|
||
|
|
id="requiredReviews"
|
||
|
|
type="number"
|
||
|
|
min={1}
|
||
|
|
value={(config.requiredReviewsPerProject as number) ?? 3}
|
||
|
|
onChange={(e) => updateConfig('requiredReviewsPerProject', parseInt(e.target.value, 10))}
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div className="space-y-2">
|
||
|
|
<Label htmlFor="scoringMode">Scoring Mode</Label>
|
||
|
|
<Select
|
||
|
|
value={(config.scoringMode as string) ?? 'criteria'}
|
||
|
|
onValueChange={(value) => updateConfig('scoringMode', value)}
|
||
|
|
>
|
||
|
|
<SelectTrigger id="scoringMode">
|
||
|
|
<SelectValue />
|
||
|
|
</SelectTrigger>
|
||
|
|
<SelectContent>
|
||
|
|
<SelectItem value="criteria">Criteria-based</SelectItem>
|
||
|
|
<SelectItem value="global">Global score</SelectItem>
|
||
|
|
<SelectItem value="binary">Binary (pass/fail)</SelectItem>
|
||
|
|
</SelectContent>
|
||
|
|
</Select>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div className="flex items-center justify-between">
|
||
|
|
<Label htmlFor="requireFeedback">Require Feedback</Label>
|
||
|
|
<Switch
|
||
|
|
id="requireFeedback"
|
||
|
|
checked={(config.requireFeedback as boolean) ?? true}
|
||
|
|
onCheckedChange={(checked) => updateConfig('requireFeedback', checked)}
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div className="flex items-center justify-between">
|
||
|
|
<Label htmlFor="coiRequired">COI Declaration Required</Label>
|
||
|
|
<Switch
|
||
|
|
id="coiRequired"
|
||
|
|
checked={(config.coiRequired as boolean) ?? true}
|
||
|
|
onCheckedChange={(checked) => updateConfig('coiRequired', checked)}
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
</CardContent>
|
||
|
|
</Card>
|
||
|
|
)
|
||
|
|
}
|
||
|
|
|
||
|
|
if (roundType === 'LIVE_FINAL') {
|
||
|
|
return (
|
||
|
|
<Card>
|
||
|
|
<CardHeader>
|
||
|
|
<CardTitle className="text-base">Live Final Configuration</CardTitle>
|
||
|
|
</CardHeader>
|
||
|
|
<CardContent className="space-y-4">
|
||
|
|
<div className="flex items-center justify-between">
|
||
|
|
<Label htmlFor="juryVotingEnabled">Jury Voting</Label>
|
||
|
|
<Switch
|
||
|
|
id="juryVotingEnabled"
|
||
|
|
checked={(config.juryVotingEnabled as boolean) ?? true}
|
||
|
|
onCheckedChange={(checked) => updateConfig('juryVotingEnabled', checked)}
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div className="flex items-center justify-between">
|
||
|
|
<Label htmlFor="audienceVotingEnabled">Audience Voting</Label>
|
||
|
|
<Switch
|
||
|
|
id="audienceVotingEnabled"
|
||
|
|
checked={(config.audienceVotingEnabled as boolean) ?? false}
|
||
|
|
onCheckedChange={(checked) => updateConfig('audienceVotingEnabled', checked)}
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{(config.audienceVotingEnabled as boolean) && (
|
||
|
|
<div className="space-y-2">
|
||
|
|
<Label htmlFor="audienceVoteWeight">Audience Vote Weight (0-1)</Label>
|
||
|
|
<Input
|
||
|
|
id="audienceVoteWeight"
|
||
|
|
type="number"
|
||
|
|
min={0}
|
||
|
|
max={1}
|
||
|
|
step={0.1}
|
||
|
|
value={(config.audienceVoteWeight as number) ?? 0}
|
||
|
|
onChange={(e) => updateConfig('audienceVoteWeight', parseFloat(e.target.value))}
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
)}
|
||
|
|
|
||
|
|
<div className="space-y-2">
|
||
|
|
<Label htmlFor="presentationDuration">Presentation Duration (min)</Label>
|
||
|
|
<Input
|
||
|
|
id="presentationDuration"
|
||
|
|
type="number"
|
||
|
|
min={1}
|
||
|
|
value={(config.presentationDurationMinutes as number) ?? 15}
|
||
|
|
onChange={(e) => updateConfig('presentationDurationMinutes', parseInt(e.target.value, 10))}
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
</CardContent>
|
||
|
|
</Card>
|
||
|
|
)
|
||
|
|
}
|
||
|
|
|
||
|
|
// Default view for other types
|
||
|
|
return (
|
||
|
|
<Card>
|
||
|
|
<CardHeader>
|
||
|
|
<CardTitle className="text-base">{roundType} Configuration</CardTitle>
|
||
|
|
</CardHeader>
|
||
|
|
<CardContent>
|
||
|
|
<p className="text-sm text-muted-foreground">
|
||
|
|
Configuration UI for {roundType} rounds is not yet implemented.
|
||
|
|
</p>
|
||
|
|
<pre className="mt-4 p-3 bg-muted rounded text-xs overflow-auto">
|
||
|
|
{JSON.stringify(config, null, 2)}
|
||
|
|
</pre>
|
||
|
|
</CardContent>
|
||
|
|
</Card>
|
||
|
|
)
|
||
|
|
}
|