128 行
3.9 KiB
TypeScript
128 行
3.9 KiB
TypeScript
import { apiRequestData } from "./http";
|
|
|
|
export type PortalRecentActivity = {
|
|
id: string;
|
|
type:
|
|
| "reimbursement.submit"
|
|
| "reimbursement.approve"
|
|
| "reimbursement.reject"
|
|
| "leave.submit"
|
|
| "leave.approve"
|
|
| "leave.reject"
|
|
| "attendance.clock_in"
|
|
| "attendance.clock_out"
|
|
| "attendance.supplement"
|
|
| "asset.assign"
|
|
| "asset.return";
|
|
actorName: string;
|
|
summary: string;
|
|
createdAt: string;
|
|
href: string;
|
|
};
|
|
|
|
export type PortalTeamStatusItem = {
|
|
employeeId: string;
|
|
name: string;
|
|
avatarUrl: string | null;
|
|
presenceStatus: string | null;
|
|
attendanceClockIn: string | null;
|
|
attendanceClockOut: string | null;
|
|
onLeave: boolean;
|
|
leaveType: string | null;
|
|
};
|
|
|
|
export type PortalHomePayload = {
|
|
workspace: {
|
|
id: string | null;
|
|
persisted: boolean;
|
|
profile: "small_team_internal";
|
|
};
|
|
greeting: { name: string };
|
|
cards: Array<{ key: string; title: string; value: string | number; href: string; severity?: "normal" | "warning" | "high" }>;
|
|
pendingTasks: { count: number; href: string };
|
|
myApplications: Array<{ domain: string; count: number; href: string }>;
|
|
riskSummary: { openCount: number; href: string } | null;
|
|
announcements: Array<{ id: string; title: string; summary: string; href: string | null }>;
|
|
quickLinks: Array<{ key: string; title: string; href: string }>;
|
|
todayPresence: { date: string; status: string | null; label: string; note: string | null; href: string } | null;
|
|
teamPresence:
|
|
| ({ date: string; href: string } & {
|
|
office: number;
|
|
remote: number;
|
|
out: number;
|
|
travel: number;
|
|
leave: number;
|
|
unspecified: number;
|
|
total: number;
|
|
})
|
|
| null;
|
|
weeklyReport: { weekStart: string; status: "draft" | "submitted"; needsHelp: boolean; href: string } | null;
|
|
weeklySummary: { weekStart: string; submittedCount: number; missingCount: number; needsHelpCount: number; href: string } | null;
|
|
todoSummary: { openCount: number; highPriorityCount: number; href: string } | null;
|
|
projectSummary:
|
|
| {
|
|
myCount: number;
|
|
overdueMilestonesCount: number;
|
|
highPriorityTaskCount: number;
|
|
openRiskCount: number;
|
|
followUpDueTodayCount: number;
|
|
closurePendingProjectCount: number;
|
|
href: string;
|
|
}
|
|
| null;
|
|
peopleLifecycle: { onboardingCount: number; offboardingCount: number; assetReturnPendingCount: number; href: string } | null;
|
|
roleCapabilities?: {
|
|
canSubmitApplications: boolean;
|
|
canApproveTasks: boolean;
|
|
canUseFinance: boolean;
|
|
canManageSystem: boolean;
|
|
canViewTeam: boolean;
|
|
isAdminOnly: boolean;
|
|
};
|
|
businessHandoffs?: {
|
|
total: number;
|
|
items: Array<{
|
|
key: string;
|
|
kind: "reimbursement_edit" | "reimbursement_batch" | "payment_review" | "payment_execute";
|
|
reference: string | null;
|
|
title: string;
|
|
nextStep: string;
|
|
ownerLabel: string;
|
|
href: string;
|
|
mobileHref: string;
|
|
}>;
|
|
};
|
|
financeSummary?: {
|
|
pendingBatchCount: number;
|
|
failedItemCount: number;
|
|
riskCount: number;
|
|
paymentBatchEnabled?: boolean;
|
|
href: string;
|
|
} | null;
|
|
adoptionSummary:
|
|
| {
|
|
activeEmployeeCount: number;
|
|
login7dCount: number;
|
|
weeklySubmittedCount: number;
|
|
weeklyMissingCount: number;
|
|
presenceUpdatedCount: number;
|
|
presenceMissingCount: number;
|
|
announcementCount: number;
|
|
login7dRate: number;
|
|
weeklySubmissionRate: number;
|
|
presenceCoverageRate: number;
|
|
hints: string[];
|
|
}
|
|
| null;
|
|
recentActivities: PortalRecentActivity[];
|
|
teamStatusBoard: PortalTeamStatusItem[];
|
|
};
|
|
|
|
export async function fetchPortalHome(signal?: AbortSignal) {
|
|
return apiRequestData<PortalHomePayload>("/api/portal/home", { signal });
|
|
}
|
|
|
|
export async function fetchPortalTeamStatus() {
|
|
return apiRequestData<PortalTeamStatusItem[]>("/api/portal/team-status");
|
|
}
|