interface Group { id: string; name: string; } interface GroupMember { id: string; group_id: string; member_type: 'user' | 'group'; member_id: string; } class GroupEditor extends HTMLElement { connectedCallback(): void { this.render(); } async render(): Promise { const res = await fetch('/api/groups'); const groups = await res.json() as Group[]; this.innerHTML = `

Groups

`; this.querySelector('.create-form')!.addEventListener('submit', async (e: Event) => { e.preventDefault(); const form = e.target as HTMLFormElement; const name = (form.elements.namedItem('name') as HTMLInputElement).value.trim(); const res = await fetch('/api/groups', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name }) }); if (res.ok) this.render(); }); this.querySelectorAll('.btn-delete').forEach(btn => { btn.addEventListener('click', async () => { if (!confirm('Delete group?')) return; await fetch(`/api/groups/${(btn as HTMLElement).dataset.id}`, { method: 'DELETE' }); this.render(); }); }); this.querySelectorAll('.btn-members').forEach(btn => { btn.addEventListener('click', () => this.toggleMembers((btn as HTMLElement).dataset.id!)); }); } async toggleMembers(groupId: string): Promise { const panel = this.querySelector(`.members-panel[data-group="${groupId}"]`) as HTMLElement; if (panel.style.display !== 'none') { panel.style.display = 'none'; return; } panel.style.display = 'block'; await this.loadMembers(groupId, panel); } async loadMembers(groupId: string, panel: HTMLElement): Promise { const res = await fetch(`/api/groups/${groupId}/members`); const members = await res.json() as GroupMember[]; panel.innerHTML = `
    ${members.map(m => `
  • ${m.member_type}: ${m.member_id}
  • `).join('') || '
  • No members
  • '}
`; panel.querySelectorAll('.btn-remove-member').forEach(btn => { btn.addEventListener('click', async () => { const el = btn as HTMLElement; await fetch(`/api/groups/${el.dataset.gid}/members/${el.dataset.mid}`, { method: 'DELETE' }); await this.loadMembers(groupId, panel); }); }); (panel.querySelector('.add-member-form') as HTMLFormElement).addEventListener('submit', async (e: Event) => { e.preventDefault(); const data = Object.fromEntries(new FormData(e.target as HTMLFormElement)); await fetch(`/api/groups/${groupId}/members`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); await this.loadMembers(groupId, panel); }); } } customElements.define('group-editor', GroupEditor);