260322:1648 Correct Coresspondence / Doing RFA / Correct CI
CI Pipeline / build (push) Failing after 12m41s
Build and Deploy / deploy (push) Failing after 2m44s

This commit is contained in:
admin
2026-03-22 16:48:12 +07:00
parent e5deedb42e
commit 11984bfa29
683 changed files with 105251 additions and 29068 deletions
@@ -135,10 +135,7 @@ describe('correspondenceService', () => {
const result = await correspondenceService.addReference(1, referenceDto);
expect(apiClient.post).toHaveBeenCalledWith(
'/correspondences/1/references',
referenceDto
);
expect(apiClient.post).toHaveBeenCalledWith('/correspondences/1/references', referenceDto);
expect(result).toEqual(mockResponse);
});
});
@@ -90,6 +90,4 @@ describe('projectService', () => {
expect(result).toEqual({});
});
});
});
+17 -17
View File
@@ -1,29 +1,29 @@
import apiClient from "@/lib/api/client";
import apiClient from '@/lib/api/client';
import { AuditQueryParams } from '@/types/dto/numbering.dto';
export interface AuditLog {
auditId: string;
userId?: number | null;
user?: {
id: number;
fullName?: string;
username: string;
};
action: string;
severity: string;
entityType?: string;
entityId?: string;
detailsJson?: Record<string, unknown>;
ipAddress?: string;
userAgent?: string;
createdAt: string;
auditId: string;
userId?: number | null;
user?: {
id: number;
fullName?: string;
username: string;
};
action: string;
severity: string;
entityType?: string;
entityId?: string;
detailsJson?: Record<string, unknown>;
ipAddress?: string;
userAgent?: string;
createdAt: string;
}
export type AuditLogQueryParams = AuditQueryParams;
export const auditLogService = {
getLogs: async (params?: AuditLogQueryParams) => {
const response = await apiClient.get<{ data: AuditLog[] } | AuditLog[]>("/audit-logs", { params });
const response = await apiClient.get<{ data: AuditLog[] } | AuditLog[]>('/audit-logs', { params });
// Support both wrapped and unwrapped scenarios
return (response.data as { data: AuditLog[] }).data ?? response.data;
},
+7 -7
View File
@@ -1,10 +1,10 @@
// File: lib/services/circulation.service.ts
import apiClient from "@/lib/api/client";
import apiClient from '@/lib/api/client';
// Import DTO ที่สร้างไว้
import { CreateCirculationDto } from "@/types/dto/circulation/create-circulation.dto";
import { SearchCirculationDto } from "@/types/dto/circulation/search-circulation.dto";
import { UpdateCirculationRoutingDto } from "@/types/dto/circulation/update-circulation-routing.dto";
import { CreateCirculationDto } from '@/types/dto/circulation/create-circulation.dto';
import { SearchCirculationDto } from '@/types/dto/circulation/search-circulation.dto';
import { UpdateCirculationRoutingDto } from '@/types/dto/circulation/update-circulation-routing.dto';
export const circulationService = {
/**
@@ -12,7 +12,7 @@ export const circulationService = {
*/
getAll: async (params?: SearchCirculationDto) => {
// GET /circulations
const response = await apiClient.get("/circulations", { params });
const response = await apiClient.get('/circulations', { params });
return response.data;
},
@@ -30,7 +30,7 @@ export const circulationService = {
*/
create: async (data: CreateCirculationDto) => {
// POST /circulations
const response = await apiClient.post("/circulations", data);
const response = await apiClient.post('/circulations', data);
return response.data;
},
@@ -50,5 +50,5 @@ export const circulationService = {
delete: async (uuid: string) => {
const response = await apiClient.delete(`/circulations/${uuid}`);
return response.data;
}
},
};
+4 -8
View File
@@ -1,9 +1,5 @@
import apiClient from "@/lib/api/client";
import {
CreateContractDto,
UpdateContractDto,
SearchContractDto,
} from "@/types/dto/contract/contract.dto";
import apiClient from '@/lib/api/client';
import { CreateContractDto, UpdateContractDto, SearchContractDto } from '@/types/dto/contract/contract.dto';
export const contractService = {
/**
@@ -11,7 +7,7 @@ export const contractService = {
* GET /contracts?projectId=1
*/
getAll: async (params?: SearchContractDto) => {
const response = await apiClient.get("/contracts", { params });
const response = await apiClient.get('/contracts', { params });
if (response.data && Array.isArray(response.data.data)) {
return response.data.data;
}
@@ -32,7 +28,7 @@ export const contractService = {
* POST /contracts
*/
create: async (data: CreateContractDto) => {
const response = await apiClient.post("/contracts", data);
const response = await apiClient.post('/contracts', data);
return response.data;
},
+11 -11
View File
@@ -1,17 +1,17 @@
// File: lib/services/correspondence.service.ts
import apiClient from "@/lib/api/client";
import { SearchCorrespondenceDto } from "@/types/dto/correspondence/search-correspondence.dto";
import { CreateCorrespondenceDto } from "@/types/dto/correspondence/create-correspondence.dto";
import apiClient from '@/lib/api/client';
import { SearchCorrespondenceDto } from '@/types/dto/correspondence/search-correspondence.dto';
import { CreateCorrespondenceDto } from '@/types/dto/correspondence/create-correspondence.dto';
// Import DTO ใหม่
import { SubmitCorrespondenceDto } from "@/types/dto/correspondence/submit-correspondence.dto";
import { WorkflowActionDto } from "@/types/dto/correspondence/workflow-action.dto";
import { AddReferenceDto, RemoveReferenceDto } from "@/types/dto/correspondence/add-reference.dto";
import { SubmitCorrespondenceDto } from '@/types/dto/correspondence/submit-correspondence.dto';
import { WorkflowActionDto } from '@/types/dto/correspondence/workflow-action.dto';
import { AddReferenceDto, RemoveReferenceDto } from '@/types/dto/correspondence/add-reference.dto';
export const correspondenceService = {
// ... (getAll, getById, create, update, delete เดิมคงไว้) ...
getAll: async (params?: SearchCorrespondenceDto) => {
const response = await apiClient.get("/correspondences", { params });
const response = await apiClient.get('/correspondences', { params });
return response.data;
},
@@ -21,7 +21,7 @@ export const correspondenceService = {
},
create: async (data: CreateCorrespondenceDto) => {
const response = await apiClient.post("/correspondences", data);
const response = await apiClient.post('/correspondences', data);
return response.data;
},
@@ -67,7 +67,7 @@ export const correspondenceService = {
removeReference: async (uuid: string, data: RemoveReferenceDto) => {
// ใช้ DELETE method โดยส่ง body ไปด้วย (axios รองรับผ่าน config.data)
const response = await apiClient.delete(`/correspondences/${uuid}/references`, {
data: data
data: data,
});
return response.data;
},
@@ -75,7 +75,7 @@ export const correspondenceService = {
* Preview Document Number
*/
previewNumber: async (data: Partial<CreateCorrespondenceDto>) => {
const response = await apiClient.post("/correspondences/preview-number", data);
const response = await apiClient.post('/correspondences/preview-number', data);
return response.data;
}
},
};
+7 -7
View File
@@ -1,28 +1,28 @@
import apiClient from "@/lib/api/client";
import { DashboardStats, ActivityLog, PendingTask } from "@/types/dashboard";
import apiClient from '@/lib/api/client';
import { DashboardStats, ActivityLog, PendingTask } from '@/types/dashboard';
export const dashboardService = {
getStats: async (): Promise<DashboardStats> => {
const response = await apiClient.get("/dashboard/stats");
const response = await apiClient.get('/dashboard/stats');
return response.data;
},
getRecentActivity: async (): Promise<ActivityLog[]> => {
try {
const response = await apiClient.get("/dashboard/activity");
const response = await apiClient.get('/dashboard/activity');
// ตรวจสอบว่า response.data เป็น array จริงๆ
if (Array.isArray(response.data)) {
return response.data;
}
return [];
} catch (error) {
} catch (_error) {
return [];
}
},
getPendingTasks: async (): Promise<PendingTask[]> => {
try {
const response = await apiClient.get("/dashboard/pending");
const response = await apiClient.get('/dashboard/pending');
// Backend คืน { data: [], meta: {} } ต้องดึง data ออกมา
if (response.data?.data && Array.isArray(response.data.data)) {
return response.data.data;
@@ -31,7 +31,7 @@ export const dashboardService = {
return response.data;
}
return [];
} catch (error) {
} catch (_error) {
return [];
}
},
@@ -1,10 +1,5 @@
import apiClient from "@/lib/api/client";
import {
NumberingMetrics,
ManualOverrideDto,
VoidReplaceDto,
CancelNumberDto,
} from "@/types/dto/numbering.dto";
import apiClient from '@/lib/api/client';
import { NumberingMetrics, ManualOverrideDto, VoidReplaceDto, CancelNumberDto } from '@/types/dto/numbering.dto';
/** A bulk-import record row */
export interface BulkImportRecord {
@@ -17,37 +12,37 @@ export interface BulkImportRecord {
export const documentNumberingService = {
// --- Admin Dashboard Metrics ---
getMetrics: async (): Promise<NumberingMetrics> => {
const response = await apiClient.get("/admin/document-numbering/metrics");
const response = await apiClient.get('/admin/document-numbering/metrics');
return response.data;
},
// --- Admin Tools ---
manualOverride: async (dto: ManualOverrideDto): Promise<void> => {
await apiClient.post("/admin/document-numbering/manual-override", dto);
await apiClient.post('/admin/document-numbering/manual-override', dto);
},
voidAndReplace: async (dto: VoidReplaceDto): Promise<{ documentNumber: string }> => {
const response = await apiClient.post("/admin/document-numbering/void-and-replace", dto);
const response = await apiClient.post('/admin/document-numbering/void-and-replace', dto);
return response.data;
},
cancelNumber: async (dto: CancelNumberDto): Promise<void> => {
await apiClient.post("/admin/document-numbering/cancel", dto);
await apiClient.post('/admin/document-numbering/cancel', dto);
},
bulkImport: async (data: FormData | BulkImportRecord[]): Promise<{ imported: number; errors: string[] }> => {
const isFormData = data instanceof FormData;
const config = isFormData ? { headers: { "Content-Type": "multipart/form-data" } } : {};
const response = await apiClient.post("/admin/document-numbering/bulk-import", data, config);
const config = isFormData ? { headers: { 'Content-Type': 'multipart/form-data' } } : {};
const response = await apiClient.post('/admin/document-numbering/bulk-import', data, config);
return response.data;
},
// --- Audit Logs ---
getAuditLogs: async () => {
// NOTE: endpoint might be merged with metrics or separate
// Currently controller has getMetrics returning audit logs too.
// But if we want separate pagination later:
// return apiClient.get("/admin/document-numbering/audit", { params });
return [];
}
// NOTE: endpoint might be merged with metrics or separate
// Currently controller has getMetrics returning audit logs too.
// But if we want separate pagination later:
// return apiClient.get("/admin/document-numbering/audit", { params });
return [];
},
};
+1 -1
View File
@@ -18,4 +18,4 @@ export * from './search.service';
export * from './notification.service';
export * from './workflow-engine.service';
export * from './monitoring.service';
export * from './json-schema.service';
export * from './json-schema.service';
+6 -10
View File
@@ -1,10 +1,6 @@
// File: lib/services/json-schema.service.ts
import apiClient from "@/lib/api/client";
import {
CreateJsonSchemaDto,
UpdateJsonSchemaDto,
SearchJsonSchemaDto
} from "@/types/dto/json-schema/json-schema.dto";
import apiClient from '@/lib/api/client';
import { CreateJsonSchemaDto, UpdateJsonSchemaDto, SearchJsonSchemaDto } from '@/types/dto/json-schema/json-schema.dto';
export const jsonSchemaService = {
/**
@@ -12,7 +8,7 @@ export const jsonSchemaService = {
*/
getAll: async (params?: SearchJsonSchemaDto) => {
// GET /json-schemas
const response = await apiClient.get("/json-schemas", { params });
const response = await apiClient.get('/json-schemas', { params });
return response.data;
},
@@ -39,7 +35,7 @@ export const jsonSchemaService = {
*/
create: async (data: CreateJsonSchemaDto) => {
// POST /json-schemas
const response = await apiClient.post("/json-schemas", data);
const response = await apiClient.post('/json-schemas', data);
return response.data;
},
@@ -68,8 +64,8 @@ export const jsonSchemaService = {
// POST /json-schemas/validate
const response = await apiClient.post(`/json-schemas/validate`, {
schemaCode: code,
data: data
data: data,
});
return response.data; // { valid: true, errors: [] }
}
},
};
+44 -43
View File
@@ -1,19 +1,19 @@
// File: lib/services/master-data.service.ts
import apiClient from "@/lib/api/client";
import apiClient from '@/lib/api/client';
// Import DTOs
import { CreateTagDto, UpdateTagDto, SearchTagDto } from "@/types/dto/master/tag.dto";
import { CreateDisciplineDto } from "@/types/dto/master/discipline.dto";
import { CreateSubTypeDto } from "@/types/dto/master/sub-type.dto";
import { SaveNumberFormatDto } from "@/types/dto/master/number-format.dto";
import { CreateRfaTypeDto, UpdateRfaTypeDto } from "@/types/dto/master/rfa-type.dto";
import { CreateCorrespondenceTypeDto, UpdateCorrespondenceTypeDto } from "@/types/dto/master/correspondence-type.dto";
import { Organization } from "@/types/organization";
import { CreateTagDto, UpdateTagDto, SearchTagDto } from '@/types/dto/master/tag.dto';
import { CreateDisciplineDto } from '@/types/dto/master/discipline.dto';
import { CreateSubTypeDto } from '@/types/dto/master/sub-type.dto';
import { SaveNumberFormatDto } from '@/types/dto/master/number-format.dto';
import { CreateRfaTypeDto, UpdateRfaTypeDto } from '@/types/dto/master/rfa-type.dto';
import { CreateCorrespondenceTypeDto, UpdateCorrespondenceTypeDto } from '@/types/dto/master/correspondence-type.dto';
import { Organization } from '@/types/organization';
import {
CreateOrganizationDto,
UpdateOrganizationDto,
SearchOrganizationDto,
} from "@/types/dto/organization/organization.dto";
} from '@/types/dto/organization/organization.dto';
const extractArrayData = <T>(value: unknown): T[] => {
let current: unknown = value;
@@ -23,7 +23,7 @@ const extractArrayData = <T>(value: unknown): T[] => {
return current as T[];
}
if (!current || typeof current !== "object" || !("data" in current)) {
if (!current || typeof current !== 'object' || !('data' in current)) {
return [];
}
@@ -38,14 +38,14 @@ export const masterDataService = {
/** ดึงรายการ Tags ทั้งหมด (Search & Pagination) */
getTags: async (params?: SearchTagDto) => {
const response = await apiClient.get("/master/tags", { params });
const response = await apiClient.get('/master/tags', { params });
// Support both wrapped and unwrapped scenarios
return response.data.data || response.data;
},
/** สร้าง Tag ใหม่ */
createTag: async (data: CreateTagDto) => {
const response = await apiClient.post("/master/tags", data);
const response = await apiClient.post('/master/tags', data);
return response.data;
},
@@ -65,14 +65,14 @@ export const masterDataService = {
/** ดึงรายชื่อองค์กรทั้งหมด */
getOrganizations: async (params?: SearchOrganizationDto) => {
const response = await apiClient.get<Organization[] | { data: Organization[] }>("/organizations", { params });
const response = await apiClient.get<Organization[] | { data: Organization[] }>('/organizations', { params });
// Support paginated response
if (response.data && Array.isArray((response.data as { data: Organization[] }).data)) {
return (response.data as { data: Organization[] }).data;
return (response.data as { data: Organization[] }).data;
}
// If response.data itself is an array
if (Array.isArray(response.data)) {
return response.data;
return response.data;
}
// If we're here, it might be { data: [], total: ... } but data is missing? or empty?
// Or it returned the object but data.data check failed (shouldn't happen if it follows schema).
@@ -82,8 +82,10 @@ export const masterDataService = {
// Fallback: Check if response.data is object?
// If it's the paginated object, return the data array if it exists
if (response.data && (response.data as { data: Organization[] }).data) {
// Maybe it's not an array?
return Array.isArray((response.data as { data: Organization[] }).data) ? (response.data as { data: Organization[] }).data : [];
// Maybe it's not an array?
return Array.isArray((response.data as { data: Organization[] }).data)
? (response.data as { data: Organization[] }).data
: [];
}
return []; // Return empty array to prevent map errors
@@ -91,7 +93,7 @@ export const masterDataService = {
/** สร้างองค์กรใหม่ */
createOrganization: async (data: CreateOrganizationDto) => {
const response = await apiClient.post("/organizations", data);
const response = await apiClient.post('/organizations', data);
return response.data;
},
@@ -107,20 +109,19 @@ export const masterDataService = {
return response.data;
},
// --- Disciplines Management (Admin / Req 6B) ---
/** ดึงรายชื่อสาขางาน (มักจะกรองตาม Contract ID) */
getDisciplines: async (contractId?: number | string) => {
const response = await apiClient.get("/master/disciplines", {
params: { contractId }
const response = await apiClient.get('/master/disciplines', {
params: { contractId },
});
return extractArrayData(response.data);
},
/** สร้างสาขางานใหม่ */
createDiscipline: async (data: CreateDisciplineDto) => {
const response = await apiClient.post("/master/disciplines", data);
const response = await apiClient.post('/master/disciplines', data);
return response.data;
},
@@ -134,15 +135,15 @@ export const masterDataService = {
/** ดึงรายชื่อประเภทย่อย (กรองตาม Contract และ Type) */
getSubTypes: async (contractId?: number | string, typeId?: number) => {
const response = await apiClient.get("/master/sub-types", {
params: { contractId, correspondenceTypeId: typeId }
const response = await apiClient.get('/master/sub-types', {
params: { contractId, correspondenceTypeId: typeId },
});
return extractArrayData(response.data);
},
/** สร้างประเภทย่อยใหม่ */
createSubType: async (data: CreateSubTypeDto) => {
const response = await apiClient.post("/master/sub-types", data);
const response = await apiClient.post('/master/sub-types', data);
return response.data;
},
@@ -150,55 +151,55 @@ export const masterDataService = {
/** ดึงประเภท RFA ทั้งหมด */
getRfaTypes: async (contractId?: number | string) => {
const response = await apiClient.get("/master/rfa-types", {
params: { contractId }
const response = await apiClient.get('/master/rfa-types', {
params: { contractId },
});
return extractArrayData(response.data);
},
/** สร้างประเภท RFA ใหม่ */
createRfaType: async (data: CreateRfaTypeDto) => {
return apiClient.post("/master/rfa-types", data).then(res => res.data);
return apiClient.post('/master/rfa-types', data).then((res) => res.data);
},
updateRfaType: async (id: number, data: UpdateRfaTypeDto) => {
return apiClient.patch(`/master/rfa-types/${id}`, data).then(res => res.data);
return apiClient.patch(`/master/rfa-types/${id}`, data).then((res) => res.data);
},
deleteRfaType: async (id: number) => {
return apiClient.delete(`/master/rfa-types/${id}`).then(res => res.data);
return apiClient.delete(`/master/rfa-types/${id}`).then((res) => res.data);
},
// --- Document Numbering Format (Admin Config) ---
// --- Correspondence Types Management ---
getCorrespondenceTypes: async () => {
const response = await apiClient.get("/master/correspondence-types");
const response = await apiClient.get('/master/correspondence-types');
return extractArrayData(response.data);
},
createCorrespondenceType: async (data: CreateCorrespondenceTypeDto) => {
return apiClient.post("/master/correspondence-types", data).then(res => res.data);
return apiClient.post('/master/correspondence-types', data).then((res) => res.data);
},
updateCorrespondenceType: async (id: number, data: UpdateCorrespondenceTypeDto) => {
return apiClient.patch(`/master/correspondence-types/${id}`, data).then(res => res.data);
return apiClient.patch(`/master/correspondence-types/${id}`, data).then((res) => res.data);
},
deleteCorrespondenceType: async (id: number) => {
return apiClient.delete(`/master/correspondence-types/${id}`).then(res => res.data);
return apiClient.delete(`/master/correspondence-types/${id}`).then((res) => res.data);
},
/** บันทึกรูปแบบเลขที่เอกสาร */
saveNumberFormat: async (data: SaveNumberFormatDto) => {
const response = await apiClient.post("/document-numbering/formats", data);
const response = await apiClient.post('/document-numbering/formats', data);
return response.data;
},
/** ดึงรูปแบบเลขที่เอกสารปัจจุบัน (เพื่อมาแก้ไข) */
getNumberFormat: async (projectId: number, typeId: number) => {
const response = await apiClient.get("/document-numbering/formats", {
params: { projectId, correspondenceTypeId: typeId }
const response = await apiClient.get('/document-numbering/formats', {
params: { projectId, correspondenceTypeId: typeId },
});
return response.data;
},
@@ -206,21 +207,21 @@ export const masterDataService = {
// --- Drawing Categories ---
getContractDrawingCategories: async (projectId?: number | string) => {
const response = await apiClient.get("/drawings/contract/categories", {
params: { projectId }
const response = await apiClient.get('/drawings/contract/categories', {
params: { projectId },
});
return extractArrayData(response.data);
},
getShopMainCategories: async (projectId: number) => {
const response = await apiClient.get("/drawings/shop/main-categories", { params: { projectId } });
const response = await apiClient.get('/drawings/shop/main-categories', { params: { projectId } });
return extractArrayData(response.data);
},
getShopSubCategories: async (projectId: number, mainCategoryId?: number) => {
const response = await apiClient.get("/drawings/shop/sub-categories", {
params: { projectId, mainCategoryId }
const response = await apiClient.get('/drawings/shop/sub-categories', {
params: { projectId, mainCategoryId },
});
return extractArrayData(response.data);
}
},
};
+4 -7
View File
@@ -11,7 +11,7 @@ interface WrappedData {
data?: unknown;
}
const extractNestedData = <T,>(value: unknown): T => {
const extractNestedData = <T>(value: unknown): T => {
let current: unknown = value;
for (let i = 0; i < 5; i += 1) {
@@ -25,7 +25,7 @@ const extractNestedData = <T,>(value: unknown): T => {
return current as T;
};
const normalizePaginatedResponse = <T,>(value: unknown): PaginatedResponse<T> => {
const normalizePaginatedResponse = <T>(value: unknown): PaginatedResponse<T> => {
const extracted = extractNestedData<unknown>(value);
if (!extracted || typeof extracted !== 'object') {
@@ -84,15 +84,12 @@ export const migrationService = {
return extractNestedData<MigrationReviewQueueItem>(data);
},
getErrors: async (params: {
page?: number;
limit?: number;
}): Promise<PaginatedResponse<MigrationErrorItem>> => {
getErrors: async (params: { page?: number; limit?: number }): Promise<PaginatedResponse<MigrationErrorItem>> => {
const { data } = await api.get('/migration/errors', { params });
return normalizePaginatedResponse<MigrationErrorItem>(data);
},
approveQueueItem: async (id: number, payload: any, idempotencyKey: string) => {
approveQueueItem: async (id: number, payload: Record<string, unknown>, idempotencyKey: string) => {
const { data } = await api.post(`/migration/queue/${id}/approve`, payload, {
headers: {
'idempotency-key': idempotencyKey,
+6 -6
View File
@@ -1,5 +1,5 @@
// File: lib/services/monitoring.service.ts
import apiClient from "@/lib/api/client";
import apiClient from '@/lib/api/client';
export interface SetMaintenanceDto {
enabled: boolean;
@@ -9,19 +9,19 @@ export interface SetMaintenanceDto {
export const monitoringService = {
/** ตรวจสอบสถานะสุขภาพระบบ (Health Check) */
getHealth: async () => {
const response = await apiClient.get("/health");
const response = await apiClient.get('/health');
return response.data;
},
/** ดึง Metrics การทำงาน (CPU, Memory, Request Count) */
getMetrics: async () => {
const response = await apiClient.get("/monitoring/metrics");
const response = await apiClient.get('/monitoring/metrics');
return response.data;
},
/** เปิด/ปิด Maintenance Mode */
setMaintenanceMode: async (data: SetMaintenanceDto) => {
const response = await apiClient.post("/monitoring/maintenance", data);
const response = await apiClient.post('/monitoring/maintenance', data);
return response.data;
}
};
},
};
@@ -1,9 +1,9 @@
import apiClient from "@/lib/api/client";
import { NotificationResponse } from "@/types/notification";
import apiClient from '@/lib/api/client';
import { NotificationResponse } from '@/types/notification';
export const notificationService = {
getUnread: async (): Promise<NotificationResponse> => {
const response = await apiClient.get("/notifications/unread");
const response = await apiClient.get('/notifications/unread');
// Backend should return { items: [], unreadCount: number }
// Or just items and we count on frontend, but typically backend gives count.
return response.data;
@@ -17,5 +17,5 @@ export const notificationService = {
markAllAsRead: async () => {
const response = await apiClient.patch(`/notifications/read-all`);
return response.data;
}
},
};
@@ -1,9 +1,9 @@
import apiClient from "@/lib/api/client";
import apiClient from '@/lib/api/client';
import {
CreateOrganizationDto,
UpdateOrganizationDto,
SearchOrganizationDto,
} from "@/types/dto/organization/organization.dto";
} from '@/types/dto/organization/organization.dto';
export const organizationService = {
/**
@@ -11,7 +11,7 @@ export const organizationService = {
* GET /organizations?projectId=1
*/
getAll: async (params?: SearchOrganizationDto) => {
const response = await apiClient.get("/organizations", { params });
const response = await apiClient.get('/organizations', { params });
// Normalize response if wrapped in data.data or direct data
if (response.data && Array.isArray(response.data.data)) {
return response.data.data;
@@ -33,7 +33,7 @@ export const organizationService = {
* POST /organizations
*/
create: async (data: CreateOrganizationDto) => {
const response = await apiClient.post("/organizations", data);
const response = await apiClient.post('/organizations', data);
return response.data;
},
+5 -9
View File
@@ -1,10 +1,6 @@
// File: lib/services/project.service.ts
import apiClient from "@/lib/api/client";
import {
CreateProjectDto,
UpdateProjectDto,
SearchProjectDto
} from "@/types/dto/project/project.dto";
import apiClient from '@/lib/api/client';
import { CreateProjectDto, UpdateProjectDto, SearchProjectDto } from '@/types/dto/project/project.dto';
export const projectService = {
// --- Basic CRUD ---
@@ -15,10 +11,10 @@ export const projectService = {
*/
getAll: async (params?: SearchProjectDto) => {
// GET /projects
const response = await apiClient.get("/projects", { params });
const response = await apiClient.get('/projects', { params });
// Handle paginated response
if (response.data && Array.isArray(response.data.data)) {
return response.data.data;
return response.data.data;
}
return response.data;
},
@@ -31,7 +27,7 @@ export const projectService = {
/** สร้างโครงการใหม่ (Admin) */
create: async (data: CreateProjectDto) => {
const response = await apiClient.post("/projects", data);
const response = await apiClient.post('/projects', data);
return response.data;
},
+5 -9
View File
@@ -1,10 +1,6 @@
// File: lib/services/rfa.service.ts
import apiClient from "@/lib/api/client";
import {
CreateRfaDto,
UpdateRfaDto,
SearchRfaDto
} from "@/types/dto/rfa/rfa.dto";
import apiClient from '@/lib/api/client';
import { CreateRfaDto, UpdateRfaDto, SearchRfaDto } from '@/types/dto/rfa/rfa.dto';
// DTO สำหรับการอนุมัติ (อาจจะย้ายไปไว้ใน folder dto/rfa/ ก็ได้ในอนาคต)
export interface WorkflowActionDto {
@@ -19,7 +15,7 @@ export const rfaService = {
*/
getAll: async (params: SearchRfaDto) => {
// GET /rfas
const response = await apiClient.get("/rfas", { params });
const response = await apiClient.get('/rfas', { params });
return response.data;
},
@@ -37,7 +33,7 @@ export const rfaService = {
*/
create: async (data: CreateRfaDto) => {
// POST /rfas
const response = await apiClient.post("/rfas", data);
const response = await apiClient.post('/rfas', data);
return response.data;
},
@@ -66,5 +62,5 @@ export const rfaService = {
// DELETE /rfas/:uuid (ADR-019)
const response = await apiClient.delete(`/rfas/${uuid}`);
return response.data;
}
},
};
+8 -8
View File
@@ -1,6 +1,6 @@
// File: lib/services/search.service.ts
import apiClient from "@/lib/api/client";
import { SearchQueryDto } from "@/types/dto/search/search-query.dto";
import apiClient from '@/lib/api/client';
import { SearchQueryDto } from '@/types/dto/search/search-query.dto';
export const searchService = {
/**
@@ -10,8 +10,8 @@ export const searchService = {
search: async (query: SearchQueryDto) => {
// ส่ง params แบบ flat ตาม DTO
// GET /search?q=...&type=...&projectId=...
const response = await apiClient.get("/search", {
params: query
const response = await apiClient.get('/search', {
params: query,
});
return response.data;
},
@@ -21,8 +21,8 @@ export const searchService = {
* ใช้ search endpoint แต่จำกัดจำนวน
*/
suggest: async (query: string) => {
const response = await apiClient.get("/search", {
params: { q: query, limit: 5 }
const response = await apiClient.get('/search', {
params: { q: query, limit: 5 },
});
// Assuming backend returns { items: [], ... } or just []
return response.data.items || response.data;
@@ -32,7 +32,7 @@ export const searchService = {
* (Optional) Re-index ข้อมูลใหม่ กรณีข้อมูลไม่ตรง (Admin Only)
*/
reindex: async (type?: string) => {
const response = await apiClient.post("/search/reindex", { type });
const response = await apiClient.post('/search/reindex', { type });
return response.data;
}
},
};
+7 -3
View File
@@ -14,7 +14,7 @@ export interface Session {
isCurrent: boolean;
}
const extractArrayData = <T,>(value: unknown): T[] => {
const extractArrayData = <T>(value: unknown): T[] => {
let current: unknown = value;
for (let i = 0; i < 5; i += 1) {
@@ -39,8 +39,12 @@ const transformSession = (session: Session | (Omit<Session, 'id'> & { id: string
export const sessionService = {
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);
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) => {
+6 -6
View File
@@ -1,10 +1,10 @@
// File: lib/services/transmittal.service.ts
import apiClient from "@/lib/api/client";
import apiClient from '@/lib/api/client';
import {
CreateTransmittalDto,
UpdateTransmittalDto,
SearchTransmittalDto
} from "@/types/dto/transmittal/transmittal.dto";
SearchTransmittalDto,
} from '@/types/dto/transmittal/transmittal.dto';
export const transmittalService = {
/**
@@ -12,7 +12,7 @@ export const transmittalService = {
*/
getAll: async (params: SearchTransmittalDto) => {
// GET /transmittals
const response = await apiClient.get("/transmittals", { params });
const response = await apiClient.get('/transmittals', { params });
return response.data;
},
@@ -30,7 +30,7 @@ export const transmittalService = {
*/
create: async (data: CreateTransmittalDto) => {
// POST /transmittals
const response = await apiClient.post("/transmittals", data);
const response = await apiClient.post('/transmittals', data);
return response.data;
},
@@ -50,5 +50,5 @@ export const transmittalService = {
// DELETE /transmittals/:uuid (ADR-019)
const response = await apiClient.delete(`/transmittals/${uuid}`);
return response.data;
}
},
};
+7 -7
View File
@@ -1,5 +1,5 @@
import apiClient from "@/lib/api/client";
import { CreateUserDto, UpdateUserDto, SearchUserDto, User, Role } from "@/types/user";
import apiClient from '@/lib/api/client';
import { CreateUserDto, UpdateUserDto, SearchUserDto, User, Role } from '@/types/user';
/** Raw API user shape (before transform) */
interface RawUser {
@@ -9,7 +9,7 @@ interface RawUser {
[key: string]: unknown;
}
const extractArrayData = <T,>(value: unknown): T[] => {
const extractArrayData = <T>(value: unknown): T[] => {
let current: unknown = value;
for (let i = 0; i < 5; i += 1) {
@@ -17,7 +17,7 @@ const extractArrayData = <T,>(value: unknown): T[] => {
return current as T[];
}
if (!current || typeof current !== "object" || !("data" in current)) {
if (!current || typeof current !== 'object' || !('data' in current)) {
return [];
}
@@ -41,12 +41,12 @@ type UserListResponse = User[] | { data: User[] | { data: User[] } };
export const userService = {
getAll: async (params?: SearchUserDto) => {
const response = await apiClient.get<UserListResponse>("/users", { params });
const response = await apiClient.get<UserListResponse>('/users', { params });
return extractArrayData<RawUser>(response.data).map(transformUser);
},
getRoles: async (): Promise<Role[]> => {
const response = await apiClient.get<{ data: unknown } | unknown>("/users/roles");
const response = await apiClient.get<{ data: unknown } | unknown>('/users/roles');
return extractArrayData<Role>(response.data);
},
@@ -56,7 +56,7 @@ export const userService = {
},
create: async (data: CreateUserDto) => {
const response = await apiClient.post<RawUser>("/users", data);
const response = await apiClient.post<RawUser>('/users', data);
return transformUser(response.data);
},
@@ -34,7 +34,7 @@ interface BackendWorkflowShape {
updated_at?: string;
}
const extractArrayData = <T,>(value: unknown): T[] => {
const extractArrayData = <T>(value: unknown): T[] => {
let current: unknown = value;
for (let i = 0; i < 5; i += 1) {
@@ -52,7 +52,7 @@ const extractArrayData = <T,>(value: unknown): T[] => {
return Array.isArray(current) ? (current as T[]) : [];
};
const extractNestedData = <T,>(value: unknown): T => {
const extractNestedData = <T>(value: unknown): T => {
let current: unknown = value;
for (let i = 0; i < 5; i += 1) {
@@ -106,9 +106,7 @@ const mapWorkflow = (backendObj: BackendWorkflowShape): Workflow => {
backendObj.workflow_code ||
'',
description:
backendObj.description ||
(typeof backendObj.dsl === 'object' ? backendObj.dsl?.description : undefined) ||
'',
backendObj.description || (typeof backendObj.dsl === 'object' ? backendObj.dsl?.description : undefined) || '',
workflowType: normalizeWorkflowType(backendObj.workflow_code),
version: backendObj.version || 1,
isActive: backendObj.is_active ?? false,