import { html, nothing } from 'lit'; import { LightElement } from '../../lib/base.js'; import { formatDate } from '../tasks/utils.js'; export class ProjectListSection extends LightElement { static properties = { _projects: { state: true }, _modal: { state: true }, _form: { state: true }, _saving: { state: true }, _error: { state: true }, }; constructor() { super(); this._projects = []; this._modal = null; this._form = this._emptyForm(); this._saving = false; this._error = null; } _emptyForm() { return { name: '', path: '', description: '' }; } async load() { this._error = null; try { const res = await fetch('/api/projects'); if (!res.ok) throw new Error(`HTTP ${res.status}`); this._projects = await res.json(); } catch (e) { this._error = e.message; } } _openAdd() { this._form = this._emptyForm(); this._error = null; this._modal = { mode: 'add' }; } _openEdit(project) { this._form = { name: project.name, path: project.path, description: project.description ?? '' }; this._error = null; this._modal = { mode: 'edit', project }; } _closeModal() { this._modal = null; this._error = null; } async _submit(e) { e.preventDefault(); if (this._saving) return; this._saving = true; this._error = null; const isEdit = this._modal?.mode === 'edit'; const url = isEdit ? `/api/projects/${this._modal.project.id}` : '/api/projects'; try { const res = await fetch(url, { method: isEdit ? 'PUT' : 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(this._form), }); if (!res.ok) throw new Error(await res.text()); this._modal = null; await this.load(); } catch (err) { this._error = err.message; } finally { this._saving = false; } } async _delete(project) { if (!confirm(`Delete project "${project.name}"?\nAll tickets will also be deleted.`)) return; try { const res = await fetch(`/api/projects/${project.id}`, { method: 'DELETE' }); if (!res.ok) throw new Error(await res.text()); await this.load(); } catch (e) { this._error = e.message; } } _navigate(project) { this.dispatchEvent(new CustomEvent('project-navigate', { bubbles: true, composed: true, detail: { id: project.id }, })); } _setField(f, v) { this._form = { ...this._form, [f]: v }; } _renderModal() { const isEdit = this._modal?.mode === 'edit'; return html`
{ if (e.target === e.currentTarget) this._closeModal(); }}>
${isEdit ? 'Edit Project' : 'New Project'}
${this._error ? html`
${this._error}
` : nothing}
this._submit(e)}>
this._setField('name', e.target.value)} />
this._setField('path', e.target.value)} />
`; } _renderCard(project) { return html`
this._navigate(project)}>
${project.name}
e.stopPropagation()}>
${project.path}
${project.description ? html`
${project.description}
` : nothing}
Updated ${formatDate(project.updated_at)}
`; } render() { return html`

Projects

${this._error ? html`
${this._error}
` : nothing} ${this._projects.length === 0 ? html`

No projects yet. Create one to get started.

` : html`
${this._projects.map(p => this._renderCard(p))}
`} ${this._modal ? this._renderModal() : nothing}
`; } }