Fix build errors: add missing Prisma models/fields and resolve TypeScript type errors
Schema: Add 11 new models (RoundTemplate, MentorNote, MentorMilestone, MentorMilestoneCompletion, EvaluationDiscussion, DiscussionComment, Message, MessageRecipient, MessageTemplate, Webhook, WebhookDelivery, DigestLog) and missing fields on existing models (Project.isDraft, ProjectFile.version, LiveVotingSession.allowAudienceVotes, User.digestFrequency, AuditLog.sessionId, MentorAssignment.completionStatus, etc). Add AUDIT_CONFIG/LOCALIZATION/DIGEST/ANALYTICS enum values. Code: Fix implicit any types, route type casts, enum casts, null safety, composite key handling, and relation field names across 11 source files. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -674,7 +674,7 @@ export default function ApplySettingsPage() {
|
||||
toast.success('Loaded preset: MOPC Classic')
|
||||
return
|
||||
}
|
||||
const template = templates?.find((t) => t.id === value)
|
||||
const template = templates?.find((t: { id: string; name: string; config: unknown }) => t.id === value)
|
||||
if (template) {
|
||||
setConfig(template.config as WizardConfig)
|
||||
setIsDirty(true)
|
||||
@@ -692,7 +692,7 @@ export default function ApplySettingsPage() {
|
||||
</SelectItem>
|
||||
{templates && templates.length > 0 && (
|
||||
<>
|
||||
{templates.map((t) => (
|
||||
{templates.map((t: { id: string; name: string }) => (
|
||||
<SelectItem key={t.id} value={t.id}>
|
||||
{t.name}
|
||||
</SelectItem>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Suspense } from 'react'
|
||||
import Link from 'next/link'
|
||||
import type { Route } from 'next'
|
||||
import { prisma } from '@/lib/prisma'
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
@@ -148,7 +149,7 @@ async function ProgramsContent() {
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem asChild>
|
||||
<Link href={`/admin/programs/${program.id}/apply-settings`}>
|
||||
<Link href={`/admin/programs/${program.id}/apply-settings` as Route}>
|
||||
<Wand2 className="mr-2 h-4 w-4" />
|
||||
Apply Settings
|
||||
</Link>
|
||||
@@ -202,7 +203,7 @@ async function ProgramsContent() {
|
||||
</Link>
|
||||
</Button>
|
||||
<Button variant="outline" size="sm" className="flex-1" asChild>
|
||||
<Link href={`/admin/programs/${program.id}/apply-settings`}>
|
||||
<Link href={`/admin/programs/${program.id}/apply-settings` as Route}>
|
||||
<Wand2 className="mr-2 h-4 w-4" />
|
||||
Apply
|
||||
</Link>
|
||||
|
||||
@@ -197,7 +197,7 @@ export default function RoundTemplatesPage() {
|
||||
{/* Templates Grid */}
|
||||
{templates && templates.length > 0 ? (
|
||||
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
||||
{templates.map((template) => {
|
||||
{templates.map((template: typeof templates[number]) => {
|
||||
const criteria = (template.criteriaJson as Array<unknown>) || []
|
||||
const hasSettings = template.settingsJson && Object.keys(template.settingsJson as object).length > 0
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ function CreateRoundContent() {
|
||||
|
||||
const loadTemplate = (templateId: string) => {
|
||||
if (!templateId || !templates) return
|
||||
const template = templates.find((t) => t.id === templateId)
|
||||
const template = templates.find((t: { id: string; name: string; roundType: string; settingsJson: unknown }) => t.id === templateId)
|
||||
if (!template) return
|
||||
|
||||
// Apply template settings
|
||||
@@ -207,7 +207,7 @@ function CreateRoundContent() {
|
||||
<SelectValue placeholder="Select a template..." />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{templates.map((t) => (
|
||||
{templates.map((t: { id: string; name: string; description?: string | null }) => (
|
||||
<SelectItem key={t.id} value={t.id}>
|
||||
{t.name}
|
||||
{t.description ? ` - ${t.description}` : ''}
|
||||
|
||||
@@ -510,11 +510,11 @@ function MilestonesSection({
|
||||
}
|
||||
|
||||
const completedCount = milestones.filter(
|
||||
(m) => m.myCompletions.length > 0
|
||||
(m: { myCompletions: unknown[] }) => m.myCompletions.length > 0
|
||||
).length
|
||||
const totalRequired = milestones.filter((m) => m.isRequired).length
|
||||
const totalRequired = milestones.filter((m: { isRequired: boolean }) => m.isRequired).length
|
||||
const requiredCompleted = milestones.filter(
|
||||
(m) => m.isRequired && m.myCompletions.length > 0
|
||||
(m: { isRequired: boolean; myCompletions: unknown[] }) => m.isRequired && m.myCompletions.length > 0
|
||||
).length
|
||||
|
||||
const handleToggle = (milestoneId: string, isCompleted: boolean) => {
|
||||
@@ -545,7 +545,7 @@ function MilestonesSection({
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="space-y-3">
|
||||
{milestones.map((milestone) => {
|
||||
{milestones.map((milestone: { id: string; name: string; description: string | null; isRequired: boolean; myCompletions: { completedAt: Date }[] }) => {
|
||||
const isCompleted = milestone.myCompletions.length > 0
|
||||
const isPending = completeMutation.isPending || uncompleteMutation.isPending
|
||||
|
||||
@@ -752,7 +752,7 @@ function NotesSection({ mentorAssignmentId }: { mentorAssignmentId: string }) {
|
||||
</div>
|
||||
) : notes && notes.length > 0 ? (
|
||||
<div className="space-y-3">
|
||||
{notes.map((note) => (
|
||||
{notes.map((note: { id: string; content: string; isVisibleToAdmin: boolean; createdAt: Date }) => (
|
||||
<div
|
||||
key={note.id}
|
||||
className="p-4 rounded-lg border space-y-2"
|
||||
|
||||
@@ -208,9 +208,9 @@ export function MembersContent() {
|
||||
<TableCell>
|
||||
<div>
|
||||
{user.role === 'MENTOR' ? (
|
||||
<p>{user._count.mentorAssignments} mentored</p>
|
||||
<p>{(user as unknown as { _count: { mentorAssignments: number; assignments: number } })._count.mentorAssignments} mentored</p>
|
||||
) : (
|
||||
<p>{user._count.assignments} assigned</p>
|
||||
<p>{(user as unknown as { _count: { mentorAssignments: number; assignments: number } })._count.assignments} assigned</p>
|
||||
)}
|
||||
</div>
|
||||
</TableCell>
|
||||
@@ -276,8 +276,8 @@ export function MembersContent() {
|
||||
<span className="text-muted-foreground">Assignments</span>
|
||||
<span>
|
||||
{user.role === 'MENTOR'
|
||||
? `${user._count.mentorAssignments} mentored`
|
||||
: `${user._count.assignments} assigned`}
|
||||
? `${(user as unknown as { _count: { mentorAssignments: number; assignments: number } })._count.mentorAssignments} mentored`
|
||||
: `${(user as unknown as { _count: { mentorAssignments: number; assignments: number } })._count.assignments} assigned`}
|
||||
</span>
|
||||
</div>
|
||||
{user.expertiseTags && user.expertiseTags.length > 0 && (
|
||||
|
||||
@@ -676,6 +676,7 @@ export const applicationRouter = router({
|
||||
// Create new draft project
|
||||
const project = await ctx.prisma.project.create({
|
||||
data: {
|
||||
programId: round.programId,
|
||||
roundId: round.id,
|
||||
title: input.title || 'Untitled Draft',
|
||||
isDraft: true,
|
||||
@@ -795,8 +796,8 @@ export const applicationRouter = router({
|
||||
title: data.projectName,
|
||||
teamName: data.teamName,
|
||||
description: data.description,
|
||||
competitionCategory: data.competitionCategory,
|
||||
oceanIssue: data.oceanIssue,
|
||||
competitionCategory: data.competitionCategory as CompetitionCategory,
|
||||
oceanIssue: data.oceanIssue as OceanIssue,
|
||||
country: data.country,
|
||||
geographicZone: data.city ? `${data.city}, ${data.country}` : data.country,
|
||||
institution: data.institution,
|
||||
@@ -838,7 +839,7 @@ export const applicationRouter = router({
|
||||
return {
|
||||
success: true,
|
||||
projectId: updated.id,
|
||||
message: `Thank you for applying to ${project.round.program.name}!`,
|
||||
message: `Thank you for applying to ${project.round?.program.name ?? 'the program'}!`,
|
||||
}
|
||||
}),
|
||||
|
||||
|
||||
@@ -863,7 +863,7 @@ export const evaluationRouter = router({
|
||||
const settings = (round.settingsJson as Record<string, unknown>) || {}
|
||||
const anonymizationLevel = (settings.anonymization_level as string) || 'fully_anonymous'
|
||||
|
||||
const anonymizedComments = discussion.comments.map((c, idx) => {
|
||||
const anonymizedComments = discussion.comments.map((c: { id: string; userId: string; user: { name: string | null }; content: string; createdAt: Date }, idx: number) => {
|
||||
let authorLabel: string
|
||||
if (anonymizationLevel === 'named' || c.userId === ctx.user.id) {
|
||||
authorLabel = c.user.name || 'Juror'
|
||||
@@ -871,7 +871,7 @@ export const evaluationRouter = router({
|
||||
const name = c.user.name || ''
|
||||
authorLabel = name
|
||||
.split(' ')
|
||||
.map((n) => n[0])
|
||||
.map((n: string) => n[0])
|
||||
.join('')
|
||||
.toUpperCase() || 'J'
|
||||
} else {
|
||||
|
||||
@@ -405,8 +405,8 @@ export const liveVotingRouter = router({
|
||||
.map((jurySc) => {
|
||||
const project = projects.find((p) => p.id === jurySc.projectId)
|
||||
const audienceSc = audienceMap.get(jurySc.projectId)
|
||||
const juryAvg = jurySc._avg.score || 0
|
||||
const audienceAvg = audienceSc?._avg.score || 0
|
||||
const juryAvg = jurySc._avg?.score || 0
|
||||
const audienceAvg = audienceSc?._avg?.score || 0
|
||||
const weightedTotal = audienceWeight > 0 && audienceSc
|
||||
? juryAvg * juryWeight + audienceAvg * audienceWeight
|
||||
: juryAvg
|
||||
|
||||
@@ -981,9 +981,9 @@ export const mentorRouter = router({
|
||||
})
|
||||
const myAssignmentIds = new Set(myAssignments.map((a) => a.id))
|
||||
|
||||
return milestones.map((milestone) => ({
|
||||
return milestones.map((milestone: typeof milestones[number]) => ({
|
||||
...milestone,
|
||||
myCompletions: milestone.completions.filter((c) =>
|
||||
myCompletions: milestone.completions.filter((c: { mentorAssignmentId: string }) =>
|
||||
myAssignmentIds.has(c.mentorAssignmentId)
|
||||
),
|
||||
}))
|
||||
@@ -1036,7 +1036,7 @@ export const mentorRouter = router({
|
||||
const completedMilestones = await ctx.prisma.mentorMilestoneCompletion.findMany({
|
||||
where: {
|
||||
mentorAssignmentId: input.mentorAssignmentId,
|
||||
milestoneId: { in: requiredMilestones.map((m) => m.id) },
|
||||
milestoneId: { in: requiredMilestones.map((m: { id: string }) => m.id) },
|
||||
},
|
||||
select: { milestoneId: true },
|
||||
})
|
||||
@@ -1057,7 +1057,7 @@ export const mentorRouter = router({
|
||||
userId: ctx.user.id,
|
||||
action: 'COMPLETE_MILESTONE',
|
||||
entityType: 'MentorMilestoneCompletion',
|
||||
entityId: completion.id,
|
||||
entityId: `${completion.milestoneId}_${completion.mentorAssignmentId}`,
|
||||
detailsJson: {
|
||||
milestoneId: input.milestoneId,
|
||||
mentorAssignmentId: input.mentorAssignmentId,
|
||||
@@ -1243,7 +1243,7 @@ export const mentorRouter = router({
|
||||
mentor: { select: { id: true, name: true, email: true } },
|
||||
project: { select: { id: true, title: true } },
|
||||
notes: { select: { id: true } },
|
||||
milestoneCompletions: { select: { id: true } },
|
||||
milestoneCompletions: { select: { milestoneId: true } },
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
@@ -247,7 +247,7 @@ export const messageRouter = router({
|
||||
subject: input.subject,
|
||||
body: input.body,
|
||||
variables: input.variables ?? undefined,
|
||||
createdBy: ctx.user.id,
|
||||
createdById: ctx.user.id,
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user