common fields

This commit is contained in:
2026-06-05 03:13:00 +05:30
parent a54250b53d
commit 65bbb305e6
19 changed files with 576 additions and 391 deletions

View File

@@ -1,31 +1,44 @@
import { useQuery, useMutation, useQueryClient, keepPreviousData } from "@tanstack/react-query";
import * as React from "react";
import { api } from "../api/client";
import { ResourceConfig } from "../types/config";
import { ConfigContext } from "../providers/ConfigContext";
import * as React from "react";
import { FieldComponents } from "../types/overrides";
import { FieldComponents, FieldComponentProps } from "../types/overrides";
import { defaultFieldComponents } from "../components/fields/DefaultFieldComponents";
import FormField from "../components/fields/FormField";
import GenericForm from "../components/GenericForm";
export function useResource<T = any>(config: ResourceConfig | undefined, options?: { fieldComponents?: FieldComponents }) {
function wrapFormField(merged: FieldComponents) {
return (props: Omit<React.ComponentProps<typeof FormField>, 'components'>) =>
<FormField {...props} components={merged} />;
}
function wrapGenericForm(merged: FieldComponents) {
return (props: Omit<React.ComponentProps<typeof GenericForm>, 'fieldComponents'>) =>
<GenericForm {...props} fieldComponents={merged} />;
}
export function useResource<T = any>(config: ResourceConfig | undefined, options?: { fieldComponents: FieldComponents }) {
const queryClient = useQueryClient();
// Return empty/disabled hooks if config is missing
const { name = '', endpoint = '', primaryKey = 'id' } = config || {};
const mergedComponents = React.useMemo(
() => options?.fieldComponents ? ({ ...defaultFieldComponents, ...options.fieldComponents }) : undefined,
[options?.fieldComponents],
);
// --- READ ALL ---
const useList = (params?: any) =>
const useList = (params?: any) =>
useQuery({
queryKey: [name, "list", params],
queryFn: async () => {
if (!endpoint) return { data: [], total: 0 };
console.log('params:', params);
// @ts-ignore
const res = await api.get<T[]>(endpoint, { params });
const total = res.headers ? parseInt(res.headers['x-total-count'] || res.headers['X-Total-Count']) : undefined;
return {
data: res.data,
total: isNaN(total as any) ? undefined : total
return {
data: res.data,
total: isNaN(total as any) ? undefined : total
};
},
enabled: !!endpoint,
@@ -38,7 +51,6 @@ export function useResource<T = any>(config: ResourceConfig | undefined, options
queryKey: [name, "detail", id, params],
queryFn: async () => {
if (!id || !endpoint) return null;
// @ts-ignore
const res = await api.get<T>(`${endpoint}/${id}`, params ? { params } : undefined);
return res.data;
},
@@ -50,7 +62,6 @@ export function useResource<T = any>(config: ResourceConfig | undefined, options
useMutation({
mutationFn: async (data: Partial<T>) => {
if (!endpoint) throw new Error("Endpoint not defined");
// @ts-ignore
const res = await api.post<T>(endpoint, data);
return res.data;
},
@@ -64,12 +75,10 @@ export function useResource<T = any>(config: ResourceConfig | undefined, options
useMutation({
mutationFn: async ({ id, data }: { id: string; data: Partial<T> }) => {
if (!endpoint) throw new Error("Endpoint not defined");
// @ts-ignore
const res = await api.put<T>(`${endpoint}/${id}`, data);
return res.data;
},
onSuccess: (updatedItem) => {
// @ts-ignore
const id = updatedItem[primaryKey];
queryClient.invalidateQueries({ queryKey: [name, "list"] });
queryClient.invalidateQueries({ queryKey: [name, "detail", id] });
@@ -81,15 +90,13 @@ export function useResource<T = any>(config: ResourceConfig | undefined, options
useMutation({
mutationFn: async ({ id, data }: { id: string; data: Partial<T> }) => {
if (!endpoint) throw new Error("Endpoint not defined");
// @ts-ignore
const res = await api.patch<T>(`${endpoint}/${id}`, data);
return res.data;
},
onSuccess: (updatedItem) => {
// @ts-ignore
const id = updatedItem[primaryKey];
const listId = updatedItem[primaryKey];
queryClient.invalidateQueries({ queryKey: [name, "list"] });
queryClient.invalidateQueries({ queryKey: [name, "detail", id] });
queryClient.invalidateQueries({ queryKey: [name, "detail", listId] });
},
});
@@ -111,12 +118,11 @@ export function useResource<T = any>(config: ResourceConfig | undefined, options
queryKey: [name, "list", params],
queryFn: async () => {
if (!endpoint) return { data: [], total: 0 };
// @ts-ignore
const res = await api.get<T[]>(endpoint, { params });
const total = res.headers ? parseInt(res.headers['x-total-count'] || res.headers['X-Total-Count']) : undefined;
return {
data: res.data,
total: isNaN(total as any) ? undefined : total
return {
data: res.data,
total: isNaN(total as any) ? undefined : total
};
},
enabled: !!endpoint,
@@ -128,7 +134,6 @@ export function useResource<T = any>(config: ResourceConfig | undefined, options
queryKey: [name, "me"],
queryFn: async () => {
if (!endpoint) return null;
// @ts-ignore
const res = await api.get<T>(`${endpoint}/me`);
return res.data;
},
@@ -140,7 +145,6 @@ export function useResource<T = any>(config: ResourceConfig | undefined, options
useMutation({
mutationFn: async (data: Partial<T>) => {
if (!endpoint) throw new Error("Endpoint not defined");
// @ts-ignore
const res = await api.put<T>(`${endpoint}/me`, data);
return res.data;
},
@@ -150,10 +154,14 @@ export function useResource<T = any>(config: ResourceConfig | undefined, options
},
});
const components = {
...defaultFieldComponents,
...options?.fieldComponents,
};
const components = React.useMemo(() => {
if (!mergedComponents) return undefined;
return {
...mergedComponents,
FormField: wrapFormField(mergedComponents),
GenericForm: wrapGenericForm(mergedComponents),
};
}, [mergedComponents]);
return {
useList,
@@ -169,9 +177,8 @@ export function useResource<T = any>(config: ResourceConfig | undefined, options
};
}
export function useResourceByName<T = any>(name: string, options?: { fieldComponents?: FieldComponents }) {
export function useResourceByName<T = any>(name: string, options?: { fieldComponents: FieldComponents }) {
const config = React.useContext(ConfigContext);
const resourceConfig = config?.resources.find((r) => r.name === name);
return useResource<T>(resourceConfig, options);
}