From 1e681da738de4eb082ed81e802ee953feb30fbb8 Mon Sep 17 00:00:00 2001 From: pptx704 Date: Tue, 24 Mar 2026 14:21:53 +0600 Subject: [PATCH] Add team management frontend MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - New /dashboard/team page with inline team name editing, slug/ID copy, members table with split-button (remove + make admin/member), add member typeahead, and danger zone (delete/leave) with confirmation dialogs - Sidebar now fetches real teams from API, supports team switching and team creation via dialog - Rename nav item Members → Team, route /dashboard/members → /dashboard/team - New src/lib/api/team.ts with typed functions for all team endpoints --- frontend/src/lib/api/team.ts | 83 ++ frontend/src/lib/components/Sidebar.svelte | 161 ++- .../src/routes/dashboard/team/+page.svelte | 1196 +++++++++++++++++ 3 files changed, 1424 insertions(+), 16 deletions(-) create mode 100644 frontend/src/lib/api/team.ts create mode 100644 frontend/src/routes/dashboard/team/+page.svelte diff --git a/frontend/src/lib/api/team.ts b/frontend/src/lib/api/team.ts new file mode 100644 index 0000000..0fae217 --- /dev/null +++ b/frontend/src/lib/api/team.ts @@ -0,0 +1,83 @@ +import { apiFetch, type ApiResult } from '$lib/api/client'; + +export type TeamMember = { + user_id: string; + email: string; + role: 'owner' | 'admin' | 'member'; + joined_at: string; +}; + +export type TeamInfo = { + id: string; + name: string; + slug: string; + created_at: string; +}; + +export type TeamDetail = { + team: TeamInfo; + members: TeamMember[]; +}; + +export type UserSearchResult = { + user_id: string; + email: string; +}; + +export type TeamWithRole = { + id: string; + name: string; + slug: string; + created_at: string; + role: string; +}; + +export async function listTeams(): Promise> { + return apiFetch('GET', '/api/v1/teams'); +} + +export async function createTeam(name: string): Promise> { + return apiFetch('POST', '/api/v1/teams', { name }); +} + +export async function switchTeam( + teamId: string +): Promise> { + return apiFetch('POST', '/api/v1/auth/switch-team', { team_id: teamId }); +} + +export async function getTeam(id: string): Promise> { + return apiFetch('GET', `/api/v1/teams/${id}`); +} + +export async function updateTeam(id: string, name: string): Promise> { + return apiFetch('PATCH', `/api/v1/teams/${id}`, { name }); +} + +export async function addMember(id: string, email: string): Promise> { + return apiFetch('POST', `/api/v1/teams/${id}/members`, { email }); +} + +export async function removeMember(id: string, userId: string): Promise> { + return apiFetch('DELETE', `/api/v1/teams/${id}/members/${userId}`); +} + +export async function updateMemberRole( + id: string, + userId: string, + role: 'admin' | 'member' +): Promise> { + return apiFetch('PATCH', `/api/v1/teams/${id}/members/${userId}`, { role }); +} + +export async function deleteTeam(id: string): Promise> { + return apiFetch('DELETE', `/api/v1/teams/${id}`); +} + +export async function leaveTeam(id: string): Promise> { + return apiFetch('POST', `/api/v1/teams/${id}/leave`); +} + +export async function searchUsers(email: string): Promise> { + return apiFetch('GET', `/api/v1/users/search?email=${encodeURIComponent(email)}`); +} diff --git a/frontend/src/lib/components/Sidebar.svelte b/frontend/src/lib/components/Sidebar.svelte index 7102b11..a4eabb2 100644 --- a/frontend/src/lib/components/Sidebar.svelte +++ b/frontend/src/lib/components/Sidebar.svelte @@ -1,7 +1,9 @@