feat(pricing): manage workspace member rates in edit flows

This commit is contained in:
2026-04-26 10:21:58 +03:30
parent f9dfd8826e
commit 2d843046fa
8 changed files with 665 additions and 213 deletions

107
src/api/rates.ts Normal file
View File

@@ -0,0 +1,107 @@
import { authFetch } from "./client";
export interface RateUser {
id: string;
first_name?: string;
last_name?: string;
mobile?: string;
profile_picture?: string;
avatar?: string;
name?: string;
}
export interface PriceUnit {
id: string;
code: string;
name: string;
local_name?: string;
symbol?: string;
}
export interface WorkspaceUserRate {
id: string;
workspace: string;
user: string;
user_details?: RateUser;
hourly_rate: string;
currency: string;
price_unit?: PriceUnit | null;
effective_from: string;
}
interface PaginatedResponse<T> {
count: number;
next: string | null;
previous: string | null;
results: T[];
}
const ensurePaginated = async <T>(response: Response): Promise<PaginatedResponse<T>> => {
if (!response.ok) {
const errorData = await response.json().catch(() => null);
throw new Error(errorData?.detail || errorData?.message || "Rate request failed");
}
const data = await response.json();
if (Array.isArray(data)) {
return { count: data.length, next: null, previous: null, results: data };
}
return {
count: data.count || data.results?.length || 0,
next: data.next || null,
previous: data.previous || null,
results: data.results || [],
};
};
export const getPriceUnits = async () => {
const response = await authFetch("/api/price-units/");
return ensurePaginated<PriceUnit>(response);
};
export const getWorkspaceUserRates = async (workspaceId: string) => {
const response = await authFetch(`/api/workspace-user-rates/?workspace=${workspaceId}`);
return ensurePaginated<WorkspaceUserRate>(response);
};
export const createWorkspaceUserRate = async (data: {
workspace_id: string;
user_id: string;
hourly_rate: string;
currency: string;
}) => {
const response = await authFetch("/api/workspace-user-rates/", {
method: "POST",
body: JSON.stringify(data),
});
if (!response.ok) {
const errorData = await response.json().catch(() => null);
throw new Error(errorData?.detail || errorData?.message || "Failed to save workspace user rate");
}
return response.json() as Promise<WorkspaceUserRate>;
};
export const updateWorkspaceUserRate = async (
rateId: string,
data: Partial<Pick<WorkspaceUserRate, "hourly_rate" | "currency">>,
) => {
const response = await authFetch(`/api/workspace-user-rates/${rateId}/`, {
method: "PATCH",
body: JSON.stringify(data),
});
if (!response.ok) {
const errorData = await response.json().catch(() => null);
throw new Error(errorData?.detail || errorData?.message || "Failed to update workspace user rate");
}
return response.json() as Promise<WorkspaceUserRate>;
};
export const deleteWorkspaceUserRate = async (rateId: string) => {
const response = await authFetch(`/api/workspace-user-rates/${rateId}/`, {
method: "DELETE",
});
if (!response.ok) {
const errorData = await response.json().catch(() => null);
throw new Error(errorData?.detail || errorData?.message || "Failed to delete workspace user rate");
}
};