diff --git a/prisma/migrations/20260507151706_drop_award_master_role/migration.sql b/prisma/migrations/20260507151706_drop_award_master_role/migration.sql new file mode 100644 index 0000000..95d6e9e --- /dev/null +++ b/prisma/migrations/20260507151706_drop_award_master_role/migration.sql @@ -0,0 +1,29 @@ +-- Drops AWARD_MASTER from the UserRole enum. +-- +-- Any row still holding AWARD_MASTER is demoted to JURY_MEMBER (singular role) +-- or filtered out of the roles[] array (multi-role) before the enum swap, so +-- the type alteration is safe even if the prod migration was missed. + +UPDATE "User" SET role = 'JURY_MEMBER' WHERE role = 'AWARD_MASTER'; +UPDATE "User" SET roles = array_remove(roles, 'AWARD_MASTER') WHERE 'AWARD_MASTER' = ANY(roles); + +CREATE TYPE "UserRole_new" AS ENUM ( + 'SUPER_ADMIN', + 'PROGRAM_ADMIN', + 'JURY_MEMBER', + 'MENTOR', + 'OBSERVER', + 'APPLICANT', + 'AUDIENCE' +); + +ALTER TABLE "User" ALTER COLUMN role DROP DEFAULT; +ALTER TABLE "User" + ALTER COLUMN role TYPE "UserRole_new" USING role::text::"UserRole_new"; +ALTER TABLE "User" ALTER COLUMN role SET DEFAULT 'APPLICANT'; + +ALTER TABLE "User" + ALTER COLUMN roles TYPE "UserRole_new"[] USING roles::text[]::"UserRole_new"[]; + +DROP TYPE "UserRole"; +ALTER TYPE "UserRole_new" RENAME TO "UserRole"; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index f8c4eca..c30f01b 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -29,7 +29,6 @@ enum UserRole { MENTOR OBSERVER APPLICANT - AWARD_MASTER AUDIENCE } @@ -1600,7 +1599,7 @@ model SpecialAward { evaluationRoundId String? juryGroupId String? eligibilityMode AwardEligibilityMode @default(STAY_IN_MAIN) - decisionMode String? // "JURY_VOTE" | "AWARD_MASTER_DECISION" | "ADMIN_DECISION" + decisionMode String? // "JURY_VOTE" | "ADMIN_DECISION" shortlistSize Int @default(10) // Eligibility job tracking diff --git a/prisma/seed.ts b/prisma/seed.ts index 072ddd9..7ac1daa 100644 --- a/prisma/seed.ts +++ b/prisma/seed.ts @@ -317,7 +317,6 @@ async function main() { { email: 'matt@monaco-opc.com', name: 'Matt', role: UserRole.SUPER_ADMIN, password: '195260Mp!' }, { email: 'matt@letsbe.solutions', name: 'Matt (Applicant)', role: UserRole.APPLICANT, password: '195260Mp!' }, { email: 'admin@monaco-opc.com', name: 'Admin', role: UserRole.PROGRAM_ADMIN, password: 'Admin123!' }, - { email: 'awards@monaco-opc.com', name: 'Award Director', role: UserRole.AWARD_MASTER, password: 'Awards123!' }, ] const staffUsers: Record = {} diff --git a/src/app/(admin)/admin/awards/[id]/edit/page.tsx b/src/app/(admin)/admin/awards/[id]/edit/page.tsx index 8594a97..fe9f855 100644 --- a/src/app/(admin)/admin/awards/[id]/edit/page.tsx +++ b/src/app/(admin)/admin/awards/[id]/edit/page.tsx @@ -58,7 +58,7 @@ export default function EditAwardPage({ const [votingEndAt, setVotingEndAt] = useState('') const [evaluationRoundId, setEvaluationRoundId] = useState('') const [eligibilityMode, setEligibilityMode] = useState<'STAY_IN_MAIN' | 'SEPARATE_POOL'>('STAY_IN_MAIN') - const [decisionMode, setDecisionMode] = useState<'JURY_VOTE' | 'AWARD_MASTER_DECISION' | 'ADMIN_DECISION'>('JURY_VOTE') + const [decisionMode, setDecisionMode] = useState<'JURY_VOTE' | 'ADMIN_DECISION'>('JURY_VOTE') // Helper to format date for datetime-local input const formatDateForInput = (date: Date | string | null | undefined): string => { @@ -236,7 +236,6 @@ export default function EditAwardPage({ Jury Vote — tallied from all jurors - Award Master — sponsor picks winner Admin Decision — admin selects winner diff --git a/src/app/(admin)/admin/awards/[id]/page.tsx b/src/app/(admin)/admin/awards/[id]/page.tsx index 6dd9f2d..e01e13b 100644 --- a/src/app/(admin)/admin/awards/[id]/page.tsx +++ b/src/app/(admin)/admin/awards/[id]/page.tsx @@ -408,7 +408,7 @@ export default function AwardDetailPage({ // Deferred queries - only load when needed const { data: allUsers } = trpc.user.list.useQuery( - { page: 1, perPage: 100, roles: ['JURY_MEMBER', 'AWARD_MASTER'] }, + { page: 1, perPage: 100, roles: ['JURY_MEMBER'] }, { enabled: activeTab === 'jurors' } ) const { data: juryGroups } = trpc.juryGroup.list.useQuery( @@ -1518,7 +1518,6 @@ export default function AwardDetailPage({ onSubmit={async (rows) => { await bulkInvite.mutateAsync({ awardId, - role: 'AWARD_MASTER', invitees: rows.map((r) => ({ name: r.name || undefined, email: r.email })), }) }} diff --git a/src/app/(admin)/admin/juries/page.tsx b/src/app/(admin)/admin/juries/page.tsx index 5dd787f..0f11144 100644 --- a/src/app/(admin)/admin/juries/page.tsx +++ b/src/app/(admin)/admin/juries/page.tsx @@ -35,7 +35,7 @@ import { import { Textarea } from '@/components/ui/textarea' import { toast } from 'sonner' import { cn, formatEnumLabel } from '@/lib/utils' -import { Plus, Scale, Users, Loader2, ArrowRight, CircleDot } from 'lucide-react' +import { Plus, Scale, Users, Loader2, ArrowRight, CircleDot, Trophy } from 'lucide-react' const capModeLabels = { HARD: 'Hard Cap', @@ -301,10 +301,10 @@ function CompetitionJuriesSection({ competition }: CompetitionJuriesSectionProps - {/* Round assignments */} - {(group as any).rounds?.length > 0 && ( + {/* Round + Special-award assignments */} + {((group as any).rounds?.length > 0 || (group as any).awards?.length > 0) && (
- {(group as any).rounds.map((r: any) => ( + {(group as any).rounds?.map((r: any) => ( ))} + {(group as any).awards?.map((a: any) => ( + + + {a.name} + + ))}
)} diff --git a/src/app/(admin)/admin/learning/[id]/page.tsx b/src/app/(admin)/admin/learning/[id]/page.tsx index 7200266..8261954 100644 --- a/src/app/(admin)/admin/learning/[id]/page.tsx +++ b/src/app/(admin)/admin/learning/[id]/page.tsx @@ -75,7 +75,6 @@ const ROLE_OPTIONS = [ { value: 'MENTOR', label: 'Mentors' }, { value: 'OBSERVER', label: 'Observers' }, { value: 'APPLICANT', label: 'Applicants' }, - { value: 'AWARD_MASTER', label: 'Award Masters' }, ] type AccessRule = diff --git a/src/app/(admin)/admin/learning/new/page.tsx b/src/app/(admin)/admin/learning/new/page.tsx index b3a17ea..526fba7 100644 --- a/src/app/(admin)/admin/learning/new/page.tsx +++ b/src/app/(admin)/admin/learning/new/page.tsx @@ -60,7 +60,6 @@ const ROLE_OPTIONS = [ { value: 'MENTOR', label: 'Mentors' }, { value: 'OBSERVER', label: 'Observers' }, { value: 'APPLICANT', label: 'Applicants' }, - { value: 'AWARD_MASTER', label: 'Award Masters' }, ] type AccessRule = diff --git a/src/app/(admin)/admin/members/[id]/page.tsx b/src/app/(admin)/admin/members/[id]/page.tsx index 66467ef..ab38f05 100644 --- a/src/app/(admin)/admin/members/[id]/page.tsx +++ b/src/app/(admin)/admin/members/[id]/page.tsx @@ -97,7 +97,6 @@ const roleColors: Record = { PROGRAM_ADMIN: 'default', SUPER_ADMIN: 'default', APPLICANT: 'secondary', - AWARD_MASTER: 'outline', AUDIENCE: 'outline', } @@ -154,7 +153,7 @@ export default function MemberDetailPage() { const handleSave = async () => { try { - const allRoles = [role, ...additionalRoles.filter((r) => r !== role)] as Array<'SUPER_ADMIN' | 'PROGRAM_ADMIN' | 'AWARD_MASTER' | 'JURY_MEMBER' | 'MENTOR' | 'OBSERVER' | 'APPLICANT' | 'AUDIENCE'> + const allRoles = [role, ...additionalRoles.filter((r) => r !== role)] as Array<'SUPER_ADMIN' | 'PROGRAM_ADMIN' | 'JURY_MEMBER' | 'MENTOR' | 'OBSERVER' | 'APPLICANT' | 'AUDIENCE'> await updateUser.mutateAsync({ id: userId, email: email || undefined, @@ -622,7 +621,6 @@ export default function MemberDetailPage() { Mentor Observer Applicant - Award Master Audience @@ -633,7 +631,7 @@ export default function MemberDetailPage() { Grant additional dashboard access beyond the primary role

- {(['JURY_MEMBER', 'OBSERVER', 'MENTOR', 'AWARD_MASTER'] as const) + {(['JURY_MEMBER', 'OBSERVER', 'MENTOR'] as const) .filter((r) => r !== role) .map((r) => (