thread-master/apps/web/src/lib/jobLabels.ts

109 lines
3.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import type { Job, JobStatus } from "../domain/types";
/** 任務類型顯示名(中文) */
export function jobTemplateLabel(
templateType: string,
t: (key: string, params?: Record<string, string | number>) => string,
): string {
switch (templateType) {
case "demo":
return t("jobs.demoLabel");
case "threads_token_renew":
return t("jobs.template.tokenRenew");
case "persona_analyze_account":
return t("jobs.template.personaAnalyzeAccount");
case "persona_analyze_text":
return t("jobs.template.personaAnalyzeText");
case "compose_mimic":
return t("jobs.template.composeMimic");
case "play_generate_script":
return t("jobs.template.playGenerateScript");
default:
return templateType || t("jobs.template.unknown");
}
}
/** 是否為約每 30 天的定期 Token 延長任務 */
export function isRecurringTokenRenew(job: { template_type: string }): boolean {
return job.template_type === "threads_token_renew";
}
/**
* 是否應在頂欄/加速輪詢中視為「即時進行中」:
* - running 一律
* - pending/queued 且已到期run_after 空或 ≤ now— 不含遠期排程(如 30 天後 token 延長)
*/
export function isJobLiveActive(
job: { status: string; run_after?: number },
nowNs: number = Date.now() * 1_000_000,
): boolean {
if (job.status === "running") return true;
if (job.status !== "pending" && job.status !== "queued") return false;
if (!job.run_after || job.run_after <= 0) return true;
return job.run_after <= nowNs;
}
/** 執行中不可手動刪除;其餘(含已排程/終態)可刪 */
export function canDeleteJob(job: { status: string }): boolean {
return job.status !== "running";
}
/** 狀態中文 */
export function jobStatusLabel(
status: JobStatus | string,
t: (key: string, params?: Record<string, string | number>) => string,
): string {
const key = `jobs.status.${status}`;
const label = t(key);
// 若缺 keyi18n 常回傳 key 本身
if (label === key) return status;
return label;
}
export function jobStatusTone(
status: JobStatus | string,
): "success" | "danger" | "warning" | "brand" | "neutral" {
switch (status) {
case "succeeded":
return "success";
case "failed":
return "danger";
case "cancelled":
case "cancel_requested":
return "neutral";
case "running":
return "brand";
case "queued":
case "pending":
return "warning";
default:
return "neutral";
}
}
/**
* 副標:定期節奏(約每 30 天)+ 進度摘要 + 下次執行時間。
* 標題已含「約每 30 天」;此處再強調一次節奏,方便列表一眼辨認。
*/
export function jobSubtitle(
job: Job,
t: (key: string, params?: Record<string, string | number>) => string,
formatTime: (n: number | null | undefined) => string,
): string {
const parts: string[] = [];
if (isRecurringTokenRenew(job)) {
parts.push(t("jobs.template.tokenRenewCadence"));
}
if (job.progress_summary) {
parts.push(job.progress_summary);
}
if (
job.run_after &&
job.run_after > 0 &&
(job.status === "queued" || job.status === "pending")
) {
parts.push(t("jobs.nextRun", { time: formatTime(job.run_after) }));
}
return parts.join(" · ");
}