feat(market-intel): 新增既有資料橋接預覽
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:
@@ -396,6 +396,24 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="market-intel-panel" data-market-intel-legacy-bridge>
|
||||
<div class="market-intel-preview-head">
|
||||
<div>
|
||||
<p class="market-intel-muted momo-mono mb-1">LEGACY SOURCE / BRIDGE PREVIEW</p>
|
||||
<h2 class="market-intel-preview-title">既有資料橋接預覽</h2>
|
||||
</div>
|
||||
<button class="market-intel-icon-button" type="button" title="重新整理橋接預覽" data-market-intel-legacy-bridge-refresh>
|
||||
<i class="fas fa-rotate-right" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="market-intel-preview-meta" data-market-intel-legacy-bridge-meta>
|
||||
<span class="market-intel-pill">loading</span>
|
||||
</div>
|
||||
<div data-market-intel-legacy-bridge-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>
|
||||
@@ -460,10 +478,11 @@
|
||||
const cliRoot = document.querySelector('[data-market-intel-cli]');
|
||||
const dbProbeRoot = document.querySelector('[data-market-intel-db-probe]');
|
||||
const seedDiffRoot = document.querySelector('[data-market-intel-seed-diff]');
|
||||
const legacyBridgeRoot = document.querySelector('[data-market-intel-legacy-bridge]');
|
||||
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 && !migrationRoot && !approvalRoot && !deployRoot) return;
|
||||
if (!root && !writerRoot && !cliRoot && !dbProbeRoot && !seedDiffRoot && !legacyBridgeRoot && !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;
|
||||
@@ -485,6 +504,10 @@
|
||||
const seedDiffBody = seedDiffRoot ? seedDiffRoot.querySelector('[data-market-intel-seed-diff-body]') : null;
|
||||
const seedDiffRefresh = seedDiffRoot ? seedDiffRoot.querySelector('[data-market-intel-seed-diff-refresh]') : null;
|
||||
const seedDiffEndpoint = "{{ url_for('market_intel.market_intel_platform_seed_db_diff') }}?execute=false";
|
||||
const legacyBridgeMeta = legacyBridgeRoot ? legacyBridgeRoot.querySelector('[data-market-intel-legacy-bridge-meta]') : null;
|
||||
const legacyBridgeBody = legacyBridgeRoot ? legacyBridgeRoot.querySelector('[data-market-intel-legacy-bridge-body]') : null;
|
||||
const legacyBridgeRefresh = legacyBridgeRoot ? legacyBridgeRoot.querySelector('[data-market-intel-legacy-bridge-refresh]') : null;
|
||||
const legacyBridgeEndpoint = "{{ url_for('market_intel.market_intel_legacy_source_bridge') }}?execute=false&limit=5";
|
||||
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;
|
||||
@@ -730,6 +753,95 @@
|
||||
}
|
||||
};
|
||||
|
||||
const renderLegacyBridgeMeta = data => {
|
||||
legacyBridgeMeta.innerHTML = [
|
||||
`mode=${data.mode || 'unknown'}`,
|
||||
`execute=${data.execute_requested ? 'true' : 'false'}`,
|
||||
`query=${data.read_only_query_executed ? 'yes' : 'no'}`,
|
||||
`sources=${data.existing_source_count || 0}/${data.source_count || 0}`,
|
||||
`rows=${data.total_existing_rows || 0}`,
|
||||
`writes=${data.writes_executed ? 'executed' : 'blocked'}`
|
||||
].map(item => `<span class="market-intel-pill">${escapeHtml(item)}</span>`).join('');
|
||||
};
|
||||
|
||||
const renderLegacyBridgeBody = data => {
|
||||
const blockers = (data.blocked_reasons || []).join(' / ');
|
||||
const sources = data.source_summaries || [];
|
||||
const operations = data.bridge_operations || [];
|
||||
const controls = data.duplicate_controls || [];
|
||||
const renderSource = item => `
|
||||
<div class="market-intel-check">
|
||||
<div>
|
||||
<strong>${escapeHtml(item.table)}</strong>
|
||||
<small>${escapeHtml(item.description || '')}</small>
|
||||
<small>target=${escapeHtml((item.planned_targets || []).join(' / '))}</small>
|
||||
</div>
|
||||
<span>${item.exists ? escapeHtml(item.row_count || 0) : 'PENDING'}</span>
|
||||
</div>
|
||||
`;
|
||||
const renderOperation = item => `
|
||||
<article class="market-intel-operation">
|
||||
<strong>${escapeHtml(item.source_table)} → ${escapeHtml(item.target_table)}</strong>
|
||||
<small>${escapeHtml(item.operation)} / ${escapeHtml(item.write_status)}</small>
|
||||
<small>dedupe=${escapeHtml(item.dedupe_key)}</small>
|
||||
</article>
|
||||
`;
|
||||
const renderControl = item => `
|
||||
<div class="market-intel-check">
|
||||
<div>
|
||||
<strong>${escapeHtml(item.key)}</strong>
|
||||
<small>${escapeHtml(item.rule)}</small>
|
||||
</div>
|
||||
<span>RULE</span>
|
||||
</div>
|
||||
`;
|
||||
|
||||
legacyBridgeBody.innerHTML = `
|
||||
<div class="market-intel-empty mb-3">這裡只盤點既有資料來源與導入規則,不搬資料、不寫 market_*。${blockers ? `阻擋:${escapeHtml(blockers)}` : ''}</div>
|
||||
<div class="market-intel-deploy-grid">
|
||||
<div data-market-intel-legacy-sources>
|
||||
<p class="market-intel-deploy-section-title">SOURCE TABLES</p>
|
||||
<div class="market-intel-check-list">${
|
||||
sources.length
|
||||
? sources.map(renderSource).join('')
|
||||
: '<div class="market-intel-empty">尚未提供來源摘要。</div>'
|
||||
}</div>
|
||||
</div>
|
||||
<div data-market-intel-legacy-operations>
|
||||
<p class="market-intel-deploy-section-title">BRIDGE OPERATIONS</p>
|
||||
<div class="market-intel-operation-list">${
|
||||
operations.length
|
||||
? operations.map(renderOperation).join('')
|
||||
: '<div class="market-intel-empty">尚未提供橋接操作。</div>'
|
||||
}</div>
|
||||
</div>
|
||||
<div data-market-intel-legacy-controls>
|
||||
<p class="market-intel-deploy-section-title">DUPLICATE CONTROLS</p>
|
||||
<div class="market-intel-check-list">${
|
||||
controls.length
|
||||
? controls.map(renderControl).join('')
|
||||
: '<div class="market-intel-empty">尚未提供去重規則。</div>'
|
||||
}</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
};
|
||||
|
||||
const loadLegacyBridge = async () => {
|
||||
if (!legacyBridgeMeta || !legacyBridgeBody) return;
|
||||
legacyBridgeBody.innerHTML = '<div class="market-intel-empty">讀取既有資料橋接預覽中...</div>';
|
||||
try {
|
||||
const response = await fetch(legacyBridgeEndpoint, { credentials: 'same-origin' });
|
||||
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
||||
const data = await response.json();
|
||||
renderLegacyBridgeMeta(data);
|
||||
renderLegacyBridgeBody(data);
|
||||
} catch (error) {
|
||||
legacyBridgeMeta.innerHTML = '<span class="market-intel-pill">error</span>';
|
||||
legacyBridgeBody.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
|
||||
@@ -979,6 +1091,9 @@
|
||||
if (seedDiffRefresh) {
|
||||
seedDiffRefresh.addEventListener('click', loadSeedDiff);
|
||||
}
|
||||
if (legacyBridgeRefresh) {
|
||||
legacyBridgeRefresh.addEventListener('click', loadLegacyBridge);
|
||||
}
|
||||
if (migrationRefresh) {
|
||||
migrationRefresh.addEventListener('click', loadMigration);
|
||||
}
|
||||
@@ -993,6 +1108,7 @@
|
||||
loadCli();
|
||||
loadDbProbe();
|
||||
loadSeedDiff();
|
||||
loadLegacyBridge();
|
||||
loadMigration();
|
||||
loadApproval();
|
||||
loadDeploy();
|
||||
|
||||
Reference in New Issue
Block a user