import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { useAuthToken } from "../hooks/use-root-data"; import { useToast } from "../hooks/use-toast"; import { useNavigate } from "@remix-run/react"; import { CashOut, CashOutGatewayResponse, SellerOrderUpdate, } from "../../src/api/types"; import { useState, useEffect } from "react"; import { createShoppingCartApi, createOrderManagementApi, createApiApi, } from "~/utils/api-client-factory"; // Order query keys export const orderKeys = { all: ["orders"] as const, lists: () => [...orderKeys.all, "list"] as const, list: (filters: string) => [...orderKeys.lists(), { filters }] as const, details: () => [...orderKeys.all, "detail"] as const, detail: (id: string) => [...orderKeys.details(), id] as const, }; /** * Hook for fetching the authenticated user's orders * @returns Query result with the user's orders */ export function useGetUserOrders() { const token = useAuthToken(); return useQuery({ queryKey: orderKeys.lists(), queryFn: async () => { if (!token) { throw new Error("Authentication token is required"); } try { const orderApi = createApiApi(token); return await orderApi.apiOrderV1OrdersList(); } catch (error) { console.error("Error fetching user orders:", error); throw error; } }, enabled: !!token, staleTime: 60 * 1000, // 1 minute cache refetchOnWindowFocus: false, }); } export function useCashOutCart(cartId: string) { const token = useAuthToken(); const queryClient = useQueryClient(); const { toast } = useToast(); const navigate = useNavigate(); return useMutation({ mutationFn: async (payload: CashOut) => { try { if (!token) { throw new Error("Authentication required"); } const shoppingCartApi = createShoppingCartApi(token); const response = await shoppingCartApi.apiShoppingcartV1CashOutCreate({ data: { shippingInfoId: payload.shippingInfoId, }, }); return response; } catch (error) { console.error(`Error during cash out for cart ${cartId}:`, error); throw error; // Re-throw the error to be caught by the hook } }, onSuccess: (data) => { queryClient.invalidateQueries({ queryKey: ["carts", "detail", cartId] }); queryClient.invalidateQueries({ queryKey: ["carts", "list"] }); queryClient.invalidateQueries({ queryKey: ["carts", "count"] }); queryClient.invalidateQueries({ queryKey: ["getWalletService"] }); if (data.redirect) { if (data.redirect.startsWith("http")) { // External URL - use window.location window.location.href = data.redirect; } else { // Internal route - use navigate navigate(data.redirect); } } else { toast({ title: "Success", description: "Order created successfully!", }); // Navigate to orders page if no redirect URL is provided navigate("/orders"); } }, onError: (error) => { toast({ title: "Error", description: `Failed to create order: ${error.message}`, variant: "destructive", }); }, }); } export function useGetInvoiceAndShippingInfo( invoiceNumber?: string, shippingInfoId?: string ) { const token = useAuthToken(); return useQuery({ queryKey: ["invoice", invoiceNumber, shippingInfoId], queryFn: async () => { if (!token) { throw new Error("Authentication token is required"); } if (!invoiceNumber || !shippingInfoId) { throw new Error("Invoice number and shipping info ID are required"); } try { const orderApi = createOrderManagementApi(token); return await orderApi.apiOrderV1InfoList({ invoiceNumber, shippingInfoId, }); } catch (error) { console.error("Error fetching invoice and shipping info:", error); throw error; } }, enabled: !!invoiceNumber && !!shippingInfoId && !!token, staleTime: 60 * 1000, // 1 minute cache refetchOnWindowFocus: false, }); } /** * Hook for fetching a specific order's details * @param orderId - The order ID to fetch * @returns Query result with the order details */ export function useGetOrderDetail(orderId: string | null) { const token = useAuthToken(); return useQuery({ queryKey: [...orderKeys.details(), orderId || "null"], queryFn: async () => { if (!orderId) return null; if (!token) { throw new Error("Authentication token is required"); } try { const orderApi = createOrderManagementApi(token); return await orderApi.apiOrderV1OrderDetailList({ orderId }); } catch (error) { console.error("Error fetching order details:", error); throw error; } }, enabled: !!orderId && !!token, staleTime: 5 * 60 * 1000, // 1 minute cache refetchOnWindowFocus: false, }); } /** * Hook for fetching the seller's orders * @returns Query result with the seller's orders */ export function useGetSellerOrders() { const token = useAuthToken(); return useQuery({ queryKey: [...orderKeys.all, "seller"], queryFn: async () => { if (!token) { throw new Error("Authentication token is required"); } try { const orderApi = createOrderManagementApi(token); return await orderApi.apiOrderV1SellerordersList(); } catch (error) { console.error("Error fetching seller orders:", error); throw error; } }, enabled: !!token, staleTime: 30 * 60 * 1000, // 30 seconds - balance between freshness and performance refetchOnMount: false, // Always refetch when component mounts refetchOnWindowFocus: false, }); } /** * Hook for fetching a specific order's details for seller * @param orderId - The order ID to fetch * @returns Query result with the order details for seller */ export function useGetSellerOrderDetail(orderId: string | null) { const token = useAuthToken(); return useQuery({ queryKey: [...orderKeys.all, "seller", "detail", orderId || "null"], queryFn: async () => { if (!orderId) return null; if (!token) { throw new Error("Authentication token is required"); } try { const orderApi = createOrderManagementApi(token); return await orderApi.apiOrderV1SellerordersDetailRead({ id: orderId }); } catch (error) { console.error("Error fetching seller order details:", error); throw error; } }, enabled: !!orderId && !!token, staleTime: 5 * 60 * 1000, // 5 minute cache refetchOnWindowFocus: false, }); } /** * Hook for updating a seller order (accept/reject order, add tracking code, etc.) * @returns Mutation for updating seller order */ export function useUpdateSellerOrder() { const token = useAuthToken(); const queryClient = useQueryClient(); const { toast } = useToast(); return useMutation< void, Error, { orderId: string; data: SellerOrderUpdate }, { previousSellerOrders?: unknown; previousOrderDetail?: unknown } >({ mutationFn: async ({ orderId, data }) => { if (!token) { throw new Error("Authentication required"); } try { const orderApi = createOrderManagementApi(token); await orderApi.apiOrderV1SellerordersPartialUpdate({ id: orderId, data, }); } catch (error) { console.error(`Error updating seller order ${orderId}:`, error); throw error; } }, // Optimistically update the cache before the mutation onMutate: async ({ orderId }) => { // Cancel any outgoing refetches to prevent race conditions await queryClient.cancelQueries({ queryKey: [...orderKeys.all, "seller"], }); await queryClient.cancelQueries({ queryKey: [...orderKeys.all, "seller", "detail", orderId], }); // Snapshot the previous values const previousSellerOrders = queryClient.getQueryData([ ...orderKeys.all, "seller", ]); const previousOrderDetail = queryClient.getQueryData([ ...orderKeys.all, "seller", "detail", orderId, ]); return { previousSellerOrders, previousOrderDetail }; }, onSuccess: async (_, { orderId }) => { // Invalidate and refetch relevant queries to refresh the data await queryClient.invalidateQueries({ queryKey: [...orderKeys.all, "seller"], }); await queryClient.invalidateQueries({ queryKey: [...orderKeys.all, "seller", "detail", orderId], }); toast({ title: "موفق", description: "سفارش با موفقیت بروزرسانی شد", }); }, onError: (error, _, context) => { // Rollback to previous values on error if (context?.previousSellerOrders) { queryClient.setQueryData( [...orderKeys.all, "seller"], context.previousSellerOrders ); } if (context?.previousOrderDetail) { queryClient.setQueryData( [...orderKeys.all, "seller", "detail"], context.previousOrderDetail ); } console.error("Update seller order error:", error); toast({ title: "خطا", description: `خطا در بروزرسانی سفارش: ${error.message}`, variant: "destructive", }); }, }); } /** * Hook for getting seller order count * @returns Query result with the seller order count */ export function useGetSellerOrderCount(enabled: boolean) { const token = useAuthToken(); return useQuery({ queryKey: [...orderKeys.all, "seller", "count"], queryFn: async () => { if (!token) { return 0; } try { const orderApi = createOrderManagementApi(token); const orders = await orderApi.apiOrderV1SellerordersList(); return orders.filter((order) => order.status === "pending").length || 0; } catch (error) { console.error("Error fetching seller order count:", error); return 0; } }, enabled: !!token && enabled, staleTime: 2 * 60 * 1000, // 2 minutes - order count doesn't change frequently gcTime: 5 * 60 * 1000, // 5 minutes refetchOnWindowFocus: false, refetchOnMount: false, }); } /** * Convenient hook for getting seller order count with local state management * Similar to useCartCount hook */ export const useSellerOrderCount = (enabled: boolean) => { const [count, setCount] = useState(0); const { data: orderCount, isLoading } = useGetSellerOrderCount(enabled); // Update local state whenever the query data changes useEffect(() => { if (!isLoading && orderCount !== undefined) { setCount(orderCount); } }, [orderCount, isLoading]); return count; };