diff --git a/apps/web/messages/en.json b/apps/web/messages/en.json index 275d67420..7fd586949 100644 --- a/apps/web/messages/en.json +++ b/apps/web/messages/en.json @@ -9173,6 +9173,9 @@ "nextAction": "Next action", "acceptance": "Acceptance", "rowNextAction": "Next action", + "spotlight": "Mainline spotlight", + "spotlightSubtitle": "Surfaces the current P0, OpenClaw Live Ops, and non-text-wall UX directly instead of burying them in the long ledger.", + "fullLedger": "Full ledger: {count} items", "loading": "Loading inserted requirement work items", "empty": "No inserted requirement work items read back yet.", "order": "order={order}", diff --git a/apps/web/messages/zh-TW.json b/apps/web/messages/zh-TW.json index 3c1de8b08..18907b925 100644 --- a/apps/web/messages/zh-TW.json +++ b/apps/web/messages/zh-TW.json @@ -9173,6 +9173,9 @@ "nextAction": "下一步", "acceptance": "驗收條件", "rowNextAction": "下一步", + "spotlight": "主線焦點", + "spotlightSubtitle": "直接露出目前 P0、OpenClaw Live Ops 與非文字牆 UX,避免工作項藏在長清單裡。", + "fullLedger": "完整總帳 {count} 項", "loading": "讀取插入需求工作項", "empty": "尚未讀回插入需求工作項。", "order": "order={order}", diff --git a/apps/web/src/app/[locale]/awooop/work-items/page.tsx b/apps/web/src/app/[locale]/awooop/work-items/page.tsx index cc8681a76..0f8b4e0b0 100644 --- a/apps/web/src/app/[locale]/awooop/work-items/page.tsx +++ b/apps/web/src/app/[locale]/awooop/work-items/page.tsx @@ -8519,6 +8519,12 @@ function CommanderInsertedRequirementsPanel({ blocked: t("statuses.blocked"), deferred: t("statuses.deferred"), }; + const spotlightIds = new Set([ + nextId, + "CIR-P1-OPENCLAW-001", + "CIR-P2-UX-002", + ]); + const spotlightItems = items.filter((item) => item.id && spotlightIds.has(item.id)); return (
+ {spotlightItems.length > 0 ? ( +
+
+
+
+ {t("spotlight")} +
+
+ {t("spotlightSubtitle")} +
+
+
+ {t("fullLedger", { count: total })} +
+
+
+ {spotlightItems.map((item) => { + const itemPriority = String(item.priority ?? "--"); + const itemStatus = String(item.status ?? "pending"); + return ( +
+
+ + {itemPriority} + + + {statusLabels[itemStatus] ?? itemStatus} + +
+
+ {item.id} +
+
+ {item.normalized_work_item} +
+
+ {item.lane} +
+
+ {item.next_action} +
+
+ ); + })} +
+
+ ) : null} +
{items.length > 0 ? ( items.map((item) => { @@ -8701,8 +8765,37 @@ export default function AwoooPWorkItemsPage() { priorityWorkOrder: null, }); const [loading, setLoading] = useState(true); + const [priorityWorkOrderReadback, setPriorityWorkOrderReadback] = + useState(null); + const [priorityWorkOrderLoading, setPriorityWorkOrderLoading] = useState(true); const [lastUpdated, setLastUpdated] = useState(null); + const fetchPriorityWorkOrderReadback = useCallback(async () => { + setPriorityWorkOrderLoading(true); + const urls = Array.from( + new Set( + [ + `${API_BASE}/api/v1/agents/awoooi-priority-work-order-readback`, + "/api/v1/agents/awoooi-priority-work-order-readback", + ].filter(Boolean) + ) + ); + for (let attempt = 0; attempt < 3; attempt += 1) { + for (const url of urls) { + const nextPriorityWorkOrder = + await fetchJson(url, 12000); + if (nextPriorityWorkOrder?.commander_inserted_requirement_work_items?.length) { + setPriorityWorkOrderReadback(nextPriorityWorkOrder); + setPriorityWorkOrderLoading(false); + return nextPriorityWorkOrder; + } + } + await new Promise((resolve) => window.setTimeout(resolve, 1200)); + } + setPriorityWorkOrderLoading(false); + return null; + }, []); + const fetchTelemetry = useCallback(async () => { setLoading(true); const encodedProjectId = encodeURIComponent(projectId); @@ -8754,6 +8847,8 @@ export default function AwoooPWorkItemsPage() { void priorityWorkOrderPromise.then((nextPriorityWorkOrder) => { if (!nextPriorityWorkOrder) return; + setPriorityWorkOrderReadback(nextPriorityWorkOrder); + setPriorityWorkOrderLoading(false); setTelemetry((current) => ({ ...current, priorityWorkOrder: nextPriorityWorkOrder, @@ -8854,10 +8949,16 @@ export default function AwoooPWorkItemsPage() { setLoading(false); }, [effectiveFocusedIncidentId, projectId]); + useEffect(() => { + fetchPriorityWorkOrderReadback(); + }, [fetchPriorityWorkOrderReadback]); + useEffect(() => { fetchTelemetry(); }, [fetchTelemetry]); + const priorityWorkOrder = + telemetry.priorityWorkOrder ?? priorityWorkOrderReadback; const workItems = useMemo(() => buildWorkItems(telemetry, t), [telemetry, t]); const latestRemediationHistory = telemetry.remediationHistory?.items?.[0] ?? null; const remediationIncidentIds = useMemo( @@ -8925,7 +9026,10 @@ export default function AwoooPWorkItemsPage() {