From 39c79f05d02bc6dc5c9e698351d241583564624f Mon Sep 17 00:00:00 2001 From: umsangdon Date: Fri, 25 Sep 2026 17:55:38 +0900 Subject: [PATCH] =?UTF-8?q?refactor(M02):=20=EC=A2=8C=EC=B8=A1=20=ED=8C=A8?= =?UTF-8?q?=EB=84=90=EC=9D=84=20=EC=8B=9C=EC=8A=A4=ED=85=9C=20=EC=96=91?= =?UTF-8?q?=EC=8B=9D=20=EB=AA=A9=EB=A1=9D=EB=A7=8C=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=E2=80=94=20=EC=B8=B5=20=C2=B7=20=ED=94=84=EB=A1=9C=EC=A0=9D?= =?UTF-8?q?=ED=8A=B8=20=C2=B7=20=ED=94=84=EB=A1=9C=EC=A0=9D=ED=8A=B8=20?= =?UTF-8?q?=EB=8B=A8=EC=B6=94=20=EB=8B=A4=EC=84=AF=20=EB=BA=8C=20=C2=B7=20?= =?UTF-8?q?=EC=A0=9C=EB=AA=A9=20=EC=A4=84=20headerSlot=20=EC=97=B0?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 작도 영역 칸은 제목 줄 가운데로. 그림 칸 위쪽 246px. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01PxvYb5ufV1kWdBvZbpDfu6 --- .../M02_MasterTemplete_Drawing.css | 9 +- .../M02_MasterTemplete_Drawing.ts | 12 +- .../M02_MasterTemplete_UI_Main.ts | 5 +- .../M02_MasterTemplete_UI_Page.ts | 4 +- .../M02_MasterTemplete_UI_Side.ts | 253 +----------------- .../M02_MasterTemplete_UI_Style.css | 38 ++- ui_template/ui_template_locale_m2.ts | 25 -- 7 files changed, 42 insertions(+), 304 deletions(-) diff --git a/M02_MasterTemplete/M02_MasterTemplete_Drawing.css b/M02_MasterTemplete/M02_MasterTemplete_Drawing.css index 67ac1940..24892a35 100644 --- a/M02_MasterTemplete/M02_MasterTemplete_Drawing.css +++ b/M02_MasterTemplete/M02_MasterTemplete_Drawing.css @@ -46,7 +46,7 @@ .m02-drawing__area { display: flex; align-items: flex-end; - gap: var(--spacing-8); + gap: var(--spacing-4, 4px); } .m02-drawing__area { @@ -63,7 +63,7 @@ } .m02-drawing__area .ui-field input { - width: 4.5rem; + width: 3rem; padding-block: 2px; } @@ -73,11 +73,14 @@ } .m02-drawing__toolbar--slot { - flex: 0 1 auto; + flex: 1 1 0; + min-width: 0; + gap: var(--spacing-8); } .m02-drawing__actions { display: flex; gap: var(--spacing-8); margin-left: auto; + white-space: nowrap; } diff --git a/M02_MasterTemplete/M02_MasterTemplete_Drawing.ts b/M02_MasterTemplete/M02_MasterTemplete_Drawing.ts index 76560d42..e0dd3c68 100644 --- a/M02_MasterTemplete/M02_MasterTemplete_Drawing.ts +++ b/M02_MasterTemplete/M02_MasterTemplete_Drawing.ts @@ -73,7 +73,7 @@ export interface DrawingTemplateHandle { /** 작도 영역 칸이 없는 양식이 쓰는 값 — 서버 `Engine_Template._A1_INNER` 와 같다. */ const DEFAULT_AREA: [number, number, number, number] = [42, 47, 812, 567]; -const AREA_LABELS = ["왼쪽 x", "아래 y", "오른쪽 x", "위 y"]; +const AREA_LABELS = ["왼x", "아래y", "오른x", "위y"]; async function importDrawingFile( file: File, @@ -112,10 +112,6 @@ export function mountDrawingTemplate( // 작도 영역 — 도면 내용이 이 칸 한가운데에 놓인다. const area = document.createElement("div"); area.className = "m02-drawing__area"; - const areaTitle = document.createElement("span"); - areaTitle.className = "m02-drawing__title"; - areaTitle.textContent = "작도 영역"; - areaTitle.title = "도면 내용이 놓이는 칸 — 양식 좌표 mm"; const start = doc.drawing_area ?? DEFAULT_AREA; const areaInputs = AREA_LABELS.map((label, index) => { const field = createInputField({ label, type: "number", value: String(start[index]) }); @@ -123,7 +119,8 @@ export function mountDrawingTemplate( field.input.addEventListener("input", () => options.onChanged?.(true)); return field; }); - area.append(areaTitle, ...areaInputs.map((field) => field.root)); + area.title = "작도 영역 — 도면 내용이 놓이는 칸(양식 좌표 mm)"; + area.append(...areaInputs.map((field) => field.root)); const readArea = (): [number, number, number, number] | null => { const values = areaInputs.map((field) => Number(field.input.value)); @@ -189,10 +186,11 @@ export function mountDrawingTemplate( .finally(() => (importButton.disabled = false)); }); const importButton = createButton({ - label: "파일 불러오기", + label: "📂", variant: "ghost", onClick: () => fileInput.click(), }); + importButton.title = importButton.ariaLabel = "파일 불러오기 (DXF · DWG)"; actions.append(importButton, fileInput); } diff --git a/M02_MasterTemplete/M02_MasterTemplete_UI_Main.ts b/M02_MasterTemplete/M02_MasterTemplete_UI_Main.ts index 97cb7ddd..2aebe784 100644 --- a/M02_MasterTemplete/M02_MasterTemplete_UI_Main.ts +++ b/M02_MasterTemplete/M02_MasterTemplete_UI_Main.ts @@ -62,12 +62,13 @@ export function createMain(isAdmin: boolean, onSaved?: () => void): MainHandle { const title = el("h2", { className: "m02-main__title", text: L("M02_PickTemplate") }); const reload = createButton({ label: L("M02_Reload"), variant: "ghost" }); const save = createButton({ label: L("M02_Save"), variant: "filled" }); + const slot = el("div", { className: "m02-main__slot" }); // 도면 양식의 작도 영역 칸이 들어옴 const notice = el("div", { className: "m02-main__notice", attrs: { hidden: "" } }); const host = el("div", { className: "m02-main__host" }); const root = el("div", { className: "m02-main", children: [ - el("div", { className: "m02-main__head", children: [title, reload, save] }), + el("div", { className: "m02-main__head", children: [title, slot, reload, save] }), notice, host, ], @@ -110,6 +111,7 @@ export function createMain(isAdmin: boolean, onSaved?: () => void): MainHandle { const drop = (): void => { editor?.destroy(); editor = null; + slot.replaceChildren(); }; async function mount(doc: unknown, at: Selection): Promise { @@ -141,6 +143,7 @@ export function createMain(isAdmin: boolean, onSaved?: () => void): MainHandle { layer: at.layer, projectId: at.projectId, onChanged: (d: boolean) => (dirty = d && !readOnly), + headerSlot: slot, }); return; } diff --git a/M02_MasterTemplete/M02_MasterTemplete_UI_Page.ts b/M02_MasterTemplete/M02_MasterTemplete_UI_Page.ts index f52b511b..89350db2 100644 --- a/M02_MasterTemplete/M02_MasterTemplete_UI_Page.ts +++ b/M02_MasterTemplete/M02_MasterTemplete_UI_Page.ts @@ -1,6 +1,6 @@ /* ============================================================================= * M02_MasterTemplete_UI_Page.ts - * 마스터 템플릿 화면 — 좌측 도킹 패널(층 · 양식 목록) / 우측 편집 칸(표·도면 부품) + * 마스터 템플릿 화면 — 좌측 도킹 패널(시스템 양식 목록) / 우측 편집 칸(표·도면 부품) * * 화면은 로그인만 확인 — 시스템 층 고치기는 시스템 관리자일 때만 켬(서버도 막음). * ========================================================================== */ @@ -20,8 +20,6 @@ export async function renderM02MasterTemplate(root: HTMLElement): Promise const main = createMain(isAdmin, () => void side.refresh()); const side = buildSide({ isAdmin, - isCompanyAdmin: isAdmin || user?.role === "ADMIN" || !!user?.is_master, - userName: user?.name, onOpen: (sel) => void main.open(sel), getOpen: main.current, confirmLeave: main.confirmLeave, diff --git a/M02_MasterTemplete/M02_MasterTemplete_UI_Side.ts b/M02_MasterTemplete/M02_MasterTemplete_UI_Side.ts index 4d30e180..f3ed0083 100644 --- a/M02_MasterTemplete/M02_MasterTemplete_UI_Side.ts +++ b/M02_MasterTemplete/M02_MasterTemplete_UI_Side.ts @@ -1,7 +1,6 @@ /* ============================================================================= * M02_MasterTemplete_UI_Side.ts - * 마스터 템플릿 좌측 패널 — 층 고르기 · 프로젝트 고르기 · 양식 목록(표/도면) · 새로 · 본떠 · 지우기 - * · 프로젝트 층 단추 다섯. 서버 길은 계약 `6_계약.md`. + * 마스터 템플릿 좌측 패널 — 시스템 양식 목록(표/도면) · 새로 · 본떠 · 지우기. 서버 길은 계약 `6_계약.md`. * ========================================================================== */ import { @@ -14,37 +13,21 @@ import { } from "@ui/ui_template_elements"; import { t as L } from "@ui/ui_template_locale"; import { openModal } from "@ui/ui_template_modal"; -import { fetchUserProjects, type ProjectItem } from "../B01_Dashboard/B01_Dashboard_Api_Fetch"; import { - applyToProject, - deleteLayerTemplate, deleteTemplate, - fetchSources, listTemplates, readTemplate, - resetProject, - saveProjectAs, saveTemplate, StaleError, type Kind, - type Layer, type TemplateInfo, } from "./M02_MasterTemplete_Api_Fetch"; import { canEdit, type Selection } from "./M02_MasterTemplete_UI_Main"; -const LAYERS: { layer: Layer; label: () => string }[] = [ - { layer: "system", label: () => L("M02_LayerSystem") }, - { layer: "company", label: () => L("M02_LayerCompany") }, - { layer: "personal", label: () => L("M02_LayerPersonal") }, - { layer: "project", label: () => L("M02_LayerProject") }, -]; const KINDS: { kind: Kind; label: () => string }[] = [ { kind: "table", label: () => L("M02_KindTable") }, { kind: "drawing", label: () => L("M02_KindDrawing") }, ]; -const LAYER_KEY = "m02:layer"; -const PROJECT_KEY = "m02:project"; - /** 새 양식의 첫 문서 — 표는 `3_표양식.md` 4장 틀 · 도면은 빈 웹캐드 문서 */ function stubDoc(kind: Kind, name: string): unknown { return kind === "table" @@ -61,29 +44,8 @@ function stubDoc(kind: Kind, name: string): unknown { : { format: 6, entities: [] }; } -const store = { - get: (key: string): string => { - try { - return window.sessionStorage.getItem(key) ?? ""; - } catch { - return ""; - } - }, - set: (key: string, value: string): void => { - try { - window.sessionStorage.setItem(key, value); - } catch { - // 저장소가 막혀도 화면은 동작 - } - }, -}; - export interface SideOptions { isAdmin: boolean; - /** 회사 관리자(회사 층 지우기 · 회사 공식으로 저장) */ - isCompanyAdmin?: boolean; - /** 로그인한 사용자 이름 — 프로젝트 기본값(내가 만든 최근 것)에 씀 */ - userName?: string; onOpen: (sel: Selection | null) => void; getOpen: () => Selection | null; /** 저장 안 한 고침을 버릴지 확인 — 취소면 false */ @@ -103,98 +65,11 @@ const why = (error: unknown): string => (error instanceof Error ? error.message const failed = (error: unknown): void => showToast(L("M02_ActionFailed").replace("{value}", why(error)), "error"); -type ApplyFrom = Parameters[1]; -interface Source { - label: string; - body: ApplyFrom; -} - -/** 가져올 곳 — 서버 `sources` 는 층마다 묶음 · 개인·프로젝트는 사람·프로젝트 아래 양식 목록 */ -function sourceItems(raw: unknown): Source[] { - const group = (raw ?? {}) as Record[] | undefined>; - const layerName: Record = { - system: L("M02_LayerSystem"), - company: L("M02_LayerCompany"), - personal: L("M02_LayerPersonal"), - project: L("M02_LayerProject"), - }; - const out: Source[] = []; - for (const from of ["system", "company", "personal", "project"] as const) { - for (const entry of group[from] ?? []) { - const templates = (Array.isArray(entry.양식) ? entry.양식 : [entry]) as TemplateInfo[]; - const owner = entry.name ? ` · ${String(entry.name)}` : ""; - for (const t of templates) { - const body: ApplyFrom = { from, 종류: t.종류, 이름: t.이름 }; - if (entry.user_id != null) body.user_id = String(entry.user_id); - if (entry.project_id != null) body.project_id = String(entry.project_id); - out.push({ label: `${layerName[from]}${owner} · ${t.이름}`, body }); - } - } - } - return out; -} - export function buildSide(opt: SideOptions): SideHandle { - const savedLayer = store.get(LAYER_KEY) as Layer; - let layer: Layer = LAYERS.some((l) => l.layer === savedLayer) ? savedLayer : "project"; - if (layer === "system" && !opt.isAdmin) layer = "project"; - // 마지막에 고른 프로젝트는 localStorage — 다시 들어와도 유지 - const lastProject = (): string => { - try { - return window.localStorage.getItem(PROJECT_KEY) ?? ""; - } catch { - return ""; - } - }; - let projectId = lastProject(); - let projects: ProjectItem[] = []; + const layer = "system" as const; let items: TemplateInfo[] = []; let seq = 0; - const layerButtons = LAYERS.map(({ layer: id, label }) => { - const button = createButton({ label: label(), variant: "ghost" }); - button.disabled = id === "system" && !opt.isAdmin; - button.addEventListener("click", async () => { - if (id !== layer && !(await opt.confirmLeave())) return; - layer = id; - store.set(LAYER_KEY, id); - opt.onOpen(null); - void refresh(); - }); - return { id, button }; - }); - const layerRow = el("div", { - className: "m02-side__layers", - children: layerButtons.map((b) => b.button), - }); - - const remember = (id: string): void => { - try { - window.localStorage.setItem(PROJECT_KEY, id); - } catch { - // 저장소가 막혀도 화면은 동작 - } - }; - // 없으면 내가 만든 프로젝트 중 최근 것 · 그래도 없으면 빈 칸 - const defaultProject = (): string => { - const mine = projects - .filter((p) => opt.userName && p.owner_name === opt.userName) - .sort((a, b) => (b.updated_at ?? "").localeCompare(a.updated_at ?? "")); - return mine[0] ? String(mine[0].id) : ""; - }; - - const projectField = createSelectField({ label: L("M02_Project"), options: [] }); - projectField.select.addEventListener("change", async () => { - if (!(await opt.confirmLeave())) { - projectField.select.value = projectId; - return; - } - projectId = projectField.select.value; - remember(projectId); - opt.onOpen(null); - void refresh(); - }); - const listHost = el("div", { className: "m02-side__lists" }); const btnNew = createButton({ label: L("M02_New"), variant: "filled" }); @@ -202,14 +77,7 @@ export function buildSide(opt: SideOptions): SideHandle { const btnDel = createButton({ label: L("M02_Delete"), variant: "danger" }); const editRow = el("div", { className: "m02-side__row", children: [btnNew, btnCopy, btnDel] }); - const projectRow = el("div", { className: "m02-side__row" }); - const root = el("div", { - className: "m02-side", - children: [layerRow, projectField.root, listHost, editRow, projectRow], - }); - - const isProject = (): boolean => layer === "project"; - const pid = (): string | null => (isProject() && projectId ? projectId : null); + const root = el("div", { className: "m02-side", children: [listHost, editRow] }); const isOpen = (info: TemplateInfo): boolean => { const cur = opt.getOpen(); @@ -247,23 +115,15 @@ export function buildSide(opt: SideOptions): SideHandle { } function paintButtons(): void { - layerButtons.forEach(({ id, button }) => button.classList.toggle("is-active", id === layer)); - projectField.root.hidden = !isProject(); - const editable = layer === "system" && canEdit(layer, opt.isAdmin); - const canDelete = - editable || layer === "personal" || (layer === "company" && !!opt.isCompanyAdmin); - editRow.hidden = isProject(); - btnNew.hidden = btnCopy.hidden = layer !== "system"; + const editable = canEdit(layer, opt.isAdmin); btnNew.disabled = !editable; btnCopy.disabled = !editable || !currentInfo(); - btnDel.disabled = !canDelete || !currentInfo(); - projectRow.hidden = !isProject(); - projectRow.querySelectorAll("button").forEach((b) => (b.disabled = !projectId)); + btnDel.disabled = !editable || !currentInfo(); } async function select(info: TemplateInfo): Promise { if (!isOpen(info) && !(await opt.confirmLeave())) return; - opt.onOpen({ layer, projectId: pid(), kind: info.종류, name: info.이름 }); + opt.onOpen({ layer, projectId: null, kind: info.종류, name: info.이름 }); paintList(); paintButtons(); } @@ -271,24 +131,8 @@ export function buildSide(opt: SideOptions): SideHandle { async function refresh(): Promise { const mine = ++seq; paintButtons(); - if (isProject()) { - if (!projects.length) { - try { - projects = await fetchUserProjects(); - } catch (error) { - showToast(L("M02_LoadFailed").replace("{value}", why(error)), "error"); - } - } - if (!projects.some((p) => String(p.id) === projectId)) projectId = defaultProject(); - projectField.setOptions([ - { value: "", text: L(projects.length ? "M02_PickProject" : "M02_NoProjects") }, - ...projects.map((p) => ({ value: String(p.id), text: p.name ?? String(p.id) })), - ]); - projectField.select.value = projectId; - remember(projectId); - } try { - items = isProject() && !projectId ? [] : await listTemplates(layer, pid()); + items = await listTemplates(layer, null); } catch (error) { items = []; showToast(L("M02_LoadFailed").replace("{value}", why(error)), "error"); @@ -364,8 +208,7 @@ export function buildSide(opt: SideOptions): SideHandle { if (!info) return; if (!(await showConfirmDialog(L("M02_DeleteConfirm").replace("{value}", info.이름)))) return; try { - if (layer === "system") await deleteTemplate(info.종류, info.이름, info.판); - else await deleteLayerTemplate(layer, info.종류, info.이름, info.판); + await deleteTemplate(info.종류, info.이름, info.판); showToast(L("M02_Deleted"), "success"); opt.onOpen(null); await refresh(); @@ -374,86 +217,6 @@ export function buildSide(opt: SideOptions): SideHandle { } }); - /* --- 프로젝트 층 단추 다섯 — 길은 계약 · 아직 없으면 404 를 토스트로 --- */ - const act = async (run: () => Promise): Promise => { - if (!(await opt.confirmLeave())) return; - try { - await run(); - showToast(L("M02_Done"), "success"); - opt.onOpen(null); - await refresh(); - } catch (error) { - failed(error); - } - }; - const projectButton = (label: string, onClick: () => void): void => { - const b = createButton({ label, variant: "ghost" }); - b.addEventListener("click", onClick); - projectRow.append(b); - }; - const saveOpenAs = async (to: "personal" | "company"): Promise => { - const cur = opt.getOpen(); - if (!cur) return void showToast(L("M02_PickTemplate"), "error"); - try { - const rows = await listTemplates(to, null); - const same = rows.some((r) => r.종류 === cur.kind && r.이름 === cur.name); - if ( - same && - !(await showConfirmDialog(L("M02_OverwriteConfirm").replace("{value}", cur.name))) - ) { - return; - } - } catch (error) { - return failed(error); - } - try { - await saveProjectAs(projectId, to, cur.kind, cur.name); - showToast(L("M02_Done"), "success"); // 열던 양식은 그대로 둠 - } catch (error) { - failed(error); - } - }; - projectButton(L("M02_ProjectReset"), async () => { - if (await showConfirmDialog(L("M02_ResetConfirm"))) void act(() => resetProject(projectId)); - }); - projectButton(L("M02_ProjectApplyCompany"), async () => { - if (await showConfirmDialog(L("M02_ApplyConfirm"))) { - void act(() => applyToProject(projectId, { from: "company" })); - } - }); - projectButton(L("M02_ProjectImport"), async () => { - let sources: Source[] = []; - try { - sources = sourceItems(await fetchSources(projectId)); - } catch (error) { - return failed(error); - } - openModal({ - title: L("M02_ImportTitle"), - closeLabel: L("M02_Close"), - dialogClass: "m02-modal", - mount: (body, close) => { - if (!sources.length) { - body.append(el("p", { className: "m02-side__empty", text: L("M02_ImportNone") })); - } - for (const src of sources) { - const b = el("button", { - className: "m02-side__item", - text: src.label, - attrs: { type: "button" }, - }); - b.addEventListener("click", () => { - close(); - void act(() => applyToProject(projectId, src.body)); - }); - body.append(b); - } - }, - }); - }); - projectButton(L("M02_ProjectSavePersonal"), () => void saveOpenAs("personal")); - projectButton(L("M02_ProjectSaveCompany"), () => void saveOpenAs("company")); - void refresh(); return { root, refresh }; } diff --git a/M02_MasterTemplete/M02_MasterTemplete_UI_Style.css b/M02_MasterTemplete/M02_MasterTemplete_UI_Style.css index 8e8e8284..545eda43 100644 --- a/M02_MasterTemplete/M02_MasterTemplete_UI_Style.css +++ b/M02_MasterTemplete/M02_MasterTemplete_UI_Style.css @@ -6,24 +6,6 @@ padding-top: var(--spacing-8); } -.m02-side__layers { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(72px, 1fr)); - gap: var(--spacing-4); -} - -.m02-side__layers .ui-btn { - padding-inline: var(--spacing-4); - white-space: nowrap; -} - -.m02-side__layers .ui-btn.is-active { - background: var(--color-accent); - border-color: var(--color-accent); - color: var(--color-on-accent, #fff); - font-weight: var(--font-weight-bold); -} - .m02-side__group { display: flex; flex-direction: column; @@ -79,18 +61,34 @@ .m02-main__head { display: flex; - flex-wrap: wrap; + flex-wrap: nowrap; align-items: center; gap: var(--spacing-12); } .m02-main__title { - flex: 1 1 auto; + flex: 0 1 auto; + min-width: 3rem; + max-width: 9rem; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; margin: 0; color: var(--color-text); font-size: var(--text-body); } +.m02-main__head .ui-btn { + white-space: nowrap; +} + +.m02-main__slot { + display: flex; + flex: 1 1 auto; + align-items: center; + min-width: 0; +} + .m02-main__notice { display: flex; align-items: center; diff --git a/ui_template/ui_template_locale_m2.ts b/ui_template/ui_template_locale_m2.ts index 47757e43..d65d32ad 100644 --- a/ui_template/ui_template_locale_m2.ts +++ b/ui_template/ui_template_locale_m2.ts @@ -6,14 +6,6 @@ export const ui_locales_m2 = { B01_Dashboard_MasterTemplate: ["마스터 템플릿", "Master Templates"], M02_Title: ["마스터 템플릿", "Master Templates"], - M02_Layer: ["층", "Layer"], - M02_LayerSystem: ["시스템", "System"], - M02_LayerCompany: ["회사", "Company"], - M02_LayerPersonal: ["개인", "Personal"], - M02_LayerProject: ["프로젝트", "Project"], - M02_Project: ["프로젝트", "Project"], - M02_PickProject: ["프로젝트를 고를 것", "Pick a project"], - M02_NoProjects: ["프로젝트 없음", "No projects"], M02_KindTable: ["표 양식", "Table templates"], M02_KindDrawing: ["도면 양식", "Drawing templates"], M02_NoTemplates: ["양식 없음", "None"], @@ -44,21 +36,6 @@ export const ui_locales_m2 = { ], M02_NoSheet: ["표 편집 부품이 아직 없음", "Table editor not available yet"], M02_NoDrawing: ["도면 편집 부품이 아직 없음", "Drawing editor not available yet"], - M02_ProjectReset: ["초기화", "Reset"], - M02_ProjectApplyCompany: ["회사 양식 적용", "Apply company templates"], - M02_ProjectImport: ["양식 가져오기", "Import template"], - M02_ProjectSavePersonal: ["내 양식으로 저장", "Save as mine"], - M02_ProjectSaveCompany: ["회사 공식으로 저장", "Save as company official"], - M02_ResetConfirm: [ - "프로젝트 양식을 처음 복사한 것으로 되돌릴까?", - "Reset project templates to the initial copy?", - ], - M02_ApplyConfirm: [ - "회사 양식을 프로젝트 양식에 덮어쓸까?", - "Overwrite project templates with the company's?", - ], - M02_ImportTitle: ["양식 가져오기", "Import template"], - M02_ImportNone: ["가져올 양식 없음", "Nothing to import"], M02_NameExists: ["이미 있는 이름", "That name already exists"], M02_BadName: [ '쓸 수 없는 글자 — / \\ : * ? " < > | 와 .. · 앞의 점은 안 됨', @@ -68,7 +45,5 @@ export const ui_locales_m2 = { "저장 안 한 고침이 있음 — 버리고 넘어갈까?", "Unsaved changes — discard and continue?", ], - M02_OverwriteConfirm: ["「{value}」 이(가) 이미 있음 — 덮을까?", "“{value}” exists — overwrite?"], - M02_Done: ["했음", "Done"], M02_ActionFailed: ["안 됨 — {value}", "Failed — {value}"], } as const;