260219:1649 20260219 TASK-BEFE-001 Fixed Blank Admin Pages
All checks were successful
Build and Deploy / deploy (push) Successful in 2m41s
All checks were successful
Build and Deploy / deploy (push) Successful in 2m41s
This commit is contained in:
@@ -1,19 +1,20 @@
|
|||||||
"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 { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||||
|
import { Loader2, Plus, Trash2 } from 'lucide-react';
|
||||||
|
import { useProjects } from '@/hooks/use-master-data';
|
||||||
import {
|
import {
|
||||||
Select,
|
drawingMasterDataService,
|
||||||
SelectContent,
|
ContractCategory,
|
||||||
SelectItem,
|
ContractSubCategory,
|
||||||
SelectTrigger,
|
} from '@/lib/services/drawing-master-data.service';
|
||||||
SelectValue,
|
import { Badge } from '@/components/ui/badge';
|
||||||
} from "@/components/ui/select";
|
import { Button } from '@/components/ui/button';
|
||||||
import { Loader2 } from "lucide-react";
|
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
import { useProjects } from "@/hooks/use-master-data";
|
import { toast } from 'sonner';
|
||||||
import { drawingMasterDataService, ContractCategory, ContractSubCategory } from "@/lib/services/drawing-master-data.service";
|
|
||||||
import { Badge } from "@/components/ui/badge";
|
|
||||||
|
|
||||||
interface Category {
|
interface Category {
|
||||||
id: number;
|
id: number;
|
||||||
@@ -29,33 +30,27 @@ export default function ContractCategoriesPage() {
|
|||||||
|
|
||||||
const columns: ColumnDef<Category>[] = [
|
const columns: ColumnDef<Category>[] = [
|
||||||
{
|
{
|
||||||
accessorKey: "catCode",
|
accessorKey: 'catCode',
|
||||||
header: "Code",
|
header: 'Code',
|
||||||
cell: ({ row }) => (
|
cell: ({ row }) => (
|
||||||
<Badge variant="outline" className="font-mono">
|
<Badge variant="outline" className="font-mono">
|
||||||
{row.getValue("catCode")}
|
{row.getValue('catCode')}
|
||||||
</Badge>
|
</Badge>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: "catName",
|
accessorKey: 'catName',
|
||||||
header: "Category Name",
|
header: '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: "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>
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -63,7 +58,7 @@ export default function ContractCategoriesPage() {
|
|||||||
<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]">
|
||||||
@@ -89,9 +84,7 @@ export default function ContractCategoriesPage() {
|
|||||||
<div className="p-6 space-y-6">
|
<div className="p-6 space-y-6">
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-2xl font-bold">Contract Drawing Categories</h1>
|
<h1 className="text-2xl font-bold">Contract Drawing Categories</h1>
|
||||||
<p className="text-muted-foreground mt-1">
|
<p className="text-muted-foreground mt-1">Manage main categories (หมวดหมู่หลัก) for contract drawings</p>
|
||||||
Manage main categories (หมวดหมู่หลัก) for contract 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">
|
||||||
@@ -107,17 +100,17 @@ export default function ContractCategoriesPage() {
|
|||||||
entityName="Category"
|
entityName="Category"
|
||||||
title="Contract Drawing Categories"
|
title="Contract Drawing Categories"
|
||||||
description="Manage main categories (หมวดหมู่หลัก) for contract drawings"
|
description="Manage main categories (หมวดหมู่หลัก) for contract drawings"
|
||||||
queryKey={["contract-drawing-categories", String(selectedProjectId)]}
|
queryKey={['contract-drawing-categories', String(selectedProjectId)]}
|
||||||
fetchFn={() => drawingMasterDataService.getContractCategories(selectedProjectId)}
|
fetchFn={() => drawingMasterDataService.getContractCategories(selectedProjectId)}
|
||||||
createFn={(data) => drawingMasterDataService.createContractCategory({ ...data, projectId: selectedProjectId })}
|
createFn={(data) => drawingMasterDataService.createContractCategory({ ...data, projectId: selectedProjectId })}
|
||||||
updateFn={(id, data) => drawingMasterDataService.updateContractCategory(id, data)}
|
updateFn={(id, data) => drawingMasterDataService.updateContractCategory(id, data)}
|
||||||
deleteFn={(id) => drawingMasterDataService.deleteContractCategory(id)}
|
deleteFn={(id) => drawingMasterDataService.deleteContractCategory(id)}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
fields={[
|
fields={[
|
||||||
{ name: "catCode", label: "Category Code", type: "text", required: true },
|
{ name: 'catCode', label: 'Category Code', type: 'text', required: true },
|
||||||
{ name: "catName", label: "Category Name", type: "text", required: true },
|
{ name: 'catName', label: 'Category Name', type: 'text', required: true },
|
||||||
{ name: "description", label: "Description", type: "textarea" },
|
{ name: 'description', label: 'Description', type: 'textarea' },
|
||||||
{ name: "sortOrder", label: "Sort Order", type: "text", required: true },
|
{ name: 'sortOrder', label: 'Sort Order', type: 'text', required: true },
|
||||||
]}
|
]}
|
||||||
filters={projectFilter}
|
filters={projectFilter}
|
||||||
/>
|
/>
|
||||||
@@ -135,76 +128,71 @@ export default function ContractCategoriesPage() {
|
|||||||
However, to keep it simple and consistent:
|
However, to keep it simple and consistent:
|
||||||
Let's add a separate section below the table or a dialog triggered by a custom cell.
|
Let's add a separate section below the table or a dialog triggered by a custom cell.
|
||||||
*/}
|
*/}
|
||||||
<div className="mt-8 border-t pt-8">
|
<div className="mt-8 border-t pt-8">
|
||||||
<CategoryMappingSection projectId={selectedProjectId} />
|
<CategoryMappingSection projectId={selectedProjectId} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function CategoryMappingSection({ projectId }: { projectId: number }) {
|
function CategoryMappingSection({ projectId }: { projectId: number }) {
|
||||||
|
// ... logic to manage mappings would go here ...
|
||||||
// ... logic to manage mappings would go here ...
|
// But to properly implement this, we need a full mapping UI.
|
||||||
// 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
|
||||||
// Let's defer this implementation pattern to a separate component to keep this file clean
|
// and just mount it here.
|
||||||
// and just mount it here.
|
return (
|
||||||
return (
|
<div className="space-y-4">
|
||||||
<div className="space-y-4">
|
<h2 className="text-xl font-semibold">Category Mappings (Map Sub-categories to Categories)</h2>
|
||||||
<h2 className="text-xl font-semibold">Category Mappings (Map Sub-categories to Categories)</h2>
|
<div className="bg-muted/30 p-4 rounded-lg border-dashed border">
|
||||||
<div className="bg-muted/30 p-4 rounded-lg border-dashed border">
|
<p className="text-sm text-muted-foreground">Select a category to view and manage its sub-categories.</p>
|
||||||
<p className="text-sm text-muted-foreground">Select a category to view and manage its sub-categories.</p>
|
{/*
|
||||||
{/*
|
|
||||||
Real implementation would be complex here.
|
Real implementation would be complex here.
|
||||||
Better approach: Add a "Manage Sub-categories" button to the Categories table if possible.
|
Better approach: Add a "Manage Sub-categories" button to the Categories table if possible.
|
||||||
Or simpler: A separate "Mapping" page.
|
Or simpler: A separate "Mapping" page.
|
||||||
*/}
|
*/}
|
||||||
<ManageMappings projectId={projectId} />
|
<ManageMappings projectId={projectId} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
import { Plus, Trash2 } from "lucide-react";
|
|
||||||
import { Button } from "@/components/ui/button";
|
|
||||||
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
|
||||||
import { toast } from "sonner"; // Use sonner instead of use-toast
|
|
||||||
|
|
||||||
function ManageMappings({ projectId }: { projectId: number }) {
|
function ManageMappings({ projectId }: { projectId: number }) {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const [selectedCat, setSelectedCat] = useState<string>("");
|
const [selectedCat, setSelectedCat] = useState<string>('');
|
||||||
const [selectedSubCat, setSelectedSubCat] = useState<string>("");
|
const [selectedSubCat, setSelectedSubCat] = useState<string>('');
|
||||||
|
|
||||||
const { data: categories = [] } = useQuery({
|
const { data: categories = [] } = useQuery({
|
||||||
queryKey: ["contract-categories", String(projectId)],
|
queryKey: ['contract-categories', String(projectId)],
|
||||||
queryFn: () => drawingMasterDataService.getContractCategories(projectId),
|
queryFn: () => drawingMasterDataService.getContractCategories(projectId),
|
||||||
});
|
});
|
||||||
|
|
||||||
const { data: subCategories = [] } = useQuery({
|
const { data: subCategories = [] } = useQuery({
|
||||||
queryKey: ["contract-sub-categories", String(projectId)],
|
queryKey: ['contract-sub-categories', String(projectId)],
|
||||||
queryFn: () => drawingMasterDataService.getContractSubCategories(projectId),
|
queryFn: () => drawingMasterDataService.getContractSubCategories(projectId),
|
||||||
});
|
});
|
||||||
|
|
||||||
const { data: mappings = [] } = useQuery({
|
const { data: mappings = [] } = useQuery({
|
||||||
queryKey: ["contract-mappings", String(projectId), selectedCat],
|
queryKey: ['contract-mappings', String(projectId), selectedCat],
|
||||||
queryFn: () => drawingMasterDataService.getContractMappings(projectId, selectedCat ? parseInt(selectedCat) : undefined),
|
queryFn: () =>
|
||||||
|
drawingMasterDataService.getContractMappings(projectId, selectedCat ? parseInt(selectedCat) : undefined),
|
||||||
enabled: !!selectedCat,
|
enabled: !!selectedCat,
|
||||||
});
|
});
|
||||||
|
|
||||||
const createMutation = useMutation({
|
const createMutation = useMutation({
|
||||||
mutationFn: drawingMasterDataService.createContractMapping,
|
mutationFn: drawingMasterDataService.createContractMapping,
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
queryClient.invalidateQueries({ queryKey: ["contract-mappings"] });
|
queryClient.invalidateQueries({ queryKey: ['contract-mappings'] });
|
||||||
toast.success("Mapping created");
|
toast.success('Mapping created');
|
||||||
setSelectedSubCat("");
|
setSelectedSubCat('');
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const deleteMutation = useMutation({
|
const deleteMutation = useMutation({
|
||||||
mutationFn: drawingMasterDataService.deleteContractMapping,
|
mutationFn: drawingMasterDataService.deleteContractMapping,
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
queryClient.invalidateQueries({ queryKey: ["contract-mappings"] });
|
queryClient.invalidateQueries({ queryKey: ['contract-mappings'] });
|
||||||
toast.success("Mapping removed");
|
toast.success('Mapping removed');
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleAdd = () => {
|
const handleAdd = () => {
|
||||||
@@ -218,65 +206,79 @@ function ManageMappings({ projectId }: { projectId: number }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="grid gap-6 md:grid-cols-2">
|
<div className="grid gap-6 md:grid-cols-2">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<label className="text-sm font-medium">Select Category</label>
|
<label className="text-sm font-medium">Select Category</label>
|
||||||
<Select value={selectedCat} onValueChange={setSelectedCat}>
|
<Select value={selectedCat} onValueChange={setSelectedCat}>
|
||||||
|
<SelectTrigger>
|
||||||
|
<SelectValue placeholder="Select Category..." />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
{categories.map((c: ContractCategory) => (
|
||||||
|
<SelectItem key={c.id} value={String(c.id)}>
|
||||||
|
{c.catCode} - {c.catName}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{selectedCat && (
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div className="flex gap-2 items-end">
|
||||||
|
<div className="flex-1 space-y-2">
|
||||||
|
<label className="text-sm font-medium">Add Sub-Category</label>
|
||||||
|
<Select value={selectedSubCat} onValueChange={setSelectedSubCat}>
|
||||||
<SelectTrigger>
|
<SelectTrigger>
|
||||||
<SelectValue placeholder="Select Category..." />
|
<SelectValue placeholder="Select Sub-Category to add..." />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
{categories.map((c: ContractCategory) => (
|
{subCategories
|
||||||
<SelectItem key={c.id} value={String(c.id)}>{c.catCode} - {c.catName}</SelectItem>
|
.filter(
|
||||||
|
(s: ContractSubCategory) =>
|
||||||
|
!mappings.find((m: { subCategory: { id: number } }) => m.subCategory.id === s.id)
|
||||||
|
)
|
||||||
|
.map((s: ContractSubCategory) => (
|
||||||
|
<SelectItem key={s.id} value={String(s.id)}>
|
||||||
|
{s.subCatCode} - {s.subCatName}
|
||||||
|
</SelectItem>
|
||||||
))}
|
))}
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
|
<Button onClick={handleAdd} disabled={!selectedSubCat || createMutation.isPending}>
|
||||||
|
<Plus className="h-4 w-4 mr-2" /> Add
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
{selectedCat && (
|
<div className="border rounded-md">
|
||||||
<div className="space-y-4">
|
<div className="p-2 bg-muted/50 font-medium text-sm grid grid-cols-[1fr,auto] gap-2">
|
||||||
<div className="flex gap-2 items-end">
|
<span>Mapped Sub-Categories</span>
|
||||||
<div className="flex-1 space-y-2">
|
<span>Action</span>
|
||||||
<label className="text-sm font-medium">Add Sub-Category</label>
|
</div>
|
||||||
<Select value={selectedSubCat} onValueChange={setSelectedSubCat}>
|
{mappings.length === 0 ? (
|
||||||
<SelectTrigger>
|
<div className="p-4 text-center text-sm text-muted-foreground">No sub-categories mapped yet.</div>
|
||||||
<SelectValue placeholder="Select Sub-Category to add..." />
|
) : (
|
||||||
</SelectTrigger>
|
<div className="divide-y">
|
||||||
<SelectContent>
|
{mappings.map((m: { id: number; subCategory: ContractSubCategory }) => (
|
||||||
{subCategories
|
<div key={m.id} className="p-2 grid grid-cols-[1fr,auto] gap-2 items-center">
|
||||||
.filter((s: ContractSubCategory) => !mappings.find((m: { subCategory: { id: number } }) => m.subCategory.id === s.id))
|
<span className="text-sm">
|
||||||
.map((s: ContractSubCategory) => (
|
{m.subCategory.subCatCode} - {m.subCategory.subCatName}
|
||||||
<SelectItem key={s.id} value={String(s.id)}>{s.subCatCode} - {s.subCatName}</SelectItem>
|
</span>
|
||||||
))}
|
<Button
|
||||||
</SelectContent>
|
variant="ghost"
|
||||||
</Select>
|
size="sm"
|
||||||
</div>
|
onClick={() => deleteMutation.mutate(m.id)}
|
||||||
<Button onClick={handleAdd} disabled={!selectedSubCat || createMutation.isPending}>
|
disabled={deleteMutation.isPending}
|
||||||
<Plus className="h-4 w-4 mr-2" /> Add
|
>
|
||||||
|
<Trash2 className="h-4 w-4 text-red-500" />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
))}
|
||||||
<div className="border rounded-md">
|
</div>
|
||||||
<div className="p-2 bg-muted/50 font-medium text-sm grid grid-cols-[1fr,auto] gap-2">
|
)}
|
||||||
<span>Mapped Sub-Categories</span>
|
</div>
|
||||||
<span>Action</span>
|
</div>
|
||||||
</div>
|
)}
|
||||||
{mappings.length === 0 ? (
|
|
||||||
<div className="p-4 text-center text-sm text-muted-foreground">No sub-categories mapped yet.</div>
|
|
||||||
) : (
|
|
||||||
<div className="divide-y">
|
|
||||||
{mappings.map((m: { id: number; subCategory: ContractSubCategory }) => (
|
|
||||||
<div key={m.id} className="p-2 grid grid-cols-[1fr,auto] gap-2 items-center">
|
|
||||||
<span className="text-sm">{m.subCategory.subCatCode} - {m.subCategory.subCatName}</span>
|
|
||||||
<Button variant="ghost" size="sm" onClick={() => deleteMutation.mutate(m.id)} disabled={deleteMutation.isPending}>
|
|
||||||
<Trash2 className="h-4 w-4 text-red-500" />
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,36 +1,36 @@
|
|||||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||||
import { BookOpen, Tag, Settings, Layers } from "lucide-react";
|
import { BookOpen, Tag, Settings, Layers } from 'lucide-react';
|
||||||
import Link from "next/link";
|
import Link from 'next/link';
|
||||||
|
|
||||||
const refMenu = [
|
const refMenu = [
|
||||||
{
|
{
|
||||||
title: "Disciplines",
|
title: 'Disciplines',
|
||||||
description: "Manage system-wide disciplines (e.g., ARCH, STR)",
|
description: 'Manage system-wide disciplines (e.g., ARCH, STR)',
|
||||||
href: "/admin/reference/disciplines",
|
href: '/admin/doc-control/reference/disciplines',
|
||||||
icon: Layers,
|
icon: Layers,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "RFA Types",
|
title: 'RFA Types',
|
||||||
description: "Manage RFA types and approve codes",
|
description: 'Manage RFA types and approve codes',
|
||||||
href: "/admin/reference/rfa-types",
|
href: '/admin/doc-control/reference/rfa-types',
|
||||||
icon: BookOpen,
|
icon: BookOpen,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Correspondence Types",
|
title: 'Correspondence Types',
|
||||||
description: "Manage generic correspondence types",
|
description: 'Manage generic correspondence types',
|
||||||
href: "/admin/reference/correspondence-types",
|
href: '/admin/doc-control/reference/correspondence-types',
|
||||||
icon: Settings,
|
icon: Settings,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Tags",
|
title: 'Tags',
|
||||||
description: "Manage system tags for documents",
|
description: 'Manage system tags for documents',
|
||||||
href: "/admin/reference/tags",
|
href: '/admin/doc-control/reference/tags',
|
||||||
icon: Tag,
|
icon: Tag,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Drawing Categories",
|
title: 'Drawing Categories',
|
||||||
description: "Manage drawing sub-types and classifications",
|
description: 'Manage drawing sub-types and classifications',
|
||||||
href: "/admin/reference/drawing-categories",
|
href: '/admin/doc-control/reference/drawing-categories',
|
||||||
icon: Layers,
|
icon: Layers,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
@@ -44,15 +44,11 @@ export default function ReferenceDataPage() {
|
|||||||
<Link key={item.href} href={item.href}>
|
<Link key={item.href} href={item.href}>
|
||||||
<Card className="hover:shadow-md transition-shadow cursor-pointer h-full">
|
<Card className="hover:shadow-md transition-shadow cursor-pointer h-full">
|
||||||
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
||||||
<CardTitle className="text-sm font-medium">
|
<CardTitle className="text-sm font-medium">{item.title}</CardTitle>
|
||||||
{item.title}
|
|
||||||
</CardTitle>
|
|
||||||
<item.icon className="h-4 w-4 text-muted-foreground" />
|
<item.icon className="h-4 w-4 text-muted-foreground" />
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<p className="text-xs text-muted-foreground">
|
<p className="text-xs text-muted-foreground">{item.description}</p>
|
||||||
{item.description}
|
|
||||||
</p>
|
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</Link>
|
</Link>
|
||||||
|
|||||||
@@ -7,6 +7,9 @@ import {
|
|||||||
SearchOrganizationDto,
|
SearchOrganizationDto,
|
||||||
} from '@/types/dto/organization/organization.dto';
|
} from '@/types/dto/organization/organization.dto';
|
||||||
import { AxiosError } from 'axios';
|
import { AxiosError } from 'axios';
|
||||||
|
import { organizationService } from '@/lib/services/organization.service';
|
||||||
|
import { projectService } from '@/lib/services/project.service';
|
||||||
|
import { contractService } from '@/lib/services/contract.service';
|
||||||
|
|
||||||
export const masterDataKeys = {
|
export const masterDataKeys = {
|
||||||
all: ['masterData'] as const,
|
all: ['masterData'] as const,
|
||||||
@@ -15,8 +18,6 @@ export const masterDataKeys = {
|
|||||||
disciplines: (contractId?: number) => [...masterDataKeys.all, 'disciplines', contractId] as const,
|
disciplines: (contractId?: number) => [...masterDataKeys.all, 'disciplines', contractId] as const,
|
||||||
};
|
};
|
||||||
|
|
||||||
import { organizationService } from '@/lib/services/organization.service';
|
|
||||||
|
|
||||||
export function useOrganizations(params?: SearchOrganizationDto) {
|
export function useOrganizations(params?: SearchOrganizationDto) {
|
||||||
return useQuery({
|
return useQuery({
|
||||||
queryKey: [...masterDataKeys.organizations(), params],
|
queryKey: [...masterDataKeys.organizations(), params],
|
||||||
@@ -29,30 +30,31 @@ export function useCreateOrganization() {
|
|||||||
return useMutation({
|
return useMutation({
|
||||||
mutationFn: (data: CreateOrganizationDto) => masterDataService.createOrganization(data),
|
mutationFn: (data: CreateOrganizationDto) => masterDataService.createOrganization(data),
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
toast.success("Organization created successfully");
|
toast.success('Organization created successfully');
|
||||||
queryClient.invalidateQueries({ queryKey: masterDataKeys.organizations() });
|
queryClient.invalidateQueries({ queryKey: masterDataKeys.organizations() });
|
||||||
},
|
},
|
||||||
onError: (error: AxiosError<{ message?: string }>) => {
|
onError: (error: AxiosError<{ message?: string }>) => {
|
||||||
toast.error("Failed to create organization", {
|
toast.error('Failed to create organization', {
|
||||||
description: error.response?.data?.message || "Unknown error"
|
description: error.response?.data?.message || 'Unknown error',
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useUpdateOrganization() {
|
export function useUpdateOrganization() {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationFn: ({ id, data }: { id: number; data: UpdateOrganizationDto }) => masterDataService.updateOrganization(id, data),
|
mutationFn: ({ id, data }: { id: number; data: UpdateOrganizationDto }) =>
|
||||||
|
masterDataService.updateOrganization(id, data),
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
toast.success("Organization updated successfully");
|
toast.success('Organization updated successfully');
|
||||||
queryClient.invalidateQueries({ queryKey: masterDataKeys.organizations() });
|
queryClient.invalidateQueries({ queryKey: masterDataKeys.organizations() });
|
||||||
},
|
},
|
||||||
onError: (error: AxiosError<{ message?: string }>) => {
|
onError: (error: AxiosError<{ message?: string }>) => {
|
||||||
toast.error("Failed to update organization", {
|
toast.error('Failed to update organization', {
|
||||||
description: error.response?.data?.message || "Unknown error"
|
description: error.response?.data?.message || 'Unknown error',
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,14 +63,14 @@ export function useDeleteOrganization() {
|
|||||||
return useMutation({
|
return useMutation({
|
||||||
mutationFn: (id: number) => masterDataService.deleteOrganization(id),
|
mutationFn: (id: number) => masterDataService.deleteOrganization(id),
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
toast.success("Organization deleted successfully");
|
toast.success('Organization deleted successfully');
|
||||||
queryClient.invalidateQueries({ queryKey: masterDataKeys.organizations() });
|
queryClient.invalidateQueries({ queryKey: masterDataKeys.organizations() });
|
||||||
},
|
},
|
||||||
onError: (error: AxiosError<{ message?: string }>) => {
|
onError: (error: AxiosError<{ message?: string }>) => {
|
||||||
toast.error("Failed to delete organization", {
|
toast.error('Failed to delete organization', {
|
||||||
description: error.response?.data?.message || "Unknown error"
|
description: error.response?.data?.message || 'Unknown error',
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,9 +81,6 @@ export function useDisciplines(contractId?: number) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add useProjects hook
|
|
||||||
import { projectService } from '@/lib/services/project.service';
|
|
||||||
|
|
||||||
export function useProjects(isActive: boolean = true) {
|
export function useProjects(isActive: boolean = true) {
|
||||||
return useQuery({
|
return useQuery({
|
||||||
queryKey: ['projects', { isActive }],
|
queryKey: ['projects', { isActive }],
|
||||||
@@ -89,9 +88,6 @@ export function useProjects(isActive: boolean = true) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add useContracts hook
|
|
||||||
import { contractService } from '@/lib/services/contract.service';
|
|
||||||
|
|
||||||
export function useContracts(projectId: number = 1) {
|
export function useContracts(projectId: number = 1) {
|
||||||
return useQuery({
|
return useQuery({
|
||||||
queryKey: ['contracts', projectId],
|
queryKey: ['contracts', projectId],
|
||||||
|
|||||||
Reference in New Issue
Block a user