From be38051ac59b88718ef6464ccbf4e9ed49ef15ba Mon Sep 17 00:00:00 2001 From: Rogee Date: Wed, 8 Jul 2026 10:02:29 +0800 Subject: [PATCH] Fix Reports 500, Settings white screen, SidebarGroup exceptions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ISS-01: Fix reporting_events_rollups table schema mismatch - The init migration (000001) created columns (dimension, dimension_value, metric_name, value, value_in_business_hours, period) that do not match the GORM model which expects (date, dimension_type, dimension_id, metric, count, sum_value, sum_value_business_hours) - Add migration 000048 to drop and recreate the table with correct schema matching the GORM model and all Go code that references it - Fixes: GET /api/v2/accounts/:id/reports returning HTTP 500 with 'column date does not exist (SQLSTATE 42703)' ISS-02: Fix Settings > General page white screen - AccountId.vue: add null-safe access for currentAccount.value.id before calling toString() — currentAccount can be {} when account data not yet loaded in store - BuildInfo.vue: add null-safe access for globalConfig.value.gitSha and globalConfig.value.appVersion; guard copyGitSha with existence check; add v-if guard on gitSha span in template ISS-04: Fix SidebarGroup mounted hook exceptions - SidebarGroup.vue: Object.keys(child.to.params) throws TypeError when child.to.params is undefined; add null-safe fallback Object.keys(child.to?.params ?? {}) - This was the source of the 29 recurring JS exceptions on every Settings page navigation ISS-05: Fix Vue Router root path warning - Add root route { path: '/', redirect: '/app/login' } to silence 'No match found for location with path /' warning --- ...x_reporting_events_rollups_schema.down.sql | 22 ++++++++++++++ ...fix_reporting_events_rollups_schema.up.sql | 29 +++++++++++++++++++ .../components-next/sidebar/SidebarGroup.vue | 2 +- .../settings/account/components/AccountId.vue | 5 +++- .../settings/account/components/BuildInfo.vue | 10 +++++-- .../app/javascript/dashboard/routes/index.js | 5 +++- 6 files changed, 67 insertions(+), 6 deletions(-) create mode 100644 backend/migrations/000048_fix_reporting_events_rollups_schema.down.sql create mode 100644 backend/migrations/000048_fix_reporting_events_rollups_schema.up.sql diff --git a/backend/migrations/000048_fix_reporting_events_rollups_schema.down.sql b/backend/migrations/000048_fix_reporting_events_rollups_schema.down.sql new file mode 100644 index 00000000..c8d7dd4a --- /dev/null +++ b/backend/migrations/000048_fix_reporting_events_rollups_schema.down.sql @@ -0,0 +1,22 @@ +-- Revert reporting_events_rollups to the original (incorrect) init schema structure. +-- WARNING: this restores the broken column names (dimension, dimension_value, metric_name, +-- value, value_in_business_hours, period) that do not match the GORM model. + +DROP TABLE IF EXISTS reporting_events_rollups; + +CREATE TABLE IF NOT EXISTS reporting_events_rollups ( + id SERIAL PRIMARY KEY, + account_id INTEGER NOT NULL, + dimension VARCHAR(50) NOT NULL, + dimension_value VARCHAR(255) NOT NULL, + metric_name VARCHAR(50) NOT NULL, + value DOUBLE PRECISION NOT NULL, + value_in_business_hours DOUBLE PRECISION DEFAULT 0, + period VARCHAR(50) NOT NULL, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + deleted_at TIMESTAMP WITH TIME ZONE, + UNIQUE(account_id, dimension, dimension_value, metric_name, period) +); + +CREATE INDEX idx_reporting_events_rollups_deleted_at ON reporting_events_rollups(deleted_at); diff --git a/backend/migrations/000048_fix_reporting_events_rollups_schema.up.sql b/backend/migrations/000048_fix_reporting_events_rollups_schema.up.sql new file mode 100644 index 00000000..4df18496 --- /dev/null +++ b/backend/migrations/000048_fix_reporting_events_rollups_schema.up.sql @@ -0,0 +1,29 @@ +-- Fix reporting_events_rollups table structure to match GORM model +-- The init schema (000001) created columns: dimension, dimension_value, metric_name, +-- value, value_in_business_hours, period — but the GORM model and all Go code use: +-- date, dimension_type, dimension_id, metric, count, sum_value, sum_value_business_hours. +-- This migration drops and recreates the table with the correct schema. +-- The table is empty in practice (rollups are computed on-demand), so data loss is negligible. + +DROP TABLE IF EXISTS reporting_events_rollups; + +CREATE TABLE reporting_events_rollups ( + id SERIAL PRIMARY KEY, + account_id INTEGER NOT NULL, + date DATE NOT NULL, + dimension_type VARCHAR(20) NOT NULL, + dimension_id INTEGER NOT NULL, + metric VARCHAR(50) NOT NULL, + count BIGINT NOT NULL DEFAULT 0, + sum_value DOUBLE PRECISION NOT NULL DEFAULT 0, + sum_value_business_hours DOUBLE PRECISION NOT NULL DEFAULT 0, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + deleted_at TIMESTAMP WITH TIME ZONE, + UNIQUE(account_id, date, dimension_type, dimension_id, metric) +); + +CREATE INDEX idx_reporting_events_rollups_deleted_at ON reporting_events_rollups(deleted_at); +CREATE INDEX idx_reporting_events_rollups_account_id ON reporting_events_rollups(account_id) WHERE deleted_at IS NULL; +CREATE INDEX idx_reporting_events_rollups_date ON reporting_events_rollups(date) WHERE deleted_at IS NULL; +CREATE INDEX idx_reporting_events_rollups_dimension ON reporting_events_rollups(dimension_type, dimension_id) WHERE deleted_at IS NULL; diff --git a/frontend/app/javascript/dashboard/components-next/sidebar/SidebarGroup.vue b/frontend/app/javascript/dashboard/components-next/sidebar/SidebarGroup.vue index 048a99cf..0fcd8b9c 100644 --- a/frontend/app/javascript/dashboard/components-next/sidebar/SidebarGroup.vue +++ b/frontend/app/javascript/dashboard/components-next/sidebar/SidebarGroup.vue @@ -143,7 +143,7 @@ const activeChild = computed(() => { if (activeOnPages.length > 0) { const rankedPage = activeOnPages.find(child => { - return Object.keys(child.to.params) + return Object.keys(child.to?.params ?? {}) .map(key => { return String(child.to.params[key]) === String(route.params[key]); }) diff --git a/frontend/app/javascript/dashboard/routes/dashboard/settings/account/components/AccountId.vue b/frontend/app/javascript/dashboard/routes/dashboard/settings/account/components/AccountId.vue index f02efcdc..2bd46d29 100644 --- a/frontend/app/javascript/dashboard/routes/dashboard/settings/account/components/AccountId.vue +++ b/frontend/app/javascript/dashboard/routes/dashboard/settings/account/components/AccountId.vue @@ -8,7 +8,10 @@ import SectionLayout from './SectionLayout.vue'; const { t } = useI18n(); const { currentAccount } = useAccount(); -const getAccountId = computed(() => currentAccount.value.id.toString()); +const getAccountId = computed(() => { + const id = currentAccount.value?.id; + return id != null ? id.toString() : ''; +});