import { apiRequestData } from "./http"; export type ProjectStatus = "draft" | "active" | "paused" | "completed" | "archived"; export type ProjectRole = "owner" | "manager" | "member" | "observer"; export type ProjectMilestoneStatus = "pending" | "in_progress" | "done" | "delayed" | "cancelled"; export type ProjectTaskStatus = "todo" | "doing" | "blocked" | "done" | "cancelled"; export type ProjectTaskPriority = "low" | "normal" | "high" | "urgent"; export type ProjectRiskLevel = "low" | "medium" | "high"; export type ProjectDeliveryRecordType = "kickoff" | "onsite" | "stage_acceptance" | "final_acceptance" | "handover"; export type ProjectCommunicationChannel = "wechat" | "phone" | "meeting" | "wecom" | "email" | "onsite" | "other"; export type ProjectClosureChecklistStatus = "pending" | "done" | "waived"; export type ConfidentialityLevel = "public" | "internal" | "confidential" | "restricted"; export type DocumentStorageType = "oa_link" | "nas" | "internal_share" | "offline_archive"; export type EmployeeBasic = { id: string; employeeNo: string; name: string; }; export type ProjectListItem = { id: string; projectNo: string; name: string; clientName: string; clientAlias: string | null; status: ProjectStatus; startsAt: string | null; targetEndAt: string | null; actualEndAt: string | null; summary: string | null; isConfidential: boolean; owner: EmployeeBasic | null; manager: EmployeeBasic | null; counts: { members: number; milestones: number; tasks: number; risks: number; overdueMilestones: number; highPriorityTasks: number; openRisks: number; }; updatedAt: string | null; }; export type ProjectMember = { id: string; employeeId: string; role: ProjectRole; joinedAt: string | null; leftAt: string | null; employee: EmployeeBasic | null; }; export type ProjectMilestone = { id: string; title: string; description: string | null; status: ProjectMilestoneStatus; dueDate: string | null; completedAt: string | null; ownerEmployeeId: string | null; sortOrder: number; }; export type ProjectTask = { id: string; title: string; description: string | null; status: ProjectTaskStatus; priority: ProjectTaskPriority; assigneeEmployeeId: string | null; dueDate: string | null; blockedReason: string | null; createdBy: string; assignee: EmployeeBasic | null; creator: EmployeeBasic | null; createdAt: string | null; updatedAt: string | null; }; export type ProjectRisk = { id: string; title: string; detail: string; level: ProjectRiskLevel; status: string; ownerEmployeeId: string | null; owner: EmployeeBasic | null; createdAt: string | null; updatedAt: string | null; }; export type ProjectCommunicationLog = { id: string; happenedAt: string | null; channel: ProjectCommunicationChannel; externalContactName: string | null; externalOrgName: string | null; summary: string; nextAction: string | null; nextFollowUpDate: string | null; createdBy: string; createdAt: string | null; updatedAt: string | null; canEdit: boolean; creator: EmployeeBasic | null; }; export type ProjectClosureChecklistItem = { id: string; itemKey: string; title: string; status: ProjectClosureChecklistStatus; note: string | null; sortOrder: number; completedAt: string | null; completedBy: string | null; canEdit: boolean; completer: EmployeeBasic | null; }; export type ProjectDetail = { id: string; projectNo: string; name: string; clientName: string; clientAlias: string | null; ownerEmployeeId: string; managerEmployeeId: string | null; status: ProjectStatus; startsAt: string | null; targetEndAt: string | null; actualEndAt: string | null; summary: string | null; isConfidential: boolean; createdAt: string | null; updatedAt: string | null; owner: EmployeeBasic | null; manager: EmployeeBasic | null; counts: { members: number; milestones: number; tasks: number; risks: number; reimbursements: number; purchaseRequests: number; travelRequests: number; todoItems: number; weeklyReports: number; communicationLogs: number; closureChecklistItems: number; contracts: number; deliveries: number; documentRefs: number; }; members: ProjectMember[]; milestones: ProjectMilestone[]; tasks: ProjectTask[]; risks: ProjectRisk[]; communicationSummary: { totalCount: number; latestHappenedAt: string | null; followUpDueTodayCount: number; overdueFollowUpCount: number; }; closureSummary: { totalCount: number; pendingCount: number; waivedCount: number; warned: boolean; facts: { openRiskCount: number; openReimbursementCount: number; openTravelCount: number; deliveryRecordCount: number; contractCount: number; confidentialDocRefCount: number; }; }; capabilities: { communicationLogEnabled: boolean; closureChecklistEnabled: boolean; }; }; export type ProjectExpenseSummary = { totalAmount: string; items: Array<{ id: string; formNumber: string; status: string; totalAmount: string; reimbursementDate: string | null; updatedAt: string | null; }>; }; export type ProjectTravelSummary = { totalAmount: string; items: Array<{ id: string; requestNo: string; status: string; estimatedAmount: string; startDate: string | null; endDate: string | null; updatedAt: string | null; }>; }; export type ProjectPurchaseSummary = { totalAmount: string; items: Array<{ id: string; requestNo: string; title: string; status: string; totalAmount: string; updatedAt: string | null; }>; }; export type ProjectWeeklyReportLink = { id: string; weeklyReportId: string; weekStart: string | null; status: "draft" | "submitted"; employee: EmployeeBasic | null; progressText: string | null; riskText: string | null; nextStepText: string | null; updatedAt: string | null; }; export type ProjectDeliveryRecord = { id: string; title: string; recordType: ProjectDeliveryRecordType; summary: string; occurredAt: string | null; owner: EmployeeBasic | null; createdAt: string | null; }; export type ConfidentialDocumentRef = { id: string; title: string; documentNo: string | null; confidentialityLevel: ConfidentialityLevel; storageType: DocumentStorageType; storagePath: string; offlineArchiveNo: string | null; updatedAt: string | null; }; function toQueryString(query: Record) { const params = new URLSearchParams(); for (const [key, value] of Object.entries(query)) { if (value == null || value === "") { continue; } params.set(key, typeof value === "boolean" ? (value ? "1" : "0") : String(value)); } const encoded = params.toString(); return encoded ? `?${encoded}` : ""; } export async function fetchProjects(params: { status?: ProjectStatus; mine?: boolean; q?: string; } = {}) { return apiRequestData<{ items: ProjectListItem[] }>(`/api/projects${toQueryString(params)}`); } export async function createProject(payload: { projectNo?: string; name: string; clientName: string; clientAlias?: string | null; ownerEmployeeId?: string; managerEmployeeId?: string | null; status?: ProjectStatus; startsAt?: string | null; targetEndAt?: string | null; actualEndAt?: string | null; summary?: string | null; isConfidential?: boolean; }) { return apiRequestData<{ item: ProjectDetail }>("/api/projects", { method: "POST", body: JSON.stringify(payload), }); } export async function fetchProject(projectId: string) { return apiRequestData<{ item: ProjectDetail }>(`/api/projects/${projectId}`); } export type ProjectClosureWarning = { pendingCount: number; message: string; } | null; export async function patchProject( projectId: string, payload: Partial<{ projectNo: string; name: string; clientName: string; clientAlias: string | null; ownerEmployeeId: string; managerEmployeeId: string | null; status: ProjectStatus; startsAt: string | null; targetEndAt: string | null; actualEndAt: string | null; summary: string | null; isConfidential: boolean; }>, ) { return apiRequestData<{ item: ProjectDetail; closureWarning: ProjectClosureWarning }>(`/api/projects/${projectId}`, { method: "PATCH", body: JSON.stringify(payload), }); } export async function archiveProject(projectId: string) { return apiRequestData<{ item: ProjectDetail; closureWarning: ProjectClosureWarning }>(`/api/projects/${projectId}/archive`, { method: "POST", body: JSON.stringify({}), }); } export async function fetchProjectMembers(projectId: string) { return apiRequestData<{ items: ProjectMember[] }>(`/api/projects/${projectId}/members`); } export async function createProjectMember(projectId: string, payload: { employeeId: string; role?: ProjectRole }) { return apiRequestData<{ item: ProjectMember }>(`/api/projects/${projectId}/members`, { method: "POST", body: JSON.stringify(payload), }); } export async function patchProjectMember( projectId: string, memberId: string, payload: Partial<{ role: ProjectRole; leftAt: string | null }>, ) { return apiRequestData<{ item: ProjectMember }>(`/api/projects/${projectId}/members/${memberId}`, { method: "PATCH", body: JSON.stringify(payload), }); } export async function fetchProjectMilestones(projectId: string) { return apiRequestData<{ items: ProjectMilestone[] }>(`/api/projects/${projectId}/milestones`); } export async function createProjectMilestone( projectId: string, payload: { title: string; description?: string | null; status?: ProjectMilestoneStatus; dueDate?: string | null; completedAt?: string | null; ownerEmployeeId?: string | null; sortOrder?: number; }, ) { return apiRequestData<{ item: ProjectMilestone }>(`/api/projects/${projectId}/milestones`, { method: "POST", body: JSON.stringify(payload), }); } export async function patchProjectMilestone( projectId: string, milestoneId: string, payload: Partial<{ title: string; description: string | null; status: ProjectMilestoneStatus; dueDate: string | null; completedAt: string | null; ownerEmployeeId: string | null; sortOrder: number; }>, ) { return apiRequestData<{ item: ProjectMilestone }>(`/api/projects/${projectId}/milestones/${milestoneId}`, { method: "PATCH", body: JSON.stringify(payload), }); } export async function fetchProjectTasks(projectId: string) { return apiRequestData<{ items: ProjectTask[] }>(`/api/projects/${projectId}/tasks`); } export async function createProjectTask( projectId: string, payload: { title: string; description?: string | null; status?: ProjectTaskStatus; priority?: ProjectTaskPriority; assigneeEmployeeId?: string | null; dueDate?: string | null; blockedReason?: string | null; }, ) { return apiRequestData<{ item: ProjectTask }>(`/api/projects/${projectId}/tasks`, { method: "POST", body: JSON.stringify(payload), }); } export async function patchProjectTask( projectId: string, taskId: string, payload: Partial<{ title: string; description: string | null; status: ProjectTaskStatus; priority: ProjectTaskPriority; assigneeEmployeeId: string | null; dueDate: string | null; blockedReason: string | null; }>, ) { return apiRequestData<{ item: ProjectTask }>(`/api/projects/${projectId}/tasks/${taskId}`, { method: "PATCH", body: JSON.stringify(payload), }); } export async function fetchProjectRisks(projectId: string) { return apiRequestData<{ items: ProjectRisk[] }>(`/api/projects/${projectId}/risks`); } export async function createProjectRisk( projectId: string, payload: { title: string; detail: string; level?: ProjectRiskLevel; status?: string; ownerEmployeeId?: string | null }, ) { return apiRequestData<{ item: ProjectRisk }>(`/api/projects/${projectId}/risks`, { method: "POST", body: JSON.stringify(payload), }); } export async function patchProjectRisk( projectId: string, riskId: string, payload: Partial<{ title: string; detail: string; level: ProjectRiskLevel; status: string; ownerEmployeeId: string | null }>, ) { return apiRequestData<{ item: ProjectRisk }>(`/api/projects/${projectId}/risks/${riskId}`, { method: "PATCH", body: JSON.stringify(payload), }); } export async function fetchProjectExpensesSummary(projectId: string) { return apiRequestData(`/api/projects/${projectId}/expenses/summary`); } export async function fetchProjectTravelSummary(projectId: string) { return apiRequestData(`/api/projects/${projectId}/travel/summary`); } export async function fetchProjectPurchaseSummary(projectId: string) { return apiRequestData(`/api/projects/${projectId}/purchase-requests/summary`); } export async function fetchProjectWeeklyReports(projectId: string) { return apiRequestData<{ items: ProjectWeeklyReportLink[] }>(`/api/projects/${projectId}/weekly-reports`); } export async function fetchProjectCommunicationLogs(projectId: string) { return apiRequestData<{ items: ProjectCommunicationLog[] }>(`/api/projects/${projectId}/communication-logs`); } export async function createProjectCommunicationLog( projectId: string, payload: { happenedAt: string; channel: ProjectCommunicationChannel; externalContactName?: string | null; externalOrgName?: string | null; summary: string; nextAction?: string | null; nextFollowUpDate?: string | null; }, ) { return apiRequestData<{ item: ProjectCommunicationLog }>(`/api/projects/${projectId}/communication-logs`, { method: "POST", body: JSON.stringify(payload), }); } export async function patchProjectCommunicationLog( projectId: string, logId: string, payload: Partial<{ happenedAt: string; channel: ProjectCommunicationChannel; externalContactName: string | null; externalOrgName: string | null; summary: string; nextAction: string | null; nextFollowUpDate: string | null; }>, ) { return apiRequestData<{ item: ProjectCommunicationLog }>(`/api/projects/${projectId}/communication-logs/${logId}`, { method: "PATCH", body: JSON.stringify(payload), }); } export async function fetchProjectClosureChecklist(projectId: string) { return apiRequestData<{ items: ProjectClosureChecklistItem[] }>(`/api/projects/${projectId}/closure-checklist`); } export async function patchProjectClosureChecklistItem( projectId: string, itemId: string, payload: Partial<{ status: ProjectClosureChecklistStatus; note: string | null }>, ) { return apiRequestData<{ item: ProjectClosureChecklistItem }>(`/api/projects/${projectId}/closure-checklist/${itemId}`, { method: "PATCH", body: JSON.stringify(payload), }); } export async function fetchProjectDeliveries(projectId: string) { return apiRequestData<{ items: ProjectDeliveryRecord[] }>(`/api/projects/${projectId}/deliveries`); } export async function createProjectDelivery( projectId: string, payload: { title: string; recordType: ProjectDeliveryRecordType; summary: string; occurredAt: string; ownerEmployeeId?: string; }, ) { return apiRequestData<{ item: ProjectDeliveryRecord }>(`/api/projects/${projectId}/deliveries`, { method: "POST", body: JSON.stringify(payload), }); } export async function fetchProjectDocumentRefs(projectId: string) { return apiRequestData<{ items: ConfidentialDocumentRef[] }>(`/api/projects/${projectId}/document-refs`); } export async function createConfidentialDocumentRef(payload: { projectId?: string | null; contractRegistryId?: string | null; title: string; documentNo?: string | null; confidentialityLevel?: ConfidentialityLevel; storageType: DocumentStorageType; storagePath: string; offlineArchiveNo?: string | null; ownerEmployeeId?: string; accessRuleJson?: Record | null; }) { return apiRequestData<{ item: ConfidentialDocumentRef & { owner?: EmployeeBasic | null; project?: { id: string; projectNo: string; name: string } | null; contractRegistry?: { id: string; contractNo: string } | null } }>("/api/confidential-doc-refs", { method: "POST", body: JSON.stringify(payload), }); }