import { showConfirmDialog } from '../confirm-dialog/confirm-dialog.js'; interface CurrentUser { userId: string; email: string; } interface Project { id: string; name: string; owner_id: string; } interface ProjectMember { id: string; project_id: string; member_type: 'user' | 'group'; member_id: string; role: string; } class ProjectEditor extends HTMLElement { private currentUser: CurrentUser | null = null; async connectedCallback(): Promise { const res = await fetch( '/api/auth/me' ); if ( res.ok ) this.currentUser = await res.json() as CurrentUser; this.render(); } async render(): Promise { const res = await fetch( '/api/projects' ); const projects = await res.json() as Project[]; this.innerHTML = `

Projects

    ${ projects.map( p => `
  • ${ p.name } Edit
  • ` ).join( '' ) }
`; 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/projects', { 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 () => { const el = btn as HTMLElement; const ok = await showConfirmDialog( { icon: '🗑', title: 'Delete Project', message: `Delete "${ el.dataset.name }"? This cannot be undone.`, confirmLabel: 'Delete', cancelLabel: 'Cancel', danger: true } ); if ( !ok ) return; await fetch( `/api/projects/${ el.dataset.id }`, { method: 'DELETE' } ); this.render(); } ); } ); this.querySelectorAll( '.btn-members' ).forEach( btn => { btn.addEventListener( 'click', () => this.toggleMembers( ( btn as HTMLElement ).dataset.id! ) ); } ); } async toggleMembers( projectId: string ): Promise { const panel = this.querySelector( `.members-panel[data-project="${ projectId }"]` ) as HTMLElement; if ( panel.style.display !== 'none' ) { panel.style.display = 'none'; return; } panel.style.display = 'block'; await this.loadMembers( projectId, panel ); } async loadMembers( projectId: string, panel: HTMLElement ): Promise { const res = await fetch( `/api/projects/${ projectId }/members` ); const members = await res.json() as ProjectMember[]; const ownerId = panel.dataset.owner ?? ''; const isOwner = this.currentUser?.userId === ownerId; const ownerLabel = isOwner ? `${ this.currentUser!.email } owner` : `${ ownerId } owner`; const memberRows = members.map( m => `
  • ${ m.member_id } ${ m.role } ${ isOwner ? `` : '' }
  • ` ).join( '' ); panel.innerHTML = `
    • ${ ownerLabel }
    • ${ memberRows || '' } ${ members.length === 0 ? '
    • No additional members
    • ' : '' }
    ${ isOwner ? `
    ` : '' } `; panel.querySelectorAll( '.btn-remove-member' ).forEach( btn => { btn.addEventListener( 'click', async () => { const el = btn as HTMLElement; await fetch( `/api/projects/${ el.dataset.pid }/members/${ el.dataset.mid }`, { method: 'DELETE' } ); await this.loadMembers( projectId, panel ); } ); } ); const addForm = panel.querySelector( '.add-member-form' ) as HTMLFormElement | null; addForm?.addEventListener( 'submit', async ( e: Event ) => { e.preventDefault(); const data = Object.fromEntries( new FormData( e.target as HTMLFormElement ) ); await fetch( `/api/projects/${ projectId }/members`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify( data ) } ); await this.loadMembers( projectId, panel ); } ); } } customElements.define( 'project-editor', ProjectEditor );