feat(workspaces): add bulk member import modal

This commit is contained in:
2026-06-18 22:53:44 +03:30
parent 29cadb83e6
commit 55ba274346
7 changed files with 961 additions and 28 deletions

View File

@@ -36,6 +36,47 @@ export interface WorkspaceMembership {
[key: string]: any;
}
export interface WorkspaceMemberImportRowInput {
line: number;
mobile: string;
role?: "admin" | "member" | "guest";
hourly_rate?: string;
currency?: string;
}
export interface WorkspaceMemberImportResultRow {
line: number | null;
mobile: string;
role: "admin" | "member" | "guest" | "owner" | string;
hourly_rate: string;
currency: string;
status: "valid" | "invalid";
action: "add_member" | "none" | string;
user: {
id: string;
full_name: string;
mobile: string;
} | null;
messages: string[];
}
export interface WorkspaceMemberImportValidationResponse {
can_commit: boolean;
import_token: string | null;
summary: {
total: number;
valid: number;
invalid: number;
};
rows: WorkspaceMemberImportResultRow[];
}
export interface WorkspaceMemberImportCommitResponse {
created_memberships: number;
created_or_updated_rates: number;
memberships: WorkspaceMembership[];
}
type QueryValue = string | number | boolean | undefined | null;
@@ -210,7 +251,7 @@ export const removeWorkspaceMembership = async (membershipId: string): Promise<v
invalidateApiCache(["workspace-memberships", "reports"]);
};
export const updateWorkspaceMembership = async (membershipId: string | number, data: { role: string }) => {
export const updateWorkspaceMembership = async (membershipId: string | number, data: { role: string }) => {
const response = await authFetch(`/api/workspace-memberships/${membershipId}/`, {
method: 'PATCH',
body: JSON.stringify(data),
@@ -225,3 +266,36 @@ export const updateWorkspaceMembership = async (membershipId: string | number, d
invalidateApiCache(["workspace-memberships", "reports"]);
return payload;
};
export const validateWorkspaceMemberImport = async (data: {
workspace: string;
rows: WorkspaceMemberImportRowInput[];
}): Promise<WorkspaceMemberImportValidationResponse> => {
const response = await authFetch("/api/workspace-memberships/import/validate/", {
method: "POST",
body: JSON.stringify(data),
});
const payload = await response.json().catch(() => null);
if (!response.ok) {
throw new Error(payload?.detail || payload?.message || "Failed to validate member import");
}
return payload;
};
export const commitWorkspaceMemberImport = async (data: {
workspace: string;
import_token: string;
}): Promise<WorkspaceMemberImportCommitResponse> => {
const response = await authFetch("/api/workspace-memberships/import/commit/", {
method: "POST",
body: JSON.stringify(data),
});
const payload = await response.json().catch(() => null);
if (!response.ok) {
throw new Error(payload?.detail || payload?.message || "Failed to import workspace members");
}
invalidateApiCache(["workspace-memberships", "workspace-rates", "reports"]);
return payload;
};