|
|
|
@@ -0,0 +1,535 @@
|
|
|
|
|
import MockAdapter from 'axios-mock-adapter';
|
|
|
|
|
import type { AxiosInstance } from 'axios';
|
|
|
|
|
import type { RoleCode } from '@/types';
|
|
|
|
|
import { employees as _employees } from './data/employees';
|
|
|
|
|
import { projects as _projects } from './data/projects';
|
|
|
|
|
import { dispatchHistories as _dispatchHistories } from './data/dispatchHistories';
|
|
|
|
|
import { tickets as _tickets } from './data/tickets';
|
|
|
|
|
import { comments as _comments } from './data/comments';
|
|
|
|
|
import { equipment as _equipment } from './data/equipment';
|
|
|
|
|
import { assignments as _assignments } from './data/assignments';
|
|
|
|
|
import { maintenances as _maintenances } from './data/maintenances';
|
|
|
|
|
import { reports as _reports } from './data/reports';
|
|
|
|
|
import { training as _training } from './data/training';
|
|
|
|
|
import { notifications as _notifications } from './data/notifications';
|
|
|
|
|
import { commonCodes as _commonCodes } from './data/commonCodes';
|
|
|
|
|
|
|
|
|
|
interface MockUser {
|
|
|
|
|
loginId: string;
|
|
|
|
|
password: string;
|
|
|
|
|
name: string;
|
|
|
|
|
role: RoleCode;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const MOCK_USERS: MockUser[] = [
|
|
|
|
|
{ loginId: 'admin', password: 'admin123', name: 'Admin', role: 'ADMIN' },
|
|
|
|
|
{ loginId: 'manager', password: 'manager123', name: 'Manager Kim', role: 'MANAGER' },
|
|
|
|
|
{ loginId: 'employee', password: 'emp123', name: 'Employee Lee', role: 'EMPLOYEE' },
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
function paginate<T>(arr: T[], page = 0, size = 20) {
|
|
|
|
|
const start = page * size;
|
|
|
|
|
const content = arr.slice(start, start + size);
|
|
|
|
|
return { content, totalElements: arr.length, totalPages: Math.ceil(arr.length / size), number: page, size };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let initialized = false;
|
|
|
|
|
|
|
|
|
|
export function setupMocks(instance: AxiosInstance) {
|
|
|
|
|
if (typeof window === 'undefined' || initialized) return;
|
|
|
|
|
initialized = true;
|
|
|
|
|
|
|
|
|
|
const mock = new MockAdapter(instance, { delayResponse: 200 });
|
|
|
|
|
|
|
|
|
|
const s: Record<string, any[]> = {
|
|
|
|
|
employees: _employees,
|
|
|
|
|
projects: _projects,
|
|
|
|
|
dispatchHistories: _dispatchHistories,
|
|
|
|
|
tickets: _tickets,
|
|
|
|
|
comments: _comments,
|
|
|
|
|
equipment: _equipment,
|
|
|
|
|
assignments: _assignments,
|
|
|
|
|
maintenances: _maintenances,
|
|
|
|
|
reports: _reports,
|
|
|
|
|
training: _training,
|
|
|
|
|
notifications: _notifications,
|
|
|
|
|
commonCodes: _commonCodes,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* Auth */
|
|
|
|
|
mock.onPost('/auth/login').reply((config) => {
|
|
|
|
|
const { loginId, password } = JSON.parse(config.data);
|
|
|
|
|
const mockUser = MOCK_USERS.find((u) => u.loginId === loginId && u.password === password);
|
|
|
|
|
if (!mockUser) {
|
|
|
|
|
return [401, { success: false, message: '아이디 또는 비밀번호가 일치하지 않습니다.', errorCode: 'AUTH_001' }];
|
|
|
|
|
}
|
|
|
|
|
const emp = s.employees.find((e: any) => e.loginId === loginId) || s.employees[0];
|
|
|
|
|
const accessToken = `mock-jwt-${mockUser.loginId}-${Date.now()}`;
|
|
|
|
|
const refreshToken = `mock-refresh-${mockUser.loginId}-${Date.now()}`;
|
|
|
|
|
return [200, { success: true, message: '로그인 성공', data: { accessToken, refreshToken, employee: emp } }];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
mock.onPost('/auth/refresh').reply((config) => {
|
|
|
|
|
const { refreshToken } = JSON.parse(config.data);
|
|
|
|
|
if (!refreshToken || !refreshToken.startsWith('mock-refresh-')) {
|
|
|
|
|
return [401, { success: false, message: '유효하지 않은 리프레시 토큰입니다.', errorCode: 'AUTH_002' }];
|
|
|
|
|
}
|
|
|
|
|
const loginId = refreshToken.split('-')[2];
|
|
|
|
|
const emp = s.employees.find((e: any) => e.loginId === loginId) || s.employees[0];
|
|
|
|
|
const accessToken = `mock-jwt-${loginId}-${Date.now()}`;
|
|
|
|
|
const newRefreshToken = `mock-refresh-${loginId}-${Date.now()}`;
|
|
|
|
|
return [200, { success: true, message: '토큰 갱신 성공', data: { accessToken, refreshToken: newRefreshToken, employee: emp } }];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
mock.onGet('/employees/me').reply((config) => {
|
|
|
|
|
const authHeader = config.headers?.Authorization;
|
|
|
|
|
if (authHeader && typeof authHeader === 'string' && authHeader.startsWith('Bearer ')) {
|
|
|
|
|
const token = authHeader.replace('Bearer ', '');
|
|
|
|
|
const loginId = token.split('-')[2];
|
|
|
|
|
const emp = s.employees.find((e: any) => e.loginId === loginId);
|
|
|
|
|
if (emp) return [200, { success: true, message: 'OK', data: emp }];
|
|
|
|
|
}
|
|
|
|
|
return [200, { success: true, message: 'OK', data: s.employees[0] }];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
mock.onGet('/employees').reply((config) => [200, { success: true, message: 'OK', data: paginate(s.employees, config.params?.page, config.params?.size) }]);
|
|
|
|
|
|
|
|
|
|
mock.onGet(/\/employees\/search/).reply((config) => {
|
|
|
|
|
const keyword = (config.params?.keyword || '').toLowerCase();
|
|
|
|
|
const filtered = s.employees.filter((e: any) => e.name.toLowerCase().includes(keyword) || e.email.toLowerCase().includes(keyword));
|
|
|
|
|
return [200, { success: true, message: 'OK', data: filtered.map((e: any) => ({ empId: e.empId, name: e.name, roleCode: e.roleCode, dispatchStatus: e.dispatchStatus, email: e.email })) }];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
mock.onGet(/\/employees\/\d+$/).reply((config) => {
|
|
|
|
|
const id = parseInt(config.url?.match(/\/employees\/(\d+)$/)?.[1] || '0');
|
|
|
|
|
const emp = s.employees.find((e: any) => e.empId === id);
|
|
|
|
|
if (!emp) return [404, { success: false, message: 'Not found' }];
|
|
|
|
|
return [200, { success: true, message: 'OK', data: emp }];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
mock.onGet(/\/employees\/\d+\/dispatch-histories/).reply(() => [200, { success: true, message: 'OK', data: s.dispatchHistories }]);
|
|
|
|
|
|
|
|
|
|
/* Tickets */
|
|
|
|
|
mock.onGet('/tickets').reply((config) => {
|
|
|
|
|
let result = [...s.tickets];
|
|
|
|
|
const { category, priority, status, searchKeyword } = config.params || {};
|
|
|
|
|
if (category && category !== 'ALL') result = result.filter((t: any) => t.category === category);
|
|
|
|
|
if (priority && priority !== 'ALL') result = result.filter((t: any) => t.priority === priority);
|
|
|
|
|
if (status && status !== 'ALL') result = result.filter((t: any) => t.status === status);
|
|
|
|
|
if (searchKeyword) result = result.filter((t: any) => t.title.toLowerCase().includes(searchKeyword.toLowerCase()));
|
|
|
|
|
return [200, { success: true, message: 'OK', data: paginate(result, config.params?.page, config.params?.size) }];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
mock.onGet(/\/tickets\/(\d+)$/).reply((config) => {
|
|
|
|
|
const id = parseInt(config.url?.match(/\/tickets\/(\d+)$/)?.[1] || '0');
|
|
|
|
|
const ticket = s.tickets.find((t: any) => t.ticketId === id);
|
|
|
|
|
if (!ticket) return [404, { success: false, message: 'Not found' }];
|
|
|
|
|
ticket.isImminent = ticket.dueDate ? new Date(ticket.dueDate).getTime() - Date.now() < 86400000 : false;
|
|
|
|
|
return [200, { success: true, message: 'OK', data: ticket }];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
mock.onPost(/\/tickets\/(\d+)\/comments/).reply((config) => {
|
|
|
|
|
const ticketId = parseInt(config.url?.match(/\/tickets\/(\d+)\/comments/)?.[1] || '0');
|
|
|
|
|
const { commentText, attachedFileUrl } = JSON.parse(config.data);
|
|
|
|
|
const newComment = { commentId: Date.now(), ticketId, userId: 1, userName: 'Admin User (본사)', commentText, attachedFileUrl: attachedFileUrl || null, createdAt: new Date().toISOString() };
|
|
|
|
|
return [201, { success: true, message: '등록 완료', data: newComment }];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
mock.onGet(/\/tickets\/(\d+)\/comments/).reply((config) => {
|
|
|
|
|
const ticketId = parseInt(config.url?.match(/\/tickets\/(\d+)\/comments/)?.[1] || '0');
|
|
|
|
|
return [200, { success: true, message: 'OK', data: s.comments.filter((c: any) => c.ticketId === ticketId) }];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
mock.onPut(/\/tickets\/(\d+)\/status/).reply((config) => {
|
|
|
|
|
const id = parseInt(config.url?.match(/\/tickets\/(\d+)\/status/)?.[1] || '0');
|
|
|
|
|
const ticket = s.tickets.find((t: any) => t.ticketId === id);
|
|
|
|
|
if (ticket) ticket.status = config.params?.status || ticket.status;
|
|
|
|
|
return [200, { success: true, message: '상태 변경 완료', data: ticket }];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
mock.onPut(/\/tickets\/(\d+)\/resolve/).reply((config) => {
|
|
|
|
|
const id = parseInt(config.url?.match(/\/tickets\/(\d+)\/resolve/)?.[1] || '0');
|
|
|
|
|
const ticket = s.tickets.find((t: any) => t.ticketId === id);
|
|
|
|
|
if (ticket) { ticket.status = 'RESOLVED'; ticket.resolutionNote = JSON.parse(config.data).resolutionNote; ticket.resolvedAt = new Date().toISOString(); }
|
|
|
|
|
return [200, { success: true, message: '해결 처리 완료', data: ticket }];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
mock.onDelete(/\/tickets\/(\d+)$/).reply(204);
|
|
|
|
|
|
|
|
|
|
/* Equipment */
|
|
|
|
|
mock.onGet('/equipment').reply((config) => {
|
|
|
|
|
let result = [...s.equipment];
|
|
|
|
|
const { type, status, manufacturer } = config.params || {};
|
|
|
|
|
if (type) result = result.filter((e: any) => e.equipmentType === type);
|
|
|
|
|
if (status) result = result.filter((e: any) => e.status === status);
|
|
|
|
|
if (manufacturer) result = result.filter((e: any) => e.manufacturer === manufacturer);
|
|
|
|
|
return [200, { success: true, message: 'OK', data: paginate(result, config.params?.page, config.params?.size) }];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
mock.onGet(/\/equipment\/(\d+)$/).reply((config) => {
|
|
|
|
|
const id = parseInt(config.url?.match(/\/equipment\/(\d+)$/)?.[1] || '0');
|
|
|
|
|
const equip = s.equipment.find((e: any) => e.equipmentId === id);
|
|
|
|
|
if (!equip) return [404, { success: false, message: 'Not found' }];
|
|
|
|
|
return [200, { success: true, message: 'OK', data: equip }];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
mock.onPost('/equipment').reply((config) => {
|
|
|
|
|
const body = JSON.parse(config.data);
|
|
|
|
|
const newEquip = { equipmentId: Date.now(), ...body, version: 0 };
|
|
|
|
|
s.equipment.push(newEquip);
|
|
|
|
|
return [201, { success: true, message: '등록 완료', data: newEquip }];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
mock.onPost(/\/equipment\/(\d+)\/assign/).reply((config) => {
|
|
|
|
|
const equipId = parseInt(config.url?.match(/\/equipment\/(\d+)\/assign/)?.[1] || '0');
|
|
|
|
|
const { assigneeId, conditionNote } = JSON.parse(config.data);
|
|
|
|
|
const equip = s.equipment.find((e: any) => e.equipmentId === equipId);
|
|
|
|
|
if (!equip || equip.status !== 'AVAILABLE') return [400, { success: false, message: '지급 가능한 재고 상태가 아닙니다.', errorCode: 'EQP_ERR_001' }];
|
|
|
|
|
equip.status = 'IN_USE';
|
|
|
|
|
const assignment = { assignmentId: Date.now(), equipmentId: equipId, userId: assigneeId, userName: s.employees.find((e: any) => e.empId === assigneeId)?.name || '', assignedDate: new Date().toISOString().split('T')[0], returnedDate: null, condOnAssign: conditionNote, condOnReturn: null };
|
|
|
|
|
s.assignments.push(assignment);
|
|
|
|
|
return [200, { success: true, message: '장비 지급 완료', data: assignment }];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
mock.onPut(/\/equipment\/(\d+)\/return/).reply((config) => {
|
|
|
|
|
const equipId = parseInt(config.url?.match(/\/equipment\/(\d+)\/return/)?.[1] || '0');
|
|
|
|
|
const { conditionNote } = JSON.parse(config.data);
|
|
|
|
|
const equip = s.equipment.find((e: any) => e.equipmentId === equipId);
|
|
|
|
|
if (equip) equip.status = 'AVAILABLE';
|
|
|
|
|
const active = s.assignments.find((a: any) => a.equipmentId === equipId && !a.returnedDate);
|
|
|
|
|
if (active) { active.returnedDate = new Date().toISOString().split('T')[0]; active.condOnReturn = conditionNote; }
|
|
|
|
|
return [200, { success: true, message: '반납 완료', data: active }];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
mock.onGet('/equipment/summary').reply(() => {
|
|
|
|
|
const counts: Record<string, number> = {};
|
|
|
|
|
s.equipment.forEach((e: any) => { counts[e.status] = (counts[e.status] || 0) + 1; });
|
|
|
|
|
counts.ALL = Object.values(counts).reduce((a: number, b: any) => a + (typeof b === 'number' ? b : 0), 0);
|
|
|
|
|
return [200, { success: true, message: 'OK', data: { counts } }];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/* Reports */
|
|
|
|
|
mock.onGet('/reports').reply((config) => {
|
|
|
|
|
let result = [...s.reports];
|
|
|
|
|
const { reportType, status } = config.params || {};
|
|
|
|
|
if (reportType) result = result.filter((r: any) => r.reportType === reportType);
|
|
|
|
|
if (status) result = result.filter((r: any) => r.status === status);
|
|
|
|
|
return [200, { success: true, message: 'OK', data: paginate(result, config.params?.page, config.params?.size) }];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
mock.onPost('/reports').reply((config) => {
|
|
|
|
|
const body = JSON.parse(config.data);
|
|
|
|
|
const newReport = { reportId: Date.now(), ...body, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString() };
|
|
|
|
|
s.reports.push(newReport);
|
|
|
|
|
return [201, { success: true, message: '보고서 생성 완료', data: newReport }];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
mock.onPut(/\/reports\/(\d+)\/review/).reply((config) => {
|
|
|
|
|
const id = parseInt(config.url?.match(/\/reports\/(\d+)\/review/)?.[1] || '0');
|
|
|
|
|
const { status, reviewComment } = JSON.parse(config.data);
|
|
|
|
|
const report = s.reports.find((r: any) => r.reportId === id);
|
|
|
|
|
if (report) { report.status = status; report.reviewComment = reviewComment || null; }
|
|
|
|
|
return [200, { success: true, message: '결재 처리 완료', data: report }];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
mock.onGet('/reports/sr-auto-complete').reply(() => [200, { success: true, message: 'OK', data: ['SR-2026-042: 오라클 인덱스 튜닝 완료', 'SR-2026-043: 배포 파이프라인 오류 수정'] }]);
|
|
|
|
|
|
|
|
|
|
/* Training */
|
|
|
|
|
mock.onGet('/training/me').reply(() => [200, { success: true, message: 'OK', data: s.training }]);
|
|
|
|
|
mock.onGet('/training/summary').reply(() => {
|
|
|
|
|
const byCourse: Record<string, any> = {};
|
|
|
|
|
s.training.forEach((t: any) => {
|
|
|
|
|
if (!byCourse[t.courseName]) byCourse[t.courseName] = { total: 0, completed: 0, totalRate: 0 };
|
|
|
|
|
byCourse[t.courseName].total++;
|
|
|
|
|
if (t.status === 'COMPLETED') byCourse[t.courseName].completed++;
|
|
|
|
|
byCourse[t.courseName].totalRate += t.progressRate;
|
|
|
|
|
});
|
|
|
|
|
const summary = Object.entries(byCourse).map(([courseName, v]) => ({ courseName, totalEnrolled: v.total, completedCount: v.completed, avgProgress: Math.round(v.totalRate / v.total) }));
|
|
|
|
|
return [200, { success: true, message: 'OK', data: summary }];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
mock.onPut(/\/training\/(\d+)$/).reply((config) => {
|
|
|
|
|
const id = parseInt(config.url?.match(/\/training\/(\d+)/)?.[1] || '0');
|
|
|
|
|
const body = JSON.parse(config.data);
|
|
|
|
|
const prog = s.training.find((t: any) => t.progressId === id);
|
|
|
|
|
if (prog) { if (body.progressRate !== undefined) prog.progressRate = body.progressRate; if (body.status) prog.status = body.status; }
|
|
|
|
|
return [200, { success: true, message: 'OK', data: prog }];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/* Notifications */
|
|
|
|
|
mock.onGet('/notifications/unread').reply(() => [200, { success: true, message: 'OK', data: { count: s.notifications.filter((n: any) => !n.isRead).length } }]);
|
|
|
|
|
mock.onGet('/notifications').reply(() => [200, { success: true, message: 'OK', data: s.notifications }]);
|
|
|
|
|
mock.onPut(/\/notifications\/(\d+)\/read$/).reply((config) => {
|
|
|
|
|
const id = parseInt(config.url?.match(/\/notifications\/(\d+)\/read/)?.[1] || '0');
|
|
|
|
|
const n = s.notifications.find((x: any) => x.notificationId === id);
|
|
|
|
|
if (n) n.isRead = true;
|
|
|
|
|
return [200, { success: true, message: 'OK', data: null }];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/* Parking */
|
|
|
|
|
mock.onGet('/health').reply(() => {
|
|
|
|
|
const now = new Date();
|
|
|
|
|
const day = now.getDay();
|
|
|
|
|
const hour = now.getHours();
|
|
|
|
|
const parked = day === 0 || day === 6 || hour < 8 || hour >= 20;
|
|
|
|
|
return [200, { success: true, message: 'OK', data: { parked } }];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
mock.onPost('/wake').reply(() => new Promise((resolve) => {
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
resolve([200, { success: true, message: '관리자에게 기동 요청이 전송되었습니다. iLO4 원격 부팅이 진행됩니다.', data: { success: true, message: '관리자에게 기동 요청이 전송되었습니다. iLO4 원격 부팅이 진행됩니다.', estimatedWakeTime: '약 5~10분 소요' } }]);
|
|
|
|
|
}, 3000);
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
/* Projects */
|
|
|
|
|
mock.onGet('/projects').reply((config) => {
|
|
|
|
|
let result = [...s.projects];
|
|
|
|
|
const { status } = config.params || {};
|
|
|
|
|
if (status) result = result.filter((p: any) => p.status === status);
|
|
|
|
|
return [200, { success: true, message: 'OK', data: result }];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
mock.onGet(/\/projects\/\d+/).reply((config) => {
|
|
|
|
|
const id = parseInt(config.url?.match(/\/projects\/(\d+)/)?.[1] || '0');
|
|
|
|
|
const project = s.projects.find((p: any) => p.projectId === id);
|
|
|
|
|
if (!project) return [404, { success: false, message: 'Not found' }];
|
|
|
|
|
return [200, { success: true, message: 'OK', data: project }];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
mock.onPost('/projects').reply((config) => {
|
|
|
|
|
const body = JSON.parse(config.data);
|
|
|
|
|
const newProject = { projectId: Date.now(), ...body, createdAt: new Date().toISOString() };
|
|
|
|
|
s.projects.push(newProject);
|
|
|
|
|
return [201, { success: true, message: '프로젝트가 등록되었습니다', data: newProject }];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
mock.onPut(/\/projects\/(\d+)/).reply((config) => {
|
|
|
|
|
const id = parseInt(config.url?.match(/\/projects\/(\d+)/)?.[1] || '0');
|
|
|
|
|
const body = JSON.parse(config.data);
|
|
|
|
|
const project = s.projects.find((p: any) => p.projectId === id);
|
|
|
|
|
if (!project) return [404, { success: false, message: 'Not found' }];
|
|
|
|
|
Object.assign(project, body);
|
|
|
|
|
return [200, { success: true, message: '프로젝트가 수정되었습니다', data: project }];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
mock.onDelete(/\/projects\/(\d+)/).reply((config) => {
|
|
|
|
|
const id = parseInt(config.url?.match(/\/projects\/(\d+)/)?.[1] || '0');
|
|
|
|
|
const idx = s.projects.findIndex((p: any) => p.projectId === id);
|
|
|
|
|
if (idx === -1) return [404, { success: false, message: 'Not found' }];
|
|
|
|
|
s.projects.splice(idx, 1);
|
|
|
|
|
return [200, { success: true, message: '프로젝트가 삭제되었습니다', data: null }];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/* Common Codes */
|
|
|
|
|
mock.onGet('/common-codes').reply((config) => {
|
|
|
|
|
let result = [...s.commonCodes];
|
|
|
|
|
const { groupCode } = config.params || {};
|
|
|
|
|
if (groupCode) result = result.filter((c: any) => c.groupCode === groupCode);
|
|
|
|
|
return [200, { success: true, message: 'OK', data: result }];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
mock.onGet('/common-codes/groups').reply(() => {
|
|
|
|
|
const groups = [...new Set(s.commonCodes.map((c: any) => c.groupCode))];
|
|
|
|
|
return [200, { success: true, message: 'OK', data: groups }];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
mock.onPost('/common-codes').reply((config) => {
|
|
|
|
|
const body = JSON.parse(config.data);
|
|
|
|
|
const newCode = { codeId: Date.now(), ...body };
|
|
|
|
|
s.commonCodes.push(newCode);
|
|
|
|
|
return [200, { success: true, message: '생성 완료', data: newCode }];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
mock.onPut(/\/common-codes\/\d+/).reply((config) => {
|
|
|
|
|
const id = parseInt(config.url?.match(/\/common-codes\/(\d+)/)?.[1] || '0');
|
|
|
|
|
const body = JSON.parse(config.data);
|
|
|
|
|
const idx = s.commonCodes.findIndex((c: any) => c.codeId === id);
|
|
|
|
|
if (idx === -1) return [404, { success: false, message: 'Not found' }];
|
|
|
|
|
s.commonCodes[idx] = { ...s.commonCodes[idx], ...body };
|
|
|
|
|
return [200, { success: true, message: '수정 완료', data: s.commonCodes[idx] }];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
mock.onDelete(/\/common-codes\/\d+/).reply((config) => {
|
|
|
|
|
const id = parseInt(config.url?.match(/\/common-codes\/(\d+)/)?.[1] || '0');
|
|
|
|
|
const idx = s.commonCodes.findIndex((c: any) => c.codeId === id);
|
|
|
|
|
if (idx === -1) return [404, { success: false, message: 'Not found' }];
|
|
|
|
|
s.commonCodes.splice(idx, 1);
|
|
|
|
|
return [200, { success: true, message: '삭제 완료', data: null }];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/* Calendar Events */
|
|
|
|
|
mock.onGet('/calendar/events').reply(() => {
|
|
|
|
|
const events: any[] = [];
|
|
|
|
|
const now = new Date();
|
|
|
|
|
// Ticket due dates
|
|
|
|
|
s.tickets.forEach((t: any) => {
|
|
|
|
|
if (t.dueDate) events.push({ eventType: 'TICKET_DUE', sourceId: t.ticketId, title: t.title, eventDate: t.dueDate.split('T')[0], color: '#3B82F6', linkUrl: `/issues/${t.ticketId}` });
|
|
|
|
|
});
|
|
|
|
|
return [200, { success: true, message: 'OK', data: events }];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/* Employee CRUD */
|
|
|
|
|
mock.onPost('/employees').reply((config) => {
|
|
|
|
|
const body = JSON.parse(config.data);
|
|
|
|
|
const newEmp = { empId: Date.now(), ...body, password: 'encoded', createdAt: new Date().toISOString() };
|
|
|
|
|
s.employees.push(newEmp);
|
|
|
|
|
return [200, { success: true, message: '직원이 생성되었습니다', data: newEmp }];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
mock.onPut(/\/employees\/(\d+)/).reply((config) => {
|
|
|
|
|
const id = parseInt(config.url?.match(/\/employees\/(\d+)/)?.[1] || '0');
|
|
|
|
|
const body = JSON.parse(config.data);
|
|
|
|
|
const emp = s.employees.find((e: any) => e.empId === id);
|
|
|
|
|
if (!emp) return [404, { success: false, message: 'Not found' }];
|
|
|
|
|
Object.assign(emp, body);
|
|
|
|
|
return [200, { success: true, message: '직원이 수정되었습니다', data: emp }];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
mock.onDelete(/\/employees\/(\d+)/).reply((config) => {
|
|
|
|
|
const id = parseInt(config.url?.match(/\/employees\/(\d+)/)?.[1] || '0');
|
|
|
|
|
const idx = s.employees.findIndex((e: any) => e.empId === id);
|
|
|
|
|
if (idx === -1) return [404, { success: false, message: 'Not found' }];
|
|
|
|
|
s.employees.splice(idx, 1);
|
|
|
|
|
return [200, { success: true, message: '직원이 삭제되었습니다', data: null }];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
mock.onPut('/employees/me/password').reply(() => [200, { success: true, message: '비밀번호가 변경되었습니다', data: null }]);
|
|
|
|
|
|
|
|
|
|
mock.onGet(/\/employees\/(\d+)\/portfolio/).reply((config) => {
|
|
|
|
|
const id = parseInt(config.url?.match(/\/employees\/(\d+)\/portfolio/)?.[1] || '0');
|
|
|
|
|
const emp = s.employees.find((e: any) => e.empId === id);
|
|
|
|
|
const dispatchList = s.dispatchHistories.filter((d: any) => d.empId === id);
|
|
|
|
|
const portfolio = dispatchList.map((d: any) => ({
|
|
|
|
|
dispatchHistory: d,
|
|
|
|
|
projectName: s.projects.find((p: any) => p.projectId === d.projectId)?.projectName || '',
|
|
|
|
|
reports: s.reports.filter((r: any) => r.dispatchId === d.dispatchId),
|
|
|
|
|
resolvedTickets: s.tickets.filter((t: any) => {
|
|
|
|
|
const project = s.projects.find((p: any) => p.projectId === d.projectId);
|
|
|
|
|
return project && t.projectId === project.projectId;
|
|
|
|
|
}),
|
|
|
|
|
}));
|
|
|
|
|
return [200, { success: true, message: 'OK', data: portfolio }];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
mock.onPut(/\/employees\/(\d+)\/dispatch-histories\/(\d+)\/achievements/).reply(() => [200, { success: true, message: 'OK', data: null }]);
|
|
|
|
|
|
|
|
|
|
/* Dispatch History CRUD */
|
|
|
|
|
mock.onGet('/dispatch-histories').reply((config) => {
|
|
|
|
|
let result = [...s.dispatchHistories];
|
|
|
|
|
const { empId, projectId } = config.params || {};
|
|
|
|
|
if (empId) result = result.filter((d: any) => d.empId === Number(empId));
|
|
|
|
|
if (projectId) result = result.filter((d: any) => d.projectId === Number(projectId));
|
|
|
|
|
return [200, { success: true, message: 'OK', data: { content: result, totalElements: result.length, totalPages: 1, number: 0, size: result.length } }];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
mock.onGet(/\/dispatch-histories\/(\d+)/).reply((config) => {
|
|
|
|
|
const id = parseInt(config.url?.match(/\/dispatch-histories\/(\d+)/)?.[1] || '0');
|
|
|
|
|
const dh = s.dispatchHistories.find((d: any) => d.dispatchId === id);
|
|
|
|
|
if (!dh) return [404, { success: false, message: 'Not found' }];
|
|
|
|
|
return [200, { success: true, message: 'OK', data: dh }];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
mock.onGet('/dispatch-histories/active').reply(() => [200, { success: true, message: 'OK', data: s.dispatchHistories.filter((d: any) => !d.endDate) }]);
|
|
|
|
|
|
|
|
|
|
mock.onPut(/\/dispatch-histories\/(\d+)\/end/).reply((config) => {
|
|
|
|
|
const id = parseInt(config.url?.match(/\/dispatch-histories\/(\d+)\/end/)?.[1] || '0');
|
|
|
|
|
const dh = s.dispatchHistories.find((d: any) => d.dispatchId === id);
|
|
|
|
|
if (dh) dh.endDate = new Date().toISOString().split('T')[0];
|
|
|
|
|
return [200, { success: true, message: '파견이 종료되었습니다', data: dh }];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/* Tickets - additional */
|
|
|
|
|
mock.onPost('/tickets').reply((config) => {
|
|
|
|
|
const body = JSON.parse(config.data);
|
|
|
|
|
const newTicket = { ticketId: Date.now(), ...body, status: 'OPEN', createdAt: new Date().toISOString(), updatedAt: new Date().toISOString() };
|
|
|
|
|
s.tickets.push(newTicket);
|
|
|
|
|
return [200, { success: true, message: '티켓이 생성되었습니다', data: newTicket }];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
mock.onDelete(/\/tickets\/(\d+)\/comments\/(\d+)/).reply(() => [200, { success: true, message: '삭제 완료', data: null }]);
|
|
|
|
|
|
|
|
|
|
/* Equipment - additional */
|
|
|
|
|
mock.onPut(/\/equipment\/(\d+)/).reply((config) => {
|
|
|
|
|
const id = parseInt(config.url?.match(/\/equipment\/(\d+)/)?.[1] || '0');
|
|
|
|
|
const body = JSON.parse(config.data);
|
|
|
|
|
const equip = s.equipment.find((e: any) => e.equipmentId === id);
|
|
|
|
|
if (!equip) return [404, { success: false, message: 'Not found' }];
|
|
|
|
|
Object.assign(equip, body);
|
|
|
|
|
return [200, { success: true, message: '수정 완료', data: equip }];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
mock.onDelete(/\/equipment\/(\d+)/).reply((config) => {
|
|
|
|
|
const id = parseInt(config.url?.match(/\/equipment\/(\d+)/)?.[1] || '0');
|
|
|
|
|
const idx = s.equipment.findIndex((e: any) => e.equipmentId === id);
|
|
|
|
|
if (idx === -1) return [404, { success: false, message: 'Not found' }];
|
|
|
|
|
s.equipment.splice(idx, 1);
|
|
|
|
|
return [200, { success: true, message: '삭제 완료', data: null }];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
mock.onGet(/\/equipment\/(\d+)\/assignments/).reply((config) => {
|
|
|
|
|
const id = parseInt(config.url?.match(/\/equipment\/(\d+)\/assignments/)?.[1] || '0');
|
|
|
|
|
return [200, { success: true, message: 'OK', data: s.assignments.filter((a: any) => a.equipmentId === id) }];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
mock.onGet(/\/equipment\/(\d+)\/maintenances/).reply((config) => {
|
|
|
|
|
const id = parseInt(config.url?.match(/\/equipment\/(\d+)\/maintenances/)?.[1] || '0');
|
|
|
|
|
return [200, { success: true, message: 'OK', data: s.maintenances.filter((m: any) => m.equipmentId === id) }];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
mock.onPost(/\/equipment\/(\d+)\/maintenance/).reply((config) => {
|
|
|
|
|
const id = parseInt(config.url?.match(/\/equipment\/(\d+)\/maintenance/)?.[1] || '0');
|
|
|
|
|
const body = JSON.parse(config.data);
|
|
|
|
|
const newMaint = { maintenanceId: Date.now(), equipmentId: id, ...body, reportedDate: new Date().toISOString().split('T')[0] };
|
|
|
|
|
s.maintenances.push(newMaint);
|
|
|
|
|
return [200, { success: true, message: '등록 완료', data: newMaint }];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/* Reports - additional */
|
|
|
|
|
mock.onGet(/\/reports\/(\d+)/).reply((config) => {
|
|
|
|
|
const id = parseInt(config.url?.match(/\/reports\/(\d+)/)?.[1] || '0');
|
|
|
|
|
const report = s.reports.find((r: any) => r.reportId === id);
|
|
|
|
|
if (!report) return [404, { success: false, message: 'Not found' }];
|
|
|
|
|
return [200, { success: true, message: 'OK', data: report }];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
mock.onPut(/\/reports\/(\d+)\/status/).reply((config) => {
|
|
|
|
|
const id = parseInt(config.url?.match(/\/reports\/(\d+)\/status/)?.[1] || '0');
|
|
|
|
|
const { status } = config.params || {};
|
|
|
|
|
const report = s.reports.find((r: any) => r.reportId === id);
|
|
|
|
|
if (report) report.status = status || report.status;
|
|
|
|
|
return [200, { success: true, message: '상태 변경 완료', data: report }];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
mock.onDelete(/\/reports\/(\d+)/).reply((config) => {
|
|
|
|
|
const id = parseInt(config.url?.match(/\/reports\/(\d+)/)?.[1] || '0');
|
|
|
|
|
const idx = s.reports.findIndex((r: any) => r.reportId === id);
|
|
|
|
|
if (idx === -1) return [404, { success: false, message: 'Not found' }];
|
|
|
|
|
s.reports.splice(idx, 1);
|
|
|
|
|
return [200, { success: true, message: '삭제 완료', data: null }];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
mock.onGet('/reports/summary').reply(() => [200, { success: true, message: 'OK', data: { submitted: s.reports.filter((r: any) => r.status === 'SUBMITTED').length, approved: s.reports.filter((r: any) => r.status === 'APPROVED').length, rejected: s.reports.filter((r: any) => r.status === 'REJECTED').length, draft: s.reports.filter((r: any) => r.status === 'DRAFT').length } }]);
|
|
|
|
|
|
|
|
|
|
/* Projects - my */
|
|
|
|
|
mock.onGet('/projects/my').reply((config) => {
|
|
|
|
|
const empId = 3; // default employee
|
|
|
|
|
const dispatchProjects = s.dispatchHistories
|
|
|
|
|
.filter((d: any) => d.empId === empId)
|
|
|
|
|
.map((d: any) => s.projects.find((p: any) => p.projectId === d.projectId))
|
|
|
|
|
.filter(Boolean);
|
|
|
|
|
return [200, { success: true, message: 'OK', data: dispatchProjects }];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/* Training */
|
|
|
|
|
mock.onGet('/training').reply((config) => {
|
|
|
|
|
const { userId } = config.params || {};
|
|
|
|
|
let result = [...s.training];
|
|
|
|
|
if (userId) result = result.filter((t: any) => t.userId === Number(userId));
|
|
|
|
|
return [200, { success: true, message: 'OK', data: result }];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/* File upload */
|
|
|
|
|
mock.onPost('/files/upload').reply(() => [200, { success: true, message: '업로드 완료', data: { url: '/uploads/mock-file.png', name: 'mock-file.png' } }]);
|
|
|
|
|
|
|
|
|
|
mock.onAny().passThrough();
|
|
|
|
|
}
|