Files
stack/apps/web/src/lib/api/teams.ts
Jason Woltje 307639eca0
All checks were successful
ci/woodpecker/push/web Pipeline was successful
feat(web): add teams settings page (MS21-UI-005) (#576)
Co-authored-by: Jason Woltje <jason@diversecanvas.com>
Co-committed-by: Jason Woltje <jason@diversecanvas.com>
2026-02-28 22:12:04 +00:00

136 lines
3.4 KiB
TypeScript

/**
* Teams API Client
* Handles workspace-scoped team API requests.
*/
import type { TeamMemberRole } from "@mosaic/shared";
import { apiDelete, apiGet, apiPost } from "./client";
const WORKSPACE_STORAGE_KEY = "mosaic-workspace-id";
function resolveWorkspaceId(explicitWorkspaceId?: string): string {
if (explicitWorkspaceId !== undefined) {
return explicitWorkspaceId;
}
if (typeof window === "undefined") {
throw new Error("Workspace context is unavailable outside the browser");
}
const workspaceId = window.localStorage.getItem(WORKSPACE_STORAGE_KEY);
if (!workspaceId) {
throw new Error("No active workspace selected");
}
return workspaceId;
}
export interface TeamRecord {
id: string;
workspaceId: string;
name: string;
description: string | null;
metadata: Record<string, unknown>;
createdAt: string;
updatedAt: string;
_count?: {
members: number;
};
}
export interface TeamMemberRecord {
teamId: string;
userId: string;
role: TeamMemberRole;
joinedAt: string;
user?: {
id: string;
name: string;
email: string;
};
}
export interface CreateTeamDto {
name: string;
description?: string;
}
export interface AddTeamMemberDto {
userId: string;
role?: TeamMemberRole;
}
/**
* Fetch all teams in the active workspace.
*/
export async function fetchTeams(workspaceId?: string): Promise<TeamRecord[]> {
const resolvedWorkspaceId = resolveWorkspaceId(workspaceId);
return apiGet<TeamRecord[]>(`/api/workspaces/${resolvedWorkspaceId}/teams`, resolvedWorkspaceId);
}
/**
* Create a team in the active workspace.
*/
export async function createTeam(dto: CreateTeamDto, workspaceId?: string): Promise<TeamRecord> {
const resolvedWorkspaceId = resolveWorkspaceId(workspaceId);
return apiPost<TeamRecord>(
`/api/workspaces/${resolvedWorkspaceId}/teams`,
dto,
resolvedWorkspaceId
);
}
/**
* Fetch team members for a team in the active workspace.
* The current backend route shape is workspace-scoped team membership.
*/
export async function fetchTeamMembers(
teamId: string,
workspaceId?: string
): Promise<TeamMemberRecord[]> {
const resolvedWorkspaceId = resolveWorkspaceId(workspaceId);
return apiGet<TeamMemberRecord[]>(
`/api/workspaces/${resolvedWorkspaceId}/teams/${teamId}/members`,
resolvedWorkspaceId
);
}
/**
* Delete a team in the active workspace.
*/
export async function deleteTeam(teamId: string, workspaceId?: string): Promise<void> {
const resolvedWorkspaceId = resolveWorkspaceId(workspaceId);
await apiDelete(`/api/workspaces/${resolvedWorkspaceId}/teams/${teamId}`, resolvedWorkspaceId);
}
/**
* Add a member to a team in the active workspace.
*/
export async function addTeamMember(
teamId: string,
data: AddTeamMemberDto,
workspaceId?: string
): Promise<TeamMemberRecord> {
const resolvedWorkspaceId = resolveWorkspaceId(workspaceId);
return apiPost<TeamMemberRecord>(
`/api/workspaces/${resolvedWorkspaceId}/teams/${teamId}/members`,
data,
resolvedWorkspaceId
);
}
/**
* Remove a member from a team in the active workspace.
*/
export async function removeTeamMember(
teamId: string,
userId: string,
workspaceId?: string
): Promise<void> {
const resolvedWorkspaceId = resolveWorkspaceId(workspaceId);
await apiDelete(
`/api/workspaces/${resolvedWorkspaceId}/teams/${teamId}/members/${userId}`,
resolvedWorkspaceId
);
}