260220:1143 20260220 TASK-BEFE-001 Fix Drawing Master Data Pages
All checks were successful
Build and Deploy / deploy (push) Successful in 4m50s

This commit is contained in:
admin
2026-02-20 11:43:23 +07:00
parent d455598dc2
commit 21b48f7d32
3 changed files with 74 additions and 59 deletions

View File

@@ -9,6 +9,7 @@ import {
Query, Query,
UseGuards, UseGuards,
ParseIntPipe, ParseIntPipe,
Logger,
} from '@nestjs/common'; } from '@nestjs/common';
import { import {
ApiTags, ApiTags,
@@ -27,6 +28,8 @@ import { RequirePermission } from '../../common/decorators/require-permission.de
@UseGuards(JwtAuthGuard, RbacGuard) @UseGuards(JwtAuthGuard, RbacGuard)
@Controller('drawings/master-data') @Controller('drawings/master-data')
export class DrawingMasterDataController { export class DrawingMasterDataController {
private readonly logger = new Logger(DrawingMasterDataController.name);
constructor(private readonly masterDataService: DrawingMasterDataService) {} constructor(private readonly masterDataService: DrawingMasterDataService) {}
// ===================================================== // =====================================================
@@ -38,9 +41,12 @@ export class DrawingMasterDataController {
@ApiQuery({ name: 'projectId', required: true, type: Number }) @ApiQuery({ name: 'projectId', required: true, type: Number })
@RequirePermission('document.view') @RequirePermission('document.view')
getVolumes(@Query('projectId', ParseIntPipe) projectId: number) { getVolumes(@Query('projectId', ParseIntPipe) projectId: number) {
this.logger.log(`Fetching Contract Volumes for Project ID: ${projectId}`);
return this.masterDataService.findAllVolumes(projectId); return this.masterDataService.findAllVolumes(projectId);
} }
// ... (Create/Update/Delete methods remain unchanged) ...
@Post('contract/volumes') @Post('contract/volumes')
@ApiOperation({ summary: 'Create Volume' }) @ApiOperation({ summary: 'Create Volume' })
@RequirePermission('master_data.drawing_category.manage') @RequirePermission('master_data.drawing_category.manage')
@@ -89,9 +95,14 @@ export class DrawingMasterDataController {
@ApiQuery({ name: 'projectId', required: true, type: Number }) @ApiQuery({ name: 'projectId', required: true, type: Number })
@RequirePermission('document.view') @RequirePermission('document.view')
getCategories(@Query('projectId', ParseIntPipe) projectId: number) { getCategories(@Query('projectId', ParseIntPipe) projectId: number) {
this.logger.log(
`Fetching Contract Categories for Project ID: ${projectId}`
);
return this.masterDataService.findAllCategories(projectId); return this.masterDataService.findAllCategories(projectId);
} }
// ... (Create/Update/Delete methods remain unchanged) ...
@Post('contract/categories') @Post('contract/categories')
@ApiOperation({ summary: 'Create Category' }) @ApiOperation({ summary: 'Create Category' })
@RequirePermission('master_data.drawing_category.manage') @RequirePermission('master_data.drawing_category.manage')
@@ -140,9 +151,14 @@ export class DrawingMasterDataController {
@ApiQuery({ name: 'projectId', required: true, type: Number }) @ApiQuery({ name: 'projectId', required: true, type: Number })
@RequirePermission('document.view') @RequirePermission('document.view')
getContractSubCats(@Query('projectId', ParseIntPipe) projectId: number) { getContractSubCats(@Query('projectId', ParseIntPipe) projectId: number) {
this.logger.log(
`Fetching Contract Sub-Categories for Project ID: ${projectId}`
);
return this.masterDataService.findAllContractSubCats(projectId); return this.masterDataService.findAllContractSubCats(projectId);
} }
// ... (Create/Update/Delete methods remain unchanged) ...
@Post('contract/sub-categories') @Post('contract/sub-categories')
@ApiOperation({ summary: 'Create Contract Sub-Category' }) @ApiOperation({ summary: 'Create Contract Sub-Category' })
@RequirePermission('master_data.drawing_category.manage') @RequirePermission('master_data.drawing_category.manage')
@@ -228,9 +244,14 @@ export class DrawingMasterDataController {
@ApiQuery({ name: 'projectId', required: true, type: Number }) @ApiQuery({ name: 'projectId', required: true, type: Number })
@RequirePermission('document.view') @RequirePermission('document.view')
getShopMainCats(@Query('projectId', ParseIntPipe) projectId: number) { getShopMainCats(@Query('projectId', ParseIntPipe) projectId: number) {
this.logger.log(
`Fetching Shop Main Categories for Project ID: ${projectId}`
);
return this.masterDataService.findAllShopMainCats(projectId); return this.masterDataService.findAllShopMainCats(projectId);
} }
// ... (Create/Update/Delete methods remain unchanged) ...
@Post('shop/main-categories') @Post('shop/main-categories')
@ApiOperation({ summary: 'Create Shop Main Category' }) @ApiOperation({ summary: 'Create Shop Main Category' })
@RequirePermission('master_data.drawing_category.manage') @RequirePermission('master_data.drawing_category.manage')
@@ -285,6 +306,9 @@ export class DrawingMasterDataController {
@Query('projectId', ParseIntPipe) projectId: number, @Query('projectId', ParseIntPipe) projectId: number,
@Query('mainCategoryId') mainCategoryId?: number @Query('mainCategoryId') mainCategoryId?: number
) { ) {
this.logger.log(
`Fetching Shop Sub-Categories for Project ID: ${projectId}, MainCategory: ${mainCategoryId}`
);
return this.masterDataService.findAllShopSubCats( return this.masterDataService.findAllShopSubCats(
projectId, projectId,
mainCategoryId ? Number(mainCategoryId) : undefined mainCategoryId ? Number(mainCategoryId) : undefined

View File

@@ -232,10 +232,10 @@ function ManageMappings({ projectId }: { projectId: number }) {
<SelectValue placeholder="Select Sub-Category to add..." /> <SelectValue placeholder="Select Sub-Category to add..." />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
{subCategories {(subCategories || [])
.filter( .filter(
(s: ContractSubCategory) => (s: ContractSubCategory) =>
!mappings.find((m: { subCategory: { id: number } }) => m.subCategory.id === s.id) !(mappings || []).find((m: { subCategory: { id: number } }) => m.subCategory?.id === s.id)
) )
.map((s: ContractSubCategory) => ( .map((s: ContractSubCategory) => (
<SelectItem key={s.id} value={String(s.id)}> <SelectItem key={s.id} value={String(s.id)}>

View File

@@ -1,19 +1,13 @@
"use client"; 'use client';
import { useState } from "react"; import { useState } from 'react';
import { GenericCrudTable } from "@/components/admin/reference/generic-crud-table"; import { GenericCrudTable } from '@/components/admin/reference/generic-crud-table';
import { ColumnDef } from "@tanstack/react-table"; import { ColumnDef } from '@tanstack/react-table';
import { import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
Select, import { Loader2, CheckCircle, XCircle } from 'lucide-react';
SelectContent, import { useProjects } from '@/hooks/use-master-data';
SelectItem, import { drawingMasterDataService } from '@/lib/services/drawing-master-data.service';
SelectTrigger, import { Badge } from '@/components/ui/badge';
SelectValue,
} from "@/components/ui/select";
import { Loader2, CheckCircle, XCircle } from "lucide-react";
import { useProjects } from "@/hooks/use-master-data";
import { drawingMasterDataService } from "@/lib/services/drawing-master-data.service";
import { Badge } from "@/components/ui/badge";
interface SubCategory { interface SubCategory {
id: number; id: number;
@@ -28,46 +22,41 @@ export default function ShopSubCategoriesPage() {
const [selectedProjectId, setSelectedProjectId] = useState<number | undefined>(undefined); const [selectedProjectId, setSelectedProjectId] = useState<number | undefined>(undefined);
const { data: projects = [], isLoading: isLoadingProjects } = useProjects(); const { data: projects = [], isLoading: isLoadingProjects } = useProjects();
console.log('Projects Data:', projects);
const columns: ColumnDef<SubCategory>[] = [ const columns: ColumnDef<SubCategory>[] = [
{ {
accessorKey: "subCategoryCode", accessorKey: 'subCategoryCode',
header: "Code", header: 'Code',
cell: ({ row }) => ( cell: ({ row }) => (
<Badge variant="outline" className="font-mono"> <Badge variant="outline" className="font-mono">
{row.getValue("subCategoryCode")} {row.getValue('subCategoryCode')}
</Badge> </Badge>
), ),
}, },
{ {
accessorKey: "subCategoryName", accessorKey: 'subCategoryName',
header: "Sub-category Name", header: 'Sub-category Name',
}, },
{ {
accessorKey: "description", accessorKey: 'description',
header: "Description", header: 'Description',
cell: ({ row }) => ( cell: ({ row }) => <span className="text-muted-foreground text-sm">{row.getValue('description') || '-'}</span>,
<span className="text-muted-foreground text-sm">
{row.getValue("description") || "-"}
</span>
),
}, },
{ {
accessorKey: "isActive", accessorKey: 'isActive',
header: "Active", header: 'Active',
cell: ({ row }) => ( cell: ({ row }) =>
row.getValue("isActive") ? ( row.getValue('isActive') ? (
<CheckCircle className="h-4 w-4 text-green-600" /> <CheckCircle className="h-4 w-4 text-green-600" />
) : ( ) : (
<XCircle className="h-4 w-4 text-red-600" /> <XCircle className="h-4 w-4 text-red-600" />
)
), ),
}, },
{ {
accessorKey: "sortOrder", accessorKey: 'sortOrder',
header: "Order", header: 'Order',
cell: ({ row }) => ( cell: ({ row }) => <span className="font-mono">{row.getValue('sortOrder')}</span>,
<span className="font-mono">{row.getValue("sortOrder")}</span>
),
}, },
]; ];
@@ -75,7 +64,7 @@ export default function ShopSubCategoriesPage() {
<div className="flex items-center gap-4"> <div className="flex items-center gap-4">
<span className="text-sm font-medium">Project:</span> <span className="text-sm font-medium">Project:</span>
<Select <Select
value={selectedProjectId?.toString() ?? ""} value={selectedProjectId?.toString() ?? ''}
onValueChange={(v) => setSelectedProjectId(v ? parseInt(v) : undefined)} onValueChange={(v) => setSelectedProjectId(v ? parseInt(v) : undefined)}
> >
<SelectTrigger className="w-[300px]"> <SelectTrigger className="w-[300px]">
@@ -101,9 +90,7 @@ export default function ShopSubCategoriesPage() {
<div className="p-6 space-y-6"> <div className="p-6 space-y-6">
<div> <div>
<h1 className="text-2xl font-bold">Shop Drawing Sub-categories</h1> <h1 className="text-2xl font-bold">Shop Drawing Sub-categories</h1>
<p className="text-muted-foreground mt-1"> <p className="text-muted-foreground mt-1">Manage sub-categories () for shop drawings</p>
Manage sub-categories () for shop drawings
</p>
</div> </div>
{projectFilter} {projectFilter}
<div className="text-center py-12 text-muted-foreground border rounded-lg border-dashed"> <div className="text-center py-12 text-muted-foreground border rounded-lg border-dashed">
@@ -119,25 +106,29 @@ export default function ShopSubCategoriesPage() {
entityName="Sub-category" entityName="Sub-category"
title="Shop Drawing Sub-categories" title="Shop Drawing Sub-categories"
description="Manage sub-categories (หมวดหมู่ย่อย) for shop drawings" description="Manage sub-categories (หมวดหมู่ย่อย) for shop drawings"
queryKey={["shop-drawing-sub-categories", String(selectedProjectId)]} queryKey={['shop-drawing-sub-categories', String(selectedProjectId)]}
fetchFn={() => drawingMasterDataService.getShopSubCategories(selectedProjectId)} fetchFn={() => drawingMasterDataService.getShopSubCategories(selectedProjectId)}
createFn={(data) => drawingMasterDataService.createShopSubCategory({ createFn={(data) =>
drawingMasterDataService.createShopSubCategory({
...data, ...data,
projectId: selectedProjectId, projectId: selectedProjectId,
isActive: data.isActive === "true" || data.isActive === true isActive: data.isActive === 'true' || data.isActive === true,
})} })
updateFn={(id, data) => drawingMasterDataService.updateShopSubCategory(id, { }
updateFn={(id, data) =>
drawingMasterDataService.updateShopSubCategory(id, {
...data, ...data,
isActive: data.isActive === "true" || data.isActive === true isActive: data.isActive === 'true' || data.isActive === true,
})} })
}
deleteFn={(id) => drawingMasterDataService.deleteShopSubCategory(id)} deleteFn={(id) => drawingMasterDataService.deleteShopSubCategory(id)}
columns={columns} columns={columns}
fields={[ fields={[
{ name: "subCategoryCode", label: "Sub-category Code", type: "text", required: true }, { name: 'subCategoryCode', label: 'Sub-category Code', type: 'text', required: true },
{ name: "subCategoryName", label: "Sub-category Name", type: "text", required: true }, { name: 'subCategoryName', label: 'Sub-category Name', type: 'text', required: true },
{ name: "description", label: "Description", type: "textarea" }, { name: 'description', label: 'Description', type: 'textarea' },
{ name: "isActive", label: "Active", type: "checkbox" }, { name: 'isActive', label: 'Active', type: 'checkbox' },
{ name: "sortOrder", label: "Sort Order", type: "text", required: true }, { name: 'sortOrder', label: 'Sort Order', type: 'text', required: true },
]} ]}
filters={projectFilter} filters={projectFilter}
/> />