Comprehensive platform audit: security, UX, performance, and visual polish
Phase 1: Security - status transition validation, Zod tightening, DB indexes, transactions Phase 2: Admin UX - search/filter for awards, learning, partners pages Phase 3: Dashboard - Recent Activity feed, Pending Actions card, quick actions Phase 4: Jury - assignments progress/urgency, autosave indicator, divergence highlighting Phase 5: Portals - observer charts, mentor search, login/onboarding polish Phase 6: Messages preview dialog, CsvExportDialog with column selection Phase 7: Performance - query optimizations, loading skeletons, useDebounce hook Phase 8: Visual - AnimatedCard, hover effects, StatusBadge, empty state CTAs Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -32,7 +32,17 @@ import {
|
||||
type Criterion,
|
||||
} from '@/components/forms/evaluation-form-builder'
|
||||
import { RoundTypeSettings } from '@/components/forms/round-type-settings'
|
||||
import { ArrowLeft, Loader2, AlertCircle, AlertTriangle, Bell, GitCompare, MessageSquare, FileText, Calendar } from 'lucide-react'
|
||||
import { ArrowLeft, Loader2, AlertCircle, AlertTriangle, Bell, GitCompare, MessageSquare, FileText, Calendar, LayoutTemplate } from 'lucide-react'
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from '@/components/ui/dialog'
|
||||
import { toast } from 'sonner'
|
||||
import { Switch } from '@/components/ui/switch'
|
||||
import { Slider } from '@/components/ui/slider'
|
||||
import { Label } from '@/components/ui/label'
|
||||
@@ -113,9 +123,23 @@ function EditRoundContent({ roundId }: { roundId: string }) {
|
||||
roundId,
|
||||
})
|
||||
|
||||
const [saveTemplateOpen, setSaveTemplateOpen] = useState(false)
|
||||
const [templateName, setTemplateName] = useState('')
|
||||
|
||||
const utils = trpc.useUtils()
|
||||
|
||||
// Mutations
|
||||
const saveAsTemplate = trpc.roundTemplate.create.useMutation({
|
||||
onSuccess: () => {
|
||||
toast.success('Round saved as template')
|
||||
setSaveTemplateOpen(false)
|
||||
setTemplateName('')
|
||||
},
|
||||
onError: (error) => {
|
||||
toast.error(error.message)
|
||||
},
|
||||
})
|
||||
|
||||
const updateRound = trpc.round.update.useMutation({
|
||||
onSuccess: () => {
|
||||
// Invalidate cache to ensure fresh data
|
||||
@@ -825,6 +849,58 @@ function EditRoundContent({ roundId }: { roundId: string }) {
|
||||
|
||||
{/* Actions */}
|
||||
<div className="flex justify-end gap-3">
|
||||
<Dialog open={saveTemplateOpen} onOpenChange={setSaveTemplateOpen}>
|
||||
<DialogTrigger asChild>
|
||||
<Button type="button" variant="outline">
|
||||
<LayoutTemplate className="mr-2 h-4 w-4" />
|
||||
Save as Template
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="sm:max-w-md">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Save as Template</DialogTitle>
|
||||
<DialogDescription>
|
||||
Save the current round configuration as a reusable template.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<div className="space-y-4 py-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="templateName">Template Name</Label>
|
||||
<Input
|
||||
id="templateName"
|
||||
value={templateName}
|
||||
onChange={(e) => setTemplateName(e.target.value)}
|
||||
placeholder="e.g., Standard Evaluation Round"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<DialogFooter>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => setSaveTemplateOpen(false)}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
disabled={!templateName.trim() || saveAsTemplate.isPending}
|
||||
onClick={() => {
|
||||
saveAsTemplate.mutate({
|
||||
name: templateName.trim(),
|
||||
roundType: roundType,
|
||||
criteriaJson: criteria,
|
||||
settingsJson: roundSettings,
|
||||
programId: round?.programId,
|
||||
})
|
||||
}}
|
||||
>
|
||||
{saveAsTemplate.isPending && (
|
||||
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
||||
)}
|
||||
Save Template
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
<Button type="button" variant="outline" asChild>
|
||||
<Link href={`/admin/rounds/${roundId}`}>Cancel</Link>
|
||||
</Button>
|
||||
|
||||
Reference in New Issue
Block a user