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() : ''; +});