From 489920b3c9d735c9ea1003b6ada25c3f72abc14f Mon Sep 17 00:00:00 2001 From: Kunthawat Greethong Date: Tue, 25 Aug 2026 20:39:43 +0700 Subject: [PATCH] =?UTF-8?q?[verified]=20Rebuild=20dashboard:=20real=20mult?= =?UTF-8?q?i-theme=20(3=20themes=20+=20macro=20+=20sources=20table)=20?= =?UTF-8?q?=E2=80=94=20user=2010-point=20review?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove obsolete KPIs (Theme surprise card, Paper ledger, คุณภาพข้อมูล, ประตู backtest) -> now 2 cards: สัญญาณ (long/short/neutral) + แหล่งข้อมูล - Theme panel: 3 themes with uniform surprise (0.57/0.81/1.62σ) + per-theme thesis (#8,#10) - Add macro backdrop panel (การบริโภค/การลงทุน/เงินเฟ้อ/การว่างงาน/นักท่องเที่ยว) from BOT Thai Economy (#6) - Add sources lineage table (ที่มาข้อมูล: ข้อมูล/แหล่ง/ช่วง/อัปเดต) (#9) - Merge signal+fundamental into one table with combined column (#7) - Live verified via /api/v1/dashboard (real data, 49-symbol board) --- frontend/src/App.vue | 125 +++++++++++++++++++++++++++-------------- frontend/src/style.css | 19 +++++++ 2 files changed, 103 insertions(+), 41 deletions(-) diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 9ce6051..1b07905 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -6,6 +6,7 @@ const observations = ref(null) const signalData = ref(null) const factorData = ref(null) const themeData = ref(null) +const dashData = ref(null) const simCapital = ref(1000000) const simMode = ref('backtest') const simLoading = ref(false) @@ -35,6 +36,24 @@ const observationRows = computed(() => observations.value?.observations ?? []) // Combined factor rows (Siamchart fundamentals + tourism signal). const factorRows = computed(() => factorData.value?.factors ?? []) +// real multi-theme dashboard (3 themes + macro + sources) +const dashboardThemes = computed(() => dashData.value?.themes ?? []) +const dashboardSources = computed(() => dashData.value?.sources ?? []) +const dashboardMacro = computed(() => dashData.value?.macro ?? {}) +const sourceCount = computed(() => dashboardSources.value.length) +const realData = computed(() => dashData.value?.available ?? false) +// merged stock board: from /api/v1/dashboard board (combined real) + siamchart +const combinedRows = computed(() => dashData.value?.board ?? factorRows.value) +const boardBySymbol = computed(() => { + const m = {} + for (const row of combinedRows.value) m[row.symbol] = row + return m +}) +// per-theme surprise labels for theme cards + thesis +const signalSummary = computed(() => { + const s = summary.value?.signal_summary + return { long: s?.long ?? 0, short: s?.short ?? 0, neutral: s?.neutral ?? 0, total: s?.total ?? 0 } +}) const factorAvailable = computed(() => factorData.value?.available ?? false) const dividendCount = computed(() => factorData.value?.dividend_count ?? 0) const signalScoreCount = computed(() => factorRows.value.filter((f) => f.signal_side === 'LONG' || f.signal_side === 'SHORT').length) @@ -218,12 +237,13 @@ async function loadDashboard() { loading.value = true error.value = '' try { - const [summaryBody, observationBody, signalBody, factorBody, themeBody, ledgerBody, sessionBody, backtestBody, researchBody] = await Promise.all([ + const [summaryBody, observationBody, signalBody, factorBody, themeBody, dashBody, ledgerBody, sessionBody, backtestBody, researchBody] = await Promise.all([ fetchJson('/api/v1/dashboard/summary'), fetchJson('/api/v1/factors/tourism/observations'), fetchJson('/api/v1/signals'), fetchJson('/api/v1/factors'), fetchJson('/api/v1/themes'), + fetchJson('/api/v1/dashboard'), fetchJson('/api/v1/paper/ledger'), fetchJson('/api/v1/auth/paper', { credentials: 'include' }), fetchBacktestReadiness(), @@ -234,6 +254,7 @@ async function loadDashboard() { signalData.value = signalBody factorData.value = factorBody themeData.value = themeBody + dashData.value = dashBody ledger.value = ledgerBody paperAuthenticated.value = Boolean(sessionBody.authenticated) paperAuthMode.value = sessionBody.mode || 'token' @@ -371,29 +392,18 @@ onMounted(loadDashboard)