import type { SalaryRole } from './performance-salary-roles' export function resolveSalarySourceMonth(displayMonth: string, submittedCount: number, environment: string | undefined, enabled: string | undefined) { const isFallback = environment === 'development' && enabled === 'true' && displayMonth === '2026-10' && submittedCount === 0 return { displayMonth, sourceMonth: isFallback ? '2026-09' : displayMonth, isFallback } } export function salaryRoleForPosition(code: string): SalaryRole | null { if (code.startsWith('ai_planning_')) return 'ai-planning' if (code === 'director') return 'ai-planning' if (code === 'ai_technical_designer') return 'ai-technical' if (['camera', 'editing', 'retouching'].includes(code)) return 'photography' if (code === 'brand_designer') return 'spatial-visual' return null } export const roundSalaryScore = (value: number) => Math.round((value + Number.EPSILON) * 100) / 100 export function averageSalaryScores(values: Array): number | null { const scores = values.filter((value): value is number => typeof value === 'number' && Number.isFinite(value)) return scores.length ? scores.reduce((sum, score) => sum + score, 0) / scores.length : null } type Section = { weight: number | null; metrics: Record } // Keep the percentages in the incumbent salary template, without normalizing them. export const SALARY_SCORE_WEIGHTS: Record = { 'ai-planning': [ { weight: .25, metrics: { brandReview: 1, collaboration: 0 } }, { weight: .7, metrics: { marketValidation: .1, onTime: .9, sharing: 0 } }, { weight: .05, metrics: { cooperation: 1 } }, ], photography: [], // The photography template does not yet define assessment weights. 'ai-technical': [], 'spatial-visual': [ { weight: .2, metrics: { aiTools: .16, collaboration: .04 } }, // The source workbook leaves this section's weight blank; do not infer 75%. { weight: null, metrics: { onTime: .9, sharing: 0 } }, { weight: .05, metrics: { cooperation: .06 } }, ], management: [ { weight: .7, metrics: { visualTone: .8, collaboration: .2 } }, { weight: .2, metrics: { marketValidation: .4, onTime: .4, sharing: .2 } }, { weight: .1, metrics: { cooperation: 1 } }, ], } export function calculateSalaryPerformance(role: SalaryRole, metrics: Record) { const sections = SALARY_SCORE_WEIGHTS[role] const weighted: Record = {} let total = 0, complete = sections.length > 0 for (const section of sections) { if (section.weight === null) complete = false let subtotal = 0 for (const [key, share] of Object.entries(section.metrics)) { const value = metrics[key] weighted[key] = typeof value === 'number' && Number.isFinite(value) ? roundSalaryScore(value * share) : null if (share === 0 || section.weight === 0) continue if (weighted[key] === null) complete = false else subtotal += value! * share } if (section.weight !== null) total += subtotal * section.weight } return { weighted, totalScore: complete ? roundSalaryScore(total) : null } } export function salaryMetricForSurvey(code: string, snapshot: unknown): string | null { const definition = snapshot && typeof snapshot === 'object' && !Array.isArray(snapshot) ? snapshot as Record : {} if (definition.surveyType === 'visual_case' || ['visual_brand_tone', 'visual_brand_tone_without_copy'].includes(code)) return 'visualTone' const known: Record = { cross_department: 'collaboration', work_cooperation: 'cooperation', course_share: 'sharing', project_result_validation: 'marketValidation', ai_work_review: 'aiTools', } return known[code] ?? null }