From 25e06e11e4ef7679302931997dca1d8f7f1543f3 Mon Sep 17 00:00:00 2001 From: Matt Date: Sun, 1 Mar 2026 14:28:26 +0100 Subject: [PATCH] feat: add all missing fields to project update mutation and edit form Adds competitionCategory, oceanIssue, institution, geographicZone, wantsMentorship, and foundedAt to the tRPC update mutation input schema and the admin project edit form UI (with CountrySelect + Switch). Co-Authored-By: Claude Opus 4.6 --- .../(admin)/admin/projects/[id]/edit/page.tsx | 183 ++++++++++++++++++ src/server/routers/project.ts | 14 +- 2 files changed, 196 insertions(+), 1 deletion(-) diff --git a/src/app/(admin)/admin/projects/[id]/edit/page.tsx b/src/app/(admin)/admin/projects/[id]/edit/page.tsx index f35c2c1..c588c91 100644 --- a/src/app/(admin)/admin/projects/[id]/edit/page.tsx +++ b/src/app/(admin)/admin/projects/[id]/edit/page.tsx @@ -19,6 +19,8 @@ import { Input } from '@/components/ui/input' import { Textarea } from '@/components/ui/textarea' import { Badge } from '@/components/ui/badge' import { Checkbox } from '@/components/ui/checkbox' +import { Switch } from '@/components/ui/switch' +import { CountrySelect } from '@/components/ui/country-select' import { Skeleton } from '@/components/ui/skeleton' import { Select, @@ -89,6 +91,13 @@ const updateProjectSchema = z.object({ 'REJECTED', ]), tags: z.array(z.string()), + competitionCategory: z.string().optional(), + oceanIssue: z.string().optional(), + institution: z.string().optional(), + country: z.string().optional(), + geographicZone: z.string().optional(), + wantsMentorship: z.boolean().optional(), + foundedAt: z.string().optional(), }) type UpdateProjectForm = z.infer @@ -157,6 +166,13 @@ function EditProjectContent({ projectId }: { projectId: string }) { description: '', status: 'SUBMITTED', tags: [], + competitionCategory: '', + oceanIssue: '', + institution: '', + country: '', + geographicZone: '', + wantsMentorship: false, + foundedAt: '', }, }) @@ -169,6 +185,13 @@ function EditProjectContent({ projectId }: { projectId: string }) { description: project.description || '', status: (project.status ?? 'SUBMITTED') as UpdateProjectForm['status'], tags: project.tags || [], + competitionCategory: project.competitionCategory || '', + oceanIssue: project.oceanIssue || '', + institution: project.institution || '', + country: project.country || '', + geographicZone: project.geographicZone || '', + wantsMentorship: project.wantsMentorship ?? false, + foundedAt: project.foundedAt ? new Date(project.foundedAt).toISOString().split('T')[0] : '', }) } }, [project, form]) @@ -229,6 +252,13 @@ function EditProjectContent({ projectId }: { projectId: string }) { description: data.description || null, status: data.status, tags: data.tags, + competitionCategory: (data.competitionCategory || null) as 'STARTUP' | 'BUSINESS_CONCEPT' | null, + oceanIssue: (data.oceanIssue || null) as 'POLLUTION_REDUCTION' | 'CLIMATE_MITIGATION' | 'TECHNOLOGY_INNOVATION' | 'SUSTAINABLE_SHIPPING' | 'BLUE_CARBON' | 'HABITAT_RESTORATION' | 'COMMUNITY_CAPACITY' | 'SUSTAINABLE_FISHING' | 'CONSUMER_AWARENESS' | 'OCEAN_ACIDIFICATION' | 'OTHER' | null, + institution: data.institution || null, + country: data.country || null, + geographicZone: data.geographicZone || null, + wantsMentorship: data.wantsMentorship, + foundedAt: data.foundedAt ? new Date(data.foundedAt).toISOString() : null, }) } @@ -438,6 +468,159 @@ function EditProjectContent({ projectId }: { projectId: string }) { + {/* Project Details */} + + + Project Details + + Additional categorization and metadata for this project + + + +
+ ( + + Competition Category + + + + )} + /> + + ( + + Ocean Issue + + + + )} + /> + + ( + + Institution + + + + + + )} + /> + + ( + + Country + + + + + + )} + /> + + ( + + Geographic Zone + + + + + + )} + /> + + ( + + Founded Date + + + + + + )} + /> +
+ + ( + +
+ Wants Mentorship + + Whether this project team is interested in mentorship + +
+ + + +
+ )} + /> +
+
+ {/* Tags */} diff --git a/src/server/routers/project.ts b/src/server/routers/project.ts index 43e4494..47d0081 100644 --- a/src/server/routers/project.ts +++ b/src/server/routers/project.ts @@ -673,6 +673,17 @@ export const projectRouter = router({ teamName: z.string().optional().nullable(), description: z.string().optional().nullable(), country: z.string().optional().nullable(), // ISO-2 code or country name (will be normalized) + competitionCategory: z.enum(['STARTUP', 'BUSINESS_CONCEPT']).optional().nullable(), + oceanIssue: z.enum([ + 'POLLUTION_REDUCTION', 'CLIMATE_MITIGATION', 'TECHNOLOGY_INNOVATION', + 'SUSTAINABLE_SHIPPING', 'BLUE_CARBON', 'HABITAT_RESTORATION', + 'COMMUNITY_CAPACITY', 'SUSTAINABLE_FISHING', 'CONSUMER_AWARENESS', + 'OCEAN_ACIDIFICATION', 'OTHER', + ]).optional().nullable(), + institution: z.string().optional().nullable(), + geographicZone: z.string().optional().nullable(), + wantsMentorship: z.boolean().optional(), + foundedAt: z.string().datetime().optional().nullable(), status: z .enum([ 'SUBMITTED', @@ -688,7 +699,7 @@ export const projectRouter = router({ }) ) .mutation(async ({ ctx, input }) => { - const { id, metadataJson, status, country, ...data } = input + const { id, metadataJson, status, country, foundedAt, ...data } = input // Normalize country to ISO-2 code if provided const normalizedCountry = country !== undefined @@ -717,6 +728,7 @@ export const projectRouter = router({ ...data, ...(status && { status }), ...(normalizedCountry !== undefined && { country: normalizedCountry }), + ...(foundedAt !== undefined && { foundedAt: foundedAt ? new Date(foundedAt) : null }), metadataJson: metadataJson as Prisma.InputJsonValue ?? undefined, }, })