/* 연간 관리 — 구독 서비스 목록 (PDF 6-2) * 시설관리의 WorkerRegistry(①~⑩ 슬라이드 패널)와 동형이되, * 서비스는 상거래 메타(관리구역·계약기간·월정액)를 갖는 카탈로그다. * DetailScreen이 axis.kind==='service' 일 때 이 패널을 등록부로 띄운다. */ const SVC_NUM = ['①', '②', '③', '④', '⑤', '⑥', '⑦', '⑧', '⑨', '⑩']; function ServiceCatalog({ schoolName, workers, activeId, isSafe, onPick, onAdd, onRemove, onClose }) { const services = workers; // DetailScreen이 항목 배열을 workers prop으로 전달(공용 시그니처) const rows = []; for (let i = 0; i < 10; i++) rows.push(services[i] || null); const total = services.reduce((n, s) => n + (Number(s.fee) || 0), 0); return (
); } window.ServiceCatalog = ServiceCatalog; /* ===================== 연간 관리 — 학교별 구독 현황 + 신규 요청 (단기대체와 동일 패턴) ===================== */ const SUBSCR_REQ_KEY = (s) => "ss_subscr_req_" + s; function subscrReqGet(school) { try { const v = JSON.parse(localStorage.getItem(SUBSCR_REQ_KEY(school))); if (Array.isArray(v)) return v; } catch (e) {} return []; } function subscrReqSet(school, list) { try { localStorage.setItem(SUBSCR_REQ_KEY(school), JSON.stringify(list)); } catch (e) {} } const SUBSCR_TYPES = ["옥외시설물 관리", "청소 용역", "방역·소독", "전지·예초", "시설 점검", "기타"]; const SUBSCR_SL = { draft: "작성 중", submitted: "제출됨", received: "접수", accepted: "서비스 개시" }; function subscrBlank() { return { id: "sr_" + Date.now().toString(36), status: "draft", svcName: "", svcEtc: "", zone: "", periodStart: "", periodEnd: "", manager: "", contact: "", detail: "", note: "", createdAt: Date.now() }; } /* 신규 요청서 — 우측 서랍 (학교 작성 / 세이프스쿨 읽기 + 서비스 개시) */ function SubsRequestForm({ initial, readOnly, onSave, onSubmit, onClose, onAccept }) { const [r, setR] = React.useState(() => ({ ...subscrBlank(), ...initial })); const set = (k, v) => setR((s) => ({ ...s, [k]: v })); const ro = !!readOnly; useLucideSS(); const txt = (label, key, ph) => ( ); const canSubmit = (r.svcName && r.svcName.trim()) || (r.svcEtc && r.svcEtc.trim()); return (