gongxiangp2/lib/performance-survey-response-progress.ts
2026-07-20 13:01:10 +08:00

97 lines
3.6 KiB
TypeScript

type PerformanceSurveyProgressQuestion = {
id: string
}
type PerformanceSurveyProgressWorkItem = {
id: string
scores?: Array<{
assignmentId?: string | null
totalScore?: unknown
scores?: Record<string, unknown> | null
}> | null
}
export function calculatePerformanceSurveyResponseProgress(input: {
questions: PerformanceSurveyProgressQuestion[]
draftScores?: Record<string, unknown> | null
submittedScores?: Record<string, unknown> | 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<string, { scores?: Record<string, unknown> | 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),
}
}