"use client"; import { useCallback, useEffect, useState } from "react"; import { ApiError, apiFetch, unwrap } from "./api"; type Query = Record; // Generic GET-list hook. Unwraps `{ data: [...] }` and tolerates either a bare // array or a paginated object with a `data` field. export function useList( path: string | null, query?: Query, ): { data: T[]; loading: boolean; error: string | null; reload: () => void; } { const [data, setData] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const queryKey = JSON.stringify(query ?? {}); const reload = useCallback(() => { if (!path) { // No path yet (e.g. a filter not chosen): show an empty, non-loading state. setData([]); setLoading(false); setError(null); return; } setLoading(true); setError(null); apiFetch(path, { query }) .then((res) => { const value = unwrap(res); setData(Array.isArray(value) ? (value as T[]) : []); }) .catch((e: unknown) => { setError(e instanceof ApiError ? e.message : "خطا در دریافت اطلاعات"); setData([]); }) .finally(() => setLoading(false)); // eslint-disable-next-line react-hooks/exhaustive-deps }, [path, queryKey]); useEffect(() => { reload(); }, [reload]); return { data, loading, error, reload }; } export interface PageMeta { current_page: number; last_page: number; per_page: number; total: number; } // Paginated GET-list hook for Laravel paginator responses // ({ data, current_page, last_page, per_page, total }). export function usePaginated( path: string | null, options?: { perPage?: number; query?: Query }, ): { data: T[]; meta: PageMeta | null; page: number; setPage: (p: number) => void; loading: boolean; error: string | null; reload: () => void; } { const perPage = options?.perPage ?? 20; const query = options?.query; const [page, setPage] = useState(1); const [data, setData] = useState([]); const [meta, setMeta] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const queryKey = JSON.stringify(query ?? {}); const reload = useCallback(() => { if (!path) { setData([]); setMeta(null); setLoading(false); setError(null); return; } setLoading(true); setError(null); apiFetch(path, { query: { ...query, page, per_page: perPage } }) .then((res) => { const r = (res ?? {}) as Record; const rows = Array.isArray(r.data) ? (r.data as T[]) : []; setData(rows); setMeta({ current_page: Number(r.current_page ?? page), last_page: Number(r.last_page ?? 1), per_page: Number(r.per_page ?? perPage), total: Number(r.total ?? rows.length), }); }) .catch((e: unknown) => { setError(e instanceof ApiError ? e.message : "خطا در دریافت اطلاعات"); setData([]); }) .finally(() => setLoading(false)); // eslint-disable-next-line react-hooks/exhaustive-deps }, [path, page, perPage, queryKey]); useEffect(() => { reload(); }, [reload]); return { data, meta, page, setPage, loading, error, reload }; } // Single-record GET hook. export function useItem(path: string | null): { data: T | null; loading: boolean; error: string | null; reload: () => void; } { const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const reload = useCallback(() => { if (!path) { setData(null); setLoading(false); setError(null); return; } setLoading(true); setError(null); apiFetch(path) .then((res) => setData(unwrap(res))) .catch((e: unknown) => setError(e instanceof ApiError ? e.message : "خطا در دریافت اطلاعات"), ) .finally(() => setLoading(false)); }, [path]); useEffect(() => { reload(); }, [reload]); return { data, loading, error, reload }; }