Round system redesign: Phases 1-7 complete
Full pipeline/track/stage architecture replacing the legacy round system.
Schema: 11 new models (Pipeline, Track, Stage, StageTransition,
ProjectStageState, RoutingRule, Cohort, CohortProject, LiveProgressCursor,
OverrideAction, AudienceVoter) + 8 new enums.
Backend: 9 new routers (pipeline, stage, routing, stageFiltering,
stageAssignment, cohort, live, decision, award) + 6 new services
(stage-engine, routing-engine, stage-filtering, stage-assignment,
stage-notifications, live-control).
Frontend: Pipeline wizard (17 components), jury stage pages (7),
applicant pipeline pages (3), public stage pages (2), admin pipeline
pages (5), shared stage components (3), SSE route, live hook.
Phase 6 refit: 23 routers/services migrated from roundId to stageId,
all frontend components refitted. Deleted round.ts (985 lines),
roundTemplate.ts, round-helpers.ts, round-settings.ts, round-type-settings.tsx,
10 legacy admin pages, 7 legacy jury pages, 3 legacy dialogs.
Phase 7 validation: 36 tests (10 unit + 8 integration files) all passing,
TypeScript 0 errors, Next.js build succeeds, 13 integrity checks,
legacy symbol sweep clean, auto-seed on first Docker startup.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 13:57:09 +01:00
|
|
|
'use client'
|
|
|
|
|
|
|
|
|
|
import { Label } from '@/components/ui/label'
|
|
|
|
|
import { Switch } from '@/components/ui/switch'
|
|
|
|
|
import { Card, CardContent } from '@/components/ui/card'
|
|
|
|
|
import { Bell } from 'lucide-react'
|
|
|
|
|
|
|
|
|
|
type NotificationsSectionProps = {
|
|
|
|
|
config: Record<string, boolean>
|
|
|
|
|
onChange: (config: Record<string, boolean>) => void
|
|
|
|
|
overridePolicy: Record<string, unknown>
|
|
|
|
|
onOverridePolicyChange: (policy: Record<string, unknown>) => void
|
2026-02-13 20:50:22 +01:00
|
|
|
isActive?: boolean
|
Round system redesign: Phases 1-7 complete
Full pipeline/track/stage architecture replacing the legacy round system.
Schema: 11 new models (Pipeline, Track, Stage, StageTransition,
ProjectStageState, RoutingRule, Cohort, CohortProject, LiveProgressCursor,
OverrideAction, AudienceVoter) + 8 new enums.
Backend: 9 new routers (pipeline, stage, routing, stageFiltering,
stageAssignment, cohort, live, decision, award) + 6 new services
(stage-engine, routing-engine, stage-filtering, stage-assignment,
stage-notifications, live-control).
Frontend: Pipeline wizard (17 components), jury stage pages (7),
applicant pipeline pages (3), public stage pages (2), admin pipeline
pages (5), shared stage components (3), SSE route, live hook.
Phase 6 refit: 23 routers/services migrated from roundId to stageId,
all frontend components refitted. Deleted round.ts (985 lines),
roundTemplate.ts, round-helpers.ts, round-settings.ts, round-type-settings.tsx,
10 legacy admin pages, 7 legacy jury pages, 3 legacy dialogs.
Phase 7 validation: 36 tests (10 unit + 8 integration files) all passing,
TypeScript 0 errors, Next.js build succeeds, 13 integrity checks,
legacy symbol sweep clean, auto-seed on first Docker startup.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 13:57:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const NOTIFICATION_EVENTS = [
|
|
|
|
|
{
|
|
|
|
|
key: 'stage.transitioned',
|
|
|
|
|
label: 'Stage Transitioned',
|
|
|
|
|
description: 'When a stage changes status (draft → active → closed)',
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: 'filtering.completed',
|
|
|
|
|
label: 'Filtering Completed',
|
|
|
|
|
description: 'When batch filtering finishes processing',
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: 'assignment.generated',
|
|
|
|
|
label: 'Assignments Generated',
|
|
|
|
|
description: 'When jury assignments are created or updated',
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: 'routing.executed',
|
|
|
|
|
label: 'Routing Executed',
|
|
|
|
|
description: 'When projects are routed into tracks/stages',
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: 'live.cursor.updated',
|
|
|
|
|
label: 'Live Cursor Updated',
|
|
|
|
|
description: 'When the live presentation moves to next project',
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: 'cohort.window.changed',
|
|
|
|
|
label: 'Cohort Window Changed',
|
|
|
|
|
description: 'When a cohort voting window opens or closes',
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: 'decision.overridden',
|
|
|
|
|
label: 'Decision Overridden',
|
|
|
|
|
description: 'When an admin overrides an automated decision',
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: 'award.winner.finalized',
|
|
|
|
|
label: 'Award Winner Finalized',
|
|
|
|
|
description: 'When a special award winner is selected',
|
|
|
|
|
},
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
export function NotificationsSection({
|
|
|
|
|
config,
|
|
|
|
|
onChange,
|
|
|
|
|
overridePolicy,
|
|
|
|
|
onOverridePolicyChange,
|
2026-02-13 20:50:22 +01:00
|
|
|
isActive,
|
Round system redesign: Phases 1-7 complete
Full pipeline/track/stage architecture replacing the legacy round system.
Schema: 11 new models (Pipeline, Track, Stage, StageTransition,
ProjectStageState, RoutingRule, Cohort, CohortProject, LiveProgressCursor,
OverrideAction, AudienceVoter) + 8 new enums.
Backend: 9 new routers (pipeline, stage, routing, stageFiltering,
stageAssignment, cohort, live, decision, award) + 6 new services
(stage-engine, routing-engine, stage-filtering, stage-assignment,
stage-notifications, live-control).
Frontend: Pipeline wizard (17 components), jury stage pages (7),
applicant pipeline pages (3), public stage pages (2), admin pipeline
pages (5), shared stage components (3), SSE route, live hook.
Phase 6 refit: 23 routers/services migrated from roundId to stageId,
all frontend components refitted. Deleted round.ts (985 lines),
roundTemplate.ts, round-helpers.ts, round-settings.ts, round-type-settings.tsx,
10 legacy admin pages, 7 legacy jury pages, 3 legacy dialogs.
Phase 7 validation: 36 tests (10 unit + 8 integration files) all passing,
TypeScript 0 errors, Next.js build succeeds, 13 integrity checks,
legacy symbol sweep clean, auto-seed on first Docker startup.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 13:57:09 +01:00
|
|
|
}: NotificationsSectionProps) {
|
|
|
|
|
const toggleEvent = (key: string, enabled: boolean) => {
|
|
|
|
|
onChange({ ...config, [key]: enabled })
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className="space-y-6">
|
|
|
|
|
<div>
|
|
|
|
|
<p className="text-sm text-muted-foreground">
|
|
|
|
|
Choose which pipeline events trigger notifications. All events are enabled by default.
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div className="space-y-2">
|
|
|
|
|
{NOTIFICATION_EVENTS.map((event) => (
|
|
|
|
|
<Card key={event.key}>
|
|
|
|
|
<CardContent className="py-3 px-4">
|
|
|
|
|
<div className="flex items-center justify-between gap-4">
|
|
|
|
|
<div className="flex items-start gap-3 min-w-0">
|
|
|
|
|
<Bell className="h-4 w-4 text-muted-foreground mt-0.5 shrink-0" />
|
|
|
|
|
<div className="min-w-0">
|
|
|
|
|
<Label className="text-sm font-medium">{event.label}</Label>
|
|
|
|
|
<p className="text-xs text-muted-foreground">{event.description}</p>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<Switch
|
|
|
|
|
checked={config[event.key] !== false}
|
|
|
|
|
onCheckedChange={(checked) => toggleEvent(event.key, checked)}
|
2026-02-13 20:50:22 +01:00
|
|
|
disabled={isActive}
|
Round system redesign: Phases 1-7 complete
Full pipeline/track/stage architecture replacing the legacy round system.
Schema: 11 new models (Pipeline, Track, Stage, StageTransition,
ProjectStageState, RoutingRule, Cohort, CohortProject, LiveProgressCursor,
OverrideAction, AudienceVoter) + 8 new enums.
Backend: 9 new routers (pipeline, stage, routing, stageFiltering,
stageAssignment, cohort, live, decision, award) + 6 new services
(stage-engine, routing-engine, stage-filtering, stage-assignment,
stage-notifications, live-control).
Frontend: Pipeline wizard (17 components), jury stage pages (7),
applicant pipeline pages (3), public stage pages (2), admin pipeline
pages (5), shared stage components (3), SSE route, live hook.
Phase 6 refit: 23 routers/services migrated from roundId to stageId,
all frontend components refitted. Deleted round.ts (985 lines),
roundTemplate.ts, round-helpers.ts, round-settings.ts, round-type-settings.tsx,
10 legacy admin pages, 7 legacy jury pages, 3 legacy dialogs.
Phase 7 validation: 36 tests (10 unit + 8 integration files) all passing,
TypeScript 0 errors, Next.js build succeeds, 13 integrity checks,
legacy symbol sweep clean, auto-seed on first Docker startup.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 13:57:09 +01:00
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</CardContent>
|
|
|
|
|
</Card>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Override Governance */}
|
|
|
|
|
<div className="space-y-3 pt-2 border-t">
|
|
|
|
|
<Label>Override Governance</Label>
|
|
|
|
|
<p className="text-xs text-muted-foreground">
|
|
|
|
|
Who can override automated decisions in this pipeline?
|
|
|
|
|
</p>
|
|
|
|
|
<div className="space-y-2">
|
|
|
|
|
<div className="flex items-center gap-2">
|
|
|
|
|
<Switch
|
|
|
|
|
checked={
|
|
|
|
|
Array.isArray(overridePolicy.allowedRoles) &&
|
|
|
|
|
overridePolicy.allowedRoles.includes('SUPER_ADMIN')
|
|
|
|
|
}
|
|
|
|
|
disabled
|
|
|
|
|
/>
|
|
|
|
|
<Label className="text-sm">Super Admins (always enabled)</Label>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="flex items-center gap-2">
|
|
|
|
|
<Switch
|
|
|
|
|
checked={
|
|
|
|
|
Array.isArray(overridePolicy.allowedRoles) &&
|
|
|
|
|
overridePolicy.allowedRoles.includes('PROGRAM_ADMIN')
|
|
|
|
|
}
|
|
|
|
|
onCheckedChange={(checked) => {
|
|
|
|
|
const roles = Array.isArray(overridePolicy.allowedRoles)
|
|
|
|
|
? [...overridePolicy.allowedRoles]
|
|
|
|
|
: ['SUPER_ADMIN']
|
|
|
|
|
if (checked && !roles.includes('PROGRAM_ADMIN')) {
|
|
|
|
|
roles.push('PROGRAM_ADMIN')
|
|
|
|
|
} else if (!checked) {
|
|
|
|
|
const idx = roles.indexOf('PROGRAM_ADMIN')
|
|
|
|
|
if (idx >= 0) roles.splice(idx, 1)
|
|
|
|
|
}
|
|
|
|
|
onOverridePolicyChange({ ...overridePolicy, allowedRoles: roles })
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
<Label className="text-sm">Program Admins</Label>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="flex items-center gap-2">
|
|
|
|
|
<Switch
|
|
|
|
|
checked={
|
|
|
|
|
Array.isArray(overridePolicy.allowedRoles) &&
|
|
|
|
|
overridePolicy.allowedRoles.includes('AWARD_MASTER')
|
|
|
|
|
}
|
|
|
|
|
onCheckedChange={(checked) => {
|
|
|
|
|
const roles = Array.isArray(overridePolicy.allowedRoles)
|
|
|
|
|
? [...overridePolicy.allowedRoles]
|
|
|
|
|
: ['SUPER_ADMIN']
|
|
|
|
|
if (checked && !roles.includes('AWARD_MASTER')) {
|
|
|
|
|
roles.push('AWARD_MASTER')
|
|
|
|
|
} else if (!checked) {
|
|
|
|
|
const idx = roles.indexOf('AWARD_MASTER')
|
|
|
|
|
if (idx >= 0) roles.splice(idx, 1)
|
|
|
|
|
}
|
|
|
|
|
onOverridePolicyChange({ ...overridePolicy, allowedRoles: roles })
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
<Label className="text-sm">Award Masters</Label>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)
|
|
|
|
|
}
|