mirror of
https://github.com/andwati/cymelle.git
synced 2026-11-09 18:25:13 +00:00
paystack
This commit is contained in:
@@ -1,25 +1,25 @@
|
||||
import {apiRequest, clearRequestSecurity} from "#/api/client";
|
||||
import type {AuthUser, LoginRequest, RegisterRequest} from "#/types/auth";
|
||||
import { apiRequest, clearRequestSecurity } from "#/api/client";
|
||||
import type { AuthUser, LoginRequest, RegisterRequest } from "#/types/auth";
|
||||
|
||||
export function getCurrentUser(signal?: AbortSignal) {
|
||||
return apiRequest<AuthUser>("/auth/me", {signal});
|
||||
return apiRequest<AuthUser>("/auth/me", { signal });
|
||||
}
|
||||
|
||||
export function login(request: LoginRequest) {
|
||||
return apiRequest<AuthUser>("/auth/login", {
|
||||
method: "POST",
|
||||
body: request,
|
||||
});
|
||||
return apiRequest<AuthUser>("/auth/login", {
|
||||
method: "POST",
|
||||
body: request,
|
||||
});
|
||||
}
|
||||
|
||||
export function register(request: RegisterRequest) {
|
||||
return apiRequest<AuthUser>("/auth/register", {
|
||||
method: "POST",
|
||||
body: request,
|
||||
});
|
||||
return apiRequest<AuthUser>("/auth/register", {
|
||||
method: "POST",
|
||||
body: request,
|
||||
});
|
||||
}
|
||||
|
||||
export async function logout() {
|
||||
await apiRequest<void>("/auth/logout", {method: "POST"});
|
||||
clearRequestSecurity();
|
||||
await apiRequest<void>("/auth/logout", { method: "POST" });
|
||||
clearRequestSecurity();
|
||||
}
|
||||
|
||||
@@ -1,110 +1,112 @@
|
||||
import type {ApiErrorResponse} from "#/types/api";
|
||||
import type { ApiErrorResponse } from "#/types/api";
|
||||
|
||||
const API_BASE_URL =
|
||||
import.meta.env.VITE_API_BASE_URL ?? "http://localhost:8080/api";
|
||||
import.meta.env.VITE_API_BASE_URL ?? "http://localhost:8080/api";
|
||||
|
||||
let csrfToken: string | null = null;
|
||||
let csrfHeaderName = "X-XSRF-TOKEN";
|
||||
|
||||
export class ApiError extends Error {
|
||||
status: number;
|
||||
code: string;
|
||||
details: unknown;
|
||||
status: number;
|
||||
code: string;
|
||||
details: unknown;
|
||||
|
||||
constructor(status: number, error: ApiErrorResponse) {
|
||||
super(error.message || "Request failed");
|
||||
this.name = "ApiError";
|
||||
this.status = status;
|
||||
this.code = error.error;
|
||||
this.details = error.details;
|
||||
}
|
||||
constructor(status: number, error: ApiErrorResponse) {
|
||||
super(error.message || "Request failed");
|
||||
this.name = "ApiError";
|
||||
this.status = status;
|
||||
this.code = error.error;
|
||||
this.details = error.details;
|
||||
}
|
||||
}
|
||||
|
||||
type RequestOptions = {
|
||||
method?: "GET" | "POST" | "DELETE" | "PUT" | "PATCH";
|
||||
body?: unknown;
|
||||
signal?: AbortSignal;
|
||||
method?: "GET" | "POST" | "DELETE" | "PUT" | "PATCH";
|
||||
body?: unknown;
|
||||
signal?: AbortSignal;
|
||||
};
|
||||
|
||||
type CsrfResponse = {
|
||||
headerName: string;
|
||||
parameterName: string;
|
||||
token: string;
|
||||
headerName: string;
|
||||
parameterName: string;
|
||||
token: string;
|
||||
};
|
||||
|
||||
async function loadCsrfToken() {
|
||||
const response = await fetch(`${API_BASE_URL}/auth/csrf`, {
|
||||
credentials: "include",
|
||||
});
|
||||
const response = await fetch(`${API_BASE_URL}/auth/csrf`, {
|
||||
credentials: "include",
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error("Could not initialize request security");
|
||||
}
|
||||
if (!response.ok) {
|
||||
throw new Error("Could not initialize request security");
|
||||
}
|
||||
|
||||
const data = (await response.json()) as CsrfResponse;
|
||||
csrfToken = data.token;
|
||||
csrfHeaderName = data.headerName;
|
||||
const data = (await response.json()) as CsrfResponse;
|
||||
csrfToken = data.token;
|
||||
csrfHeaderName = data.headerName;
|
||||
}
|
||||
|
||||
function isMutating(method: string) {
|
||||
return method !== "GET";
|
||||
return method !== "GET";
|
||||
}
|
||||
|
||||
export async function apiRequest<T>(
|
||||
path: string,
|
||||
options: RequestOptions = {},
|
||||
path: string,
|
||||
options: RequestOptions = {},
|
||||
): Promise<T> {
|
||||
const method = options.method ?? "GET";
|
||||
const method = options.method ?? "GET";
|
||||
|
||||
if (isMutating(method) && !csrfToken) {
|
||||
await loadCsrfToken();
|
||||
}
|
||||
if (isMutating(method) && !csrfToken) {
|
||||
await loadCsrfToken();
|
||||
}
|
||||
|
||||
const response = await fetch(`${API_BASE_URL}${path}`, {
|
||||
method,
|
||||
credentials: "include",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
...(isMutating(method) && csrfToken ? {[csrfHeaderName]: csrfToken} : {}),
|
||||
},
|
||||
body: options.body ? JSON.stringify(options.body) : undefined,
|
||||
signal: options.signal,
|
||||
});
|
||||
const response = await fetch(`${API_BASE_URL}${path}`, {
|
||||
method,
|
||||
credentials: "include",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
...(isMutating(method) && csrfToken
|
||||
? { [csrfHeaderName]: csrfToken }
|
||||
: {}),
|
||||
},
|
||||
body: options.body ? JSON.stringify(options.body) : undefined,
|
||||
signal: options.signal,
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
let errorBody: ApiErrorResponse;
|
||||
if (!response.ok) {
|
||||
let errorBody: ApiErrorResponse;
|
||||
|
||||
try {
|
||||
errorBody = await response.json();
|
||||
} catch {
|
||||
errorBody = {
|
||||
error: "REQUEST_FAILED",
|
||||
message: `Request failed with status ${response.status}`,
|
||||
details: null,
|
||||
timestamp: new Date().toISOString(),
|
||||
path,
|
||||
};
|
||||
}
|
||||
try {
|
||||
errorBody = await response.json();
|
||||
} catch {
|
||||
errorBody = {
|
||||
error: "REQUEST_FAILED",
|
||||
message: `Request failed with status ${response.status}`,
|
||||
details: null,
|
||||
timestamp: new Date().toISOString(),
|
||||
path,
|
||||
};
|
||||
}
|
||||
|
||||
throw new ApiError(response.status, errorBody);
|
||||
}
|
||||
throw new ApiError(response.status, errorBody);
|
||||
}
|
||||
|
||||
if (response.status === 204) {
|
||||
return undefined as T;
|
||||
}
|
||||
if (response.status === 204) {
|
||||
return undefined as T;
|
||||
}
|
||||
|
||||
const text = await response.text();
|
||||
if (!text) {
|
||||
return undefined as T;
|
||||
}
|
||||
const text = await response.text();
|
||||
if (!text) {
|
||||
return undefined as T;
|
||||
}
|
||||
|
||||
return JSON.parse(text) as T;
|
||||
return JSON.parse(text) as T;
|
||||
}
|
||||
|
||||
export function clearRequestSecurity() {
|
||||
csrfToken = null;
|
||||
csrfToken = null;
|
||||
}
|
||||
|
||||
export async function initializeRequestSecurity() {
|
||||
await loadCsrfToken();
|
||||
await loadCsrfToken();
|
||||
}
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
import {apiRequest} from "#/api/client";
|
||||
import type {FareCalculationResponse} from "#/types/fare";
|
||||
import { apiRequest } from "#/api/client";
|
||||
import type { FareCalculationResponse } from "#/types/fare";
|
||||
|
||||
export function calculateFare(
|
||||
distanceKm: number,
|
||||
surgeMultiplier?: number,
|
||||
signal?: AbortSignal,
|
||||
distanceKm: number,
|
||||
surgeMultiplier?: number,
|
||||
signal?: AbortSignal,
|
||||
) {
|
||||
const params = new URLSearchParams();
|
||||
params.set("distanceKm", String(distanceKm));
|
||||
const params = new URLSearchParams();
|
||||
params.set("distanceKm", String(distanceKm));
|
||||
|
||||
if (surgeMultiplier !== undefined) {
|
||||
params.set("surgeMultiplier", String(surgeMultiplier));
|
||||
}
|
||||
if (surgeMultiplier !== undefined) {
|
||||
params.set("surgeMultiplier", String(surgeMultiplier));
|
||||
}
|
||||
|
||||
return apiRequest<FareCalculationResponse>(
|
||||
`/fare/calculate?${params.toString()}`,
|
||||
{signal},
|
||||
);
|
||||
}
|
||||
return apiRequest<FareCalculationResponse>(
|
||||
`/fare/calculate?${params.toString()}`,
|
||||
{ signal },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
import {apiRequest} from "#/api/client";
|
||||
import type {InventoryResponse, LowStockInventoryResponse,} from "#/types/inventory";
|
||||
import { apiRequest } from "#/api/client";
|
||||
import type {
|
||||
InventoryResponse,
|
||||
LowStockInventoryResponse,
|
||||
} from "#/types/inventory";
|
||||
|
||||
export function getInventory(signal?: AbortSignal) {
|
||||
return apiRequest<InventoryResponse>("/inventory", {signal});
|
||||
return apiRequest<InventoryResponse>("/inventory", { signal });
|
||||
}
|
||||
|
||||
export function getLowStockInventory(
|
||||
threshold?: number,
|
||||
signal?: AbortSignal,
|
||||
) {
|
||||
const params = new URLSearchParams();
|
||||
export function getLowStockInventory(threshold?: number, signal?: AbortSignal) {
|
||||
const params = new URLSearchParams();
|
||||
|
||||
if (threshold !== undefined) {
|
||||
params.set("threshold", String(threshold));
|
||||
}
|
||||
if (threshold !== undefined) {
|
||||
params.set("threshold", String(threshold));
|
||||
}
|
||||
|
||||
const query = params.toString();
|
||||
const query = params.toString();
|
||||
|
||||
return apiRequest<LowStockInventoryResponse>(
|
||||
`/inventory/low-stock${query ? `?${query}` : ""}`,
|
||||
{signal},
|
||||
);
|
||||
}
|
||||
return apiRequest<LowStockInventoryResponse>(
|
||||
`/inventory/low-stock${query ? `?${query}` : ""}`,
|
||||
{ signal },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,62 +1,65 @@
|
||||
import {apiRequest} from "#/api/client";
|
||||
import type {PageResponse} from "#/types/api";
|
||||
import { apiRequest } from "#/api/client";
|
||||
import type { PageResponse } from "#/types/api";
|
||||
import type {
|
||||
CancelOrderResponse,
|
||||
CreateOrderRequest,
|
||||
Order,
|
||||
OrderFilters,
|
||||
OrderStatus,
|
||||
OrderSummary,
|
||||
CancelOrderResponse,
|
||||
CreateOrderRequest,
|
||||
Order,
|
||||
OrderFilters,
|
||||
OrderStatus,
|
||||
OrderSummary,
|
||||
} from "#/types/order";
|
||||
|
||||
export function getOrders(
|
||||
filters: OrderFilters,
|
||||
page = 0,
|
||||
size = 20,
|
||||
signal?: AbortSignal,
|
||||
filters: OrderFilters,
|
||||
page = 0,
|
||||
size = 20,
|
||||
signal?: AbortSignal,
|
||||
) {
|
||||
const params = new URLSearchParams();
|
||||
const params = new URLSearchParams();
|
||||
|
||||
if (filters.status) {
|
||||
params.set("status", filters.status);
|
||||
}
|
||||
if (filters.status) {
|
||||
params.set("status", filters.status);
|
||||
}
|
||||
|
||||
if (filters.from) {
|
||||
params.set("from", filters.from);
|
||||
}
|
||||
if (filters.from) {
|
||||
params.set("from", filters.from);
|
||||
}
|
||||
|
||||
if (filters.to) {
|
||||
params.set("to", filters.to);
|
||||
}
|
||||
if (filters.to) {
|
||||
params.set("to", filters.to);
|
||||
}
|
||||
|
||||
params.set("page", String(page));
|
||||
params.set("size", String(size));
|
||||
params.set("page", String(page));
|
||||
params.set("size", String(size));
|
||||
|
||||
return apiRequest<PageResponse<OrderSummary>>(`/orders?${params.toString()}`, {
|
||||
signal,
|
||||
});
|
||||
return apiRequest<PageResponse<OrderSummary>>(
|
||||
`/orders?${params.toString()}`,
|
||||
{
|
||||
signal,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
export function getOrder(id: string, signal?: AbortSignal) {
|
||||
return apiRequest<Order>(`/orders/${id}`, {signal});
|
||||
return apiRequest<Order>(`/orders/${id}`, { signal });
|
||||
}
|
||||
|
||||
export function createOrder(request: CreateOrderRequest) {
|
||||
return apiRequest<Order>("/orders", {
|
||||
method: "POST",
|
||||
body: request,
|
||||
});
|
||||
return apiRequest<Order>("/orders", {
|
||||
method: "POST",
|
||||
body: request,
|
||||
});
|
||||
}
|
||||
|
||||
export function cancelOrder(id: string) {
|
||||
return apiRequest<CancelOrderResponse>(`/orders/${id}`, {
|
||||
method: "DELETE",
|
||||
});
|
||||
return apiRequest<CancelOrderResponse>(`/orders/${id}`, {
|
||||
method: "DELETE",
|
||||
});
|
||||
}
|
||||
|
||||
export function updateOrderStatus(id: string, status: OrderStatus) {
|
||||
return apiRequest<Order>(`/orders/${id}/status`, {
|
||||
method: "PATCH",
|
||||
body: {status},
|
||||
});
|
||||
return apiRequest<Order>(`/orders/${id}/status`, {
|
||||
method: "PATCH",
|
||||
body: { status },
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
import { apiRequest } from "#/api/client";
|
||||
import type {
|
||||
CheckoutQuote,
|
||||
CheckoutQuoteRequest,
|
||||
PaystackInitializeRequest,
|
||||
PaystackInitializeResponse,
|
||||
PaystackVerifyResponse,
|
||||
} from "#/types/payment";
|
||||
|
||||
export function getCheckoutQuote(
|
||||
request: CheckoutQuoteRequest,
|
||||
signal?: AbortSignal,
|
||||
) {
|
||||
return apiRequest<CheckoutQuote>("/checkout/quote", {
|
||||
method: "POST",
|
||||
body: request,
|
||||
signal,
|
||||
});
|
||||
}
|
||||
|
||||
export function initializePaystackPayment(request: PaystackInitializeRequest) {
|
||||
return apiRequest<PaystackInitializeResponse>(
|
||||
"/payments/paystack/initialize",
|
||||
{
|
||||
method: "POST",
|
||||
body: request,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
export function verifyPaystackPayment(reference: string) {
|
||||
return apiRequest<PaystackVerifyResponse>(
|
||||
`/payments/paystack/verify/${encodeURIComponent(reference)}`,
|
||||
{
|
||||
method: "POST",
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -1,28 +1,28 @@
|
||||
import {apiRequest} from "#/api/client";
|
||||
import type {Product, ProductRequest} from "#/types/product";
|
||||
import { apiRequest } from "#/api/client";
|
||||
import type { Product, ProductRequest } from "#/types/product";
|
||||
|
||||
export function getProducts(signal?: AbortSignal) {
|
||||
return apiRequest<Product[]>("/products", {signal});
|
||||
return apiRequest<Product[]>("/products", { signal });
|
||||
}
|
||||
|
||||
export function getProduct(id: string, signal?: AbortSignal) {
|
||||
return apiRequest<Product>(`/products/${id}`, {signal});
|
||||
return apiRequest<Product>(`/products/${id}`, { signal });
|
||||
}
|
||||
|
||||
export function createProduct(request: ProductRequest) {
|
||||
return apiRequest<Product>("/products", {
|
||||
method: "POST",
|
||||
body: request,
|
||||
});
|
||||
return apiRequest<Product>("/products", {
|
||||
method: "POST",
|
||||
body: request,
|
||||
});
|
||||
}
|
||||
|
||||
export function updateProduct(id: string, request: ProductRequest) {
|
||||
return apiRequest<Product>(`/products/${id}`, {
|
||||
method: "PUT",
|
||||
body: request,
|
||||
});
|
||||
return apiRequest<Product>(`/products/${id}`, {
|
||||
method: "PUT",
|
||||
body: request,
|
||||
});
|
||||
}
|
||||
|
||||
export function deactivateProduct(id: string) {
|
||||
return apiRequest<Product>(`/products/${id}`, {method: "DELETE"});
|
||||
return apiRequest<Product>(`/products/${id}`, { method: "DELETE" });
|
||||
}
|
||||
|
||||
@@ -1,32 +1,32 @@
|
||||
import {apiRequest} from "#/api/client";
|
||||
import type {CreateRideRequest, Ride, RideStatus} from "#/types/ride";
|
||||
import { apiRequest } from "#/api/client";
|
||||
import type { CreateRideRequest, Ride, RideStatus } from "#/types/ride";
|
||||
|
||||
export function getRides(signal?: AbortSignal) {
|
||||
return apiRequest<Ride[]>("/rides", {signal});
|
||||
return apiRequest<Ride[]>("/rides", { signal });
|
||||
}
|
||||
|
||||
export function requestRide(request: CreateRideRequest) {
|
||||
return apiRequest<Ride>("/rides", {
|
||||
method: "POST",
|
||||
body: request,
|
||||
});
|
||||
return apiRequest<Ride>("/rides", {
|
||||
method: "POST",
|
||||
body: request,
|
||||
});
|
||||
}
|
||||
|
||||
export function acceptRide(id: string) {
|
||||
return apiRequest<Ride>(`/rides/${id}/accept`, {method: "POST"});
|
||||
return apiRequest<Ride>(`/rides/${id}/accept`, { method: "POST" });
|
||||
}
|
||||
|
||||
export function completeRide(id: string) {
|
||||
return apiRequest<Ride>(`/rides/${id}/complete`, {method: "POST"});
|
||||
return apiRequest<Ride>(`/rides/${id}/complete`, { method: "POST" });
|
||||
}
|
||||
|
||||
export function cancelRide(id: string) {
|
||||
return apiRequest<Ride>(`/rides/${id}`, {method: "DELETE"});
|
||||
return apiRequest<Ride>(`/rides/${id}`, { method: "DELETE" });
|
||||
}
|
||||
|
||||
export function updateRideStatus(id: string, status: RideStatus) {
|
||||
return apiRequest<Ride>(`/rides/${id}/status`, {
|
||||
method: "PATCH",
|
||||
body: {status},
|
||||
});
|
||||
return apiRequest<Ride>(`/rides/${id}/status`, {
|
||||
method: "PATCH",
|
||||
body: { status },
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,32 +1,32 @@
|
||||
import {apiRequest} from "#/api/client";
|
||||
import type {AdminUserRequest, UserSummary} from "#/types/user";
|
||||
import { apiRequest } from "#/api/client";
|
||||
import type { AdminUserRequest, UserSummary } from "#/types/user";
|
||||
|
||||
export function getUsers(signal?: AbortSignal) {
|
||||
return apiRequest<UserSummary[]>("/users", {signal});
|
||||
return apiRequest<UserSummary[]>("/users", { signal });
|
||||
}
|
||||
|
||||
export function getCustomers(signal?: AbortSignal) {
|
||||
return apiRequest<UserSummary[]>("/users/customers", {signal});
|
||||
return apiRequest<UserSummary[]>("/users/customers", { signal });
|
||||
}
|
||||
|
||||
export function getDrivers(signal?: AbortSignal) {
|
||||
return apiRequest<UserSummary[]>("/users/drivers", {signal});
|
||||
return apiRequest<UserSummary[]>("/users/drivers", { signal });
|
||||
}
|
||||
|
||||
export function createUser(request: AdminUserRequest) {
|
||||
return apiRequest<UserSummary>("/users", {
|
||||
method: "POST",
|
||||
body: request,
|
||||
});
|
||||
return apiRequest<UserSummary>("/users", {
|
||||
method: "POST",
|
||||
body: request,
|
||||
});
|
||||
}
|
||||
|
||||
export function updateUser(id: string, request: AdminUserRequest) {
|
||||
return apiRequest<UserSummary>(`/users/${id}`, {
|
||||
method: "PUT",
|
||||
body: request,
|
||||
});
|
||||
return apiRequest<UserSummary>(`/users/${id}`, {
|
||||
method: "PUT",
|
||||
body: request,
|
||||
});
|
||||
}
|
||||
|
||||
export function disableUser(id: string) {
|
||||
return apiRequest<UserSummary>(`/users/${id}`, {method: "DELETE"});
|
||||
return apiRequest<UserSummary>(`/users/${id}`, { method: "DELETE" });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user