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 <noreply@anthropic.com>
This commit is contained in:
2026-03-01 14:28:26 +01:00
parent f200eda692
commit 25e06e11e4
2 changed files with 196 additions and 1 deletions

View File

@@ -19,6 +19,8 @@ import { Input } from '@/components/ui/input'
import { Textarea } from '@/components/ui/textarea' import { Textarea } from '@/components/ui/textarea'
import { Badge } from '@/components/ui/badge' import { Badge } from '@/components/ui/badge'
import { Checkbox } from '@/components/ui/checkbox' 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 { Skeleton } from '@/components/ui/skeleton'
import { import {
Select, Select,
@@ -89,6 +91,13 @@ const updateProjectSchema = z.object({
'REJECTED', 'REJECTED',
]), ]),
tags: z.array(z.string()), 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<typeof updateProjectSchema> type UpdateProjectForm = z.infer<typeof updateProjectSchema>
@@ -157,6 +166,13 @@ function EditProjectContent({ projectId }: { projectId: string }) {
description: '', description: '',
status: 'SUBMITTED', status: 'SUBMITTED',
tags: [], tags: [],
competitionCategory: '',
oceanIssue: '',
institution: '',
country: '',
geographicZone: '',
wantsMentorship: false,
foundedAt: '',
}, },
}) })
@@ -169,6 +185,13 @@ function EditProjectContent({ projectId }: { projectId: string }) {
description: project.description || '', description: project.description || '',
status: (project.status ?? 'SUBMITTED') as UpdateProjectForm['status'], status: (project.status ?? 'SUBMITTED') as UpdateProjectForm['status'],
tags: project.tags || [], 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]) }, [project, form])
@@ -229,6 +252,13 @@ function EditProjectContent({ projectId }: { projectId: string }) {
description: data.description || null, description: data.description || null,
status: data.status, status: data.status,
tags: data.tags, 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 }) {
</CardContent> </CardContent>
</Card> </Card>
{/* Project Details */}
<Card>
<CardHeader>
<CardTitle className="text-lg">Project Details</CardTitle>
<CardDescription>
Additional categorization and metadata for this project
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
<FormField
control={form.control}
name="competitionCategory"
render={({ field }) => (
<FormItem>
<FormLabel>Competition Category</FormLabel>
<Select
onValueChange={field.onChange}
value={field.value || ''}
>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="Select category..." />
</SelectTrigger>
</FormControl>
<SelectContent>
<SelectItem value="STARTUP">Startup</SelectItem>
<SelectItem value="BUSINESS_CONCEPT">Business Concept</SelectItem>
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="oceanIssue"
render={({ field }) => (
<FormItem>
<FormLabel>Ocean Issue</FormLabel>
<Select
onValueChange={field.onChange}
value={field.value || ''}
>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="Select ocean issue..." />
</SelectTrigger>
</FormControl>
<SelectContent>
<SelectItem value="POLLUTION_REDUCTION">Pollution Reduction</SelectItem>
<SelectItem value="CLIMATE_MITIGATION">Climate Mitigation</SelectItem>
<SelectItem value="TECHNOLOGY_INNOVATION">Technology Innovation</SelectItem>
<SelectItem value="SUSTAINABLE_SHIPPING">Sustainable Shipping</SelectItem>
<SelectItem value="BLUE_CARBON">Blue Carbon</SelectItem>
<SelectItem value="HABITAT_RESTORATION">Habitat Restoration</SelectItem>
<SelectItem value="COMMUNITY_CAPACITY">Community Capacity</SelectItem>
<SelectItem value="SUSTAINABLE_FISHING">Sustainable Fishing</SelectItem>
<SelectItem value="CONSUMER_AWARENESS">Consumer Awareness</SelectItem>
<SelectItem value="OCEAN_ACIDIFICATION">Ocean Acidification</SelectItem>
<SelectItem value="OTHER">Other</SelectItem>
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="institution"
render={({ field }) => (
<FormItem>
<FormLabel>Institution</FormLabel>
<FormControl>
<Input placeholder="Institution or organization" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="country"
render={({ field }) => (
<FormItem>
<FormLabel>Country</FormLabel>
<FormControl>
<CountrySelect
value={field.value || ''}
onChange={field.onChange}
placeholder="Select country..."
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="geographicZone"
render={({ field }) => (
<FormItem>
<FormLabel>Geographic Zone</FormLabel>
<FormControl>
<Input placeholder="e.g. Europe, France" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="foundedAt"
render={({ field }) => (
<FormItem>
<FormLabel>Founded Date</FormLabel>
<FormControl>
<Input type="date" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
<FormField
control={form.control}
name="wantsMentorship"
render={({ field }) => (
<FormItem className="flex flex-row items-center justify-between rounded-lg border p-3">
<div className="space-y-0.5">
<FormLabel>Wants Mentorship</FormLabel>
<FormDescription>
Whether this project team is interested in mentorship
</FormDescription>
</div>
<FormControl>
<Switch
checked={field.value ?? false}
onCheckedChange={field.onChange}
/>
</FormControl>
</FormItem>
)}
/>
</CardContent>
</Card>
{/* Tags */} {/* Tags */}
<Card> <Card>
<CardHeader> <CardHeader>

View File

@@ -673,6 +673,17 @@ export const projectRouter = router({
teamName: z.string().optional().nullable(), teamName: z.string().optional().nullable(),
description: z.string().optional().nullable(), description: z.string().optional().nullable(),
country: z.string().optional().nullable(), // ISO-2 code or country name (will be normalized) 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 status: z
.enum([ .enum([
'SUBMITTED', 'SUBMITTED',
@@ -688,7 +699,7 @@ export const projectRouter = router({
}) })
) )
.mutation(async ({ ctx, input }) => { .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 // Normalize country to ISO-2 code if provided
const normalizedCountry = country !== undefined const normalizedCountry = country !== undefined
@@ -717,6 +728,7 @@ export const projectRouter = router({
...data, ...data,
...(status && { status }), ...(status && { status }),
...(normalizedCountry !== undefined && { country: normalizedCountry }), ...(normalizedCountry !== undefined && { country: normalizedCountry }),
...(foundedAt !== undefined && { foundedAt: foundedAt ? new Date(foundedAt) : null }),
metadataJson: metadataJson as Prisma.InputJsonValue ?? undefined, metadataJson: metadataJson as Prisma.InputJsonValue ?? undefined,
}, },
}) })