diff --git a/frontend/app/(admin)/admin/doc-control/drawings/contract/categories/page.tsx b/frontend/app/(admin)/admin/doc-control/drawings/contract/categories/page.tsx index 557b336..16b8076 100644 --- a/frontend/app/(admin)/admin/doc-control/drawings/contract/categories/page.tsx +++ b/frontend/app/(admin)/admin/doc-control/drawings/contract/categories/page.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useState } from 'react'; +import React, { useState } from 'react'; import { GenericCrudTable } from '@/components/admin/reference/generic-crud-table'; import { ColumnDef } from '@tanstack/react-table'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; @@ -120,19 +120,6 @@ export default function ContractCategoriesPage() { filters={projectFilter} /> - {/* - Note: For mapping, we should ideally have a separate "Mappings" column or action button. - Since GenericCrudTable might not support custom action columns easily without modification, - we are currently just listing categories. To add mapping functionality, we might need - to either extend GenericCrudTable or create a dedicated page for mappings. - - Given the constraints, I will add a "Mapped Sub-categories" management section - that opens when clicking a category ROW or adding a custom action if GenericCrudTable supports it. - For now, let's assume we need to extend GenericCrudTable or replace it to support this specific requirement. - - However, to keep it simple and consistent: - Let's add a separate section below the table or a dialog triggered by a custom cell. - */}
@@ -140,22 +127,47 @@ export default function ContractCategoriesPage() { ); } +// ===================================================== +// Error Boundary to prevent mapping section from crashing the page +// ===================================================== +class MappingErrorBoundary extends React.Component< + { children: React.ReactNode }, + { hasError: boolean; error: Error | null } +> { + constructor(props: { children: React.ReactNode }) { + super(props); + this.state = { hasError: false, error: null }; + } + + static getDerivedStateFromError(error: Error) { + return { hasError: true, error }; + } + + render() { + if (this.state.hasError) { + return ( +
+

Failed to load mapping section

+

{this.state.error?.message || 'Unknown error'}

+ +
+ ); + } + return this.props.children; + } +} + function CategoryMappingSection({ projectId }: { projectId: number }) { - // ... logic to manage mappings would go here ... - // But to properly implement this, we need a full mapping UI. - // Let's defer this implementation pattern to a separate component to keep this file clean - // and just mount it here. return (

Category Mappings (Map Sub-categories to Categories)

Select a category to view and manage its sub-categories.

- {/* - Real implementation would be complex here. - Better approach: Add a "Manage Sub-categories" button to the Categories table if possible. - Or simpler: A separate "Mapping" page. - */} - + + +
); @@ -166,38 +178,54 @@ function ManageMappings({ projectId }: { projectId: number }) { const [selectedCat, setSelectedCat] = useState(''); const [selectedSubCat, setSelectedSubCat] = useState(''); - const { data: categories = [] } = useQuery({ - queryKey: ['contract-categories', String(projectId)], + const { + data: rawCategories, + isLoading: isLoadingCats, + isError: isCatError, + } = useQuery({ + queryKey: ['contract-categories-mapping', String(projectId)], queryFn: () => drawingMasterDataService.getContractCategories(projectId), }); - const { data: subCategories = [] } = useQuery({ - queryKey: ['contract-sub-categories', String(projectId)], + const { + data: rawSubCategories, + isLoading: isLoadingSubCats, + isError: isSubCatError, + } = useQuery({ + queryKey: ['contract-sub-categories-mapping', String(projectId)], queryFn: () => drawingMasterDataService.getContractSubCategories(projectId), }); - const { data: mappings = [] } = useQuery({ + const { data: rawMappings, isLoading: isLoadingMappings } = useQuery({ queryKey: ['contract-mappings', String(projectId), selectedCat], queryFn: () => drawingMasterDataService.getContractMappings(projectId, selectedCat ? parseInt(selectedCat) : undefined), enabled: !!selectedCat, }); + // Ensure data is always an array - defensive against unexpected API responses + const categories = Array.isArray(rawCategories) ? rawCategories : []; + const subCategories = Array.isArray(rawSubCategories) ? rawSubCategories : []; + const mappings = Array.isArray(rawMappings) ? rawMappings : []; + const createMutation = useMutation({ - mutationFn: drawingMasterDataService.createContractMapping, + mutationFn: (data: { projectId: number; categoryId: number; subCategoryId: number }) => + drawingMasterDataService.createContractMapping(data), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['contract-mappings'] }); toast.success('Mapping created'); setSelectedSubCat(''); }, + onError: () => toast.error('Failed to create mapping'), }); const deleteMutation = useMutation({ - mutationFn: drawingMasterDataService.deleteContractMapping, + mutationFn: (id: number) => drawingMasterDataService.deleteContractMapping(id), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['contract-mappings'] }); toast.success('Mapping removed'); }, + onError: () => toast.error('Failed to delete mapping'), }); const handleAdd = () => { @@ -209,8 +237,20 @@ function ManageMappings({ projectId }: { projectId: number }) { }); }; + if (isLoadingCats || isLoadingSubCats) { + return

Loading data...

; + } + + if (isCatError || isSubCatError) { + return ( +

+ Failed to load categories or sub-categories. Please check your permissions. +

+ ); + } + return ( -
+