import { apiRequestBlob, apiRequestData, type ApiBlobResponse } from "./http"; export type EmployeeStatus = "active" | "archived"; export type EmployeeEmploymentStatus = "onboarding" | "probation" | "active" | "offboarding" | "inactive"; export type EmployeeContractType = "labor" | "nda" | "non_compete" | "internship"; export type EmployeeContractStatus = "active" | "expired" | "terminated"; export type EmployeeTrainingType = "internal" | "external" | "online"; export type EmployeeTrainingStatus = "planned" | "in_progress" | "completed" | "cancelled"; export type EmployeeCertificateStatus = "valid" | "expired"; export type WecomDirectorySyncOperation = "create" | "update" | "disable"; export type WecomDirectorySyncStatus = "running" | "success" | "failed" | "skipped"; export type WecomDirectoryStateStatus = "unbound" | "bound" | "synced" | "sync_success" | "sync_skipped" | "sync_failed"; export type WecomDirectorySyncRun = { id: string; employeeId: string; externalUserId: string | null; operation: WecomDirectorySyncOperation; status: WecomDirectorySyncStatus; attempts: number; errorCode: string | null; errorMessage: string | null; responseSummary: Record | null; triggeredBy: string | null; createdAt: string | null; updatedAt: string | null; }; export type WecomDirectoryState = { status: WecomDirectoryStateStatus; externalUserId: string | null; bound: boolean; latestSync: WecomDirectorySyncRun | null; }; export type WecomHrMasterSyncMode = "dry_run" | "apply"; export type WecomHrMasterSyncRunStatus = "running" | "success" | "partial" | "failed" | "skipped"; export type WecomHrMasterSyncItemStatus = "planned" | "success" | "skipped" | "conflict" | "failed"; export type WecomHrMasterSyncItem = { id: string; runId: string; employeeId: string | null; externalUserId: string; action: "create" | "update" | "noop" | "skip" | "conflict" | "failed"; status: WecomHrMasterSyncItemStatus; errorCode: string | null; errorMessage: string | null; retryCount: number; fieldChanges: Record | null; sourceSummary: Record | null; createdAt: string | null; updatedAt: string | null; }; export type WecomHrMasterSyncRun = { id: string; mode: WecomHrMasterSyncMode; status: WecomHrMasterSyncRunStatus; tokenSource: Record | null; requestedUserids: string[] | null; limit: number | null; summary: { total?: number; created?: number; updated?: number; noop?: number; skipped?: number; conflicts?: number; failed?: number; planned?: number; success?: number; } | null; errorCode: string | null; errorMessage: string | null; triggeredBy: string | null; startedAt: string | null; finishedAt: string | null; createdAt: string | null; updatedAt: string | null; itemCount?: number; items?: WecomHrMasterSyncItem[]; }; export type EmployeeDepartmentSummary = { id: string; name: string; }; export type EmployeeSummary = { id: string; employeeNo: string; name: string; phone: string; email: string; status: EmployeeStatus; departmentId: string; department: EmployeeDepartmentSummary | null; directManagerId: string | null; directManager: { id: string; employeeNo: string; name: string } | null; title: string; employmentStatus: EmployeeEmploymentStatus | null; hireDate: string | null; avatarUrl: string | null; roleCodes: string[]; createdAt: string | null; updatedAt: string | null; wecomDirectory?: WecomDirectoryState; }; export type EmployeeListResponse = { page: number; pageSize: number; total: number; items: EmployeeSummary[]; }; export type EmployeeLifecycleEvent = { id: string; employeeId: string; eventType: "onboard" | "regularize" | "transfer" | "offboard"; effectiveDate: string | null; payloadJson: Record | null; createdAt: string | null; createdBy: string | null; }; export type EmployeeChecklistTask = { id: string; employeeId: string; scene: "onboarding" | "offboarding" | "transfer"; taskType: | "account_setup" | "asset_issue" | "policy_ack" | "asset_return" | "access_revoke" | "expense_clearance" | "handover_note"; title: string; status: "pending" | "completed" | "skipped"; dueDate: string | null; completedAt: string | null; completedBy: string | null; payloadJson: Record | null; createdAt: string | null; }; export type EmployeeOrganizationNode = { id: string; employeeNo: string; name: string; title: string; status: EmployeeStatus; avatarUrl: string | null; department: EmployeeDepartmentSummary | null; }; export type EmployeeContract = { id: string; employeeId: string; contractNo: string; contractType: EmployeeContractType; startDate: string | null; endDate: string | null; terminatedDate: string | null; attachmentPath: string | null; attachmentUrl: string | null; status: EmployeeContractStatus; createdAt: string | null; createdBy: string | null; updatedAt: string | null; updatedBy: string | null; }; export type EmployeeTraining = { id: string; employeeId: string; name: string; trainingType: EmployeeTrainingType; startDate: string | null; endDate: string | null; status: EmployeeTrainingStatus; notes: string | null; createdAt: string | null; createdBy: string | null; updatedAt: string | null; updatedBy: string | null; }; export type EmployeeCertificate = { id: string; employeeId: string; name: string; issuer: string; obtainedDate: string | null; expiryDate: string | null; certificateNo: string | null; attachmentPath: string | null; status: EmployeeCertificateStatus; createdAt: string | null; createdBy: string | null; updatedAt: string | null; updatedBy: string | null; }; export type OrganizationTreeNode = { id: string; employeeNo: string; name: string; title: string; status: EmployeeStatus; avatarUrl: string | null; department: EmployeeDepartmentSummary | null; externalManager: { id: string; employeeNo: string; name: string; department: EmployeeDepartmentSummary | null; } | null; children: OrganizationTreeNode[]; }; export type OrganizationDepartmentSection = { department: EmployeeDepartmentSummary | null; roots: OrganizationTreeNode[]; }; export type EmployeeProfile = { employee: { id: string; employeeNo: string; name: string; phone: string; email: string; status: EmployeeStatus; employmentStatus: EmployeeEmploymentStatus; departmentId: string; title: string; directManagerId: string | null; hireDate: string | null; probationEndDate: string | null; lastWorkingDate: string | null; workLocation: string | null; emergencyContactName: string | null; emergencyContactPhone: string | null; notes: string | null; avatarUrl: string | null; archivedAt: string | null; roleCodes: string[]; department: EmployeeDepartmentSummary | null; directManager: { id: string; employeeNo: string; name: string; avatarUrl: string | null } | null; wecomDirectory?: WecomDirectoryState; }; lifecycle: { events: EmployeeLifecycleEvent[]; checklistTasks: EmployeeChecklistTask[]; blockers: { openAssetCount: number; pendingWorkflowTaskCount: number; canCompleteOffboarding: boolean; }; }; assignedAssets: Array<{ id: string; assetNo: string; name: string; status: string; location: string | null; }>; organization: { reportingChain: EmployeeOrganizationNode[]; directReports: EmployeeOrganizationNode[]; }; }; export type FetchEmployeesParams = { page: number; pageSize: number; status?: EmployeeStatus; departmentId?: string; q?: string; title?: string; managerId?: string; hireDateFrom?: string; hireDateTo?: string; employmentStatus?: EmployeeEmploymentStatus; }; export type BatchOperationResult = { summary: { total: number; updated: number; }; employeeIds: string[]; }; export type EmployeeStatsResponse = { asOfDate: string; employmentStatusSummary: { total: number; items: Array<{ status: EmployeeEmploymentStatus; count: number; }>; }; departmentHeadcount: Array<{ departmentId: string; departmentName: string; count: number; }>; recentMovements: { dateFrom: string; dateTo: string; hires: number; departures: number; }; expiringContracts: { dateFrom: string; dateTo: string; count: number; }; certificateSummary: { dateFrom: string; dateTo: string; validHolderCount: number; expiringSoonCount: number; }; salarySummary: { currentMonth: { month: string; totalNetAmount: number; averageNetAmount: number; issuedCount: number; } | null; previousMonth: { month: string; totalNetAmount: number; averageNetAmount: number; issuedCount: number; } | null; } | null; performanceSummary: { cycle: { id: string; name: string; cycleType: "monthly" | "quarterly" | "semi_annual" | "annual"; status: "draft" | "active" | "completed"; startDate: string | null; endDate: string | null; } | null; averageScore: number | null; reviewedCount: number; distribution: Array<{ score: number; count: number; }>; }; }; export type BatchTransferDepartmentPayload = { employeeIds: string[]; departmentId: string; }; export type BatchToggleUsersPayload = { employeeIds: string[]; }; export type BatchSetManagerPayload = { employeeIds: string[]; managerId: string; }; export async function fetchEmployees(params: FetchEmployeesParams) { const search = new URLSearchParams(); search.set("page", String(params.page)); search.set("pageSize", String(params.pageSize)); if (params.status) search.set("status", params.status); if (params.departmentId) search.set("departmentId", params.departmentId); if (params.q) search.set("q", params.q); if (params.title) search.set("title", params.title); if (params.managerId) search.set("managerId", params.managerId); if (params.hireDateFrom) search.set("hireDateFrom", params.hireDateFrom); if (params.hireDateTo) search.set("hireDateTo", params.hireDateTo); if (params.employmentStatus) search.set("employmentStatus", params.employmentStatus); return apiRequestData(`/api/employees?${search.toString()}`); } export async function fetchEmployeeProfile(employeeId: string) { return apiRequestData(`/api/employees/${employeeId}/profile`); } export async function uploadEmployeeAvatar(employeeId: string, file: File) { const formData = new FormData(); formData.append("file", file); return apiRequestData<{ employeeId: string; avatarUrl: string | null }>(`/api/employees/${employeeId}/avatar`, { method: "PUT", body: formData, }); } export async function deleteEmployeeAvatar(employeeId: string) { return apiRequestData<{ employeeId: string; avatarUrl: string | null }>(`/api/employees/${employeeId}/avatar`, { method: "DELETE", }); } export async function patchEmployeeProfile( employeeId: string, payload: Partial<{ departmentId: string; directManagerId: string | null; title: string; hireDate: string | null; probationEndDate: string | null; lastWorkingDate: string | null; workLocation: string | null; emergencyContactName: string | null; emergencyContactPhone: string | null; notes: string | null; }>, ) { return apiRequestData(`/api/employees/${employeeId}/profile`, { method: "PATCH", body: JSON.stringify(payload), }); } export async function startEmployeeOnboarding( employeeId: string, payload: { effectiveDate?: string; probationEndDate?: string | null; notes?: string | null } = {}, ) { return apiRequestData(`/api/employees/${employeeId}/onboarding/start`, { method: "POST", body: JSON.stringify(payload), }); } export async function regularizeEmployee( employeeId: string, payload: { effectiveDate?: string; notes?: string | null } = {}, ) { return apiRequestData(`/api/employees/${employeeId}/regularize`, { method: "POST", body: JSON.stringify(payload), }); } export async function transferEmployee( employeeId: string, payload: { effectiveDate?: string; departmentId?: string; directManagerId?: string | null; title?: string; notes?: string | null; } = {}, ) { return apiRequestData(`/api/employees/${employeeId}/transfer`, { method: "POST", body: JSON.stringify(payload), }); } export async function startEmployeeOffboarding( employeeId: string, payload: { effectiveDate?: string; lastWorkingDate?: string | null; notes?: string | null } = {}, ) { return apiRequestData(`/api/employees/${employeeId}/offboarding/start`, { method: "POST", body: JSON.stringify(payload), }); } export async function fetchEmployeeLifecycle(employeeId: string) { return apiRequestData(`/api/employees/${employeeId}/lifecycle`); } export async function fetchEmployeeWecomDirectorySync(employeeId: string) { return apiRequestData<{ employeeId: string; wecomDirectory: WecomDirectoryState; items: WecomDirectorySyncRun[]; }>(`/api/employees/${employeeId}/wecom-directory-sync`); } export async function retryEmployeeWecomDirectorySync(employeeId: string, operation?: WecomDirectorySyncOperation) { return apiRequestData<{ employeeId: string; operation: WecomDirectorySyncOperation; result: { ok: boolean; skipped: boolean; status: WecomDirectorySyncStatus; code: string; message: string | null; run: WecomDirectorySyncRun | null; } | null; wecomDirectory: WecomDirectoryState; }>(`/api/employees/${employeeId}/wecom-directory-sync/retry`, { method: "POST", body: JSON.stringify(operation ? { operation } : {}), }); } export async function runWecomHrMasterSync(payload: { mode: WecomHrMasterSyncMode; userids?: string[]; limit?: number; }) { return apiRequestData("/api/employees/wecom-hr-master-sync/runs", { method: "POST", body: JSON.stringify(payload), }); } export async function fetchWecomHrMasterSyncRuns(pageSize = 5) { return apiRequestData<{ items: WecomHrMasterSyncRun[] }>( `/api/employees/wecom-hr-master-sync/runs?pageSize=${pageSize}`, ); } export async function fetchWecomHrMasterSyncRun(runId: string) { return apiRequestData(`/api/employees/wecom-hr-master-sync/runs/${runId}`); } export async function fetchEmployeeOrganization() { return apiRequestData<{ items: OrganizationDepartmentSection[] }>("/api/employees/organization"); } export async function fetchEmployeeStats() { return apiRequestData("/api/employees/stats"); } export async function fetchEmployeeContracts(employeeId: string) { return apiRequestData<{ items: EmployeeContract[] }>(`/api/employees/${employeeId}/contracts`); } export async function createEmployeeContract( employeeId: string, payload: { contractNo: string; contractType: EmployeeContractType; startDate: string; endDate: string; terminatedDate?: string | null; attachmentPath?: string | null; }, ) { return apiRequestData(`/api/employees/${employeeId}/contracts`, { method: "POST", body: JSON.stringify(payload), }); } export async function patchEmployeeContract( employeeId: string, contractId: string, payload: Partial<{ contractNo: string; contractType: EmployeeContractType; startDate: string; endDate: string; terminatedDate: string | null; attachmentPath: string | null; }>, ) { return apiRequestData(`/api/employees/${employeeId}/contracts/${contractId}`, { method: "PATCH", body: JSON.stringify(payload), }); } export async function uploadEmployeeContractAttachment(employeeId: string, contractId: string, file: File) { const formData = new FormData(); formData.append("file", file); return apiRequestData(`/api/employees/${employeeId}/contracts/${contractId}/attachment`, { method: "PUT", body: formData, }); } export async function downloadEmployeeContractAttachment(attachmentUrl: string): Promise { return apiRequestBlob(attachmentUrl); } export async function deleteEmployeeContract(employeeId: string, contractId: string) { return apiRequestData<{ contractId: string; deleted: boolean }>(`/api/employees/${employeeId}/contracts/${contractId}`, { method: "DELETE", }); } export async function fetchEmployeeTrainings(employeeId: string) { return apiRequestData<{ items: EmployeeTraining[] }>(`/api/employees/${employeeId}/trainings`); } export async function createEmployeeTraining( employeeId: string, payload: { name: string; trainingType: EmployeeTrainingType; startDate: string; endDate?: string | null; status: EmployeeTrainingStatus; notes?: string | null; }, ) { return apiRequestData(`/api/employees/${employeeId}/trainings`, { method: "POST", body: JSON.stringify(payload), }); } export async function patchEmployeeTraining( employeeId: string, trainingId: string, payload: Partial<{ name: string; trainingType: EmployeeTrainingType; startDate: string; endDate: string | null; status: EmployeeTrainingStatus; notes: string | null; }>, ) { return apiRequestData(`/api/employees/${employeeId}/trainings/${trainingId}`, { method: "PATCH", body: JSON.stringify(payload), }); } export async function fetchEmployeeCertificates(employeeId: string) { return apiRequestData<{ items: EmployeeCertificate[] }>(`/api/employees/${employeeId}/certificates`); } export async function createEmployeeCertificate( employeeId: string, payload: { name: string; issuer: string; obtainedDate: string; expiryDate?: string | null; certificateNo?: string | null; attachmentPath?: string | null; }, ) { return apiRequestData(`/api/employees/${employeeId}/certificates`, { method: "POST", body: JSON.stringify(payload), }); } export async function patchEmployeeCertificate( employeeId: string, certificateId: string, payload: Partial<{ name: string; issuer: string; obtainedDate: string; expiryDate: string | null; certificateNo: string | null; attachmentPath: string | null; }>, ) { return apiRequestData(`/api/employees/${employeeId}/certificates/${certificateId}`, { method: "PATCH", body: JSON.stringify(payload), }); } export async function completeEmployeeChecklistTask(employeeId: string, taskId: string) { return apiRequestData(`/api/employees/${employeeId}/checklist-tasks/${taskId}/complete`, { method: "POST", body: JSON.stringify({}), }); } export async function skipEmployeeChecklistTask(employeeId: string, taskId: string) { return apiRequestData(`/api/employees/${employeeId}/checklist-tasks/${taskId}/skip`, { method: "POST", body: JSON.stringify({}), }); } export async function batchTransferEmployeesDepartment(payload: BatchTransferDepartmentPayload) { return apiRequestData("/api/users/batch/transfer-department", { method: "POST", body: JSON.stringify(payload), }); } export async function batchEnableUsers(payload: BatchToggleUsersPayload) { return apiRequestData("/api/users/batch/enable", { method: "POST", body: JSON.stringify(payload), }); } export async function batchDisableUsers(payload: BatchToggleUsersPayload) { return apiRequestData("/api/users/batch/disable", { method: "POST", body: JSON.stringify(payload), }); } export async function batchSetEmployeesManager(payload: BatchSetManagerPayload) { return apiRequestData("/api/users/batch/set-manager", { method: "POST", body: JSON.stringify(payload), }); }