- 기계 보기 부품(A00_Common/line_stack_machine_sheet) — 836-1 제어 구역 그대로(기계 표 꼴 훅 sheet · 잠금 lock) + 기본 정보 · 손료 근거 접이 카드 + 비슷한 기계에서 시작 · 값 바꾸면 브라우저 계산기로 즉시 - 서버 POST /api/m02/machine-sheet(M02_MyMachine_Sheet) — 식 고름 + 식 문서 + 처음 줄 값 + 연결 기록 · 쓰기 없음 · 파이썬 bind ↔ TS bindDoc 짝 - 덮음 ↔ 기계 줄(common_util_machine_sheet) — 고침 없음 = 줄 바이트 같음 · 내 기계 바로 · 직접 자리 맞춤 - 내 기계 = M01 에 없는 기계만 — 화면 만들기 · 고치기에서 M01 과 같은 이름 · 규격 409(불러오기 자동 생성은 그대로) · [새 기계] · 고침 열림 - 옛 기계 양식 부품(ui_template_machine_form) 지움 — 쓰는 곳 0(부품 모음 견본 줄도 걷음) - 낡은 시험 test_821_3 test_stack_line_copy — 836-2 자동 따라옴(내 기계 고치면 SK 금액 따라감 · 갱신 할 것 없음)에 맞춤 - 지킴 시험 test_840_2 — M01 기계 655 대 박은 식 셈 = 요소 셈 · 줄 그대로 · 굴착기 덮음 · 내 기계 꼴 · M01 같은 이름 막음 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
135 lines
4.8 KiB
TypeScript
135 lines
4.8 KiB
TypeScript
/* =============================================================================
|
|
* M02_MyMachine_View.ts
|
|
* M02 「내 기계」 화면(PLAN 821-3 · 요구서 796 3 장) — 좌측 「개인관리」 › [기계] 단추로 가운데에 뜸(개인 층).
|
|
* 목록 = 이름 · 규격 · 방식 · 시간당 경비(서버 셈) · 쓰는 곳 수 · 넣은 날 · 고친 날 · 찾기
|
|
* 줄 누름 = 기계 보기 한 장(`M02_MyMachine_Form` · 840-2 M01 기계 상세와 같은 꼴) · 고침 · [저장] · 쓰는 곳 · [지우기]
|
|
* [새 기계] = M01 에 없는 기계 등록(비슷한 기계에서 시작 · M01 과 같은 이름 · 규격 = 막음) · 불러오기 자동 생성은 그대로
|
|
* ========================================================================== */
|
|
|
|
import { createDataTable } from "@ui/ui_template_data_table";
|
|
import { createButton } from "@ui/ui_template_button";
|
|
import { createInputField, el, showConfirmDialog, showToast } from "@ui/ui_template_elements";
|
|
import { createText } from "@ui/ui_template_text";
|
|
import { listMyMachines, type MyMachineRow } from "./M02_MyMachine_Api";
|
|
import { mountMyMachineForm } from "./M02_MyMachine_Form";
|
|
import { mt, type MtKey } from "./M02_MyMachine_Text";
|
|
import "./M02_MyMachine.css";
|
|
|
|
export interface MyMachineViewOptions {
|
|
owner: string | null;
|
|
/** 저장 안 한 고침 표시(페이지 떠남 물음) */
|
|
setDirty: (dirty: boolean) => void;
|
|
}
|
|
|
|
const day = (t: string | undefined): string => (t ?? "").slice(0, 10);
|
|
|
|
export function buildMyMachineView(opt: MyMachineViewOptions): HTMLElement {
|
|
const host = el("div", { className: "m02-mymach-view" });
|
|
let rows: MyMachineRow[] = [];
|
|
let shown: MyMachineRow[] = [];
|
|
let dirty = (): boolean => false;
|
|
const mark = (d: boolean): void => opt.setDirty(d);
|
|
|
|
const search = createInputField({ type: "search", placeholder: mt("Find") });
|
|
const hint = createText("");
|
|
const fail = el("div");
|
|
const table = createDataTable({
|
|
caption: mt("Crumb_Machine"),
|
|
columns: [
|
|
{ key: "이름", label: mt("Name"), width: 180 },
|
|
{ key: "규격", label: mt("Spec"), width: 120 },
|
|
{ key: "방식", label: mt("How"), width: 100 },
|
|
{ key: "시간당", label: mt("Hourly"), kind: "number", decimals: 0, width: 110 },
|
|
{ key: "쓰는곳", label: mt("Uses_Count"), kind: "number", decimals: 0, width: 70 },
|
|
{ key: "넣은날", label: mt("Made"), width: 96 },
|
|
{ key: "고친날", label: mt("Edited"), width: 96 },
|
|
],
|
|
rows: [],
|
|
empty: mt("None"),
|
|
onRowClick: (i) => shown[i] && void openForm(shown[i].키),
|
|
});
|
|
const draw = (): void => {
|
|
const words = search.input.value.trim().toLowerCase().split(/\s+/).filter(Boolean);
|
|
shown = rows.filter((r) => {
|
|
const text = `${r.이름} ${r.규격} ${r.키}`.toLowerCase();
|
|
return words.every((w) => text.includes(w));
|
|
});
|
|
hint.textContent = rows.length ? `${shown.length} / ${rows.length}` : "";
|
|
table.setRows(
|
|
shown.map((r) => ({
|
|
이름: r.이름,
|
|
규격: r.규격,
|
|
방식: mt(r.방식 as MtKey),
|
|
시간당: r.시간당.ok ? (r.시간당.계 ?? null) : null,
|
|
쓰는곳: r.쓰는곳수,
|
|
넣은날: day(r.넣은날),
|
|
고친날: day(r.고친날),
|
|
})),
|
|
);
|
|
};
|
|
search.input.addEventListener("input", draw);
|
|
|
|
const list = el("div", {
|
|
className: "m02-mymach-view__list",
|
|
children: [
|
|
el("div", {
|
|
className: "ui-toolbar",
|
|
children: [
|
|
search.root,
|
|
hint,
|
|
createButton({
|
|
label: mt("New"),
|
|
variant: "filled",
|
|
size: 28,
|
|
iconStart: "plus",
|
|
onClick: () => void openForm(),
|
|
}),
|
|
],
|
|
}),
|
|
createText(mt("View_Note"), "muted"),
|
|
fail,
|
|
table.root,
|
|
],
|
|
});
|
|
|
|
async function showList(): Promise<void> {
|
|
dirty = () => false;
|
|
mark(false);
|
|
host.replaceChildren(list);
|
|
fail.replaceChildren();
|
|
try {
|
|
rows = await listMyMachines(opt.owner);
|
|
} catch (error) {
|
|
rows = [];
|
|
fail.replaceChildren(
|
|
createText(error instanceof Error ? error.message : mt("Personal_Only"), "error"),
|
|
);
|
|
}
|
|
draw();
|
|
}
|
|
|
|
async function openForm(key?: string): Promise<void> {
|
|
const slot = el("div");
|
|
host.replaceChildren(slot);
|
|
try {
|
|
const h = await mountMyMachineForm(slot, {
|
|
owner: opt.owner,
|
|
key,
|
|
onSaved: (got) => void openForm(got.줄.키),
|
|
onDeleted: () => void showList(),
|
|
onBack: async () => {
|
|
if (!dirty() || (await showConfirmDialog(mt("Leave_Ask")))) await showList();
|
|
},
|
|
onDirty: mark,
|
|
});
|
|
dirty = h.dirty;
|
|
} catch (error) {
|
|
showToast(error instanceof Error ? error.message : mt("Failed"), "error");
|
|
await showList();
|
|
}
|
|
}
|
|
|
|
void showList();
|
|
return host;
|
|
}
|