diff --git a/backend/docker-compose.yml b/backend/docker-compose.yml
index 2a9505a..1df6e97 100644
--- a/backend/docker-compose.yml
+++ b/backend/docker-compose.yml
@@ -35,7 +35,7 @@ services:
# ใช้ Command นี้เพื่อตั้ง Password
command: redis-server --requirepass "Center2025"
ports:
- - '6379:6379'
+ - '16379:6379'
volumes:
- redis_data:/data
networks:
diff --git a/backend/src/modules/dashboard/dashboard.service.ts b/backend/src/modules/dashboard/dashboard.service.ts
index f50a977..86a697e 100644
--- a/backend/src/modules/dashboard/dashboard.service.ts
+++ b/backend/src/modules/dashboard/dashboard.service.ts
@@ -130,24 +130,34 @@ export class DashboardService {
.createQueryBuilder('log')
.leftJoin('log.user', 'user')
.select([
+ 'log.auditId',
'log.action',
'log.entityType',
'log.entityId',
'log.detailsJson',
'log.createdAt',
'user.username',
+ 'user.firstName',
+ 'user.lastName',
])
.orderBy('log.createdAt', 'DESC')
.limit(limit)
.getMany();
return logs.map((log) => ({
+ id: log.auditId,
action: log.action,
entityType: log.entityType,
entityId: log.entityId,
details: log.detailsJson,
createdAt: log.createdAt,
- username: log.user?.username,
+ user: log.user
+ ? {
+ username: log.user.username,
+ firstName: log.user.firstName,
+ lastName: log.user.lastName,
+ }
+ : undefined,
}));
}
diff --git a/backend/src/modules/dashboard/dto/get-activity.dto.ts b/backend/src/modules/dashboard/dto/get-activity.dto.ts
index 82a7fde..98b05cd 100644
--- a/backend/src/modules/dashboard/dto/get-activity.dto.ts
+++ b/backend/src/modules/dashboard/dto/get-activity.dto.ts
@@ -22,6 +22,9 @@ export class GetActivityDto {
* DTO สำหรับ Response ของ Activity Item
*/
export class ActivityItemDto {
+ @ApiPropertyOptional({ description: 'ID ของ Activity' })
+ id!: string;
+
@ApiPropertyOptional({ description: 'Action ที่กระทำ' })
action!: string;
@@ -37,6 +40,10 @@ export class ActivityItemDto {
@ApiPropertyOptional({ description: 'วันที่กระทำ' })
createdAt!: Date;
- @ApiPropertyOptional({ description: 'ชื่อผู้ใช้' })
- username?: string;
+ @ApiPropertyOptional({ description: 'ข้อมูลผู้ใช้' })
+ user?: {
+ username: string;
+ firstName?: string;
+ lastName?: string;
+ };
}
diff --git a/frontend/components/dashboard/pending-tasks.tsx b/frontend/components/dashboard/pending-tasks.tsx
index ab253e8..c6aa3f7 100644
--- a/frontend/components/dashboard/pending-tasks.tsx
+++ b/frontend/components/dashboard/pending-tasks.tsx
@@ -53,7 +53,7 @@ export function PendingTasks({ tasks, isLoading }: PendingTasksProps) {
) : (
tasks.map((task) => (
diff --git a/frontend/lib/services/dashboard.service.ts b/frontend/lib/services/dashboard.service.ts
index 22f1274..33dac4f 100644
--- a/frontend/lib/services/dashboard.service.ts
+++ b/frontend/lib/services/dashboard.service.ts
@@ -1,6 +1,31 @@
import apiClient from '@/lib/api/client';
import { DashboardStats, ActivityLog, PendingTask } from '@/types/dashboard';
+interface RawActivityLog {
+ id: string;
+ action: string;
+ entityType?: string;
+ entityId?: string;
+ details?: Record;
+ createdAt: string;
+ user?: {
+ username: string;
+ firstName?: string;
+ lastName?: string;
+ };
+}
+
+interface RawPendingTask {
+ instanceId: string;
+ workflowCode: string;
+ currentState: string;
+ entityType: string;
+ entityId: string;
+ documentNumber: string;
+ subject: string;
+ assignedAt: string;
+}
+
export const dashboardService = {
getStats: async (): Promise => {
const response = await apiClient.get('/dashboard/stats');
@@ -10,9 +35,23 @@ export const dashboardService = {
getRecentActivity: async (): Promise => {
try {
const response = await apiClient.get('/dashboard/activity');
- // ตรวจสอบว่า response.data เป็น array จริงๆ
if (Array.isArray(response.data)) {
- return response.data;
+ return (response.data as RawActivityLog[]).map((log) => {
+ const firstName = log.user?.firstName || '';
+ const lastName = log.user?.lastName || '';
+ const fullName = firstName || lastName ? `${firstName} ${lastName}`.trim() : log.user?.username || 'System';
+ const initials = firstName && lastName ? `${firstName[0]}${lastName[0]}` : (log.user?.username?.[0] || 'S').toUpperCase();
+
+ return {
+ ...log,
+ user: {
+ name: fullName,
+ initials: initials,
+ },
+ description: (log.details?.description as string) || `${log.action} ${log.entityType || ''} ${log.entityId || ''}`,
+ targetUrl: `/${(log.entityType || 'correspondence').toLowerCase()}s/${log.entityId || ''}`,
+ } as ActivityLog;
+ });
}
return [];
} catch (_error) {
@@ -23,14 +62,24 @@ export const dashboardService = {
getPendingTasks: async (): Promise => {
try {
const response = await apiClient.get('/dashboard/pending');
- // Backend คืน { data: [], meta: {} } ต้องดึง data ออกมา
- if (response.data?.data && Array.isArray(response.data.data)) {
- return response.data.data;
- }
- if (Array.isArray(response.data)) {
- return response.data;
- }
- return [];
+ const rawTasks = (response.data?.data || (Array.isArray(response.data) ? response.data : [])) as RawPendingTask[];
+
+ return rawTasks.map((task) => {
+ const assignedAt = new Date(task.assignedAt);
+ const now = new Date();
+ const diffTime = now.getTime() - assignedAt.getTime();
+ const daysOverdue = Math.max(0, Math.floor(diffTime / (1000 * 60 * 60 * 24)));
+
+ return {
+ ...task,
+ publicId: task.instanceId,
+ title: task.subject || task.documentNumber,
+ description: task.currentState || task.workflowCode,
+ daysOverdue,
+ url: `/${(task.entityType || 'correspondence').toLowerCase()}s/${task.entityId}`,
+ priority: daysOverdue > 2 ? 'HIGH' : 'MEDIUM',
+ };
+ });
} catch (_error) {
return [];
}
diff --git a/frontend/types/dashboard.ts b/frontend/types/dashboard.ts
index bf8c5a0..42ea4e3 100644
--- a/frontend/types/dashboard.ts
+++ b/frontend/types/dashboard.ts
@@ -8,20 +8,32 @@ export interface DashboardStats {
}
export interface ActivityLog {
- id: number;
+ id: string;
+ action: string;
+ entityType?: string;
+ entityId?: string;
+ details?: Record;
+ createdAt: string;
+ username?: string;
user: {
name: string;
initials: string;
avatar?: string;
};
- action: string;
- description: string;
- createdAt: string;
targetUrl: string;
+ description: string;
}
export interface PendingTask {
- id: number;
+ publicId: string;
+ workflowCode: string;
+ currentState: string;
+ entityType: string;
+ entityId: string;
+ documentNumber: string;
+ subject: string;
+ assignedAt: string;
+ // Derived fields for UI
title: string;
description: string;
daysOverdue: number;