type PerformanceSurveyProgressQuestion = { id: string } type PerformanceSurveyProgressWorkItem = { id: string scores?: Array<{ assignmentId?: string | null totalScore?: unknown scores?: Record | null }> | null } export function calculatePerformanceSurveyResponseProgress(input: { questions: PerformanceSurveyProgressQuestion[] draftScores?: Record | null submittedScores?: Record | null submitted: boolean }) { const scoreSource = input.submitted ? input.submittedScores || {} : input.draftScores || {} const values = input.questions.map((question) => { const rawValue = scoreSource[question.id] if (rawValue === null || rawValue === undefined || rawValue === '') return null const numericValue = Number(rawValue) return Number.isFinite(numericValue) ? numericValue : null }) const ratedValues = values.filter((value): value is number => value !== null) const totalCount = input.questions.length const ratedCount = ratedValues.length return { ratedCount, totalCount, totalScore: ratedValues.reduce((sum, value) => sum + value, 0), percent: totalCount > 0 ? Math.min(100, (ratedCount / totalCount) * 100) : 0, } } export function calculatePerformanceSurveyWorkItemProgress(input: { workItems: PerformanceSurveyProgressWorkItem[] questions: PerformanceSurveyProgressQuestion[] scoreDrafts?: Record | null } | undefined> | null assignmentId?: string | null }) { const assignmentId = String(input.assignmentId || '') const persistedScores = input.workItems .map((item) => ({ workItemId: item.id, score: assignmentId ? item.scores?.find((candidate) => candidate.assignmentId === assignmentId) : undefined, })) .filter((item) => Boolean(item.score)) const persistedWorkItemIds = persistedScores.map((item) => item.workItemId) const persistedIdSet = new Set(persistedWorkItemIds) const draftCompletedWorkItemIds = input.workItems .filter((item) => { if (persistedIdSet.has(item.id) || input.questions.length === 0) return false return input.questions.every((question) => { const rawValue = input.scoreDrafts?.[item.id]?.scores?.[question.id] if (rawValue === null || rawValue === undefined || rawValue === '') return false return Number.isFinite(Number(rawValue)) }) }) .map((item) => item.id) const persistedTotalScore = persistedScores.reduce((sum, item) => { const totalScore = Number(item.score?.totalScore) if (Number.isFinite(totalScore)) return sum + totalScore return sum + input.questions.reduce((scoreSum, question) => { const value = Number(item.score?.scores?.[question.id]) return Number.isFinite(value) ? scoreSum + value : scoreSum }, 0) }, 0) const draftTotalScore = input.workItems.reduce((sum, item) => { if (persistedIdSet.has(item.id)) return sum return sum + input.questions.reduce((scoreSum, question) => { const rawValue = input.scoreDrafts?.[item.id]?.scores?.[question.id] if (rawValue === null || rawValue === undefined || rawValue === '') return scoreSum const value = Number(rawValue) return Number.isFinite(value) ? scoreSum + value : scoreSum }, 0) }, 0) return { completedCount: persistedWorkItemIds.length + draftCompletedWorkItemIds.length, persistedCount: persistedWorkItemIds.length, totalScore: persistedTotalScore + draftTotalScore, persistedWorkItemIds, draftCompletedWorkItemIds, unscoredWorkItemIds: input.workItems .filter((item) => !persistedIdSet.has(item.id)) .map((item) => item.id), } }