import { useMemo } from 'react' import type { Code } from '../api/client' import { useCodes } from './useCodes' /** 원문자 ①(U+2460)부터 ⑳까지. 그 밖은 번호를 그대로 쓴다. */ const CIRCLED_BASE = 0x2460 const CIRCLED_MAX = 20 /** * 진행단계를 다루는 헬퍼. 단계 개수·라벨·구간·진행상태·KPI 임계값이 전부 코드표에서 온다. * * 예전에는 stages.ts에 12개 라벨 배열이 박혀 있어서 단계를 하나 늘리려면 백엔드 4곳, * 프론트 8곳을 같이 고쳐야 했다. 이제 코드표 한 줄이면 된다. */ export interface Stages { /** 등록된 단계 수. 지금은 13. */ count: number /** [1, 2, ... count] */ numbers: number[] /** 화면 표시명. 모르는 번호면 번호를 문자열로 돌려준다. */ label(stage: number): string /** ①②③… 20을 넘으면 번호 문자열. */ symbol(stage: number): string /** 단계가 속한 업무구간 key. 미지정(null)이면 null. */ groupKey(stage: number | null): string | null /** 업무구간 목록(정렬순). 칸반 색과 필터가 쓴다. */ groups: Code[] /** 업무구간의 tone. 모르면 null. */ groupTone(key: string | null): string | null /** 진행상태 - 'applied' | 'active' | 'done'. 단계 미지정이면 null. */ progressOf(stage: number | null): string | null /** 이 KPI에 집계하기 시작하는 단계 번호. 지정이 없으면 null. */ kpiThreshold(kpi: string): number | null /** 해당 단계가 이 KPI 집계 대상인지(임계값 이상인지). */ countsToward(kpi: string, stage: number | null): boolean } export function useStages(): Stages { const stageCodes = useCodes('STAGE') const groups = useCodes('STAGE_GROUP') return useMemo(() => { const byNumber = new Map() for (const code of stageCodes) { byNumber.set(Number(code.code), code) } const numbers = stageCodes.map((code) => Number(code.code)).sort((a, b) => a - b) function attrOf(stage: number | null, key: string): string | null { if (stage === null) { return null } const value = byNumber.get(stage)?.attrs[key] return value == null ? null : String(value) } function kpiThreshold(kpi: string): number | null { for (const [number, code] of byNumber) { if (code.attrs.kpiFrom === kpi) { return number } } return null } return { count: numbers.length, numbers, label: (stage) => byNumber.get(stage)?.label ?? String(stage), symbol: (stage) => stage >= 1 && stage <= CIRCLED_MAX ? String.fromCharCode(CIRCLED_BASE + stage - 1) : String(stage), groupKey: (stage) => attrOf(stage, 'groupKey'), groups, groupTone: (key) => { const found = groups.find((group) => group.code === key) const tone = found?.attrs.tone return tone == null ? null : String(tone) }, progressOf: (stage) => attrOf(stage, 'progress'), kpiThreshold, countsToward: (kpi, stage) => { const from = kpiThreshold(kpi) return from !== null && stage !== null && stage >= from }, } }, [stageCodes, groups]) }