mirror of
https://github.com/andwati/cymelle.git
synced 2026-11-14 04:35:11 +00:00
paystack
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
export function formatCurrency(amount: number, currency = "KES") {
|
||||
return new Intl.NumberFormat("en-KE", {
|
||||
style: "currency",
|
||||
currency,
|
||||
maximumFractionDigits: 2,
|
||||
}).format(amount);
|
||||
}
|
||||
return new Intl.NumberFormat("en-KE", {
|
||||
style: "currency",
|
||||
currency,
|
||||
maximumFractionDigits: 2,
|
||||
}).format(amount);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
export function formatDate(value: string | null | undefined) {
|
||||
if (!value) {
|
||||
return "—";
|
||||
}
|
||||
if (!value) {
|
||||
return "—";
|
||||
}
|
||||
|
||||
return new Intl.DateTimeFormat("en-KE", {
|
||||
dateStyle: "medium",
|
||||
timeStyle: "short",
|
||||
}).format(new Date(value));
|
||||
}
|
||||
return new Intl.DateTimeFormat("en-KE", {
|
||||
dateStyle: "medium",
|
||||
timeStyle: "short",
|
||||
}).format(new Date(value));
|
||||
}
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
import * as React from "react"
|
||||
import * as React from "react";
|
||||
|
||||
const MOBILE_BREAKPOINT = 768
|
||||
const MOBILE_BREAKPOINT = 768;
|
||||
|
||||
export function useIsMobile() {
|
||||
const [isMobile, setIsMobile] = React.useState<boolean | undefined>(undefined)
|
||||
const [isMobile, setIsMobile] = React.useState<boolean | undefined>(
|
||||
undefined,
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`)
|
||||
const onChange = () => {
|
||||
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
|
||||
}
|
||||
mql.addEventListener("change", onChange)
|
||||
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
|
||||
return () => mql.removeEventListener("change", onChange)
|
||||
}, [])
|
||||
React.useEffect(() => {
|
||||
const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);
|
||||
const onChange = () => {
|
||||
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
|
||||
};
|
||||
mql.addEventListener("change", onChange);
|
||||
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
|
||||
return () => mql.removeEventListener("change", onChange);
|
||||
}, []);
|
||||
|
||||
return !!isMobile
|
||||
return !!isMobile;
|
||||
}
|
||||
|
||||
@@ -1,111 +1,111 @@
|
||||
import {login, logout, register} from "#/api/auth";
|
||||
import type {AuthUser, LoginRequest, RegisterRequest} from "#/types/auth";
|
||||
import {useMutation, useQueryClient} from "@tanstack/react-query";
|
||||
import {createContext, useContext, useState} from "react";
|
||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import { createContext, useContext, useState } from "react";
|
||||
import { login, logout, register } from "#/api/auth";
|
||||
import type { AuthUser, LoginRequest, RegisterRequest } from "#/types/auth";
|
||||
|
||||
type AuthContextValue = {
|
||||
user: AuthUser | null;
|
||||
isLoading: boolean;
|
||||
login: (request: LoginRequest) => Promise<AuthUser>;
|
||||
register: (request: RegisterRequest) => Promise<AuthUser>;
|
||||
logout: () => Promise<void>;
|
||||
user: AuthUser | null;
|
||||
isLoading: boolean;
|
||||
login: (request: LoginRequest) => Promise<AuthUser>;
|
||||
register: (request: RegisterRequest) => Promise<AuthUser>;
|
||||
logout: () => Promise<void>;
|
||||
};
|
||||
|
||||
const AuthContext = createContext<AuthContextValue | null>(null);
|
||||
const storedUserKey = "cymelle.auth.user";
|
||||
|
||||
export const authQueryKeys = {
|
||||
me: ["auth", "me"] as const,
|
||||
me: ["auth", "me"] as const,
|
||||
};
|
||||
|
||||
export function AuthProvider({children}: {children: React.ReactNode}) {
|
||||
const queryClient = useQueryClient();
|
||||
const [user, setUser] = useState<AuthUser | null>(() => readStoredUser());
|
||||
export function AuthProvider({ children }: { children: React.ReactNode }) {
|
||||
const queryClient = useQueryClient();
|
||||
const [user, setUser] = useState<AuthUser | null>(() => readStoredUser());
|
||||
|
||||
const loginMutation = useMutation({
|
||||
mutationFn: login,
|
||||
onSuccess: (user) => {
|
||||
queryClient.clear();
|
||||
persistUser(user);
|
||||
setUser(user);
|
||||
queryClient.setQueryData(authQueryKeys.me, user);
|
||||
},
|
||||
});
|
||||
const loginMutation = useMutation({
|
||||
mutationFn: login,
|
||||
onSuccess: (user) => {
|
||||
queryClient.clear();
|
||||
persistUser(user);
|
||||
setUser(user);
|
||||
queryClient.setQueryData(authQueryKeys.me, user);
|
||||
},
|
||||
});
|
||||
|
||||
const registerMutation = useMutation({
|
||||
mutationFn: register,
|
||||
onSuccess: (user) => {
|
||||
queryClient.clear();
|
||||
persistUser(user);
|
||||
setUser(user);
|
||||
queryClient.setQueryData(authQueryKeys.me, user);
|
||||
},
|
||||
});
|
||||
const registerMutation = useMutation({
|
||||
mutationFn: register,
|
||||
onSuccess: (user) => {
|
||||
queryClient.clear();
|
||||
persistUser(user);
|
||||
setUser(user);
|
||||
queryClient.setQueryData(authQueryKeys.me, user);
|
||||
},
|
||||
});
|
||||
|
||||
const logoutMutation = useMutation({
|
||||
mutationFn: logout,
|
||||
onSettled: () => {
|
||||
persistUser(null);
|
||||
setUser(null);
|
||||
queryClient.clear();
|
||||
},
|
||||
});
|
||||
const logoutMutation = useMutation({
|
||||
mutationFn: logout,
|
||||
onSettled: () => {
|
||||
persistUser(null);
|
||||
setUser(null);
|
||||
queryClient.clear();
|
||||
},
|
||||
});
|
||||
|
||||
async function logoutUser() {
|
||||
persistUser(null);
|
||||
setUser(null);
|
||||
queryClient.clear();
|
||||
await logoutMutation.mutateAsync().catch(() => undefined);
|
||||
}
|
||||
async function logoutUser() {
|
||||
persistUser(null);
|
||||
setUser(null);
|
||||
queryClient.clear();
|
||||
await logoutMutation.mutateAsync().catch(() => undefined);
|
||||
}
|
||||
|
||||
const value: AuthContextValue = {
|
||||
user,
|
||||
isLoading: false,
|
||||
login: loginMutation.mutateAsync,
|
||||
register: registerMutation.mutateAsync,
|
||||
logout: logoutUser,
|
||||
};
|
||||
const value: AuthContextValue = {
|
||||
user,
|
||||
isLoading: false,
|
||||
login: loginMutation.mutateAsync,
|
||||
register: registerMutation.mutateAsync,
|
||||
logout: logoutUser,
|
||||
};
|
||||
|
||||
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
|
||||
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
|
||||
}
|
||||
|
||||
export function useAuth() {
|
||||
const context = useContext(AuthContext);
|
||||
const context = useContext(AuthContext);
|
||||
|
||||
if (!context) {
|
||||
throw new Error("useAuth must be used within AuthProvider");
|
||||
}
|
||||
if (!context) {
|
||||
throw new Error("useAuth must be used within AuthProvider");
|
||||
}
|
||||
|
||||
return context;
|
||||
return context;
|
||||
}
|
||||
|
||||
function readStoredUser() {
|
||||
if (typeof window === "undefined") {
|
||||
return null;
|
||||
}
|
||||
if (typeof window === "undefined") {
|
||||
return null;
|
||||
}
|
||||
|
||||
const raw = window.localStorage.getItem(storedUserKey);
|
||||
if (!raw) {
|
||||
return null;
|
||||
}
|
||||
const raw = window.localStorage.getItem(storedUserKey);
|
||||
if (!raw) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return JSON.parse(raw) as AuthUser;
|
||||
} catch {
|
||||
window.localStorage.removeItem(storedUserKey);
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return JSON.parse(raw) as AuthUser;
|
||||
} catch {
|
||||
window.localStorage.removeItem(storedUserKey);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function persistUser(user: AuthUser | null) {
|
||||
if (typeof window === "undefined") {
|
||||
return;
|
||||
}
|
||||
if (typeof window === "undefined") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!user) {
|
||||
window.localStorage.removeItem(storedUserKey);
|
||||
return;
|
||||
}
|
||||
if (!user) {
|
||||
window.localStorage.removeItem(storedUserKey);
|
||||
return;
|
||||
}
|
||||
|
||||
window.localStorage.setItem(storedUserKey, JSON.stringify(user));
|
||||
window.localStorage.setItem(storedUserKey, JSON.stringify(user));
|
||||
}
|
||||
|
||||
@@ -1,121 +1,140 @@
|
||||
import type {Product} from "#/types/product";
|
||||
import {createContext, useContext, useEffect, useMemo, useState} from "react";
|
||||
import { createContext, useContext, useEffect, useMemo, useState } from "react";
|
||||
import type { Product } from "#/types/product";
|
||||
|
||||
export type CartItem = {
|
||||
product: Product;
|
||||
quantity: number;
|
||||
product: Product;
|
||||
quantity: number;
|
||||
};
|
||||
|
||||
type CartContextValue = {
|
||||
items: CartItem[];
|
||||
itemCount: number;
|
||||
totalAmount: number;
|
||||
currency: string;
|
||||
addItem: (product: Product, quantity: number) => void;
|
||||
updateQuantity: (productId: string, quantity: number) => void;
|
||||
removeItem: (productId: string) => void;
|
||||
clearCart: () => void;
|
||||
items: CartItem[];
|
||||
itemCount: number;
|
||||
totalAmount: number;
|
||||
currency: string;
|
||||
addItem: (product: Product, quantity: number) => void;
|
||||
updateQuantity: (productId: string, quantity: number) => void;
|
||||
removeItem: (productId: string) => void;
|
||||
clearCart: () => void;
|
||||
};
|
||||
|
||||
const CartContext = createContext<CartContextValue | null>(null);
|
||||
const CART_STORAGE_KEY = "cymelle_cart";
|
||||
|
||||
export function CartProvider({children}: {children: React.ReactNode}) {
|
||||
const [items, setItems] = useState<CartItem[]>(() => {
|
||||
if (typeof window === "undefined") {
|
||||
return [];
|
||||
}
|
||||
export function CartProvider({ children }: { children: React.ReactNode }) {
|
||||
const [items, setItems] = useState<CartItem[]>(() => {
|
||||
if (typeof window === "undefined") {
|
||||
return [];
|
||||
}
|
||||
|
||||
try {
|
||||
return JSON.parse(window.localStorage.getItem(CART_STORAGE_KEY) ?? "[]") as CartItem[];
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
});
|
||||
try {
|
||||
return JSON.parse(
|
||||
window.localStorage.getItem(CART_STORAGE_KEY) ?? "[]",
|
||||
) as CartItem[];
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
});
|
||||
|
||||
const itemCount = useMemo(
|
||||
() => items.reduce((total, item) => total + item.quantity, 0),
|
||||
[items],
|
||||
);
|
||||
const itemCount = useMemo(
|
||||
() => items.reduce((total, item) => total + item.quantity, 0),
|
||||
[items],
|
||||
);
|
||||
|
||||
const totalAmount = useMemo(
|
||||
() => items.reduce((total, item) => total + item.product.price * item.quantity, 0),
|
||||
[items],
|
||||
);
|
||||
const totalAmount = useMemo(
|
||||
() =>
|
||||
items.reduce(
|
||||
(total, item) => total + item.product.price * item.quantity,
|
||||
0,
|
||||
),
|
||||
[items],
|
||||
);
|
||||
|
||||
const currency = items[0]?.product.currency ?? "KES";
|
||||
const currency = items[0]?.product.currency ?? "KES";
|
||||
|
||||
useEffect(() => {
|
||||
window.localStorage.setItem(CART_STORAGE_KEY, JSON.stringify(items));
|
||||
}, [items]);
|
||||
useEffect(() => {
|
||||
window.localStorage.setItem(CART_STORAGE_KEY, JSON.stringify(items));
|
||||
}, [items]);
|
||||
|
||||
function addItem(product: Product, quantity: number) {
|
||||
const safeQuantity = Math.min(Math.max(quantity, 1), product.availableQuantity);
|
||||
function addItem(product: Product, quantity: number) {
|
||||
const safeQuantity = Math.min(
|
||||
Math.max(quantity, 1),
|
||||
product.availableQuantity,
|
||||
);
|
||||
|
||||
setItems((currentItems) => {
|
||||
const existing = currentItems.find((item) => item.product.id === product.id);
|
||||
setItems((currentItems) => {
|
||||
const existing = currentItems.find(
|
||||
(item) => item.product.id === product.id,
|
||||
);
|
||||
|
||||
if (existing) {
|
||||
return currentItems.map((item) =>
|
||||
item.product.id === product.id
|
||||
? {
|
||||
...item,
|
||||
product,
|
||||
quantity: Math.min(item.quantity + safeQuantity, product.availableQuantity),
|
||||
}
|
||||
: item,
|
||||
);
|
||||
}
|
||||
if (existing) {
|
||||
return currentItems.map((item) =>
|
||||
item.product.id === product.id
|
||||
? {
|
||||
...item,
|
||||
product,
|
||||
quantity: Math.min(
|
||||
item.quantity + safeQuantity,
|
||||
product.availableQuantity,
|
||||
),
|
||||
}
|
||||
: item,
|
||||
);
|
||||
}
|
||||
|
||||
return [...currentItems, {product, quantity: safeQuantity}];
|
||||
});
|
||||
}
|
||||
return [...currentItems, { product, quantity: safeQuantity }];
|
||||
});
|
||||
}
|
||||
|
||||
function updateQuantity(productId: string, quantity: number) {
|
||||
setItems((currentItems) =>
|
||||
currentItems.flatMap((item) => {
|
||||
if (item.product.id !== productId) {
|
||||
return [item];
|
||||
}
|
||||
function updateQuantity(productId: string, quantity: number) {
|
||||
setItems((currentItems) =>
|
||||
currentItems.flatMap((item) => {
|
||||
if (item.product.id !== productId) {
|
||||
return [item];
|
||||
}
|
||||
|
||||
const nextQuantity = Math.min(Math.max(quantity, 0), item.product.availableQuantity);
|
||||
return nextQuantity === 0 ? [] : [{...item, quantity: nextQuantity}];
|
||||
}),
|
||||
);
|
||||
}
|
||||
const nextQuantity = Math.min(
|
||||
Math.max(quantity, 0),
|
||||
item.product.availableQuantity,
|
||||
);
|
||||
return nextQuantity === 0 ? [] : [{ ...item, quantity: nextQuantity }];
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
function removeItem(productId: string) {
|
||||
setItems((currentItems) => currentItems.filter((item) => item.product.id !== productId));
|
||||
}
|
||||
function removeItem(productId: string) {
|
||||
setItems((currentItems) =>
|
||||
currentItems.filter((item) => item.product.id !== productId),
|
||||
);
|
||||
}
|
||||
|
||||
function clearCart() {
|
||||
setItems([]);
|
||||
}
|
||||
function clearCart() {
|
||||
setItems([]);
|
||||
}
|
||||
|
||||
return (
|
||||
<CartContext.Provider
|
||||
value={{
|
||||
items,
|
||||
itemCount,
|
||||
totalAmount,
|
||||
currency,
|
||||
addItem,
|
||||
updateQuantity,
|
||||
removeItem,
|
||||
clearCart,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</CartContext.Provider>
|
||||
);
|
||||
return (
|
||||
<CartContext.Provider
|
||||
value={{
|
||||
items,
|
||||
itemCount,
|
||||
totalAmount,
|
||||
currency,
|
||||
addItem,
|
||||
updateQuantity,
|
||||
removeItem,
|
||||
clearCart,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</CartContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export function useCart() {
|
||||
const context = useContext(CartContext);
|
||||
const context = useContext(CartContext);
|
||||
|
||||
if (!context) {
|
||||
throw new Error("useCart must be used within CartProvider");
|
||||
}
|
||||
if (!context) {
|
||||
throw new Error("useCart must be used within CartProvider");
|
||||
}
|
||||
|
||||
return context;
|
||||
return context;
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import {calculateFare} from "#/api/fare";
|
||||
import {useMutation} from "@tanstack/react-query";
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
import { calculateFare } from "#/api/fare";
|
||||
|
||||
type FareCalculationInput = {
|
||||
distanceKm: number;
|
||||
surgeMultiplier?: number;
|
||||
distanceKm: number;
|
||||
surgeMultiplier?: number;
|
||||
};
|
||||
|
||||
export function useFareCalculation() {
|
||||
return useMutation({
|
||||
mutationFn: ({distanceKm, surgeMultiplier}: FareCalculationInput) =>
|
||||
calculateFare(distanceKm, surgeMultiplier),
|
||||
});
|
||||
}
|
||||
return useMutation({
|
||||
mutationFn: ({ distanceKm, surgeMultiplier }: FareCalculationInput) =>
|
||||
calculateFare(distanceKm, surgeMultiplier),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
import {getInventory, getLowStockInventory} from "#/api/inventory";
|
||||
import {useQuery} from "@tanstack/react-query";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { getInventory, getLowStockInventory } from "#/api/inventory";
|
||||
|
||||
export const inventoryQueryKeys = {
|
||||
all: ["inventory"] as const,
|
||||
list: () => [...inventoryQueryKeys.all, "list"] as const,
|
||||
lowStock: (threshold?: number) =>
|
||||
[...inventoryQueryKeys.all, "low-stock", threshold ?? "default"] as const,
|
||||
all: ["inventory"] as const,
|
||||
list: () => [...inventoryQueryKeys.all, "list"] as const,
|
||||
lowStock: (threshold?: number) =>
|
||||
[...inventoryQueryKeys.all, "low-stock", threshold ?? "default"] as const,
|
||||
};
|
||||
|
||||
export function useInventory() {
|
||||
return useQuery({
|
||||
queryKey: inventoryQueryKeys.list(),
|
||||
queryFn: ({signal}) => getInventory(signal),
|
||||
});
|
||||
return useQuery({
|
||||
queryKey: inventoryQueryKeys.list(),
|
||||
queryFn: ({ signal }) => getInventory(signal),
|
||||
});
|
||||
}
|
||||
|
||||
export function useLowStockInventory(threshold?: number) {
|
||||
return useQuery({
|
||||
queryKey: inventoryQueryKeys.lowStock(threshold),
|
||||
queryFn: ({signal}) => getLowStockInventory(threshold, signal),
|
||||
});
|
||||
}
|
||||
return useQuery({
|
||||
queryKey: inventoryQueryKeys.lowStock(threshold),
|
||||
queryFn: ({ signal }) => getLowStockInventory(threshold, signal),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,64 +1,75 @@
|
||||
import {cancelOrder, createOrder, getOrders, updateOrderStatus} from "#/api/orders";
|
||||
import {inventoryQueryKeys} from "#/hooks/useInventory";
|
||||
import {productQueryKeys} from "#/hooks/useProducts";
|
||||
import type {OrderFilters} from "#/types/order";
|
||||
import {useMutation, useQuery, useQueryClient} from "@tanstack/react-query";
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import {
|
||||
cancelOrder,
|
||||
createOrder,
|
||||
getOrders,
|
||||
updateOrderStatus,
|
||||
} from "#/api/orders";
|
||||
import { inventoryQueryKeys } from "#/hooks/useInventory";
|
||||
import { productQueryKeys } from "#/hooks/useProducts";
|
||||
import type { OrderFilters } from "#/types/order";
|
||||
|
||||
export const orderQueryKeys = {
|
||||
all: ["orders"] as const,
|
||||
list: (filters: OrderFilters, page = 0, size = 20) =>
|
||||
[...orderQueryKeys.all, "list", filters, page, size] as const,
|
||||
all: ["orders"] as const,
|
||||
list: (filters: OrderFilters, page = 0, size = 20) =>
|
||||
[...orderQueryKeys.all, "list", filters, page, size] as const,
|
||||
};
|
||||
|
||||
export function useOrders(filters: OrderFilters, page = 0, size = 20) {
|
||||
return useQuery({
|
||||
queryKey: orderQueryKeys.list(filters, page, size),
|
||||
queryFn: ({signal}) => getOrders(filters, page, size, signal),
|
||||
});
|
||||
return useQuery({
|
||||
queryKey: orderQueryKeys.list(filters, page, size),
|
||||
queryFn: ({ signal }) => getOrders(filters, page, size, signal),
|
||||
});
|
||||
}
|
||||
|
||||
export function useCancelOrder() {
|
||||
const queryClient = useQueryClient();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: cancelOrder,
|
||||
onSuccess: async () => {
|
||||
await Promise.all([
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: orderQueryKeys.all,
|
||||
}),
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: inventoryQueryKeys.all,
|
||||
}),
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: productQueryKeys.all,
|
||||
}),
|
||||
]);
|
||||
},
|
||||
});
|
||||
return useMutation({
|
||||
mutationFn: cancelOrder,
|
||||
onSuccess: async () => {
|
||||
await Promise.all([
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: orderQueryKeys.all,
|
||||
}),
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: inventoryQueryKeys.all,
|
||||
}),
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: productQueryKeys.all,
|
||||
}),
|
||||
]);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateOrder() {
|
||||
const queryClient = useQueryClient();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: createOrder,
|
||||
onSuccess: async () => {
|
||||
await Promise.all([
|
||||
queryClient.invalidateQueries({queryKey: orderQueryKeys.all}),
|
||||
queryClient.invalidateQueries({queryKey: inventoryQueryKeys.all}),
|
||||
queryClient.invalidateQueries({queryKey: productQueryKeys.all}),
|
||||
]);
|
||||
},
|
||||
});
|
||||
return useMutation({
|
||||
mutationFn: createOrder,
|
||||
onSuccess: async () => {
|
||||
await Promise.all([
|
||||
queryClient.invalidateQueries({ queryKey: orderQueryKeys.all }),
|
||||
queryClient.invalidateQueries({ queryKey: inventoryQueryKeys.all }),
|
||||
queryClient.invalidateQueries({ queryKey: productQueryKeys.all }),
|
||||
]);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useUpdateOrderStatus() {
|
||||
const queryClient = useQueryClient();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: ({id, status}: {id: string; status: import("#/types/order").OrderStatus}) =>
|
||||
updateOrderStatus(id, status),
|
||||
onSuccess: () => queryClient.invalidateQueries({queryKey: orderQueryKeys.all}),
|
||||
});
|
||||
return useMutation({
|
||||
mutationFn: ({
|
||||
id,
|
||||
status,
|
||||
}: {
|
||||
id: string;
|
||||
status: import("#/types/order").OrderStatus;
|
||||
}) => updateOrderStatus(id, status),
|
||||
onSuccess: () =>
|
||||
queryClient.invalidateQueries({ queryKey: orderQueryKeys.all }),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import {
|
||||
getCheckoutQuote,
|
||||
initializePaystackPayment,
|
||||
verifyPaystackPayment,
|
||||
} from "#/api/payments";
|
||||
import { inventoryQueryKeys } from "#/hooks/useInventory";
|
||||
import { orderQueryKeys } from "#/hooks/useOrders";
|
||||
import { productQueryKeys } from "#/hooks/useProducts";
|
||||
import type {
|
||||
CheckoutQuoteRequest,
|
||||
PaystackInitializeRequest,
|
||||
} from "#/types/payment";
|
||||
|
||||
export const paymentQueryKeys = {
|
||||
all: ["payments"] as const,
|
||||
quote: (request: CheckoutQuoteRequest) =>
|
||||
[...paymentQueryKeys.all, "quote", request] as const,
|
||||
};
|
||||
|
||||
export function useCheckoutQuote(
|
||||
request: CheckoutQuoteRequest,
|
||||
enabled: boolean,
|
||||
) {
|
||||
return useQuery({
|
||||
queryKey: paymentQueryKeys.quote(request),
|
||||
queryFn: ({ signal }) => getCheckoutQuote(request, signal),
|
||||
enabled,
|
||||
});
|
||||
}
|
||||
|
||||
export function useInitializePaystackPayment() {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: (request: PaystackInitializeRequest) =>
|
||||
initializePaystackPayment(request),
|
||||
onSuccess: async () => {
|
||||
await Promise.all([
|
||||
queryClient.invalidateQueries({ queryKey: orderQueryKeys.all }),
|
||||
queryClient.invalidateQueries({ queryKey: inventoryQueryKeys.all }),
|
||||
queryClient.invalidateQueries({ queryKey: productQueryKeys.all }),
|
||||
]);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useVerifyPaystackPayment() {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: verifyPaystackPayment,
|
||||
onSuccess: async () => {
|
||||
await Promise.all([
|
||||
queryClient.invalidateQueries({ queryKey: orderQueryKeys.all }),
|
||||
queryClient.invalidateQueries({ queryKey: inventoryQueryKeys.all }),
|
||||
queryClient.invalidateQueries({ queryKey: productQueryKeys.all }),
|
||||
]);
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -1,50 +1,59 @@
|
||||
import {createProduct, deactivateProduct, getProduct, getProducts, updateProduct} from "#/api/products";
|
||||
import type {ProductRequest} from "#/types/product";
|
||||
import {useMutation, useQuery, useQueryClient} from "@tanstack/react-query";
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import {
|
||||
createProduct,
|
||||
deactivateProduct,
|
||||
getProduct,
|
||||
getProducts,
|
||||
updateProduct,
|
||||
} from "#/api/products";
|
||||
import type { ProductRequest } from "#/types/product";
|
||||
|
||||
export const productQueryKeys = {
|
||||
all: ["products"] as const,
|
||||
detail: (id: string) => [...productQueryKeys.all, "detail", id] as const,
|
||||
all: ["products"] as const,
|
||||
detail: (id: string) => [...productQueryKeys.all, "detail", id] as const,
|
||||
};
|
||||
|
||||
export function useProducts() {
|
||||
return useQuery({
|
||||
queryKey: productQueryKeys.all,
|
||||
queryFn: ({signal}) => getProducts(signal),
|
||||
});
|
||||
return useQuery({
|
||||
queryKey: productQueryKeys.all,
|
||||
queryFn: ({ signal }) => getProducts(signal),
|
||||
});
|
||||
}
|
||||
|
||||
export function useProduct(id: string) {
|
||||
return useQuery({
|
||||
queryKey: productQueryKeys.detail(id),
|
||||
queryFn: ({signal}) => getProduct(id, signal),
|
||||
});
|
||||
return useQuery({
|
||||
queryKey: productQueryKeys.detail(id),
|
||||
queryFn: ({ signal }) => getProduct(id, signal),
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateProduct() {
|
||||
const queryClient = useQueryClient();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: createProduct,
|
||||
onSuccess: () => queryClient.invalidateQueries({queryKey: productQueryKeys.all}),
|
||||
});
|
||||
return useMutation({
|
||||
mutationFn: createProduct,
|
||||
onSuccess: () =>
|
||||
queryClient.invalidateQueries({ queryKey: productQueryKeys.all }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useUpdateProduct() {
|
||||
const queryClient = useQueryClient();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: ({id, request}: {id: string; request: ProductRequest}) =>
|
||||
updateProduct(id, request),
|
||||
onSuccess: () => queryClient.invalidateQueries({queryKey: productQueryKeys.all}),
|
||||
});
|
||||
return useMutation({
|
||||
mutationFn: ({ id, request }: { id: string; request: ProductRequest }) =>
|
||||
updateProduct(id, request),
|
||||
onSuccess: () =>
|
||||
queryClient.invalidateQueries({ queryKey: productQueryKeys.all }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useDeactivateProduct() {
|
||||
const queryClient = useQueryClient();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: deactivateProduct,
|
||||
onSuccess: () => queryClient.invalidateQueries({queryKey: productQueryKeys.all}),
|
||||
});
|
||||
return useMutation({
|
||||
mutationFn: deactivateProduct,
|
||||
onSuccess: () =>
|
||||
queryClient.invalidateQueries({ queryKey: productQueryKeys.all }),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,67 +1,72 @@
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import {
|
||||
acceptRide,
|
||||
cancelRide,
|
||||
completeRide,
|
||||
getRides,
|
||||
requestRide,
|
||||
updateRideStatus,
|
||||
acceptRide,
|
||||
cancelRide,
|
||||
completeRide,
|
||||
getRides,
|
||||
requestRide,
|
||||
updateRideStatus,
|
||||
} from "#/api/rides";
|
||||
import type {CreateRideRequest, RideStatus} from "#/types/ride";
|
||||
import {useMutation, useQuery, useQueryClient} from "@tanstack/react-query";
|
||||
import type { CreateRideRequest, RideStatus } from "#/types/ride";
|
||||
|
||||
export const rideQueryKeys = {
|
||||
all: ["rides"] as const,
|
||||
all: ["rides"] as const,
|
||||
};
|
||||
|
||||
export function useRides() {
|
||||
return useQuery({
|
||||
queryKey: rideQueryKeys.all,
|
||||
queryFn: ({signal}) => getRides(signal),
|
||||
});
|
||||
return useQuery({
|
||||
queryKey: rideQueryKeys.all,
|
||||
queryFn: ({ signal }) => getRides(signal),
|
||||
});
|
||||
}
|
||||
|
||||
export function useRequestRide() {
|
||||
const queryClient = useQueryClient();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: (request: CreateRideRequest) => requestRide(request),
|
||||
onSuccess: () => queryClient.invalidateQueries({queryKey: rideQueryKeys.all}),
|
||||
});
|
||||
return useMutation({
|
||||
mutationFn: (request: CreateRideRequest) => requestRide(request),
|
||||
onSuccess: () =>
|
||||
queryClient.invalidateQueries({ queryKey: rideQueryKeys.all }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useAcceptRide() {
|
||||
const queryClient = useQueryClient();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: acceptRide,
|
||||
onSuccess: () => queryClient.invalidateQueries({queryKey: rideQueryKeys.all}),
|
||||
});
|
||||
return useMutation({
|
||||
mutationFn: acceptRide,
|
||||
onSuccess: () =>
|
||||
queryClient.invalidateQueries({ queryKey: rideQueryKeys.all }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useCompleteRide() {
|
||||
const queryClient = useQueryClient();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: completeRide,
|
||||
onSuccess: () => queryClient.invalidateQueries({queryKey: rideQueryKeys.all}),
|
||||
});
|
||||
return useMutation({
|
||||
mutationFn: completeRide,
|
||||
onSuccess: () =>
|
||||
queryClient.invalidateQueries({ queryKey: rideQueryKeys.all }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useCancelRide() {
|
||||
const queryClient = useQueryClient();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: cancelRide,
|
||||
onSuccess: () => queryClient.invalidateQueries({queryKey: rideQueryKeys.all}),
|
||||
});
|
||||
return useMutation({
|
||||
mutationFn: cancelRide,
|
||||
onSuccess: () =>
|
||||
queryClient.invalidateQueries({ queryKey: rideQueryKeys.all }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useUpdateRideStatus() {
|
||||
const queryClient = useQueryClient();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: ({id, status}: {id: string; status: RideStatus}) =>
|
||||
updateRideStatus(id, status),
|
||||
onSuccess: () => queryClient.invalidateQueries({queryKey: rideQueryKeys.all}),
|
||||
});
|
||||
return useMutation({
|
||||
mutationFn: ({ id, status }: { id: string; status: RideStatus }) =>
|
||||
updateRideStatus(id, status),
|
||||
onSuccess: () =>
|
||||
queryClient.invalidateQueries({ queryKey: rideQueryKeys.all }),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,58 +1,69 @@
|
||||
import {createUser, disableUser, getCustomers, getDrivers, getUsers, updateUser} from "#/api/users";
|
||||
import type {AdminUserRequest} from "#/types/user";
|
||||
import {useMutation, useQuery, useQueryClient} from "@tanstack/react-query";
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import {
|
||||
createUser,
|
||||
disableUser,
|
||||
getCustomers,
|
||||
getDrivers,
|
||||
getUsers,
|
||||
updateUser,
|
||||
} from "#/api/users";
|
||||
import type { AdminUserRequest } from "#/types/user";
|
||||
|
||||
export const userQueryKeys = {
|
||||
all: ["users"] as const,
|
||||
list: () => [...userQueryKeys.all, "list"] as const,
|
||||
customers: () => [...userQueryKeys.all, "customers"] as const,
|
||||
drivers: () => [...userQueryKeys.all, "drivers"] as const,
|
||||
all: ["users"] as const,
|
||||
list: () => [...userQueryKeys.all, "list"] as const,
|
||||
customers: () => [...userQueryKeys.all, "customers"] as const,
|
||||
drivers: () => [...userQueryKeys.all, "drivers"] as const,
|
||||
};
|
||||
|
||||
export function useUsers() {
|
||||
return useQuery({
|
||||
queryKey: userQueryKeys.list(),
|
||||
queryFn: ({signal}) => getUsers(signal),
|
||||
});
|
||||
return useQuery({
|
||||
queryKey: userQueryKeys.list(),
|
||||
queryFn: ({ signal }) => getUsers(signal),
|
||||
});
|
||||
}
|
||||
|
||||
export function useCustomers() {
|
||||
return useQuery({
|
||||
queryKey: userQueryKeys.customers(),
|
||||
queryFn: ({signal}) => getCustomers(signal),
|
||||
});
|
||||
return useQuery({
|
||||
queryKey: userQueryKeys.customers(),
|
||||
queryFn: ({ signal }) => getCustomers(signal),
|
||||
});
|
||||
}
|
||||
|
||||
export function useDrivers() {
|
||||
return useQuery({
|
||||
queryKey: userQueryKeys.drivers(),
|
||||
queryFn: ({signal}) => getDrivers(signal),
|
||||
});
|
||||
return useQuery({
|
||||
queryKey: userQueryKeys.drivers(),
|
||||
queryFn: ({ signal }) => getDrivers(signal),
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateUser() {
|
||||
const queryClient = useQueryClient();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: createUser,
|
||||
onSuccess: () => queryClient.invalidateQueries({queryKey: userQueryKeys.all}),
|
||||
});
|
||||
return useMutation({
|
||||
mutationFn: createUser,
|
||||
onSuccess: () =>
|
||||
queryClient.invalidateQueries({ queryKey: userQueryKeys.all }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useUpdateUser() {
|
||||
const queryClient = useQueryClient();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: ({id, request}: {id: string; request: AdminUserRequest}) => updateUser(id, request),
|
||||
onSuccess: () => queryClient.invalidateQueries({queryKey: userQueryKeys.all}),
|
||||
});
|
||||
return useMutation({
|
||||
mutationFn: ({ id, request }: { id: string; request: AdminUserRequest }) =>
|
||||
updateUser(id, request),
|
||||
onSuccess: () =>
|
||||
queryClient.invalidateQueries({ queryKey: userQueryKeys.all }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useDisableUser() {
|
||||
const queryClient = useQueryClient();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: disableUser,
|
||||
onSuccess: () => queryClient.invalidateQueries({queryKey: userQueryKeys.all}),
|
||||
});
|
||||
return useMutation({
|
||||
mutationFn: disableUser,
|
||||
onSuccess: () =>
|
||||
queryClient.invalidateQueries({ queryKey: userQueryKeys.all }),
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user