payment-backoffice/app/features/AdminList/AdminResourceList.utils.ts
2026-01-07 15:41:36 +01:00

147 lines
4.0 KiB
TypeScript

export type TCreateResourcePayload = Record<string, unknown>;
/**
* Transforms create payload to convert string numbers to actual numbers
* for fields that should be numeric (e.g., rate, limit, etc.)
*/
function transformCreatePayload(
payload: TCreateResourcePayload
): TCreateResourcePayload {
const transformed: TCreateResourcePayload = { ...payload };
for (const [key, value] of Object.entries(transformed)) {
// Convert string numbers to actual numbers for common numeric fields
if (typeof value === "string" && value.trim() !== "") {
const numericValue = Number(value);
// Only convert if it's a valid number and the key suggests it should be numeric
if (
!Number.isNaN(numericValue) &&
(key.toLowerCase().includes("rate") ||
key.toLowerCase().includes("price") ||
key.toLowerCase().includes("amount") ||
key.toLowerCase().includes("limit") ||
key.toLowerCase().includes("count"))
) {
transformed[key] = numericValue;
}
}
}
return transformed;
}
export async function createResourceApi(
endpointBase: string,
payload: TCreateResourcePayload,
resourceName: string
) {
// Transform the payload to convert string numbers to actual numbers
const transformedPayload = transformCreatePayload(payload);
const response = await fetch(`${endpointBase}/create`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(transformedPayload),
});
if (!response.ok) {
const errorData = await response
.json()
.catch(() => ({ message: `Failed to create ${resourceName}` }));
throw new Error(errorData?.message || `Failed to create ${resourceName}`);
}
return response.json();
}
export async function deleteResourceApi(
endpointBase: string,
id: number | string,
resourceName: string
) {
const response = await fetch(`${endpointBase}/${id}`, {
method: "DELETE",
});
if (!response.ok) {
const errorData = await response
.json()
.catch(() => ({ message: `Failed to delete ${resourceName}` }));
throw new Error(errorData?.message || `Failed to delete ${resourceName}`);
}
try {
return await response.json();
} catch {
return { success: true };
}
}
export type TUpdateResourcePayload = Record<string, unknown>;
/**
* Converts a key to PascalCase (e.g., "enabled" -> "Enabled", "first_name" -> "FirstName")
*/
function toPascalCase(key: string): string {
return key
.split("_")
.map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
.join("");
}
/**
* Transforms frontend data to backend format
* - data object uses lowercase keys (matching API response)
* - fields array uses PascalCase (required by backend)
*/
function transformResourceUpdateData(updates: Record<string, unknown>): {
data: Record<string, unknown>;
fields: string[];
} {
const data: Record<string, unknown> = {};
const fields: string[] = [];
for (const [key, value] of Object.entries(updates)) {
// Skip undefined/null values
if (value === undefined || value === null) {
continue;
}
// Use the key as-is for data (matching API response casing)
data[key] = value;
// Convert to PascalCase for fields array (required by backend)
fields.push(toPascalCase(key));
}
return { data, fields };
}
export async function updateResourceApi(
endpointBase: string,
id: number | string,
payload: TUpdateResourcePayload,
resourceName: string
) {
// Transform the payload to match backend format
const transformedPayload = transformResourceUpdateData(payload);
const response = await fetch(`${endpointBase}/${id}`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(transformedPayload),
});
if (!response.ok) {
const errorData = await response
.json()
.catch(() => ({ message: `Failed to update ${resourceName}` }));
throw new Error(errorData?.message || `Failed to update ${resourceName}`);
}
return response.json();
}