260228:1427 20260228:14:00 workflow update
All checks were successful
Build and Deploy / deploy (push) Successful in 2m37s
All checks were successful
Build and Deploy / deploy (push) Successful in 2m37s
This commit is contained in:
@@ -52,12 +52,26 @@ export class WorkflowEngineController {
|
|||||||
return this.workflowService.createDefinition(dto);
|
return this.workflowService.createDefinition(dto);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Get('definitions')
|
||||||
|
@ApiOperation({ summary: 'ดึง Workflow Definition ทั้งหมด' })
|
||||||
|
@RequirePermission('system.manage_all')
|
||||||
|
async getDefinitions() {
|
||||||
|
return this.workflowService.getDefinitions();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('definitions/:id')
|
||||||
|
@ApiOperation({ summary: 'ดึง Workflow Definition ด้วย ID' })
|
||||||
|
@RequirePermission('system.manage_all')
|
||||||
|
async getDefinitionById(@Param('id') id: string) {
|
||||||
|
return this.workflowService.getDefinitionById(id);
|
||||||
|
}
|
||||||
|
|
||||||
@Patch('definitions/:id')
|
@Patch('definitions/:id')
|
||||||
@ApiOperation({ summary: 'แก้ไข Workflow Definition (Re-compile DSL)' })
|
@ApiOperation({ summary: 'แก้ไข Workflow Definition (Re-compile DSL)' })
|
||||||
@RequirePermission('system.manage_all')
|
@RequirePermission('system.manage_all')
|
||||||
async updateDefinition(
|
async updateDefinition(
|
||||||
@Param('id') id: string,
|
@Param('id') id: string,
|
||||||
@Body() dto: UpdateWorkflowDefinitionDto,
|
@Body() dto: UpdateWorkflowDefinitionDto
|
||||||
) {
|
) {
|
||||||
return this.workflowService.update(id, dto);
|
return this.workflowService.update(id, dto);
|
||||||
}
|
}
|
||||||
@@ -81,7 +95,7 @@ export class WorkflowEngineController {
|
|||||||
async processTransition(
|
async processTransition(
|
||||||
@Param('id') instanceId: string,
|
@Param('id') instanceId: string,
|
||||||
@Body() dto: WorkflowTransitionDto,
|
@Body() dto: WorkflowTransitionDto,
|
||||||
@Request() req: any,
|
@Request() req: any
|
||||||
) {
|
) {
|
||||||
// ดึง User ID จาก Token (req.user มาจาก JwtStrategy)
|
// ดึง User ID จาก Token (req.user มาจาก JwtStrategy)
|
||||||
const userId = req.user?.userId;
|
const userId = req.user?.userId;
|
||||||
@@ -91,7 +105,7 @@ export class WorkflowEngineController {
|
|||||||
dto.action,
|
dto.action,
|
||||||
userId,
|
userId,
|
||||||
dto.comment,
|
dto.comment,
|
||||||
dto.payload,
|
dto.payload
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -121,6 +121,33 @@ export class WorkflowEngineService {
|
|||||||
return this.workflowDefRepo.save(definition);
|
return this.workflowDefRepo.save(definition);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ดึง Workflow Definition ทั้งหมด (เฉพาะ Version ล่าสุดของแต่ละ Workflow Code)
|
||||||
|
*/
|
||||||
|
async getDefinitions(): Promise<WorkflowDefinition[]> {
|
||||||
|
// หา version ล่าสุดของแต่ละ workflow_code
|
||||||
|
// ใช้ query builder เพื่อ group by และหา max version
|
||||||
|
const latestDefinitions = await this.workflowDefRepo
|
||||||
|
.createQueryBuilder('def')
|
||||||
|
.where(
|
||||||
|
'def.version = (SELECT MAX(sub.version) FROM workflow_definitions sub WHERE sub.workflow_code = def.workflow_code)',
|
||||||
|
)
|
||||||
|
.getMany();
|
||||||
|
|
||||||
|
return latestDefinitions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ดึง Workflow Definition ตาม ID หรือ Code
|
||||||
|
*/
|
||||||
|
async getDefinitionById(id: string): Promise<WorkflowDefinition> {
|
||||||
|
const definition = await this.workflowDefRepo.findOne({ where: { id } });
|
||||||
|
if (!definition) {
|
||||||
|
throw new NotFoundException(`Workflow Definition with ID "${id}" not found`);
|
||||||
|
}
|
||||||
|
return definition;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ดึง Action ที่ทำได้ ณ State ปัจจุบัน
|
* ดึง Action ที่ทำได้ ณ State ปัจจุบัน
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import Link from 'next/link';
|
|||||||
export default function WorkflowEditPage() {
|
export default function WorkflowEditPage() {
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const id = params?.id === 'new' ? null : Number(params?.id);
|
const id = params?.id === 'new' ? null : params?.id as string;
|
||||||
|
|
||||||
const [workflowData, setWorkflowData] = useState<Partial<Workflow>>({
|
const [workflowData, setWorkflowData] = useState<Partial<Workflow>>({
|
||||||
workflowName: '',
|
workflowName: '',
|
||||||
@@ -31,7 +31,7 @@ export default function WorkflowEditPage() {
|
|||||||
isActive: true,
|
isActive: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { data: fetchedWorkflow, isLoading: loadingWorkflow } = useWorkflowDefinition(id as number);
|
const { data: fetchedWorkflow, isLoading: loadingWorkflow } = useWorkflowDefinition(id as string);
|
||||||
const createMutation = useCreateWorkflowDefinition();
|
const createMutation = useCreateWorkflowDefinition();
|
||||||
const updateMutation = useUpdateWorkflowDefinition();
|
const updateMutation = useUpdateWorkflowDefinition();
|
||||||
|
|
||||||
|
|||||||
@@ -1,35 +1,15 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useState, useEffect } from 'react';
|
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Card } from '@/components/ui/card';
|
import { Card } from '@/components/ui/card';
|
||||||
import { Badge } from '@/components/ui/badge';
|
import { Badge } from '@/components/ui/badge';
|
||||||
import { Plus, Edit, Copy, Trash, Loader2 } from 'lucide-react';
|
import { Plus, Edit, Copy, Trash, Loader2 } from 'lucide-react';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
|
import { useWorkflowDefinitions } from '@/hooks/use-workflows';
|
||||||
import { Workflow } from '@/types/workflow';
|
import { Workflow } from '@/types/workflow';
|
||||||
import { workflowApi } from '@/lib/api/workflows';
|
|
||||||
import { toast } from 'sonner';
|
|
||||||
|
|
||||||
export default function WorkflowsPage() {
|
export default function WorkflowsPage() {
|
||||||
const [workflows, setWorkflows] = useState<Workflow[]>([]);
|
const { data: workflows = [], isLoading: loading } = useWorkflowDefinitions();
|
||||||
const [loading, setLoading] = useState(true);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const fetchWorkflows = async () => {
|
|
||||||
setLoading(true);
|
|
||||||
try {
|
|
||||||
const data = await workflowApi.getWorkflows();
|
|
||||||
setWorkflows(data);
|
|
||||||
} catch (error) {
|
|
||||||
toast.error('Failed to load workflows');
|
|
||||||
console.error('[WorkflowsPage]', error);
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
fetchWorkflows();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="p-6 space-y-6">
|
<div className="p-6 space-y-6">
|
||||||
@@ -52,7 +32,7 @@ export default function WorkflowsPage() {
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="grid gap-4">
|
<div className="grid gap-4">
|
||||||
{workflows.map((workflow) => (
|
{workflows.map((workflow: Workflow) => (
|
||||||
<Card key={workflow.workflowId} className="p-6">
|
<Card key={workflow.workflowId} className="p-6">
|
||||||
<div className="flex justify-between items-start">
|
<div className="flex justify-between items-start">
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
|
|||||||
@@ -7,6 +7,23 @@ import {
|
|||||||
GetAvailableActionsDto,
|
GetAvailableActionsDto,
|
||||||
} from '@/types/dto/workflow-engine/workflow-engine.dto';
|
} from '@/types/dto/workflow-engine/workflow-engine.dto';
|
||||||
|
|
||||||
|
import { Workflow } from '@/types/workflow';
|
||||||
|
|
||||||
|
const mapWorkflow = (backendObj: any): Workflow => {
|
||||||
|
if (!backendObj) throw new Error('Workflow not found');
|
||||||
|
return {
|
||||||
|
workflowId: backendObj.id,
|
||||||
|
workflowName: backendObj.dsl?.workflowName || backendObj.workflow_code,
|
||||||
|
description: backendObj.description || backendObj.dsl?.description || '',
|
||||||
|
workflowType: backendObj.workflow_code,
|
||||||
|
version: backendObj.version || 1,
|
||||||
|
isActive: backendObj.is_active,
|
||||||
|
dslDefinition: typeof backendObj.dsl === 'string' ? backendObj.dsl : backendObj.dsl?.dslDefinition || JSON.stringify(backendObj.dsl, null, 2),
|
||||||
|
stepCount: backendObj.compiled?.states ? Object.keys(backendObj.compiled.states).length : 0,
|
||||||
|
updatedAt: backendObj.updated_at || new Date().toISOString(),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export const workflowEngineService = {
|
export const workflowEngineService = {
|
||||||
// --- Engine Execution (Low-Level) ---
|
// --- Engine Execution (Low-Level) ---
|
||||||
|
|
||||||
@@ -34,18 +51,20 @@ export const workflowEngineService = {
|
|||||||
* ดึง Workflow Definition ทั้งหมด
|
* ดึง Workflow Definition ทั้งหมด
|
||||||
* GET /workflow-engine/definitions
|
* GET /workflow-engine/definitions
|
||||||
*/
|
*/
|
||||||
getDefinitions: async () => {
|
getDefinitions: async (): Promise<Workflow[]> => {
|
||||||
const response = await apiClient.get('/workflow-engine/definitions');
|
const response = await apiClient.get('/workflow-engine/definitions');
|
||||||
return response.data?.data || response.data;
|
const data = response.data?.data || response.data;
|
||||||
|
return Array.isArray(data) ? data.map(mapWorkflow) : data;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ดึง Workflow Definition ตาม ID
|
* ดึง Workflow Definition ตาม ID
|
||||||
* GET /workflow-engine/definitions/:id
|
* GET /workflow-engine/definitions/:id
|
||||||
*/
|
*/
|
||||||
getDefinitionById: async (id: string | number) => {
|
getDefinitionById: async (id: string | number): Promise<Workflow> => {
|
||||||
const response = await apiClient.get(`/workflow-engine/definitions/${id}`);
|
const response = await apiClient.get(`/workflow-engine/definitions/${id}`);
|
||||||
return response.data?.data || response.data;
|
const data = response.data?.data || response.data;
|
||||||
|
return mapWorkflow(data);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export interface WorkflowStep {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface Workflow {
|
export interface Workflow {
|
||||||
workflowId: number;
|
workflowId: string | number;
|
||||||
workflowName: string;
|
workflowName: string;
|
||||||
description: string;
|
description: string;
|
||||||
workflowType: WorkflowType;
|
workflowType: WorkflowType;
|
||||||
|
|||||||
Reference in New Issue
Block a user