260220:1700 20260220 TASK-BEFE-001 Refactor by ADR-014 #5
All checks were successful
Build and Deploy / deploy (push) Successful in 2m33s
All checks were successful
Build and Deploy / deploy (push) Successful in 2m33s
This commit is contained in:
@@ -13,13 +13,22 @@ import {
|
|||||||
Shield,
|
Shield,
|
||||||
Menu,
|
Menu,
|
||||||
Layers,
|
Layers,
|
||||||
BookOpen,
|
LucideIcon,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { Can } from '@/components/common/can';
|
import { Can } from '@/components/common/can';
|
||||||
|
import { useAuthStore } from '@/lib/stores/auth-store';
|
||||||
|
|
||||||
export const mainNavItems = [
|
export type NavItem = {
|
||||||
|
title: string;
|
||||||
|
href: string;
|
||||||
|
icon: LucideIcon;
|
||||||
|
permission?: string | null;
|
||||||
|
adminOnly?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const mainNavItems: NavItem[] = [
|
||||||
{
|
{
|
||||||
title: 'Dashboard',
|
title: 'Dashboard',
|
||||||
href: '/dashboard',
|
href: '/dashboard',
|
||||||
@@ -67,24 +76,7 @@ export const mainNavItems = [
|
|||||||
href: '/admin',
|
href: '/admin',
|
||||||
icon: Shield,
|
icon: Shield,
|
||||||
permission: null,
|
permission: null,
|
||||||
},
|
adminOnly: true,
|
||||||
{
|
|
||||||
title: 'Security',
|
|
||||||
href: '/admin/access-control/roles',
|
|
||||||
icon: Shield,
|
|
||||||
permission: 'system.manage_security',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'System Logs',
|
|
||||||
href: '/admin/monitoring/system-logs/numbering',
|
|
||||||
icon: Layers,
|
|
||||||
permission: 'system.view_logs',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Reference Data',
|
|
||||||
href: '/admin/doc-control/reference',
|
|
||||||
icon: BookOpen,
|
|
||||||
permission: 'master_data.view',
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -95,6 +87,8 @@ interface SidebarProps {
|
|||||||
export function Sidebar({ className }: SidebarProps) {
|
export function Sidebar({ className }: SidebarProps) {
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
const [collapsed, setCollapsed] = useState(false);
|
const [collapsed, setCollapsed] = useState(false);
|
||||||
|
const user = useAuthStore((state) => state.user);
|
||||||
|
const isAdmin = user?.role === 'ADMIN' || user?.role === 'DC';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -119,6 +113,8 @@ export function Sidebar({ className }: SidebarProps) {
|
|||||||
<div className="flex-1 overflow-y-auto py-4">
|
<div className="flex-1 overflow-y-auto py-4">
|
||||||
<nav className="grid gap-1 px-2">
|
<nav className="grid gap-1 px-2">
|
||||||
{mainNavItems.map((item, index) => {
|
{mainNavItems.map((item, index) => {
|
||||||
|
if (item.adminOnly && !isAdmin) return null;
|
||||||
|
|
||||||
const isActive = pathname.startsWith(item.href);
|
const isActive = pathname.startsWith(item.href);
|
||||||
|
|
||||||
const LinkComponent = (
|
const LinkComponent = (
|
||||||
@@ -172,6 +168,8 @@ import { Sheet, SheetContent, SheetTrigger, SheetTitle } from '@/components/ui/s
|
|||||||
export function MobileSidebar() {
|
export function MobileSidebar() {
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
|
const user = useAuthStore((state) => state.user);
|
||||||
|
const isAdmin = user?.role === 'ADMIN' || user?.role === 'DC';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Sheet open={open} onOpenChange={setOpen}>
|
<Sheet open={open} onOpenChange={setOpen}>
|
||||||
@@ -189,6 +187,8 @@ export function MobileSidebar() {
|
|||||||
<div className="flex-1 overflow-y-auto py-4 h-[calc(100vh-4rem)]">
|
<div className="flex-1 overflow-y-auto py-4 h-[calc(100vh-4rem)]">
|
||||||
<nav className="grid gap-1 px-2">
|
<nav className="grid gap-1 px-2">
|
||||||
{mainNavItems.map((item, index) => {
|
{mainNavItems.map((item, index) => {
|
||||||
|
if (item.adminOnly && !isAdmin) return null;
|
||||||
|
|
||||||
const isActive = pathname.startsWith(item.href);
|
const isActive = pathname.startsWith(item.href);
|
||||||
|
|
||||||
const LinkComponent = (
|
const LinkComponent = (
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { SearchContractDrawingDto, CreateContractDrawingDto } from '@/types/dto/
|
|||||||
import { SearchShopDrawingDto, CreateShopDrawingDto } from '@/types/dto/drawing/shop-drawing.dto';
|
import { SearchShopDrawingDto, CreateShopDrawingDto } from '@/types/dto/drawing/shop-drawing.dto';
|
||||||
import { SearchAsBuiltDrawingDto, CreateAsBuiltDrawingDto } from '@/types/dto/drawing/asbuilt-drawing.dto';
|
import { SearchAsBuiltDrawingDto, CreateAsBuiltDrawingDto } from '@/types/dto/drawing/asbuilt-drawing.dto';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { ContractDrawing, ShopDrawing, AsBuiltDrawing } from "@/types/drawing";
|
import { ContractDrawing, ShopDrawing, AsBuiltDrawing } from '@/types/drawing';
|
||||||
|
|
||||||
type DrawingType = 'CONTRACT' | 'SHOP' | 'AS_BUILT';
|
type DrawingType = 'CONTRACT' | 'SHOP' | 'AS_BUILT';
|
||||||
type DrawingSearchParams = SearchContractDrawingDto | SearchShopDrawingDto | SearchAsBuiltDrawingDto;
|
type DrawingSearchParams = SearchContractDrawingDto | SearchShopDrawingDto | SearchAsBuiltDrawingDto;
|
||||||
@@ -31,38 +31,44 @@ export function useDrawings(type: DrawingType, params: DrawingSearchParams) {
|
|||||||
response = await contractDrawingService.getAll(params as SearchContractDrawingDto);
|
response = await contractDrawingService.getAll(params as SearchContractDrawingDto);
|
||||||
// Map ContractDrawing to Drawing
|
// Map ContractDrawing to Drawing
|
||||||
if (response && response.data) {
|
if (response && response.data) {
|
||||||
response.data = response.data.map((d: ContractDrawing) => ({
|
const mappedData = response.data.map((d: ContractDrawing) => ({
|
||||||
...d,
|
...d,
|
||||||
drawingId: d.id,
|
drawingId: d.id,
|
||||||
drawingNumber: d.contractDrawingNo,
|
drawingNumber: d.contractDrawingNo,
|
||||||
type: 'CONTRACT',
|
type: 'CONTRACT',
|
||||||
}));
|
}));
|
||||||
|
// Re-wrap to preserve meta
|
||||||
|
response = { ...response, data: mappedData };
|
||||||
}
|
}
|
||||||
} else if (type === 'SHOP') {
|
} else if (type === 'SHOP') {
|
||||||
response = await shopDrawingService.getAll(params as SearchShopDrawingDto);
|
response = await shopDrawingService.getAll(params as SearchShopDrawingDto);
|
||||||
// Map ShopDrawing to Drawing
|
// Map ShopDrawing to Drawing
|
||||||
if (response && response.data) {
|
if (response && response.data) {
|
||||||
response.data = response.data.map((d: ShopDrawing) => ({
|
const mappedData = response.data.map((d: ShopDrawing) => ({
|
||||||
...d,
|
...d,
|
||||||
drawingId: d.id,
|
drawingId: d.id,
|
||||||
type: 'SHOP',
|
type: 'SHOP',
|
||||||
title: d.currentRevision?.title || "Untitled",
|
title: d.currentRevision?.title || 'Untitled',
|
||||||
revision: d.currentRevision?.revisionNumber,
|
revision: d.currentRevision?.revisionNumber,
|
||||||
legacyDrawingNumber: d.currentRevision?.legacyDrawingNumber,
|
legacyDrawingNumber: d.currentRevision?.legacyDrawingNumber,
|
||||||
}));
|
}));
|
||||||
|
// Re-wrap to preserve meta
|
||||||
|
response = { ...response, data: mappedData };
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
response = await asBuiltDrawingService.getAll(params as SearchAsBuiltDrawingDto);
|
response = await asBuiltDrawingService.getAll(params as SearchAsBuiltDrawingDto);
|
||||||
// Map AsBuiltDrawing to Drawing
|
// Map AsBuiltDrawing to Drawing
|
||||||
if (response && response.data) {
|
if (response && response.data) {
|
||||||
response.data = response.data.map((d: AsBuiltDrawing) => ({
|
const mappedData = response.data.map((d: AsBuiltDrawing) => ({
|
||||||
...d,
|
...d,
|
||||||
drawingId: d.id,
|
drawingId: d.id,
|
||||||
type: 'AS_BUILT',
|
type: 'AS_BUILT',
|
||||||
title: d.currentRevision?.title || "Untitled",
|
title: d.currentRevision?.title || 'Untitled',
|
||||||
revision: d.currentRevision?.revisionNumber,
|
revision: d.currentRevision?.revisionNumber,
|
||||||
}));
|
}));
|
||||||
}
|
// Re-wrap to preserve meta
|
||||||
|
response = { ...response, data: mappedData };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return response;
|
return response;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
// File: lib/services/asbuilt-drawing.service.ts
|
// File: lib/services/asbuilt-drawing.service.ts
|
||||||
import apiClient from "@/lib/api/client";
|
import apiClient from '@/lib/api/client';
|
||||||
import {
|
import {
|
||||||
CreateAsBuiltDrawingDto,
|
CreateAsBuiltDrawingDto,
|
||||||
CreateAsBuiltDrawingRevisionDto,
|
CreateAsBuiltDrawingRevisionDto,
|
||||||
SearchAsBuiltDrawingDto
|
SearchAsBuiltDrawingDto,
|
||||||
} from "@/types/dto/drawing/asbuilt-drawing.dto";
|
} from '@/types/dto/drawing/asbuilt-drawing.dto';
|
||||||
|
|
||||||
export const asBuiltDrawingService = {
|
export const asBuiltDrawingService = {
|
||||||
/**
|
/**
|
||||||
* Get As Built Drawings list
|
* Get As Built Drawings list
|
||||||
*/
|
*/
|
||||||
getAll: async (params: SearchAsBuiltDrawingDto) => {
|
getAll: async (params: SearchAsBuiltDrawingDto) => {
|
||||||
const response = await apiClient.get("/drawings/asbuilt", { params });
|
const response = await apiClient.get('/drawings/asbuilt', { params });
|
||||||
return response.data;
|
return response.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -27,7 +27,7 @@ export const asBuiltDrawingService = {
|
|||||||
* Create New As Built Drawing
|
* Create New As Built Drawing
|
||||||
*/
|
*/
|
||||||
create: async (data: CreateAsBuiltDrawingDto | FormData) => {
|
create: async (data: CreateAsBuiltDrawingDto | FormData) => {
|
||||||
const response = await apiClient.post("/drawings/asbuilt", data);
|
const response = await apiClient.post('/drawings/asbuilt', data);
|
||||||
return response.data;
|
return response.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -37,5 +37,5 @@ export const asBuiltDrawingService = {
|
|||||||
createRevision: async (id: string | number, data: CreateAsBuiltDrawingRevisionDto) => {
|
createRevision: async (id: string | number, data: CreateAsBuiltDrawingRevisionDto) => {
|
||||||
const response = await apiClient.post(`/drawings/asbuilt/${id}/revisions`, data);
|
const response = await apiClient.post(`/drawings/asbuilt/${id}/revisions`, data);
|
||||||
return response.data;
|
return response.data;
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,18 +1,16 @@
|
|||||||
// File: lib/services/contract-drawing.service.ts
|
// File: lib/services/contract-drawing.service.ts
|
||||||
import apiClient from "@/lib/api/client";
|
import apiClient from '@/lib/api/client';
|
||||||
import {
|
import {
|
||||||
CreateContractDrawingDto,
|
CreateContractDrawingDto,
|
||||||
UpdateContractDrawingDto,
|
UpdateContractDrawingDto,
|
||||||
SearchContractDrawingDto
|
SearchContractDrawingDto,
|
||||||
} from "@/types/dto/drawing/contract-drawing.dto";
|
} from '@/types/dto/drawing/contract-drawing.dto';
|
||||||
|
|
||||||
export const contractDrawingService = {
|
export const contractDrawingService = {
|
||||||
/**
|
|
||||||
* ดึงรายการแบบสัญญา (Contract Drawings)
|
|
||||||
*/
|
|
||||||
getAll: async (params: SearchContractDrawingDto) => {
|
getAll: async (params: SearchContractDrawingDto) => {
|
||||||
// GET /drawings/contract?projectId=1&page=1...
|
// GET /drawings/contract?projectId=1&page=1...
|
||||||
const response = await apiClient.get("/drawings/contract", { params });
|
const response = await apiClient.get('/drawings/contract', { params });
|
||||||
|
// The interceptor returns { statusCode, message, data, meta }
|
||||||
return response.data;
|
return response.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -28,7 +26,7 @@ export const contractDrawingService = {
|
|||||||
* สร้างแบบสัญญาใหม่
|
* สร้างแบบสัญญาใหม่
|
||||||
*/
|
*/
|
||||||
create: async (data: CreateContractDrawingDto | FormData) => {
|
create: async (data: CreateContractDrawingDto | FormData) => {
|
||||||
const response = await apiClient.post("/drawings/contract", data);
|
const response = await apiClient.post('/drawings/contract', data);
|
||||||
return response.data;
|
return response.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -46,5 +44,5 @@ export const contractDrawingService = {
|
|||||||
delete: async (id: string | number) => {
|
delete: async (id: string | number) => {
|
||||||
const response = await apiClient.delete(`/drawings/contract/${id}`);
|
const response = await apiClient.delete(`/drawings/contract/${id}`);
|
||||||
return response.data;
|
return response.data;
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
// File: lib/services/shop-drawing.service.ts
|
// File: lib/services/shop-drawing.service.ts
|
||||||
import apiClient from "@/lib/api/client";
|
import apiClient from '@/lib/api/client';
|
||||||
import {
|
import {
|
||||||
CreateShopDrawingDto,
|
CreateShopDrawingDto,
|
||||||
CreateShopDrawingRevisionDto,
|
CreateShopDrawingRevisionDto,
|
||||||
SearchShopDrawingDto
|
SearchShopDrawingDto,
|
||||||
} from "@/types/dto/drawing/shop-drawing.dto";
|
} from '@/types/dto/drawing/shop-drawing.dto';
|
||||||
|
|
||||||
export const shopDrawingService = {
|
export const shopDrawingService = {
|
||||||
/**
|
/**
|
||||||
* ดึงรายการแบบก่อสร้าง (Shop Drawings)
|
* ดึงรายการแบบก่อสร้าง (Shop Drawings)
|
||||||
*/
|
*/
|
||||||
getAll: async (params: SearchShopDrawingDto) => {
|
getAll: async (params: SearchShopDrawingDto) => {
|
||||||
const response = await apiClient.get("/drawings/shop", { params });
|
const response = await apiClient.get('/drawings/shop', { params });
|
||||||
return response.data;
|
return response.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -27,7 +27,7 @@ export const shopDrawingService = {
|
|||||||
* สร้าง Shop Drawing ใหม่ (พร้อม Revision 0)
|
* สร้าง Shop Drawing ใหม่ (พร้อม Revision 0)
|
||||||
*/
|
*/
|
||||||
create: async (data: CreateShopDrawingDto | FormData) => {
|
create: async (data: CreateShopDrawingDto | FormData) => {
|
||||||
const response = await apiClient.post("/drawings/shop", data);
|
const response = await apiClient.post('/drawings/shop', data);
|
||||||
return response.data;
|
return response.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -37,5 +37,5 @@ export const shopDrawingService = {
|
|||||||
createRevision: async (id: string | number, data: CreateShopDrawingRevisionDto) => {
|
createRevision: async (id: string | number, data: CreateShopDrawingRevisionDto) => {
|
||||||
const response = await apiClient.post(`/drawings/shop/${id}/revisions`, data);
|
const response = await apiClient.post(`/drawings/shop/${id}/revisions`, data);
|
||||||
return response.data;
|
return response.data;
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
// File: lib/services/workflow-engine.service.ts
|
// File: lib/services/workflow-engine.service.ts
|
||||||
import apiClient from "@/lib/api/client";
|
import apiClient from '@/lib/api/client';
|
||||||
import {
|
import {
|
||||||
CreateWorkflowDefinitionDto,
|
CreateWorkflowDefinitionDto,
|
||||||
UpdateWorkflowDefinitionDto,
|
UpdateWorkflowDefinitionDto,
|
||||||
EvaluateWorkflowDto,
|
EvaluateWorkflowDto,
|
||||||
GetAvailableActionsDto
|
GetAvailableActionsDto,
|
||||||
} from "@/types/dto/workflow-engine/workflow-engine.dto";
|
} from '@/types/dto/workflow-engine/workflow-engine.dto';
|
||||||
|
|
||||||
export const workflowEngineService = {
|
export const workflowEngineService = {
|
||||||
// --- Engine Execution (Low-Level) ---
|
// --- Engine Execution (Low-Level) ---
|
||||||
@@ -15,8 +15,8 @@ export const workflowEngineService = {
|
|||||||
* POST /workflow-engine/available-actions
|
* POST /workflow-engine/available-actions
|
||||||
*/
|
*/
|
||||||
getAvailableActions: async (data: GetAvailableActionsDto) => {
|
getAvailableActions: async (data: GetAvailableActionsDto) => {
|
||||||
const response = await apiClient.post("/workflow-engine/available-actions", data);
|
const response = await apiClient.post('/workflow-engine/available-actions', data);
|
||||||
return response.data; // string[] e.g. ['APPROVE', 'REJECT']
|
return response.data?.data || response.data; // string[] e.g. ['APPROVE', 'REJECT']
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -24,8 +24,8 @@ export const workflowEngineService = {
|
|||||||
* POST /workflow-engine/evaluate
|
* POST /workflow-engine/evaluate
|
||||||
*/
|
*/
|
||||||
evaluate: async (data: EvaluateWorkflowDto) => {
|
evaluate: async (data: EvaluateWorkflowDto) => {
|
||||||
const response = await apiClient.post("/workflow-engine/evaluate", data);
|
const response = await apiClient.post('/workflow-engine/evaluate', data);
|
||||||
return response.data; // { nextState: '...', events: [...] }
|
return response.data?.data || response.data; // { nextState: '...', events: [...] }
|
||||||
},
|
},
|
||||||
|
|
||||||
// --- Definition Management (Admin / Workflow Editor) ---
|
// --- Definition Management (Admin / Workflow Editor) ---
|
||||||
@@ -35,8 +35,8 @@ export const workflowEngineService = {
|
|||||||
* GET /workflow-engine/definitions
|
* GET /workflow-engine/definitions
|
||||||
*/
|
*/
|
||||||
getDefinitions: async () => {
|
getDefinitions: async () => {
|
||||||
const response = await apiClient.get("/workflow-engine/definitions");
|
const response = await apiClient.get('/workflow-engine/definitions');
|
||||||
return response.data;
|
return response.data?.data || response.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -45,7 +45,7 @@ export const workflowEngineService = {
|
|||||||
*/
|
*/
|
||||||
getDefinitionById: async (id: string | number) => {
|
getDefinitionById: async (id: string | number) => {
|
||||||
const response = await apiClient.get(`/workflow-engine/definitions/${id}`);
|
const response = await apiClient.get(`/workflow-engine/definitions/${id}`);
|
||||||
return response.data;
|
return response.data?.data || response.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -53,8 +53,8 @@ export const workflowEngineService = {
|
|||||||
* POST /workflow-engine/definitions
|
* POST /workflow-engine/definitions
|
||||||
*/
|
*/
|
||||||
createDefinition: async (data: CreateWorkflowDefinitionDto) => {
|
createDefinition: async (data: CreateWorkflowDefinitionDto) => {
|
||||||
const response = await apiClient.post("/workflow-engine/definitions", data);
|
const response = await apiClient.post('/workflow-engine/definitions', data);
|
||||||
return response.data;
|
return response.data?.data || response.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -63,7 +63,7 @@ export const workflowEngineService = {
|
|||||||
*/
|
*/
|
||||||
updateDefinition: async (id: string | number, data: UpdateWorkflowDefinitionDto) => {
|
updateDefinition: async (id: string | number, data: UpdateWorkflowDefinitionDto) => {
|
||||||
const response = await apiClient.patch(`/workflow-engine/definitions/${id}`, data);
|
const response = await apiClient.patch(`/workflow-engine/definitions/${id}`, data);
|
||||||
return response.data;
|
return response.data?.data || response.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -72,6 +72,6 @@ export const workflowEngineService = {
|
|||||||
*/
|
*/
|
||||||
deleteDefinition: async (id: string | number) => {
|
deleteDefinition: async (id: string | number) => {
|
||||||
const response = await apiClient.delete(`/workflow-engine/definitions/${id}`);
|
const response = await apiClient.delete(`/workflow-engine/definitions/${id}`);
|
||||||
return response.data;
|
return response.data?.data || response.data;
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user