// Projects view — CRUD list styled like a Jira projects page. const ProjectsView = { async render(container) { container.innerHTML = `

Projects

Manage your projects and their structure
Loading…
`; container.querySelector('#new-project').addEventListener('click', () => this.openForm()); await this.load(); }, async load() { const body = document.getElementById('projects-body'); try { const projects = await API.listProjects(); if (!projects.length) { body.innerHTML = `

No projects yet

Create your first project to get started.

`; return; } body.innerHTML = `
${projects.map((p) => this.row(p)).join('')}
ID Name Note Created Actions
`; body.querySelectorAll('[data-edit]').forEach((b) => b.addEventListener('click', () => this.openForm(Number(b.dataset.edit))) ); body.querySelectorAll('[data-del]').forEach((b) => b.addEventListener('click', () => this.remove(Number(b.dataset.del))) ); } catch (e) { body.innerHTML = `

Failed to load

${UI.esc(e.message)}

`; } }, row(p) { return ` ${p.id} ${UI.esc(p.name)} ${UI.esc(p.note || '—')} ${UI.fmtDate(p.creatid)}
`; }, async openForm(id) { let p = { name: '', note: '', struc: {} }; if (id) { try { p = await API.getProject(id); } catch (e) { return UI.toast(e.message, true); } } const strucText = typeof p.struc === 'object' ? JSON.stringify(p.struc ?? {}, null, 2) : (p.struc || ''); const modal = UI.openModal(`
`); modal.querySelector('#f-cancel').addEventListener('click', UI.closeModal); modal.querySelector('#f-save').addEventListener('click', async () => { const name = modal.querySelector('#f-name').value.trim(); if (!name) return UI.toast('Name is required', true); const payload = { name, note: modal.querySelector('#f-note').value.trim(), struc: modal.querySelector('#f-struc').value.trim() }; try { if (id) await API.updateProject(id, payload); else await API.createProject(payload); UI.closeModal(); UI.toast(id ? 'Project updated' : 'Project created'); this.load(); } catch (e) { UI.toast(e.message, true); } }); }, async remove(id) { if (!confirm('Delete this project? Tickets linked to it will be unlinked.')) return; try { await API.deleteProject(id); UI.toast('Project deleted'); this.load(); } catch (e) { UI.toast(e.message, true); } } };