fix(work-items): surface mainline progress order
All checks were successful
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 1m9s
CD Pipeline / build-and-deploy (push) Successful in 8m17s
CD Pipeline / post-deploy-checks (push) Successful in 2m46s
All checks were successful
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 1m9s
CD Pipeline / build-and-deploy (push) Successful in 8m17s
CD Pipeline / post-deploy-checks (push) Successful in 2m46s
This commit is contained in:
@@ -9531,31 +9531,49 @@
|
||||
"noSecret": "no secret read"
|
||||
}
|
||||
},
|
||||
"mainlineProgress": {
|
||||
"eyebrow": "Mainline Work Progress",
|
||||
"title": "All Work Items And Priority Order",
|
||||
"subtitle": "Reads the production priority work-order readback and surfaces totals, completion, current P0, next action, and the first 8 ordered items before the deeper automation / ledger views.",
|
||||
"loading": "Reading production work-order",
|
||||
"completion": "Overall completion",
|
||||
"currentP0": "Current P0",
|
||||
"primaryBlocker": "Primary blocker",
|
||||
"blockerCount": "{count} blockers",
|
||||
"nextAction": "Next ordered action",
|
||||
"statuses": {
|
||||
"done": "Completed",
|
||||
"in_progress": "In progress",
|
||||
"pending": "Not started",
|
||||
"blocked": "Blocked",
|
||||
"deferred": "Deferred"
|
||||
}
|
||||
},
|
||||
"commanderInsertedRequirements": {
|
||||
"eyebrow": "主線優先序",
|
||||
"title": "統帥插入需求工作項",
|
||||
"subtitle": "把本輪中途插入的要求收成正式工作項,依 P0/P1/P2/P3 排序,並顯示狀態、驗收條件與下一步。",
|
||||
"total": "總工作項",
|
||||
"next": "目前優先項",
|
||||
"nextAction": "下一步",
|
||||
"acceptance": "驗收條件",
|
||||
"rowNextAction": "下一步",
|
||||
"spotlight": "主線焦點",
|
||||
"spotlightSubtitle": "直接露出目前 P0、OpenClaw Live Ops 與非文字牆 UX,避免工作項藏在長清單裡。",
|
||||
"fullLedger": "完整總帳 {count} 項",
|
||||
"loading": "讀取插入需求工作項",
|
||||
"empty": "尚未讀回插入需求工作項。",
|
||||
"eyebrow": "Mainline priority",
|
||||
"title": "Commander Inserted Requirement Work Items",
|
||||
"subtitle": "Turns midstream requirements into ordered P0/P1/P2/P3 work items with status, acceptance, and next action.",
|
||||
"total": "Total work items",
|
||||
"next": "Current priority",
|
||||
"nextAction": "Next action",
|
||||
"acceptance": "Acceptance",
|
||||
"rowNextAction": "Next action",
|
||||
"spotlight": "Mainline spotlight",
|
||||
"spotlightSubtitle": "Pins current P0, OpenClaw Live Ops, and UI work so they do not hide in a long ledger.",
|
||||
"fullLedger": "Full ledger {count} items",
|
||||
"loading": "Reading inserted requirement work items",
|
||||
"empty": "No inserted requirement work items read back yet.",
|
||||
"order": "order={order}",
|
||||
"source": "source={source}",
|
||||
"metrics": {
|
||||
"active": "active"
|
||||
},
|
||||
"statuses": {
|
||||
"done": "已完成",
|
||||
"inProgress": "進行中",
|
||||
"pending": "未開始",
|
||||
"blocked": "阻塞",
|
||||
"deferred": "延後"
|
||||
"done": "Completed",
|
||||
"inProgress": "In progress",
|
||||
"pending": "Not started",
|
||||
"blocked": "Blocked",
|
||||
"deferred": "Deferred"
|
||||
}
|
||||
},
|
||||
"operatorSop": {
|
||||
|
||||
@@ -9531,6 +9531,24 @@
|
||||
"noSecret": "no secret read"
|
||||
}
|
||||
},
|
||||
"mainlineProgress": {
|
||||
"eyebrow": "主線工作進度",
|
||||
"title": "全部工作項目與優先順序",
|
||||
"subtitle": "直接讀正式 priority work-order readback,先看總數、完成度、目前 P0、下一步與前 8 個 ordered items;細節再下鑽 automation / ledger。",
|
||||
"loading": "讀取正式 work-order",
|
||||
"completion": "整體完成度",
|
||||
"currentP0": "目前 P0",
|
||||
"primaryBlocker": "主要阻擋",
|
||||
"blockerCount": "{count} 個 blocker",
|
||||
"nextAction": "下一個 ordered action",
|
||||
"statuses": {
|
||||
"done": "已完成",
|
||||
"in_progress": "進行中",
|
||||
"pending": "未開始",
|
||||
"blocked": "阻塞",
|
||||
"deferred": "延後"
|
||||
}
|
||||
},
|
||||
"commanderInsertedRequirements": {
|
||||
"eyebrow": "主線優先序",
|
||||
"title": "統帥插入需求工作項",
|
||||
|
||||
@@ -1091,6 +1091,7 @@ type PriorityWorkOrderResponse = {
|
||||
active_p0_state?: string | null;
|
||||
active_p0_workplan_id?: string | null;
|
||||
active_p0_readiness_percent?: number | null;
|
||||
active_p0_primary_blocker?: string | null;
|
||||
active_p0_live_active_blockers?: string[] | null;
|
||||
next_executable_mainline_workplan_id?: string | null;
|
||||
next_executable_mainline_state?: string | null;
|
||||
@@ -11065,6 +11066,216 @@ function commanderStatusTone(status: string) {
|
||||
}
|
||||
}
|
||||
|
||||
function MainlineWorkProgressStrip({
|
||||
priority,
|
||||
loading,
|
||||
}: {
|
||||
priority: PriorityWorkOrderResponse | null;
|
||||
loading: boolean;
|
||||
}) {
|
||||
const t = useTranslations("awooop.workItems.mainlineProgress");
|
||||
const summary = priority?.summary;
|
||||
const items = (priority?.commander_inserted_requirement_work_items ?? [])
|
||||
.slice()
|
||||
.sort((left, right) => (left.order ?? 0) - (right.order ?? 0));
|
||||
const total = toCount(
|
||||
summary?.commander_inserted_requirement_work_item_count ?? items.length
|
||||
);
|
||||
const done = toCount(summary?.commander_inserted_requirement_done_count);
|
||||
const inProgress = toCount(
|
||||
summary?.commander_inserted_requirement_in_progress_count
|
||||
);
|
||||
const pending = toCount(summary?.commander_inserted_requirement_pending_count);
|
||||
const blocked = toCount(summary?.commander_inserted_requirement_blocked_count);
|
||||
const deferred = toCount(summary?.commander_inserted_requirement_deferred_count);
|
||||
const completion = total > 0 ? Math.round((done / total) * 100) : 0;
|
||||
const readiness = toCount(summary?.active_p0_readiness_percent);
|
||||
const nextId =
|
||||
summary?.commander_inserted_requirement_next_id ?? items[0]?.id ?? "--";
|
||||
const nextPriority =
|
||||
summary?.commander_inserted_requirement_next_priority ??
|
||||
items[0]?.priority ??
|
||||
"--";
|
||||
const nextAction =
|
||||
summary?.commander_inserted_requirement_next_action ??
|
||||
items[0]?.next_action ??
|
||||
"--";
|
||||
const currentP0 = summary?.active_p0_workplan_id ?? "--";
|
||||
const currentState = summary?.active_p0_state ?? "--";
|
||||
const activeBlockers = summary?.active_p0_live_active_blockers ?? [];
|
||||
const primaryBlocker =
|
||||
summary?.active_p0_primary_blocker ?? activeBlockers[0] ?? "--";
|
||||
const visibleItems = items.slice(0, 8);
|
||||
const statusMetrics = [
|
||||
{
|
||||
key: "done",
|
||||
label: t("statuses.done"),
|
||||
value: done,
|
||||
tone: commanderStatusTone("done"),
|
||||
},
|
||||
{
|
||||
key: "inProgress",
|
||||
label: t("statuses.in_progress"),
|
||||
value: inProgress,
|
||||
tone: commanderStatusTone("in_progress"),
|
||||
},
|
||||
{
|
||||
key: "pending",
|
||||
label: t("statuses.pending"),
|
||||
value: pending,
|
||||
tone: commanderStatusTone("pending"),
|
||||
},
|
||||
{
|
||||
key: "blocked",
|
||||
label: t("statuses.blocked"),
|
||||
value: blocked,
|
||||
tone: commanderStatusTone("blocked"),
|
||||
},
|
||||
{
|
||||
key: "deferred",
|
||||
label: t("statuses.deferred"),
|
||||
value: deferred,
|
||||
tone: commanderStatusTone("deferred"),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<section
|
||||
className="border border-[#e0ddd4] bg-white"
|
||||
data-testid="mainline-work-progress-strip"
|
||||
>
|
||||
<div className="grid gap-px bg-[#e0ddd4] lg:grid-cols-[minmax(0,1.2fr)_minmax(280px,0.8fr)]">
|
||||
<div className="bg-white p-4">
|
||||
<div className="flex flex-wrap items-start justify-between gap-4">
|
||||
<div className="min-w-0">
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<span className="inline-flex items-center gap-2 border border-[#c9d8ea] bg-[#eef5ff] px-2 py-0.5 text-xs font-semibold text-[#1f5b9b]">
|
||||
<ListChecks className="h-3.5 w-3.5" aria-hidden="true" />
|
||||
{t("eyebrow")}
|
||||
</span>
|
||||
<span className="font-mono text-xs text-[#77736a]">
|
||||
{loading ? t("loading") : `${nextPriority}:${nextId}`}
|
||||
</span>
|
||||
</div>
|
||||
<h3 className="mt-2 text-base font-semibold tracking-normal text-[#141413]">
|
||||
{t("title")}
|
||||
</h3>
|
||||
<p className="mt-1 max-w-4xl text-xs leading-5 text-[#5f5b52]">
|
||||
{t("subtitle")}
|
||||
</p>
|
||||
</div>
|
||||
<div className="min-w-[220px]">
|
||||
<div className="flex items-center justify-between gap-3 text-xs font-semibold text-[#5f5b52]">
|
||||
<span>{t("completion")}</span>
|
||||
<span className="font-mono text-[#141413]">
|
||||
{loading ? "--" : `${completion}% · ${done}/${total}`}
|
||||
</span>
|
||||
</div>
|
||||
<div className="mt-2 h-2 border border-[#d8d3c7] bg-[#faf9f3]">
|
||||
<div
|
||||
className="h-full bg-[#17602a]"
|
||||
style={{ width: loading ? "0%" : `${Math.min(100, completion)}%` }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-4 grid gap-2 sm:grid-cols-2 xl:grid-cols-5">
|
||||
{statusMetrics.map((metric) => (
|
||||
<div key={metric.key} className={cn("border px-3 py-2", metric.tone)}>
|
||||
<div className="text-[11px] font-semibold">{metric.label}</div>
|
||||
<div className="mt-1 font-mono text-lg font-semibold text-[#141413]">
|
||||
{loading ? "--" : metric.value}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="mt-4 grid gap-2 xl:grid-cols-4">
|
||||
{visibleItems.map((item) => {
|
||||
const itemPriority = String(item.priority ?? "--");
|
||||
const itemStatus = String(item.status ?? "pending");
|
||||
return (
|
||||
<div
|
||||
key={item.id ?? item.order}
|
||||
className="min-w-0 border border-[#e0ddd4] bg-[#faf9f3] p-3"
|
||||
>
|
||||
<div className="flex flex-wrap items-center gap-1.5">
|
||||
<span
|
||||
className={cn(
|
||||
"border px-2 py-0.5 font-mono text-[11px] font-semibold",
|
||||
commanderPriorityTone(itemPriority)
|
||||
)}
|
||||
>
|
||||
{itemPriority}
|
||||
</span>
|
||||
<span
|
||||
className={cn(
|
||||
"border px-2 py-0.5 text-[11px] font-semibold",
|
||||
commanderStatusTone(itemStatus)
|
||||
)}
|
||||
>
|
||||
{t(`statuses.${itemStatus}` as never)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="mt-2 truncate font-mono text-xs font-semibold text-[#141413]">
|
||||
{item.id}
|
||||
</div>
|
||||
<div className="mt-1 line-clamp-2 text-xs leading-5 text-[#5f5b52]">
|
||||
{item.normalized_work_item ?? item.request}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-[#faf9f3] p-4">
|
||||
<div className="grid gap-3">
|
||||
<div className="border border-[#d8d3c7] bg-white p-3">
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<div className="text-xs font-semibold text-[#77736a]">
|
||||
{t("currentP0")}
|
||||
</div>
|
||||
<span className="font-mono text-xs font-semibold text-[#141413]">
|
||||
{loading ? "--" : currentP0}
|
||||
</span>
|
||||
</div>
|
||||
<div className="mt-2 font-mono text-xl font-semibold text-[#141413]">
|
||||
{loading ? "--" : `${readiness}%`}
|
||||
</div>
|
||||
<div className="mt-1 line-clamp-2 text-xs leading-5 text-[#5f5b52]">
|
||||
{currentState}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="border border-[#d8d3c7] bg-white p-3">
|
||||
<div className="text-xs font-semibold text-[#77736a]">
|
||||
{t("primaryBlocker")}
|
||||
</div>
|
||||
<div className="mt-2 line-clamp-3 break-words font-mono text-xs leading-5 text-[#141413]">
|
||||
{loading ? "--" : primaryBlocker}
|
||||
</div>
|
||||
<div className="mt-2 text-xs leading-5 text-[#5f5b52]">
|
||||
{t("blockerCount", { count: activeBlockers.length })}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="border border-[#c9d8ea] bg-[#eef5ff] p-3">
|
||||
<div className="text-xs font-semibold text-[#1f5b9b]">
|
||||
{t("nextAction")}
|
||||
</div>
|
||||
<div className="mt-2 line-clamp-4 break-words text-xs font-semibold leading-5 text-[#141413]">
|
||||
{loading ? "--" : nextAction}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
function CommanderInsertedRequirementsPanel({
|
||||
priority,
|
||||
loading,
|
||||
@@ -11775,6 +11986,11 @@ export default function AwoooPWorkItemsPage() {
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<MainlineWorkProgressStrip
|
||||
priority={commanderInsertedRequirementWorkOrder}
|
||||
loading={priorityWorkOrderLoading && !priorityWorkOrder}
|
||||
/>
|
||||
|
||||
<SecurityMainlineCockpit
|
||||
priority={commanderInsertedRequirementWorkOrder}
|
||||
locale={locale}
|
||||
|
||||
Reference in New Issue
Block a user