import * as React from 'react'; import { Box, Paper, CircularProgress } from '@mui/material'; import { ResourceConfig } from '../types/config'; import type { ResourceField } from '../types/config'; import { useResource } from '../hooks/useResource'; import GenericForm from './GenericForm'; import EnhancedTable from './EnhancedTable'; import FilterBar from './FilterBar'; import { useParams, useLocation, useNavigate } from 'react-router-dom'; interface ResourceViewProps { config: ResourceConfig; onNavigateToResource?: (resourceName: string, id: string) => void; } import { GridPaginationModel } from '@mui/x-data-grid'; function getFilterDisplayFields(field: ResourceField): string[] { if (!field.displayField) return []; return (Array.isArray(field.displayField) ? field.displayField : [field.displayField]).filter( (df): df is string => !!df ); } function applyClientFilters( data: any[], filters: Record, fields: Record ): any[] { const entries = Object.entries(filters).filter(([_, v]) => { if (v == null || v === "" || (Array.isArray(v) && v.length === 0)) return false; if (typeof v === "object" && !Array.isArray(v) && Object.values(v).every((x) => x == null || x === "")) return false; return true; }); if (entries.length === 0) return data; return data.filter((item) => entries.every(([fieldName, filterValue]) => { const field = fields[fieldName]; if (!field) return true; const itemValue = item[fieldName]; if (typeof filterValue === "object" && !Array.isArray(filterValue)) { if (field.type === "number") { if (filterValue.min != null && filterValue.min !== "" && Number(itemValue) < Number(filterValue.min)) return false; if (filterValue.max != null && filterValue.max !== "" && Number(itemValue) > Number(filterValue.max)) return false; return true; } if (field.type === "datetime" || field.type === "date") { const itemTime = new Date(itemValue).getTime(); if (filterValue.start && new Date(filterValue.start).getTime() > itemTime) return false; if (filterValue.end && new Date(filterValue.end).getTime() < itemTime) return false; return true; } return true; } if (Array.isArray(filterValue)) { if (field.type === "array" && Array.isArray(itemValue)) { return itemValue.some((el: any) => { if (el != null && typeof el === "object") { const dispFields = getFilterDisplayFields(field); return dispFields.some((df) => filterValue.includes(String(el[df]))); } return filterValue.includes(String(el)); }); } if (itemValue && typeof itemValue === "object") { const dispFields = getFilterDisplayFields(field); const itemDisplay = dispFields.map((df) => itemValue[df]).filter((v) => v != null).join(" "); return filterValue.includes(itemDisplay); } return filterValue.includes(String(itemValue)); } if (!filterValue) return true; if (field.type === "boolean") { return String(itemValue) === filterValue; } if (field.type === "array" && Array.isArray(itemValue)) { return itemValue.some((el: any) => { if (el != null && typeof el === "object") { const dispFields = getFilterDisplayFields(field); return dispFields.some((df) => String(el[df]) === String(filterValue)); } return String(el) === String(filterValue); }); } if (itemValue && typeof itemValue === "object") { const dispFields = getFilterDisplayFields(field); return dispFields.some((df) => String(itemValue[df]) === String(filterValue)); } return String(itemValue) === String(filterValue); }) ); } export default function ResourceView({ config, onNavigateToResource }: ResourceViewProps) { const { id } = useParams(); const location = useLocation(); const navigate = useNavigate(); const isCreate = location.pathname.endsWith('/create'); const isEdit = location.pathname.includes('/edit/'); const isView = !!id && !isEdit; const isList = !id && !isCreate; const isServer = config.filterOptions?.mode !== "client"; const [paginationModel, setPaginationModel] = React.useState({ page: 0, pageSize: 10, }); const [appliedFilters, setAppliedFilters] = React.useState>({}); const { useList, useRead, useCreate, useUpdate, useDelete } = useResource(config); const queryParams = React.useMemo(() => { if (!isServer) return { limit: 10000 }; return { skip: paginationModel.page * paginationModel.pageSize, limit: paginationModel.pageSize, }; }, [isServer, paginationModel]); const listQuery = useList(queryParams); const itemQuery = useRead(id || ""); const rawData = listQuery.data?.data || []; const totalCount = listQuery.data?.total; const filteredData = React.useMemo( () => (isServer ? rawData : applyClientFilters(rawData, appliedFilters, config.fields)), [isServer, rawData, appliedFilters, config.fields] ); const createMutation = useCreate(); const updateMutation = useUpdate(); const deleteMutation = useDelete(); const handleEdit = (item: any) => { navigate(`/admin/${config.name}/edit/${item[config.primaryKey]}`); }; const handleCreate = () => { navigate(`/admin/${config.name}/create`); }; const handleSave = async (formData: any) => { try { if (isEdit) { await updateMutation.mutateAsync({ id: id!, data: formData }); } else { await createMutation.mutateAsync(formData); } navigate(`/admin/${config.name}`); } catch (err) { console.error('Save failed:', err); } }; const handleDelete = async (itemId: string) => { if (window.confirm('Are you sure you want to delete this item?')) { await deleteMutation.mutateAsync(itemId); } }; if (isList && listQuery.isLoading) return ; if ((isEdit || isView) && itemQuery.isLoading) return ; return ( {isList ? ( {!isServer && config.filterOptions?.fields && config.filterOptions.fields.length > 0 && ( setAppliedFilters({})} /> )} navigate(`/admin/${res}/${id}`)} /> ) : ( navigate(`/admin/${config.name}`)} loading={createMutation.isPending || updateMutation.isPending} readOnly={isView} onEditClick={() => navigate(`/admin/${config.name}/edit/${id}`)} /> )} ); }