export function createTemplatesMenu({ panel, fetchProjects, openProject, onShow, onHide }) { let state = { templates: [], projects: [], loading: false, error: null }; const setState = (next) => { state = { ...state, ...next }; render(); }; const renderEmpty = (parent, text) => { const empty = document.createElement("div"); empty.className = "templates-empty"; empty.textContent = text; parent.appendChild(empty); }; const renderSection = (title, items, emptyText) => { const section = document.createElement("div"); section.className = "templates-section"; const subtitle = document.createElement("div"); subtitle.className = "templates-subtitle"; subtitle.textContent = title; section.appendChild(subtitle); if (!items || !items.length) { renderEmpty(section, emptyText); } else { items.forEach((item) => { const card = document.createElement("button"); card.className = "example-card template-card"; card.type = "button"; card.addEventListener("click", () => openProject?.(item.id, item.type)); const titleEl = document.createElement("div"); titleEl.className = "card-title"; titleEl.textContent = item.name || item.id; card.appendChild(titleEl); const meta = document.createElement("div"); meta.className = "card-meta"; const typePill = document.createElement("span"); typePill.className = "card-pill"; typePill.dataset.kind = item.type || "project"; typePill.textContent = item.type === "template" ? "Шаблон" : "Проект"; meta.appendChild(typePill); if (item.updatedAt) { const updated = document.createElement("span"); updated.textContent = new Date(item.updatedAt).toLocaleDateString(); meta.appendChild(updated); } card.appendChild(meta); section.appendChild(card); }); } return section; }; const render = () => { if (!panel) return; panel.innerHTML = ""; const header = document.createElement("div"); header.className = "templates-header"; header.innerHTML = `Проекты и шаблоны`; const actions = document.createElement("div"); actions.className = "templates-actions"; const refresh = document.createElement("button"); refresh.className = "templates-close"; refresh.type = "button"; refresh.title = "Обновить список"; refresh.setAttribute("aria-label", "Обновить список"); refresh.innerHTML = ` `; refresh.addEventListener("click", () => load()); const closeBtn = document.createElement("button"); closeBtn.className = "templates-close"; closeBtn.type = "button"; closeBtn.setAttribute("aria-label", "Закрыть проекты и шаблоны"); closeBtn.innerHTML = ` `; closeBtn.addEventListener("click", hide); actions.appendChild(refresh); actions.appendChild(closeBtn); header.appendChild(actions); panel.appendChild(header); if (state.loading) { const loadingEl = document.createElement("div"); loadingEl.className = "templates-empty"; loadingEl.textContent = "Загрузка списка проектов..."; panel.appendChild(loadingEl); return; } if (state.error) { const errorEl = document.createElement("div"); errorEl.className = "templates-empty"; errorEl.style.color = "var(--error)"; errorEl.textContent = state.error; panel.appendChild(errorEl); } panel.appendChild(renderSection("Шаблоны", state.templates, "Нет шаблонов")); panel.appendChild(renderSection("Мои проекты", state.projects, "Нет сохранённых проектов")); }; const load = async () => { if (!fetchProjects) return; setState({ loading: true, error: null }); try { const data = await fetchProjects(); setState({ loading: false, templates: data?.templates || [], projects: data?.projects || [], error: null }); } catch (err) { setState({ loading: false, error: err?.message || "Не удалось загрузить проекты" }); } }; const hide = () => { if (panel) { panel.classList.add("hidden"); panel.style.display = ""; } onHide?.(); }; const show = () => { render(); if (panel) { panel.classList.remove("hidden"); panel.style.display = "grid"; } load(); onShow?.(); }; const toggle = () => { if (!panel) return; if (panel.classList.contains("hidden")) { show(); } else { hide(); } }; const setData = (data = {}) => { setState({ templates: data.templates || [], projects: data.projects || [], loading: false, error: data.error || null }); }; return { render, show, hide, toggle, reload: load, setData }; }