From f0c671cb33e518ed69caf7e76a5f24e0ad890002 Mon Sep 17 00:00:00 2001 From: Yury Popov Date: Mon, 11 Aug 2025 17:50:20 +0300 Subject: [PATCH 1/4] feat: add metrics infrustructure to ui --- src/types/window.d.ts | 2 ++ src/uiFactory/types.ts | 1 + src/utils/yaMetrica.ts | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 39 insertions(+) create mode 100644 src/utils/yaMetrica.ts diff --git a/src/types/window.d.ts b/src/types/window.d.ts index 0b9398ec28..2acaa38ea5 100644 --- a/src/types/window.d.ts +++ b/src/types/window.d.ts @@ -44,4 +44,6 @@ interface Window { systemSettings?: import('../services/settings').SettingsObject; api: import('../services/api/index').YdbEmbeddedAPI; + + [key: `yaCounter${number}`]: number; } diff --git a/src/uiFactory/types.ts b/src/uiFactory/types.ts index d4502a7583..82a374590d 100644 --- a/src/uiFactory/types.ts +++ b/src/uiFactory/types.ts @@ -43,6 +43,7 @@ export interface UIFactory { countHealthcheckIssuesByType: (issueTrees: IssuesTree[]) => Record; }; hasAccess?: boolean; + yaMetricaId?: number; } export type HandleCreateDB = (params: {clusterName: string}) => Promise; diff --git a/src/utils/yaMetrica.ts b/src/utils/yaMetrica.ts new file mode 100644 index 0000000000..6937d3c8ec --- /dev/null +++ b/src/utils/yaMetrica.ts @@ -0,0 +1,36 @@ +import {uiFactory} from '../uiFactory/uiFactory'; + +const yaMetricaId = uiFactory.yaMetricaId; + +class FakeMetrica { + private warnShown = false; + + hit() { + this.warnOnce(); + } + + params() { + this.warnOnce(); + } + + userParams() { + this.warnOnce(); + } + + reachGoal() { + this.warnOnce(); + } + + private warnOnce() { + if (!this.warnShown) { + console.warn('YaMetrica counter is not defined'); + this.warnShown = true; + } + } +} + +export function getMetrica() { + const metricaInstance = yaMetricaId && window[`yaCounter${yaMetricaId}`]; + + return metricaInstance ?? new FakeMetrica(); +} From 57e4ad3f0d3626cbe0e38a2306c3231ff326fb74 Mon Sep 17 00:00:00 2001 From: Yury Popov Date: Mon, 11 Aug 2025 18:35:01 +0300 Subject: [PATCH 2/4] fix types --- src/types/window.d.ts | 2 +- src/utils/yaMetrica.ts | 15 ++++++++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/types/window.d.ts b/src/types/window.d.ts index 2acaa38ea5..8bc682785c 100644 --- a/src/types/window.d.ts +++ b/src/types/window.d.ts @@ -45,5 +45,5 @@ interface Window { api: import('../services/api/index').YdbEmbeddedAPI; - [key: `yaCounter${number}`]: number; + [key: `yaCounter${number}`]: any; } diff --git a/src/utils/yaMetrica.ts b/src/utils/yaMetrica.ts index 6937d3c8ec..a029aa1775 100644 --- a/src/utils/yaMetrica.ts +++ b/src/utils/yaMetrica.ts @@ -1,8 +1,15 @@ import {uiFactory} from '../uiFactory/uiFactory'; +export interface Counter { + hit: (...args: unknown[]) => void; + params: (...args: unknown[]) => void; + userParams: (...args: unknown[]) => void; + reachGoal: (...args: unknown[]) => void; +} + const yaMetricaId = uiFactory.yaMetricaId; -class FakeMetrica { +class FakeMetrica implements Counter { private warnShown = false; hit() { @@ -23,14 +30,16 @@ class FakeMetrica { private warnOnce() { if (!this.warnShown) { - console.warn('YaMetrica counter is not defined'); + console.warn('YaMetrica counter is not defined\n'); this.warnShown = true; } } } export function getMetrica() { - const metricaInstance = yaMetricaId && window[`yaCounter${yaMetricaId}`]; + const metricaInstance = yaMetricaId + ? (window[`yaCounter${yaMetricaId}`] as Counter) + : undefined; return metricaInstance ?? new FakeMetrica(); } From e9442ed799cb59c0d16ce37fd67f99a5cc50011d Mon Sep 17 00:00:00 2001 From: Yury Popov Date: Tue, 12 Aug 2025 18:01:58 +0300 Subject: [PATCH 3/4] fix: add multiple metrics --- src/uiFactory/types.ts | 2 +- src/utils/yaMetrica.ts | 15 +++++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/uiFactory/types.ts b/src/uiFactory/types.ts index 82a374590d..6818a39e84 100644 --- a/src/uiFactory/types.ts +++ b/src/uiFactory/types.ts @@ -43,7 +43,7 @@ export interface UIFactory { countHealthcheckIssuesByType: (issueTrees: IssuesTree[]) => Record; }; hasAccess?: boolean; - yaMetricaId?: number; + yaMetricaMap?: Record; } export type HandleCreateDB = (params: {clusterName: string}) => Promise; diff --git a/src/utils/yaMetrica.ts b/src/utils/yaMetrica.ts index a029aa1775..bffd32cb34 100644 --- a/src/utils/yaMetrica.ts +++ b/src/utils/yaMetrica.ts @@ -7,11 +7,17 @@ export interface Counter { reachGoal: (...args: unknown[]) => void; } -const yaMetricaId = uiFactory.yaMetricaId; +const yaMetricaMap = uiFactory.yaMetricaMap; class FakeMetrica implements Counter { + name: string; + private warnShown = false; + constructor(name: string) { + this.name = name; + } + hit() { this.warnOnce(); } @@ -30,16 +36,17 @@ class FakeMetrica implements Counter { private warnOnce() { if (!this.warnShown) { - console.warn('YaMetrica counter is not defined\n'); + console.warn(`YaMetrica counter "${this.name}" is not defined\n`); this.warnShown = true; } } } -export function getMetrica() { +export function getMetrica(name: string) { + const yaMetricaId = yaMetricaMap?.[name]; const metricaInstance = yaMetricaId ? (window[`yaCounter${yaMetricaId}`] as Counter) : undefined; - return metricaInstance ?? new FakeMetrica(); + return metricaInstance ?? new FakeMetrica(name); } From eece75f3eea62354f1cfc27e6d2b541c36b952e6 Mon Sep 17 00:00:00 2001 From: Yury Popov Date: Wed, 13 Aug 2025 17:09:49 +0300 Subject: [PATCH 4/4] fix: add docs --- src/utils/yaMetrica.ts | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/utils/yaMetrica.ts b/src/utils/yaMetrica.ts index bffd32cb34..b6465eee4b 100644 --- a/src/utils/yaMetrica.ts +++ b/src/utils/yaMetrica.ts @@ -1,5 +1,13 @@ import {uiFactory} from '../uiFactory/uiFactory'; +/** + * Interface for a counter that provides methods for tracking metrics. + * + * @method hit - Tracks a hit event with optional arguments. https://yandex.ru/support/metrica/ru/objects/hit + * @method params - Sets parameters for the counter with optional arguments. https://yandex.ru/support/metrica/ru/objects/params-method + * @method userParams - Sets user-specific parameters for the counter with optional arguments. https://yandex.ru/support/metrica/ru/objects/user-params + * @method reachGoal - Tracks a goal achievement event with optional arguments. https://yandex.ru/support/metrica/ru/objects/reachgoal + */ export interface Counter { hit: (...args: unknown[]) => void; params: (...args: unknown[]) => void; @@ -9,6 +17,14 @@ export interface Counter { const yaMetricaMap = uiFactory.yaMetricaMap; +/** + * A fake implementation of a counter metric for Yandex.Metrica. + * This class is used when the actual Yandex.Metrica counter is not defined, + * and it provides a warning message the first time any of its methods are called. + * + * @property name - The name of the counter. + * @property warnShown - Flag to indicate if the warning has been shown. + */ class FakeMetrica implements Counter { name: string; @@ -36,12 +52,19 @@ class FakeMetrica implements Counter { private warnOnce() { if (!this.warnShown) { - console.warn(`YaMetrica counter "${this.name}" is not defined\n`); + console.warn(`Yandex.Metrica counter "${this.name}" is not defined\n`); this.warnShown = true; } } } +/** + * Retrieves a Yandex Metrica instance by name from the global window object. + * If no instance is found for the given name, returns a FakeMetrica instance instead. + * + * @param name The name of the metrica to retrieve + * @returns The Yandex Metrica instance if found, otherwise a FakeMetrica instance + */ export function getMetrica(name: string) { const yaMetricaId = yaMetricaMap?.[name]; const metricaInstance = yaMetricaId