import * as React from 'react'; import { Box, Paper, CircularProgress } from '@mui/material'; import { ResourceConfig } from '../types/config'; import type { ResourceField } from '../types/config'; import { FieldComponents } from '../types/overrides'; import { useResource } from '../hooks/useResource'; import { resolveTemplate } from '../utils/options'; 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; fieldComponents?: FieldComponents; } import { GridPaginationModel } from '@mui/x-data-grid'; function getDisplayString(item: any, field: ResourceField): string { if (item == null || typeof item !== 'object') return String(item ?? ''); if (field.enumOption?.value) return resolveTemplate(field.enumOption.value, item); const df = field.displayField; if (!df) return item.name ?? item.title ?? item.label ?? item.id ?? JSON.stringify(item); if (Array.isArray(df)) { const parts = df.map((k: string) => item[k]).filter((v: any) => v != null); return parts.length > 0 ? parts.join(' ') : ''; } return String(item[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) => filterValue.includes(getDisplayString(el, field)) ); } if (itemValue && typeof itemValue === "object") { return filterValue.includes(getDisplayString(itemValue, field)); } 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) => getDisplayString(el, field) === String(filterValue) ); } if (itemValue && typeof itemValue === "object") { return getDisplayString(itemValue, field) === String(filterValue); } return String(itemValue) === String(filterValue); }) ); } export default function ResourceView({ config, onNavigateToResource, fieldComponents }: 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}`)} fieldComponents={fieldComponents} /> )} ); }