192 lines
5.6 KiB
TypeScript
192 lines
5.6 KiB
TypeScript
const CSMS_URL = process.env.NEXT_PUBLIC_CSMS_URL ?? "http://localhost:3001";
|
|
|
|
async function apiFetch<T>(path: string, init?: RequestInit): Promise<T> {
|
|
const res = await fetch(`${CSMS_URL}${path}`, {
|
|
...init,
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
...init?.headers,
|
|
},
|
|
credentials: "include",
|
|
});
|
|
if (!res.ok) {
|
|
const text = await res.text().catch(() => res.statusText);
|
|
throw new Error(`API ${path} failed (${res.status}): ${text}`);
|
|
}
|
|
return res.json() as Promise<T>;
|
|
}
|
|
|
|
// ── Types ──────────────────────────────────────────────────────────────────
|
|
|
|
export type Stats = {
|
|
totalChargePoints: number;
|
|
onlineChargePoints: number;
|
|
activeTransactions: number;
|
|
totalIdTags: number;
|
|
todayEnergyWh: number;
|
|
};
|
|
|
|
export type ConnectorSummary = {
|
|
id: number;
|
|
connectorId: number;
|
|
status: string;
|
|
lastStatusAt: string | null;
|
|
};
|
|
|
|
export type ChargePoint = {
|
|
id: number;
|
|
chargePointIdentifier: string;
|
|
chargePointVendor: string | null;
|
|
chargePointModel: string | null;
|
|
registrationStatus: string;
|
|
lastHeartbeatAt: string | null;
|
|
lastBootNotificationAt: string | null;
|
|
feePerKwh: number;
|
|
connectors: ConnectorSummary[];
|
|
};
|
|
|
|
export type Transaction = {
|
|
id: number;
|
|
chargePointIdentifier: string | null;
|
|
connectorNumber: number | null;
|
|
idTag: string;
|
|
idTagStatus: string | null;
|
|
startTimestamp: string;
|
|
stopTimestamp: string | null;
|
|
startMeterValue: number | null;
|
|
stopMeterValue: number | null;
|
|
energyWh: number | null;
|
|
stopIdTag: string | null;
|
|
stopReason: string | null;
|
|
chargeAmount: number | null;
|
|
};
|
|
|
|
export type IdTag = {
|
|
idTag: string;
|
|
status: string;
|
|
expiryDate: string | null;
|
|
parentIdTag: string | null;
|
|
userId: string | null;
|
|
balance: number;
|
|
createdAt: string;
|
|
};
|
|
|
|
export type UserRow = {
|
|
id: string;
|
|
name: string | null;
|
|
email: string;
|
|
emailVerified: boolean;
|
|
username: string | null;
|
|
role: string | null;
|
|
banned: boolean | null;
|
|
banReason: string | null;
|
|
createdAt: string;
|
|
};
|
|
|
|
export type PaginatedTransactions = {
|
|
data: Transaction[];
|
|
total: number;
|
|
page: number;
|
|
totalPages: number;
|
|
};
|
|
|
|
// ── API functions ──────────────────────────────────────────────────────────
|
|
|
|
export const api = {
|
|
stats: {
|
|
get: () => apiFetch<Stats>("/api/stats"),
|
|
},
|
|
chargePoints: {
|
|
list: () => apiFetch<ChargePoint[]>("/api/charge-points"),
|
|
get: (id: number) => apiFetch<ChargePoint>(`/api/charge-points/${id}`),
|
|
create: (data: {
|
|
chargePointIdentifier: string;
|
|
chargePointVendor?: string;
|
|
chargePointModel?: string;
|
|
registrationStatus?: "Accepted" | "Pending" | "Rejected";
|
|
feePerKwh?: number;
|
|
}) =>
|
|
apiFetch<ChargePoint>("/api/charge-points", {
|
|
method: "POST",
|
|
body: JSON.stringify(data),
|
|
}),
|
|
update: (id: string, data: {
|
|
feePerKwh?: number;
|
|
registrationStatus?: "Accepted" | "Pending" | "Rejected";
|
|
chargePointVendor?: string;
|
|
chargePointModel?: string;
|
|
}) =>
|
|
apiFetch<ChargePoint>(`/api/charge-points/${id}`, {
|
|
method: "PATCH",
|
|
body: JSON.stringify(data),
|
|
}),
|
|
delete: (id: string) =>
|
|
apiFetch<{ success: true }>(`/api/charge-points/${id}`, { method: "DELETE" }),
|
|
},
|
|
transactions: {
|
|
list: (params?: { page?: number; limit?: number; status?: "active" | "completed" }) => {
|
|
const q = new URLSearchParams();
|
|
if (params?.page) q.set("page", String(params.page));
|
|
if (params?.limit) q.set("limit", String(params.limit));
|
|
if (params?.status) q.set("status", params.status);
|
|
const qs = q.toString();
|
|
return apiFetch<PaginatedTransactions>(`/api/transactions${qs ? "?" + qs : ""}`);
|
|
},
|
|
get: (id: number) => apiFetch<Transaction>(`/api/transactions/${id}`),
|
|
stop: (id: number) =>
|
|
apiFetch<Transaction & { online: boolean }>(`/api/transactions/${id}/stop`, {
|
|
method: "POST",
|
|
}),
|
|
delete: (id: number) =>
|
|
apiFetch<{ success: true }>(`/api/transactions/${id}`, { method: "DELETE" }),
|
|
},
|
|
idTags: {
|
|
list: () => apiFetch<IdTag[]>("/api/id-tags"),
|
|
get: (idTag: string) => apiFetch<IdTag>(`/api/id-tags/${idTag}`),
|
|
create: (data: {
|
|
idTag: string;
|
|
status?: string;
|
|
expiryDate?: string;
|
|
parentIdTag?: string;
|
|
userId?: string | null;
|
|
balance?: number;
|
|
}) => apiFetch<IdTag>("/api/id-tags", { method: "POST", body: JSON.stringify(data) }),
|
|
update: (
|
|
idTag: string,
|
|
data: {
|
|
status?: string;
|
|
expiryDate?: string | null;
|
|
parentIdTag?: string | null;
|
|
userId?: string | null;
|
|
balance?: number;
|
|
},
|
|
) => apiFetch<IdTag>(`/api/id-tags/${idTag}`, { method: "PATCH", body: JSON.stringify(data) }),
|
|
delete: (idTag: string) =>
|
|
apiFetch<{ success: true }>(`/api/id-tags/${idTag}`, { method: "DELETE" }),
|
|
},
|
|
users: {
|
|
list: () => apiFetch<UserRow[]>("/api/users"),
|
|
create: (data: {
|
|
name: string;
|
|
email: string;
|
|
password: string;
|
|
username?: string;
|
|
role?: string;
|
|
}) =>
|
|
apiFetch<{ user: UserRow }>("/api/auth/admin/create-user", {
|
|
method: "POST",
|
|
body: JSON.stringify(data),
|
|
}),
|
|
update: (
|
|
id: string,
|
|
data: {
|
|
name?: string;
|
|
username?: string | null;
|
|
role?: string;
|
|
banned?: boolean;
|
|
banReason?: string | null;
|
|
},
|
|
) => apiFetch<UserRow>(`/api/users/${id}`, { method: "PATCH", body: JSON.stringify(data) }),
|
|
},
|
|
};
|