159 行
4.7 KiB
TypeScript
159 行
4.7 KiB
TypeScript
import { apiRequestBlob, apiRequestData, type ApiBlobResponse } from "./http";
|
|
|
|
export type PresenceStatus = "office" | "remote" | "out" | "travel" | "leave";
|
|
export type PresenceTimeRange = "full_day" | "morning" | "afternoon";
|
|
export type PresenceSource = "manual" | "leave_sync" | "admin_override";
|
|
export type PresenceHistoryStatusFilter = PresenceStatus | "unspecified";
|
|
|
|
export type PresenceItem = {
|
|
id: string;
|
|
employeeId: string;
|
|
date: string;
|
|
status: PresenceStatus;
|
|
timeRange: PresenceTimeRange;
|
|
note: string | null;
|
|
source: PresenceSource;
|
|
updatedAt: string | null;
|
|
};
|
|
|
|
export type PresenceEmployee = {
|
|
id: string;
|
|
employeeNo: string;
|
|
name: string;
|
|
title: string;
|
|
avatarUrl: string | null;
|
|
employmentStatus: string;
|
|
department: { id: string; name: string } | null;
|
|
};
|
|
|
|
export type TeamPresenceItem = {
|
|
employee: PresenceEmployee;
|
|
presence: PresenceItem | null;
|
|
};
|
|
|
|
export type MyPresenceHistoryItem = {
|
|
date: string;
|
|
presence: PresenceItem | null;
|
|
};
|
|
|
|
export type TeamPresenceHistoryItem = {
|
|
date: string;
|
|
employee: PresenceEmployee;
|
|
presence: PresenceItem | null;
|
|
};
|
|
|
|
export type PresenceSummary = {
|
|
office: number;
|
|
remote: number;
|
|
out: number;
|
|
travel: number;
|
|
leave: number;
|
|
unspecified: number;
|
|
total: number;
|
|
};
|
|
|
|
export type MyPresenceHistoryResponse = {
|
|
dateFrom: string;
|
|
dateTo: string;
|
|
summary: PresenceSummary;
|
|
items: MyPresenceHistoryItem[];
|
|
};
|
|
|
|
export type TeamPresenceHistoryResponse = {
|
|
dateFrom: string;
|
|
dateTo: string;
|
|
summary: PresenceSummary;
|
|
items: TeamPresenceHistoryItem[];
|
|
};
|
|
|
|
export async function fetchMyPresence(date?: string) {
|
|
const suffix = date ? `?date=${encodeURIComponent(date)}` : "";
|
|
return apiRequestData<{ item: PresenceItem | null; date: string }>(`/api/presence/me${suffix}`);
|
|
}
|
|
|
|
export async function upsertMyPresence(payload: {
|
|
date?: string;
|
|
status: Extract<PresenceStatus, "office" | "remote" | "out" | "travel">;
|
|
timeRange?: PresenceTimeRange;
|
|
note?: string | null;
|
|
}) {
|
|
return apiRequestData<{ item: PresenceItem }>("/api/presence/me", {
|
|
method: "PUT",
|
|
body: JSON.stringify(payload),
|
|
});
|
|
}
|
|
|
|
export async function fetchTeamPresence(date?: string) {
|
|
const suffix = date ? `?date=${encodeURIComponent(date)}` : "";
|
|
return apiRequestData<{ date: string; summary: PresenceSummary; items: TeamPresenceItem[] }>(`/api/presence/team${suffix}`);
|
|
}
|
|
|
|
export async function fetchMyPresenceHistory(params: {
|
|
dateFrom?: string;
|
|
dateTo?: string;
|
|
}) {
|
|
const search = new URLSearchParams();
|
|
if (params.dateFrom) search.set("dateFrom", params.dateFrom);
|
|
if (params.dateTo) search.set("dateTo", params.dateTo);
|
|
|
|
const query = search.toString();
|
|
return apiRequestData<MyPresenceHistoryResponse>(`/api/presence/me/history${query ? `?${query}` : ""}`);
|
|
}
|
|
|
|
export async function fetchTeamPresenceHistory(params: {
|
|
dateFrom?: string;
|
|
dateTo?: string;
|
|
departmentId?: string;
|
|
employeeId?: string;
|
|
status?: PresenceHistoryStatusFilter;
|
|
}) {
|
|
const search = new URLSearchParams();
|
|
if (params.dateFrom) search.set("dateFrom", params.dateFrom);
|
|
if (params.dateTo) search.set("dateTo", params.dateTo);
|
|
if (params.departmentId) search.set("departmentId", params.departmentId);
|
|
if (params.employeeId) search.set("employeeId", params.employeeId);
|
|
if (params.status) search.set("status", params.status);
|
|
|
|
const query = search.toString();
|
|
return apiRequestData<TeamPresenceHistoryResponse>(`/api/presence/team/history${query ? `?${query}` : ""}`);
|
|
}
|
|
|
|
export async function fetchPresenceSummary(date?: string) {
|
|
const suffix = date ? `?date=${encodeURIComponent(date)}` : "";
|
|
return apiRequestData<{ date: string; summary: PresenceSummary }>(`/api/presence/summary${suffix}`);
|
|
}
|
|
|
|
export async function exportPresenceHistory(params: {
|
|
format: "csv" | "xlsx";
|
|
dateFrom?: string;
|
|
dateTo?: string;
|
|
departmentId?: string;
|
|
employeeId?: string;
|
|
status?: PresenceHistoryStatusFilter;
|
|
}): Promise<ApiBlobResponse> {
|
|
const search = new URLSearchParams();
|
|
search.set("format", params.format);
|
|
if (params.dateFrom) search.set("dateFrom", params.dateFrom);
|
|
if (params.dateTo) search.set("dateTo", params.dateTo);
|
|
if (params.departmentId) search.set("departmentId", params.departmentId);
|
|
if (params.employeeId) search.set("employeeId", params.employeeId);
|
|
if (params.status) search.set("status", params.status);
|
|
|
|
return apiRequestBlob(`/api/exports/presence-history?${search.toString()}`);
|
|
}
|
|
|
|
export async function patchTeamPresence(
|
|
employeeId: string,
|
|
payload: {
|
|
date: string;
|
|
status: Extract<PresenceStatus, "office" | "remote" | "out" | "travel">;
|
|
timeRange?: PresenceTimeRange;
|
|
note?: string | null;
|
|
},
|
|
) {
|
|
return apiRequestData<{ item: PresenceItem }>(`/api/presence/team/${employeeId}`, {
|
|
method: "PUT",
|
|
body: JSON.stringify(payload),
|
|
});
|
|
}
|