feat(market-intel): add match review plan preview
All checks were successful
CD Pipeline / deploy (push) Successful in 1m2s
All checks were successful
CD Pipeline / deploy (push) Successful in 1m2s
This commit is contained in:
@@ -504,6 +504,24 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="market-intel-panel" data-market-intel-match-review>
|
||||
<div class="market-intel-preview-head">
|
||||
<div>
|
||||
<p class="market-intel-muted momo-mono mb-1">MATCH REVIEW / PLAN</p>
|
||||
<h2 class="market-intel-preview-title">商品比對審核計畫</h2>
|
||||
</div>
|
||||
<button class="market-intel-icon-button" type="button" title="重新整理商品比對審核計畫" data-market-intel-match-review-refresh>
|
||||
<i class="fas fa-rotate-right" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="market-intel-preview-meta" data-market-intel-match-review-meta>
|
||||
<span class="market-intel-pill">loading</span>
|
||||
</div>
|
||||
<div data-market-intel-match-review-body>
|
||||
<div class="market-intel-empty">讀取商品比對審核計畫中...</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="market-intel-panel" data-market-intel-migration>
|
||||
<div class="market-intel-preview-head">
|
||||
<div>
|
||||
@@ -574,10 +592,11 @@
|
||||
const mcpActivationRoot = document.querySelector('[data-market-intel-mcp-activation]');
|
||||
const mcpFetchGateRoot = document.querySelector('[data-market-intel-mcp-fetch-gate]');
|
||||
const schedulerRoot = document.querySelector('[data-market-intel-scheduler]');
|
||||
const matchReviewRoot = document.querySelector('[data-market-intel-match-review]');
|
||||
const migrationRoot = document.querySelector('[data-market-intel-migration]');
|
||||
const approvalRoot = document.querySelector('[data-market-intel-approval]');
|
||||
const deployRoot = document.querySelector('[data-market-intel-deploy]');
|
||||
if (!root && !writerRoot && !cliRoot && !dbProbeRoot && !seedDiffRoot && !legacyBridgeRoot && !mcpReadinessRoot && !mcpPreflightRoot && !mcpActivationRoot && !mcpFetchGateRoot && !schedulerRoot && !migrationRoot && !approvalRoot && !deployRoot) return;
|
||||
if (!root && !writerRoot && !cliRoot && !dbProbeRoot && !seedDiffRoot && !legacyBridgeRoot && !mcpReadinessRoot && !mcpPreflightRoot && !mcpActivationRoot && !mcpFetchGateRoot && !schedulerRoot && !matchReviewRoot && !migrationRoot && !approvalRoot && !deployRoot) return;
|
||||
|
||||
const meta = root ? root.querySelector('[data-market-intel-preview-meta]') : null;
|
||||
const body = root ? root.querySelector('[data-market-intel-preview-body]') : null;
|
||||
@@ -623,6 +642,10 @@
|
||||
const schedulerBody = schedulerRoot ? schedulerRoot.querySelector('[data-market-intel-scheduler-body]') : null;
|
||||
const schedulerRefresh = schedulerRoot ? schedulerRoot.querySelector('[data-market-intel-scheduler-refresh]') : null;
|
||||
const schedulerEndpoint = "{{ url_for('market_intel.market_intel_scheduler_plan') }}";
|
||||
const matchReviewMeta = matchReviewRoot ? matchReviewRoot.querySelector('[data-market-intel-match-review-meta]') : null;
|
||||
const matchReviewBody = matchReviewRoot ? matchReviewRoot.querySelector('[data-market-intel-match-review-body]') : null;
|
||||
const matchReviewRefresh = matchReviewRoot ? matchReviewRoot.querySelector('[data-market-intel-match-review-refresh]') : null;
|
||||
const matchReviewEndpoint = "{{ url_for('market_intel.market_intel_match_review_plan') }}";
|
||||
const migrationMeta = migrationRoot ? migrationRoot.querySelector('[data-market-intel-migration-meta]') : null;
|
||||
const migrationBody = migrationRoot ? migrationRoot.querySelector('[data-market-intel-migration-body]') : null;
|
||||
const migrationRefresh = migrationRoot ? migrationRoot.querySelector('[data-market-intel-migration-refresh]') : null;
|
||||
@@ -1423,6 +1446,109 @@
|
||||
}
|
||||
};
|
||||
|
||||
const renderMatchReviewMeta = data => {
|
||||
const thresholds = data.thresholds || {};
|
||||
matchReviewMeta.innerHTML = [
|
||||
`mode=${data.mode || 'unknown'}`,
|
||||
`ready=${data.ready_for_review_queue ? 'yes' : 'no'}`,
|
||||
`signals=${(data.scoring_signals || []).length}`,
|
||||
`auto_confirm=${thresholds.auto_confirm_enabled ? 'on' : 'off'}`,
|
||||
`writes=${data.writes_executed ? 'executed' : 'blocked'}`
|
||||
].map(item => `<span class="market-intel-pill">${escapeHtml(item)}</span>`).join('');
|
||||
};
|
||||
|
||||
const renderMatchReviewBody = data => {
|
||||
const blockers = (data.blocked_reasons || []).join(' / ');
|
||||
const checks = Object.entries(data.gate_checks || {});
|
||||
const signals = data.scoring_signals || [];
|
||||
const actions = data.review_actions || [];
|
||||
const sequence = data.operator_sequence || [];
|
||||
const renderCheck = ([name, passed]) => `
|
||||
<div class="market-intel-check">
|
||||
<div>
|
||||
<strong>${escapeHtml(name)}</strong>
|
||||
</div>
|
||||
<span>${passed ? 'PASS' : 'BLOCK'}</span>
|
||||
</div>
|
||||
`;
|
||||
const renderSignal = item => `
|
||||
<article class="market-intel-operation">
|
||||
<strong>${escapeHtml(item.key)}</strong>
|
||||
<small>${escapeHtml(item.label)}</small>
|
||||
<small>weight=${escapeHtml(item.weight)} / ${escapeHtml(item.source)}</small>
|
||||
</article>
|
||||
`;
|
||||
const renderAction = item => `
|
||||
<div class="market-intel-check">
|
||||
<div>
|
||||
<strong>${escapeHtml(item.key)}</strong>
|
||||
<small>${escapeHtml(item.label)}</small>
|
||||
</div>
|
||||
<span>${item.requires_operator ? 'HITL' : 'AUTO'}</span>
|
||||
</div>
|
||||
`;
|
||||
matchReviewBody.innerHTML = `
|
||||
<div class="market-intel-empty mb-3">商品比對目前只建立審核規則與 HITL 流程;不建立 queue、不自動確認、不寫 DB、不呼叫外部 MCP。${blockers ? `阻擋:${escapeHtml(blockers)}` : ''}</div>
|
||||
<div class="market-intel-deploy-grid">
|
||||
<div data-market-intel-match-review-checks>
|
||||
<p class="market-intel-deploy-section-title">MATCH GATES</p>
|
||||
<div class="market-intel-check-list">${
|
||||
checks.length
|
||||
? checks.map(renderCheck).join('')
|
||||
: '<div class="market-intel-empty">尚未提供比對 gate。</div>'
|
||||
}</div>
|
||||
</div>
|
||||
<div data-market-intel-match-review-signals>
|
||||
<p class="market-intel-deploy-section-title">SCORING SIGNALS</p>
|
||||
<div class="market-intel-operation-list">${
|
||||
signals.length
|
||||
? signals.map(renderSignal).join('')
|
||||
: '<div class="market-intel-empty">尚未提供比對訊號。</div>'
|
||||
}</div>
|
||||
</div>
|
||||
<div data-market-intel-match-review-actions>
|
||||
<p class="market-intel-deploy-section-title">REVIEW ACTIONS</p>
|
||||
<div class="market-intel-check-list">${
|
||||
actions.length
|
||||
? actions.map(renderAction).join('')
|
||||
: '<div class="market-intel-empty">尚未提供審核動作。</div>'
|
||||
}</div>
|
||||
</div>
|
||||
<div data-market-intel-match-review-sequence>
|
||||
<p class="market-intel-deploy-section-title">OPERATOR SEQUENCE</p>
|
||||
<div class="market-intel-check-list">${
|
||||
sequence.length
|
||||
? sequence.map((item, index) => `
|
||||
<div class="market-intel-check">
|
||||
<div>
|
||||
<strong>${escapeHtml(`step_${index + 1}`)}</strong>
|
||||
<small>${escapeHtml(item)}</small>
|
||||
</div>
|
||||
<span>REQUIRED</span>
|
||||
</div>
|
||||
`).join('')
|
||||
: '<div class="market-intel-empty">尚未提供操作順序。</div>'
|
||||
}</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
};
|
||||
|
||||
const loadMatchReview = async () => {
|
||||
if (!matchReviewMeta || !matchReviewBody) return;
|
||||
matchReviewBody.innerHTML = '<div class="market-intel-empty">讀取商品比對審核計畫中...</div>';
|
||||
try {
|
||||
const response = await fetch(matchReviewEndpoint, { credentials: 'same-origin' });
|
||||
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
||||
const data = await response.json();
|
||||
renderMatchReviewMeta(data);
|
||||
renderMatchReviewBody(data);
|
||||
} catch (error) {
|
||||
matchReviewMeta.innerHTML = '<span class="market-intel-pill">error</span>';
|
||||
matchReviewBody.innerHTML = `<div class="market-intel-empty">商品比對審核計畫讀取失敗:${escapeHtml(error.message)}</div>`;
|
||||
}
|
||||
};
|
||||
|
||||
const renderMigrationMeta = data => {
|
||||
const seedWriter = data.command_plan && data.command_plan.seed_writer_command
|
||||
? data.command_plan.seed_writer_command
|
||||
@@ -1690,6 +1816,9 @@
|
||||
if (schedulerRefresh) {
|
||||
schedulerRefresh.addEventListener('click', loadScheduler);
|
||||
}
|
||||
if (matchReviewRefresh) {
|
||||
matchReviewRefresh.addEventListener('click', loadMatchReview);
|
||||
}
|
||||
if (migrationRefresh) {
|
||||
migrationRefresh.addEventListener('click', loadMigration);
|
||||
}
|
||||
@@ -1710,6 +1839,7 @@
|
||||
loadMcpActivation();
|
||||
loadMcpFetchGate();
|
||||
loadScheduler();
|
||||
loadMatchReview();
|
||||
loadMigration();
|
||||
loadApproval();
|
||||
loadDeploy();
|
||||
|
||||
Reference in New Issue
Block a user