260321:1700 Correct Coresspondence / Doing RFA
This commit is contained in:
@@ -15,6 +15,24 @@ import {
|
||||
SearchOrganizationDto,
|
||||
} from "@/types/dto/organization/organization.dto";
|
||||
|
||||
const extractArrayData = <T>(value: unknown): T[] => {
|
||||
let current: unknown = value;
|
||||
|
||||
for (let i = 0; i < 5; i += 1) {
|
||||
if (Array.isArray(current)) {
|
||||
return current as T[];
|
||||
}
|
||||
|
||||
if (!current || typeof current !== "object" || !("data" in current)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
current = (current as { data?: unknown }).data;
|
||||
}
|
||||
|
||||
return Array.isArray(current) ? (current as T[]) : [];
|
||||
};
|
||||
|
||||
export const masterDataService = {
|
||||
// --- Tags Management ---
|
||||
|
||||
@@ -77,7 +95,7 @@ export const masterDataService = {
|
||||
return response.data;
|
||||
},
|
||||
|
||||
/** แก้ไของค์กร */
|
||||
/** แก้ไองค์กร */
|
||||
updateOrganization: async (uuid: string, data: UpdateOrganizationDto) => {
|
||||
const response = await apiClient.put(`/organizations/${uuid}`, data);
|
||||
return response.data;
|
||||
@@ -97,7 +115,7 @@ export const masterDataService = {
|
||||
const response = await apiClient.get("/master/disciplines", {
|
||||
params: { contractId }
|
||||
});
|
||||
return response.data.data || response.data;
|
||||
return extractArrayData(response.data);
|
||||
},
|
||||
|
||||
/** สร้างสาขางานใหม่ */
|
||||
@@ -119,7 +137,7 @@ export const masterDataService = {
|
||||
const response = await apiClient.get("/master/sub-types", {
|
||||
params: { contractId, correspondenceTypeId: typeId }
|
||||
});
|
||||
return response.data.data || response.data;
|
||||
return extractArrayData(response.data);
|
||||
},
|
||||
|
||||
/** สร้างประเภทย่อยใหม่ */
|
||||
@@ -135,7 +153,7 @@ export const masterDataService = {
|
||||
const response = await apiClient.get("/master/rfa-types", {
|
||||
params: { contractId }
|
||||
});
|
||||
return response.data.data || response.data;
|
||||
return extractArrayData(response.data);
|
||||
},
|
||||
|
||||
/** สร้างประเภท RFA ใหม่ */
|
||||
@@ -156,7 +174,7 @@ export const masterDataService = {
|
||||
// --- Correspondence Types Management ---
|
||||
getCorrespondenceTypes: async () => {
|
||||
const response = await apiClient.get("/master/correspondence-types");
|
||||
return response.data.data || response.data;
|
||||
return extractArrayData(response.data);
|
||||
},
|
||||
|
||||
createCorrespondenceType: async (data: CreateCorrespondenceTypeDto) => {
|
||||
@@ -191,18 +209,18 @@ export const masterDataService = {
|
||||
const response = await apiClient.get("/drawings/contract/categories", {
|
||||
params: { projectId }
|
||||
});
|
||||
return response.data.data || response.data;
|
||||
return extractArrayData(response.data);
|
||||
},
|
||||
|
||||
getShopMainCategories: async (projectId: number) => {
|
||||
const response = await apiClient.get("/drawings/shop/main-categories", { params: { projectId } });
|
||||
return response.data.data || response.data;
|
||||
return extractArrayData(response.data);
|
||||
},
|
||||
|
||||
getShopSubCategories: async (projectId: number, mainCategoryId?: number) => {
|
||||
const response = await apiClient.get("/drawings/shop/sub-categories", {
|
||||
params: { projectId, mainCategoryId }
|
||||
});
|
||||
return response.data.data || response.data;
|
||||
return extractArrayData(response.data);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -7,6 +7,68 @@ import {
|
||||
CommitBatchDto,
|
||||
} from '@/types/migration';
|
||||
|
||||
interface WrappedData {
|
||||
data?: unknown;
|
||||
}
|
||||
|
||||
const extractNestedData = <T,>(value: unknown): T => {
|
||||
let current: unknown = value;
|
||||
|
||||
for (let i = 0; i < 5; i += 1) {
|
||||
if (!current || typeof current !== 'object' || !('data' in current)) {
|
||||
return current as T;
|
||||
}
|
||||
|
||||
current = (current as WrappedData).data;
|
||||
}
|
||||
|
||||
return current as T;
|
||||
};
|
||||
|
||||
const normalizePaginatedResponse = <T,>(value: unknown): PaginatedResponse<T> => {
|
||||
const extracted = extractNestedData<unknown>(value);
|
||||
|
||||
if (!extracted || typeof extracted !== 'object') {
|
||||
return {
|
||||
items: [],
|
||||
total: 0,
|
||||
page: 1,
|
||||
limit: 0,
|
||||
totalPages: 0,
|
||||
};
|
||||
}
|
||||
|
||||
const response = extracted as Partial<PaginatedResponse<T>> & { data?: unknown };
|
||||
|
||||
if (Array.isArray(response.items)) {
|
||||
return {
|
||||
items: response.items,
|
||||
total: response.total ?? response.items.length,
|
||||
page: response.page ?? 1,
|
||||
limit: response.limit ?? response.items.length,
|
||||
totalPages: response.totalPages ?? 1,
|
||||
};
|
||||
}
|
||||
|
||||
if (Array.isArray(response.data)) {
|
||||
return {
|
||||
items: response.data as T[],
|
||||
total: response.total ?? response.data.length,
|
||||
page: response.page ?? 1,
|
||||
limit: response.limit ?? response.data.length,
|
||||
totalPages: response.totalPages ?? 1,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
items: [],
|
||||
total: 0,
|
||||
page: 1,
|
||||
limit: 0,
|
||||
totalPages: 0,
|
||||
};
|
||||
};
|
||||
|
||||
export const migrationService = {
|
||||
getReviewQueue: async (params: {
|
||||
page?: number;
|
||||
@@ -14,12 +76,12 @@ export const migrationService = {
|
||||
status?: MigrationReviewStatus;
|
||||
}): Promise<PaginatedResponse<MigrationReviewQueueItem>> => {
|
||||
const { data } = await api.get('/migration/queue', { params });
|
||||
return data?.data || data;
|
||||
return normalizePaginatedResponse<MigrationReviewQueueItem>(data);
|
||||
},
|
||||
|
||||
getQueueItem: async (id: number): Promise<MigrationReviewQueueItem> => {
|
||||
const { data } = await api.get(`/migration/queue/${id}`);
|
||||
return data?.data || data;
|
||||
return extractNestedData<MigrationReviewQueueItem>(data);
|
||||
},
|
||||
|
||||
getErrors: async (params: {
|
||||
@@ -27,7 +89,7 @@ export const migrationService = {
|
||||
limit?: number;
|
||||
}): Promise<PaginatedResponse<MigrationErrorItem>> => {
|
||||
const { data } = await api.get('/migration/errors', { params });
|
||||
return data?.data || data;
|
||||
return normalizePaginatedResponse<MigrationErrorItem>(data);
|
||||
},
|
||||
|
||||
approveQueueItem: async (id: number, payload: any, idempotencyKey: string) => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import apiClient from '@/lib/api/client';
|
||||
|
||||
export interface Session {
|
||||
id: string; // tokenId
|
||||
id: number;
|
||||
userId: number;
|
||||
user: {
|
||||
username: string;
|
||||
@@ -14,10 +14,33 @@ export interface Session {
|
||||
isCurrent: boolean;
|
||||
}
|
||||
|
||||
const extractArrayData = <T,>(value: unknown): T[] => {
|
||||
let current: unknown = value;
|
||||
|
||||
for (let i = 0; i < 5; i += 1) {
|
||||
if (Array.isArray(current)) {
|
||||
return current as T[];
|
||||
}
|
||||
|
||||
if (!current || typeof current !== 'object' || !('data' in current)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
current = (current as { data?: unknown }).data;
|
||||
}
|
||||
|
||||
return Array.isArray(current) ? (current as T[]) : [];
|
||||
};
|
||||
|
||||
const transformSession = (session: Session | (Omit<Session, 'id'> & { id: string | number })): Session => ({
|
||||
...session,
|
||||
id: typeof session.id === 'number' ? session.id : Number(session.id),
|
||||
});
|
||||
|
||||
export const sessionService = {
|
||||
getActiveSessions: async () => {
|
||||
const response = await apiClient.get<Session[] | { data: Session[] }>('/auth/sessions');
|
||||
return (response.data as { data: Session[] }).data ?? response.data;
|
||||
getActiveSessions: async (): Promise<Session[]> => {
|
||||
const response = await apiClient.get<Session[] | { data: Session[] } | { data: { data: Session[] } }>('/auth/sessions');
|
||||
return extractArrayData<Session | (Omit<Session, 'id'> & { id: string | number })>(response.data).map(transformSession);
|
||||
},
|
||||
|
||||
revokeSession: async (sessionId: number) => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import apiClient from "@/lib/api/client";
|
||||
import { CreateUserDto, UpdateUserDto, SearchUserDto, User } from "@/types/user";
|
||||
import { CreateUserDto, UpdateUserDto, SearchUserDto, User, Role } from "@/types/user";
|
||||
|
||||
/** Raw API user shape (before transform) */
|
||||
interface RawUser {
|
||||
@@ -9,6 +9,24 @@ interface RawUser {
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
const extractArrayData = <T,>(value: unknown): T[] => {
|
||||
let current: unknown = value;
|
||||
|
||||
for (let i = 0; i < 5; i += 1) {
|
||||
if (Array.isArray(current)) {
|
||||
return current as T[];
|
||||
}
|
||||
|
||||
if (!current || typeof current !== "object" || !("data" in current)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
current = (current as { data?: unknown }).data;
|
||||
}
|
||||
|
||||
return Array.isArray(current) ? (current as T[]) : [];
|
||||
};
|
||||
|
||||
const transformUser = (user: RawUser): User => {
|
||||
return {
|
||||
...(user as unknown as User),
|
||||
@@ -24,26 +42,12 @@ type UserListResponse = User[] | { data: User[] | { data: User[] } };
|
||||
export const userService = {
|
||||
getAll: async (params?: SearchUserDto) => {
|
||||
const response = await apiClient.get<UserListResponse>("/users", { params });
|
||||
|
||||
// Handle both paginated and non-paginated responses
|
||||
let rawData: RawUser[] | unknown = response.data;
|
||||
if (rawData && !Array.isArray(rawData) && 'data' in (rawData as object)) {
|
||||
rawData = (rawData as { data: unknown }).data;
|
||||
}
|
||||
if (rawData && !Array.isArray(rawData) && typeof rawData === 'object' && 'data' in (rawData as object)) {
|
||||
rawData = (rawData as { data: unknown }).data;
|
||||
}
|
||||
if (!Array.isArray(rawData)) return [];
|
||||
|
||||
return (rawData as RawUser[]).map(transformUser);
|
||||
return extractArrayData<RawUser>(response.data).map(transformUser);
|
||||
},
|
||||
|
||||
getRoles: async () => {
|
||||
getRoles: async (): Promise<Role[]> => {
|
||||
const response = await apiClient.get<{ data: unknown } | unknown>("/users/roles");
|
||||
if (response.data && typeof response.data === 'object' && 'data' in (response.data as object)) {
|
||||
return (response.data as { data: unknown }).data;
|
||||
}
|
||||
return response.data;
|
||||
return extractArrayData<Role>(response.data);
|
||||
},
|
||||
|
||||
getByUuid: async (uuid: string) => {
|
||||
|
||||
@@ -7,18 +7,112 @@ import {
|
||||
GetAvailableActionsDto,
|
||||
} from '@/types/dto/workflow-engine/workflow-engine.dto';
|
||||
|
||||
import { Workflow } from '@/types/workflow';
|
||||
import { Workflow, WorkflowType } from '@/types/workflow';
|
||||
|
||||
const mapWorkflow = (backendObj: any): Workflow => {
|
||||
interface WorkflowResponseShape {
|
||||
data?: unknown;
|
||||
}
|
||||
|
||||
interface WorkflowDslShape {
|
||||
workflowName?: string;
|
||||
description?: string;
|
||||
dslDefinition?: string;
|
||||
workflow?: string;
|
||||
states?: unknown;
|
||||
}
|
||||
|
||||
interface BackendWorkflowShape {
|
||||
id?: string | number;
|
||||
workflow_code?: string;
|
||||
description?: string;
|
||||
version?: number;
|
||||
is_active?: boolean;
|
||||
dsl?: string | WorkflowDslShape;
|
||||
compiled?: {
|
||||
states?: Record<string, unknown>;
|
||||
};
|
||||
updated_at?: string;
|
||||
}
|
||||
|
||||
const extractArrayData = <T,>(value: unknown): T[] => {
|
||||
let current: unknown = value;
|
||||
|
||||
for (let i = 0; i < 5; i += 1) {
|
||||
if (Array.isArray(current)) {
|
||||
return current as T[];
|
||||
}
|
||||
|
||||
if (!current || typeof current !== 'object' || !('data' in current)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
current = (current as { data?: unknown }).data;
|
||||
}
|
||||
|
||||
return Array.isArray(current) ? (current as T[]) : [];
|
||||
};
|
||||
|
||||
const extractNestedData = <T,>(value: unknown): T => {
|
||||
let current: unknown = value;
|
||||
|
||||
for (let i = 0; i < 5; i += 1) {
|
||||
if (!current || typeof current !== 'object' || !('data' in current)) {
|
||||
return current as T;
|
||||
}
|
||||
|
||||
current = (current as WorkflowResponseShape).data;
|
||||
}
|
||||
|
||||
return current as T;
|
||||
};
|
||||
|
||||
const extractDslDefinition = (dsl: BackendWorkflowShape['dsl']): string => {
|
||||
if (typeof dsl === 'string') {
|
||||
return dsl;
|
||||
}
|
||||
|
||||
if (!dsl || typeof dsl !== 'object') {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (typeof dsl.dslDefinition === 'string') {
|
||||
return dsl.dslDefinition;
|
||||
}
|
||||
|
||||
return JSON.stringify(dsl, null, 2);
|
||||
};
|
||||
|
||||
const normalizeWorkflowType = (workflowCode?: string): WorkflowType => {
|
||||
const normalizedCode = workflowCode?.toUpperCase() ?? '';
|
||||
|
||||
if (normalizedCode.includes('RFA')) {
|
||||
return 'RFA';
|
||||
}
|
||||
|
||||
if (normalizedCode.includes('DRAWING')) {
|
||||
return 'DRAWING';
|
||||
}
|
||||
|
||||
return 'CORRESPONDENCE';
|
||||
};
|
||||
|
||||
const mapWorkflow = (backendObj: BackendWorkflowShape): 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?.toUpperCase() || backendObj.workflow_code,
|
||||
workflowId: backendObj.id ?? backendObj.workflow_code ?? '',
|
||||
workflowName:
|
||||
(typeof backendObj.dsl === 'object' ? backendObj.dsl?.workflowName : undefined) ||
|
||||
(typeof backendObj.dsl === 'object' ? backendObj.dsl?.workflow : undefined) ||
|
||||
backendObj.workflow_code ||
|
||||
'',
|
||||
description:
|
||||
backendObj.description ||
|
||||
(typeof backendObj.dsl === 'object' ? backendObj.dsl?.description : undefined) ||
|
||||
'',
|
||||
workflowType: normalizeWorkflowType(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),
|
||||
isActive: backendObj.is_active ?? false,
|
||||
dslDefinition: extractDslDefinition(backendObj.dsl),
|
||||
stepCount: backendObj.compiled?.states ? Object.keys(backendObj.compiled.states).length : 0,
|
||||
updatedAt: backendObj.updated_at || new Date().toISOString(),
|
||||
};
|
||||
@@ -53,8 +147,7 @@ export const workflowEngineService = {
|
||||
*/
|
||||
getDefinitions: async (): Promise<Workflow[]> => {
|
||||
const response = await apiClient.get('/workflow-engine/definitions');
|
||||
const data = response.data?.data || response.data;
|
||||
return Array.isArray(data) ? data.map(mapWorkflow) : data;
|
||||
return extractArrayData<BackendWorkflowShape>(response.data).map((workflow) => mapWorkflow(workflow));
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -63,7 +156,7 @@ export const workflowEngineService = {
|
||||
*/
|
||||
getDefinitionById: async (id: string | number): Promise<Workflow> => {
|
||||
const response = await apiClient.get(`/workflow-engine/definitions/${id}`);
|
||||
const data = response.data?.data || response.data;
|
||||
const data = extractNestedData<BackendWorkflowShape>(response.data);
|
||||
return mapWorkflow(data);
|
||||
},
|
||||
|
||||
|
||||
Reference in New Issue
Block a user