176 lines
5.1 KiB
TypeScript
176 lines
5.1 KiB
TypeScript
import {
|
|
AlertTriangle,
|
|
CheckCircle2,
|
|
Clock3,
|
|
Loader2,
|
|
XCircle
|
|
} from "lucide-react";
|
|
import type {
|
|
ScheduledConditionItem,
|
|
ScheduledConditionStatus,
|
|
ScheduledWorkOrderItem,
|
|
ScheduledWorkOrderStage
|
|
} from "@/features/workbench/types";
|
|
|
|
export const CONDITION_FILTER_ALL = "__all__";
|
|
|
|
export const statusLabels: Record<ScheduledConditionStatus, string> = {
|
|
completed: "完成",
|
|
running: "执行中",
|
|
warning: "关注",
|
|
error: "异常",
|
|
pending: "预约"
|
|
};
|
|
|
|
export const statusClassNames: Record<ScheduledConditionStatus, string> = {
|
|
completed: "border-green-100 bg-green-50 text-green-700",
|
|
running: "border-blue-100 bg-blue-50 text-blue-700",
|
|
warning: "border-orange-100 bg-orange-50 text-orange-700",
|
|
error: "border-red-100 bg-red-50 text-red-700",
|
|
pending: "border-slate-200 bg-slate-100 text-slate-600"
|
|
};
|
|
|
|
const statusIcons: Record<ScheduledConditionStatus, typeof CheckCircle2> = {
|
|
completed: CheckCircle2,
|
|
running: Loader2,
|
|
warning: AlertTriangle,
|
|
error: XCircle,
|
|
pending: Clock3
|
|
};
|
|
|
|
const statusIconClassNames: Record<ScheduledConditionStatus, string> = {
|
|
completed: "bg-green-50 text-green-700",
|
|
running: "bg-blue-50 text-blue-700",
|
|
warning: "bg-orange-50 text-orange-700",
|
|
error: "bg-red-50 text-red-700",
|
|
pending: "bg-slate-100 text-slate-600"
|
|
};
|
|
|
|
const conditionStatusOrder: ScheduledConditionStatus[] = ["running", "warning", "error", "completed", "pending"];
|
|
|
|
export type ConditionTaskFilterValue = typeof CONDITION_FILTER_ALL | string;
|
|
export type ConditionStatusFilterValue = typeof CONDITION_FILTER_ALL | ScheduledConditionStatus;
|
|
|
|
export type ConditionTaskFilterOption = {
|
|
value: ConditionTaskFilterValue;
|
|
label: string;
|
|
count: number;
|
|
};
|
|
|
|
export type ConditionStatusFilterOption = {
|
|
value: ScheduledConditionStatus;
|
|
label: string;
|
|
count: number;
|
|
};
|
|
|
|
export function groupScheduledConditions(conditions: ScheduledConditionItem[]) {
|
|
const recentConditions = conditions
|
|
.filter((condition) => condition.kind === "condition")
|
|
.sort((a, b) => getConditionTime(b) - getConditionTime(a));
|
|
|
|
const futureWorkOrders = conditions
|
|
.filter((condition) => condition.kind === "work_order")
|
|
.sort((a, b) => getConditionTime(a) - getConditionTime(b));
|
|
|
|
return { recentConditions, futureWorkOrders };
|
|
}
|
|
|
|
export function createTaskFilterOptions(conditions: ScheduledConditionItem[]): ConditionTaskFilterOption[] {
|
|
const taskCounts = new Map<string, number>();
|
|
|
|
conditions.forEach((condition) => {
|
|
taskCounts.set(condition.title, (taskCounts.get(condition.title) ?? 0) + 1);
|
|
});
|
|
|
|
return [...taskCounts.entries()]
|
|
.map(([label, count]) => ({ value: label, label, count }))
|
|
.sort((a, b) => b.count - a.count || a.label.localeCompare(b.label, "zh-CN"));
|
|
}
|
|
|
|
export function createStatusFilterOptions(conditions: ScheduledConditionItem[]): ConditionStatusFilterOption[] {
|
|
const statusCounts = new Map<ScheduledConditionStatus, number>();
|
|
|
|
conditions.forEach((condition) => {
|
|
statusCounts.set(condition.status, (statusCounts.get(condition.status) ?? 0) + 1);
|
|
});
|
|
|
|
return conditionStatusOrder.map((status) => ({
|
|
value: status,
|
|
label: statusLabels[status],
|
|
count: statusCounts.get(status) ?? 0
|
|
}));
|
|
}
|
|
|
|
export function getConditionTime(condition: ScheduledConditionItem) {
|
|
return Date.parse(condition.scheduledAt);
|
|
}
|
|
|
|
export function formatTime(value: string) {
|
|
const match = value.match(/T(\d{2}:\d{2})/);
|
|
return match?.[1] ?? value;
|
|
}
|
|
|
|
export function formatTimeRange(value: string, durationMinutes: number | undefined) {
|
|
const startTime = formatTime(value);
|
|
if (typeof durationMinutes !== "number") {
|
|
return startTime;
|
|
}
|
|
|
|
const endDate = new Date(Date.parse(value) + durationMinutes * 60_000);
|
|
const endTime = `${endDate.getHours().toString().padStart(2, "0")}:${endDate.getMinutes().toString().padStart(2, "0")}`;
|
|
return `${startTime} - ${endTime}`;
|
|
}
|
|
|
|
export function formatDuration(durationMinutes: number | undefined, status: ScheduledConditionStatus) {
|
|
if (status === "running") {
|
|
return "执行中";
|
|
}
|
|
|
|
if (typeof durationMinutes !== "number") {
|
|
return "未记录";
|
|
}
|
|
|
|
return `${durationMinutes} 分钟`;
|
|
}
|
|
|
|
export function getRiskLabel(riskLevel: ScheduledConditionItem["riskLevel"]) {
|
|
if (riskLevel === "critical") {
|
|
return "高风险";
|
|
}
|
|
|
|
if (riskLevel === "attention") {
|
|
return "关注";
|
|
}
|
|
|
|
return "正常";
|
|
}
|
|
|
|
export function getWorkOrderCurrentStage(condition: ScheduledWorkOrderItem) {
|
|
return (
|
|
condition.stages.find((stage) => stage.status === "current") ??
|
|
condition.stages.find((stage) => stage.status === "pending") ??
|
|
condition.stages.at(-1) ??
|
|
null
|
|
);
|
|
}
|
|
|
|
export function getWorkOrderStagePillClassName(stageId?: ScheduledWorkOrderStage["id"]) {
|
|
if (stageId === "execution") {
|
|
return "border-blue-100 bg-blue-50 text-blue-700";
|
|
}
|
|
|
|
if (stageId === "reply") {
|
|
return "border-green-100 bg-green-50 text-green-700";
|
|
}
|
|
|
|
return "border-indigo-100 bg-indigo-50 text-indigo-700";
|
|
}
|
|
|
|
export function getStatusIcon(status: ScheduledConditionStatus) {
|
|
return statusIcons[status];
|
|
}
|
|
|
|
export function getStatusIconClassName(status: ScheduledConditionStatus) {
|
|
return statusIconClassNames[status];
|
|
}
|