diff --git a/M02_MasterTemplete/M02_MasterTemplete_UI_Page.ts b/M02_MasterTemplete/M02_MasterTemplete_UI_Page.ts index 809105b3..e68eac33 100644 --- a/M02_MasterTemplete/M02_MasterTemplete_UI_Page.ts +++ b/M02_MasterTemplete/M02_MasterTemplete_UI_Page.ts @@ -20,6 +20,7 @@ export async function renderM02MasterTemplate(root: HTMLElement): Promise const main = createMain(isAdmin); const side = buildSide({ isAdmin, + userName: user?.name, onOpen: (sel) => void main.open(sel), getOpen: main.current, }); diff --git a/M02_MasterTemplete/M02_MasterTemplete_UI_Side.ts b/M02_MasterTemplete/M02_MasterTemplete_UI_Side.ts index f2a5256b..41b38e3a 100644 --- a/M02_MasterTemplete/M02_MasterTemplete_UI_Side.ts +++ b/M02_MasterTemplete/M02_MasterTemplete_UI_Side.ts @@ -78,6 +78,8 @@ const store = { export interface SideOptions { isAdmin: boolean; + /** 로그인한 사용자 이름 — 프로젝트 기본값(내가 만든 최근 것)에 씀 */ + userName?: string; onOpen: (sel: Selection | null) => void; getOpen: () => Selection | null; } @@ -126,7 +128,15 @@ 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"; - let projectId = store.get(PROJECT_KEY); + // 마지막에 고른 프로젝트는 localStorage — 다시 들어와도 유지 + const lastProject = (): string => { + try { + return window.localStorage.getItem(PROJECT_KEY) ?? ""; + } catch { + return ""; + } + }; + let projectId = lastProject(); let projects: ProjectItem[] = []; let items: TemplateInfo[] = []; let seq = 0; @@ -147,10 +157,25 @@ export function buildSide(opt: SideOptions): SideHandle { 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", () => { projectId = projectField.select.value; - store.set(PROJECT_KEY, projectId); + remember(projectId); opt.onOpen(null); void refresh(); }); @@ -234,16 +259,13 @@ export function buildSide(opt: SideOptions): SideHandle { showToast(L("M02_LoadFailed").replace("{value}", why(error)), "error"); } } - if (!projects.some((p) => String(p.id) === projectId)) { - projectId = projects[0] ? String(projects[0].id) : ""; - } - projectField.setOptions( - projects.length - ? projects.map((p) => ({ value: String(p.id), text: p.name ?? String(p.id) })) - : [{ value: "", text: L("M02_NoProjects") }], - ); + 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; - store.set(PROJECT_KEY, projectId); + remember(projectId); } try { items = isProject() && !projectId ? [] : await listTemplates(layer, pid()); diff --git a/M02_MasterTemplete/M02_MasterTemplete_UI_Style.css b/M02_MasterTemplete/M02_MasterTemplete_UI_Style.css index 0ce4d0f6..8e8e8284 100644 --- a/M02_MasterTemplete/M02_MasterTemplete_UI_Style.css +++ b/M02_MasterTemplete/M02_MasterTemplete_UI_Style.css @@ -8,13 +8,19 @@ .m02-side__layers { display: grid; - grid-template-columns: repeat(4, 1fr); + 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-accent); + color: var(--color-on-accent, #fff); font-weight: var(--font-weight-bold); }