Files
Aislo/M02_MasterTemplete/M02_Labor_Values_Model.ts
T
eomsangdonandClaude Opus 5.5 43a59a6893 refactor(M02): 일위대가 입력 목록 · 넣을 값 칸 부품 옮김(39-3 C2) — 선택 입력에서 M02_Labor_Inputs.py · M02_Labor_Values(_Model).ts 로 · 옛 자리는 다시 내보냄 · 입력 목록 길 logic-lib/inputs
- 서버: inputs · combo_logics · _num 몸을 M02_Labor_Inputs.py 로(logic_inputs = 로직 키들 입력 + 자동값) · M02_MasterTemplete_Inputs.py 는 그것을 부름
- 길: GET /api/m02/logic-lib/inputs?keys= (로그인 · 일위대가 입력 모달용)
- 화면: 값 카드(createInputsValueCard) · 값 함수 → M02_Labor_Values.ts · M02_Labor_Values_Model.ts · 값 칸 하나(createValueControl)를 떼어 값 카드와 입력 모달이 같이 씀 · CSS 도 옮김
- 옛 자리(M02_MasterTemplete_Inputs.ts · _Inputs_Model.ts)는 새 파일을 다시 내보냄 — B05 고르기 모달 그대로
- 시험: 새 test_m02_labor_values.py · 옮긴 자리 따라 test_item_picker_detail · test_m02_card_shell · helper_m02_inputs 고침

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MgChuAcyBqUkhHMTXomQC7
2026-10-04 17:27:30 +09:00

117 lines
5.2 KiB
TypeScript

/* =============================================================================
* M02_Labor_Values_Model.ts
* 넣을 값 셈(화면 없음 · 39-3) — 옛 선택 입력 `M02_MasterTemplete_Inputs_Model.ts` 의 값 함수를 옮김.
* 일위대가 입력 모달 · 프로젝트 고르기 모달 값 카드가 같은 값 칸 규칙(고르기 = 목록 · 범위 = 숫자 ·
* 빈 값 = 자동값)을 씀. 일위대가 입력값 = `{입력 이름: 값}`(빈 값은 안 둠 = 자동값 · 모르는 이름 버림).
* ========================================================================== */
import type { ItemInput } from "./M02_MasterTemplete_Api_Fetch";
const text = (value: unknown): string =>
value === null || value === undefined
? ""
: typeof value === "object"
? JSON.stringify(value)
: String(value);
/** 고르기 · 범위 표시 — 선택지 목록이면 「a / b / c」 · 범위(배열 둘 · {최소, 최대})면 「최소 ~ 최대」 */
export function valueHint(input: ItemInput): string {
if (Array.isArray(input.고르기) && input.고르기.length) return input.고르기.map(text).join(" / ");
const range = input.범위;
if (Array.isArray(range) && range.length >= 2) return `${text(range[0])} ~ ${text(range[1])}`;
if (range && typeof range === "object") {
const r = range as Record<string, unknown>;
const lo = r.최소 ?? r.min;
const hi = r.최대 ?? r.max;
if (lo !== undefined || hi !== undefined) return `${text(lo)} ~ ${text(hi)}`;
}
return text(range);
}
/** 자동값 글 — 없으면 빈 글 */
export const autoText = (input: ItemInput): string => text(input.자동값);
/** `입력값.json` — `{로직키: {입력이름: {값, 확인}}}` (서버 검사 모양 · 값은 수 · 글 · 빈 값) */
export type ValueDoc = Record<
string,
Record<string, { 값: string | number | null; 확인: boolean }>
>;
const number = (value: unknown): number | undefined =>
typeof value === "number" && Number.isFinite(value) ? value : undefined;
/** 범위 모양 둘(`[최소, 최대]` · `{최소, 최대}`)을 하나로 — 범위가 아니면 null */
export function rangeOf(input: ItemInput): { min?: number; max?: number } | null {
const raw = input.범위;
if (Array.isArray(raw)) return { min: number(raw[0]), max: number(raw[1]) };
if (raw && typeof raw === "object") {
const r = raw as Record<string, unknown>;
return { min: number(r.최소 ?? r.min), max: number(r.최대 ?? r.max) };
}
return null;
}
/** 고르기 목록 → 화면 값 · 이름 짝 — `raw` 는 원래 모양(숫자 목록이면 숫자로 저장) */
export function choicesOf(input: ItemInput): { 값: string; 이름: string; raw: string | number }[] {
return (input.고르기 ?? []).map((one) => {
const object =
typeof one === "object" && one !== null ? (one as Record<string, unknown>) : null;
const raw = (object ? (object.값 ?? object.이름 ?? "") : one) as string | number;
return { 값: String(raw), 이름: object ? String(object.이름 ?? raw) : String(one), raw };
});
}
/** 범위 밖 글인가 — 빈 글 · 범위 없는 입력은 아님 */
export function isOutside(input: ItemInput, raw: string): boolean {
const range = rangeOf(input);
if (!range || raw.trim() === "") return false;
const value = Number(raw);
return (
!Number.isFinite(value) ||
(range.min !== undefined && value < range.min) ||
(range.max !== undefined && value > range.max)
);
}
/** 칸 글 → 저장 값 — 빈 글 = null(자동값) · 범위 = 수 · 고르기 = 원래 모양 · 그 밖 = 글 */
export function storedOf(input: ItemInput, raw: string): string | number | null {
if (raw === "") return null;
if (rangeOf(input)) return Number(raw);
return choicesOf(input).find((c) => c.값 === raw)?.raw ?? raw;
}
/* ── 일위대가 입력값(39-3) — 로직 한 줄의 `{입력 이름: 값}` ───────────────────────────── */
export type LaborValues = Record<string, string | number>;
/** 로직 입력 목록에 있는 이름 · 빈 값 아닌 것만 남김 + 사라진 이름(로직 입력이 바뀌어 버려질 값) */
export function keepKnown(
inputs: ItemInput[],
values: LaborValues | null | undefined,
): { kept: LaborValues; lost: string[] } {
const names = new Set(inputs.map((i) => i.이름));
const kept: LaborValues = {};
const lost: string[] = [];
for (const [name, value] of Object.entries(values ?? {})) {
if (!names.has(name)) lost.push(name);
else if (value !== "" && value !== null && value !== undefined) kept[name] = value;
}
return { kept, lost };
}
/** 줄 아래 작은 글 — 「지역 서울 · 규격 25mm」(넣은 값만 · 입력 차례) + 빈 값 수(자동값도 없는 입력) */
export function valueLine(
inputs: ItemInput[],
values: LaborValues | null | undefined,
): { text: string; empty: number } {
const parts: string[] = [];
let empty = 0;
for (const input of inputs) {
const value = values?.[input.이름];
if (value !== undefined && value !== "") {
parts.push(`${input.이름} ${value}${input.단위 ? input.단위 : ""}`);
} else if (autoText(input) === "") empty += 1;
}
return { text: parts.join(" · "), empty };
}