From 0d3dbae710f7106e4b79d970e3a2f495850d88b2 Mon Sep 17 00:00:00 2001 From: TT <92159997+ssharkkky@users.noreply.github.com> Date: Mon, 7 Sep 2026 18:31:03 +0800 Subject: [PATCH] feat(channel-monitor-v2): add user ranking toggle and keep missing TTFT neutral Give operators a V2 setting (channel_monitor_hide_user_ranking) to hide the user ranking tab and the /users payload from non-admin channel monitor v2 viewers. Default false (ranking stays visible). Admin endpoints and views always keep the ranking. Also stop painting first-token '-' as a red/empty failure when traffic is sync-only and has no first-token samples: a missing TTFT is "not applicable" (health-unknown), not a breached latency budget. Changes: - new setting key + fail-open parse (only literal "true" hides), wired through public/admin settings DTOs, handlers, and the V2 runtime - ChannelMonitorV2Service.Users returns an empty list to non-admin viewers when the toggle is on - frontend: feature flag + SettingsView toggle + user V2 view hides the ranking tab and skips the /users call - monitorFormat: isTtftUnavailable/ttftDisplayState keep missing TTFT neutral Tests: - setting parse defaults to visible and only literal "true" hides - V2 Users honors the toggle for non-admin viewers - frontend MetricCell/monitorFormat keep missing TTFT neutral --- .../internal/handler/admin/setting_handler.go | 1 + .../handler/admin/setting_handler_update.go | 8 ++++ backend/internal/handler/dto/settings.go | 2 + backend/internal/handler/setting_handler.go | 1 + backend/internal/server/api_contract_test.go | 2 + .../internal/service/channel_monitor_v2.go | 15 +++++++ .../service/channel_monitor_v2_test.go | 40 ++++++++++++++++++ backend/internal/service/domain_constants.go | 4 ++ backend/internal/service/setting_parse.go | 11 +++++ backend/internal/service/setting_public.go | 27 ++++++++---- .../service/setting_service_public_test.go | 15 +++++++ backend/internal/service/setting_update.go | 1 + backend/internal/service/settings_view.go | 2 + frontend/src/api/admin/settings.ts | 2 + .../channel-monitor-v2/MetricCell.vue | 23 ++++++---- .../__tests__/MetricCell.spec.ts | 13 ++++++ .../__tests__/monitorFormat.spec.ts | 18 ++++++++ .../channel-monitor-v2/monitorFormat.ts | 26 +++++++++--- .../src/i18n/locales/en/admin/settings.ts | 3 ++ .../src/i18n/locales/zh/admin/settings.ts | 3 ++ frontend/src/types/index.ts | 2 + frontend/src/utils/featureFlags.ts | 6 +++ frontend/src/views/admin/SettingsView.vue | 37 ++++++++++++---- .../src/views/user/ChannelStatusV2View.vue | 42 ++++++++++++++----- 24 files changed, 262 insertions(+), 42 deletions(-) diff --git a/backend/internal/handler/admin/setting_handler.go b/backend/internal/handler/admin/setting_handler.go index e8b2c49a68..ebc2c11dfe 100644 --- a/backend/internal/handler/admin/setting_handler.go +++ b/backend/internal/handler/admin/setting_handler.go @@ -376,6 +376,7 @@ func (h *SettingHandler) GetSettings(c *gin.Context) { ChannelMonitorDefaultIntervalSeconds: settings.ChannelMonitorDefaultIntervalSeconds, ChannelMonitorHideThroughput: settings.ChannelMonitorHideThroughput, ChannelMonitorShowQuota: settings.ChannelMonitorShowQuota, + ChannelMonitorHideUserRanking: settings.ChannelMonitorHideUserRanking, GrokDefaultTextModel: settings.GrokDefaultTextModel, GrokCrossClientModelMapEnabled: settings.GrokCrossClientModelMapEnabled, diff --git a/backend/internal/handler/admin/setting_handler_update.go b/backend/internal/handler/admin/setting_handler_update.go index b4840f0ba6..4d9447bd0e 100644 --- a/backend/internal/handler/admin/setting_handler_update.go +++ b/backend/internal/handler/admin/setting_handler_update.go @@ -334,6 +334,7 @@ type UpdateSettingsRequest struct { ChannelMonitorDefaultIntervalSeconds *int `json:"channel_monitor_default_interval_seconds"` ChannelMonitorHideThroughput *bool `json:"channel_monitor_hide_throughput"` ChannelMonitorShowQuota *bool `json:"channel_monitor_show_quota"` + ChannelMonitorHideUserRanking *bool `json:"channel_monitor_hide_user_ranking"` // Grok model mapping policy GrokDefaultTextModel *string `json:"grok_default_text_model"` @@ -1906,6 +1907,12 @@ func (h *SettingHandler) UpdateSettings(c *gin.Context) { } return previousSettings.ChannelMonitorShowQuota }(), + ChannelMonitorHideUserRanking: func() bool { + if req.ChannelMonitorHideUserRanking != nil { + return *req.ChannelMonitorHideUserRanking + } + return previousSettings.ChannelMonitorHideUserRanking + }(), GrokDefaultTextModel: func() string { if req.GrokDefaultTextModel != nil { return *req.GrokDefaultTextModel @@ -2366,6 +2373,7 @@ func (h *SettingHandler) UpdateSettings(c *gin.Context) { ChannelMonitorDefaultIntervalSeconds: updatedSettings.ChannelMonitorDefaultIntervalSeconds, ChannelMonitorHideThroughput: updatedSettings.ChannelMonitorHideThroughput, ChannelMonitorShowQuota: updatedSettings.ChannelMonitorShowQuota, + ChannelMonitorHideUserRanking: updatedSettings.ChannelMonitorHideUserRanking, GrokDefaultTextModel: updatedSettings.GrokDefaultTextModel, GrokCrossClientModelMapEnabled: updatedSettings.GrokCrossClientModelMapEnabled, diff --git a/backend/internal/handler/dto/settings.go b/backend/internal/handler/dto/settings.go index ff9797d86f..eba009a7e6 100644 --- a/backend/internal/handler/dto/settings.go +++ b/backend/internal/handler/dto/settings.go @@ -307,6 +307,7 @@ type SystemSettings struct { ChannelMonitorDefaultIntervalSeconds int `json:"channel_monitor_default_interval_seconds"` ChannelMonitorHideThroughput bool `json:"channel_monitor_hide_throughput"` ChannelMonitorShowQuota bool `json:"channel_monitor_show_quota"` + ChannelMonitorHideUserRanking bool `json:"channel_monitor_hide_user_ranking"` // Grok model mapping policy (admin settings; empty account mapping falls back to these). GrokDefaultTextModel string `json:"grok_default_text_model"` @@ -417,6 +418,7 @@ type PublicSettings struct { ChannelMonitorDefaultIntervalSeconds int `json:"channel_monitor_default_interval_seconds"` ChannelMonitorHideThroughput bool `json:"channel_monitor_hide_throughput"` ChannelMonitorShowQuota bool `json:"channel_monitor_show_quota"` + ChannelMonitorHideUserRanking bool `json:"channel_monitor_hide_user_ranking"` AvailableChannelsEnabled bool `json:"available_channels_enabled"` diff --git a/backend/internal/handler/setting_handler.go b/backend/internal/handler/setting_handler.go index d314a4f94b..ec597a7641 100644 --- a/backend/internal/handler/setting_handler.go +++ b/backend/internal/handler/setting_handler.go @@ -108,6 +108,7 @@ func (h *SettingHandler) GetPublicSettings(c *gin.Context) { ChannelMonitorDefaultIntervalSeconds: settings.ChannelMonitorDefaultIntervalSeconds, ChannelMonitorHideThroughput: settings.ChannelMonitorHideThroughput, ChannelMonitorShowQuota: settings.ChannelMonitorShowQuota, + ChannelMonitorHideUserRanking: settings.ChannelMonitorHideUserRanking, AvailableChannelsEnabled: settings.AvailableChannelsEnabled, diff --git a/backend/internal/server/api_contract_test.go b/backend/internal/server/api_contract_test.go index 2fe0a1c227..d64fb2a307 100644 --- a/backend/internal/server/api_contract_test.go +++ b/backend/internal/server/api_contract_test.go @@ -992,6 +992,7 @@ func TestAPIContracts(t *testing.T) { "channel_monitor_mode": "v1", "channel_monitor_hide_throughput": true, "channel_monitor_show_quota": false, + "channel_monitor_hide_user_ranking": false, "channel_monitor_default_interval_seconds": 60, "available_channels_enabled": false, "model_plaza_enabled": false, @@ -1305,6 +1306,7 @@ func TestAPIContracts(t *testing.T) { "channel_monitor_mode": "v1", "channel_monitor_hide_throughput": true, "channel_monitor_show_quota": false, + "channel_monitor_hide_user_ranking": false, "channel_monitor_default_interval_seconds": 60, "available_channels_enabled": false, "model_plaza_enabled": false, diff --git a/backend/internal/service/channel_monitor_v2.go b/backend/internal/service/channel_monitor_v2.go index 1afc20293c..cb2251de7a 100644 --- a/backend/internal/service/channel_monitor_v2.go +++ b/backend/internal/service/channel_monitor_v2.go @@ -405,6 +405,18 @@ func (s *ChannelMonitorV2Service) hideThroughputForViewer(ctx context.Context, a return s.settings.GetChannelMonitorRuntime(ctx).HideThroughput } +func (s *ChannelMonitorV2Service) hideUserRankingForViewer(ctx context.Context, admin bool) bool { + if admin { + return false + } + // Missing reader keeps the current ranking tab visible. Unlike throughput, + // ranking is already public and must stay on until an operator turns it off. + if s == nil || s.settings == nil { + return false + } + return s.settings.GetChannelMonitorRuntime(ctx).HideUserRanking +} + func (s *ChannelMonitorV2Service) GetConfig(ctx context.Context) (*ChannelMonitorV2Config, error) { return s.repo.GetConfig(ctx) } @@ -652,6 +664,9 @@ func (s *ChannelMonitorV2Service) Users(ctx context.Context, filter ChannelMonit if err != nil { return nil, err } + if s.hideUserRankingForViewer(ctx, admin) { + return &ChannelMonitorV2List[ChannelMonitorV2UserRow]{Items: []ChannelMonitorV2UserRow{}}, nil + } result, err := s.repo.GetUsers(ctx, filter, *cfg, admin) if err != nil { return nil, err diff --git a/backend/internal/service/channel_monitor_v2_test.go b/backend/internal/service/channel_monitor_v2_test.go index 9172b333df..d26f43eef2 100644 --- a/backend/internal/service/channel_monitor_v2_test.go +++ b/backend/internal/service/channel_monitor_v2_test.go @@ -252,6 +252,20 @@ func TestChannelMonitorV2HealthBlendsErrorTTFTAndCache(t *testing.T) { require.Nil(t, health.Score) } +func TestChannelMonitorV2HealthLeavesMissingTTFTUnknown(t *testing.T) { + health := ChannelMonitorV2HealthFor(ChannelMonitorV2Metric{ + RequestCount: 200, + ErrorRate: 0, + CacheRate: 0, + CacheRateDenominator: 200, + TTFT: ChannelMonitorV2Latency{SampleCount: 0}, + }) + require.Equal(t, "unknown", health.TTFT) + require.Nil(t, health.TTFTScore) + require.NotEqual(t, "critical", health.Overall) + require.Equal(t, "healthy", health.Overall) +} + func TestChannelMonitorV2DefaultHealthThresholdsAreTolerant(t *testing.T) { p50 := int64(2500) health := ChannelMonitorV2HealthFor(ChannelMonitorV2Metric{ @@ -287,6 +301,32 @@ func TestErrorRateTTFTAndCacheScoreHelpers(t *testing.T) { require.Equal(t, "healthy", cacheRateBand(0.50, 0.20, 0.05)) } +type channelMonitorV2RuntimeStub struct { + rt ChannelMonitorRuntime +} + +func (s channelMonitorV2RuntimeStub) GetChannelMonitorRuntime(context.Context) ChannelMonitorRuntime { + return s.rt +} + +func TestChannelMonitorV2UsersHiddenWhenSettingEnabled(t *testing.T) { + selfID, otherID := int64(7), int64(9) + repo := &channelMonitorV2RepoStub{config: ChannelMonitorV2Config{Enabled: true}, users: &ChannelMonitorV2List[ChannelMonitorV2UserRow]{Items: []ChannelMonitorV2UserRow{ + {UserID: &otherID, Email: "other@example.com", Username: "other"}, + {UserID: &selfID, Email: "self@example.com", Username: "self"}, + }}} + svc := NewChannelMonitorV2Service(repo) + svc.SetRuntimeReader(channelMonitorV2RuntimeStub{rt: ChannelMonitorRuntime{HideUserRanking: true}}) + + hidden, err := svc.Users(context.Background(), ChannelMonitorV2Filter{}, selfID, false) + require.NoError(t, err) + require.Empty(t, hidden.Items) + + admin, err := svc.Users(context.Background(), ChannelMonitorV2Filter{}, selfID, true) + require.NoError(t, err) + require.Len(t, admin.Items, 2) +} + func TestChannelMonitorV2UsersRemovesOtherUserIdentity(t *testing.T) { selfID, otherID := int64(7), int64(9) repo := &channelMonitorV2RepoStub{config: ChannelMonitorV2Config{Enabled: true}, users: &ChannelMonitorV2List[ChannelMonitorV2UserRow]{Items: []ChannelMonitorV2UserRow{ diff --git a/backend/internal/service/domain_constants.go b/backend/internal/service/domain_constants.go index 50de7a05d1..9947a9b49f 100644 --- a/backend/internal/service/domain_constants.go +++ b/backend/internal/service/domain_constants.go @@ -484,6 +484,10 @@ const ( // fail-closed (only the literal "true" enables it). Admin endpoints always // keep the full snapshots regardless of this flag. SettingKeyChannelMonitorShowQuota = "channel_monitor_show_quota" + // SettingKeyChannelMonitorHideUserRanking hides the user ranking tab and + // /users payload from non-admin channel-monitor v2 viewers. + // Default false (keep the current ranking tab). Admin endpoints always keep it. + SettingKeyChannelMonitorHideUserRanking = "channel_monitor_hide_user_ranking" // SettingKeyGrokDefaultTextModel is the fallback Grok text model for empty // request models and built-in Grok aliases (e.g. "grok" → this id). Default grok-4.5. diff --git a/backend/internal/service/setting_parse.go b/backend/internal/service/setting_parse.go index ade4de0d41..7fc929e9c6 100644 --- a/backend/internal/service/setting_parse.go +++ b/backend/internal/service/setting_parse.go @@ -191,6 +191,7 @@ func (s *SettingService) InitializeDefaultSettings(ctx context.Context) error { SettingKeyChannelMonitorDefaultIntervalSeconds: "60", SettingKeyChannelMonitorHideThroughput: "true", SettingKeyChannelMonitorShowQuota: "false", + SettingKeyChannelMonitorHideUserRanking: "false", // Grok compatibility defaults: cross-client mapping stays enabled unless // operators explicitly disable it. @@ -804,6 +805,7 @@ func (s *SettingService) parseSettings(settings map[string]string) *SystemSettin // 配额展示默认关闭且 fail-closed:仅字面 "true" 视为开启 // (与 setting_public.go 公开读取路径保持一致)。 result.ChannelMonitorShowQuota = settings[SettingKeyChannelMonitorShowQuota] == "true" + result.ChannelMonitorHideUserRanking = isTrueSettingValue(settings[SettingKeyChannelMonitorHideUserRanking]) // Grok default mapping policy result.GrokDefaultTextModel = strings.TrimSpace(settings[SettingKeyGrokDefaultTextModel]) @@ -1010,6 +1012,15 @@ func isFalseSettingValue(value string) bool { } } +func isTrueSettingValue(value string) bool { + switch strings.ToLower(strings.TrimSpace(value)) { + case "true", "1", "on", "enabled": + return true + default: + return false + } +} + func normalizeVisibleMethodSettingSource(method, source string, enabled bool) (string, error) { _ = enabled source = strings.TrimSpace(source) diff --git a/backend/internal/service/setting_public.go b/backend/internal/service/setting_public.go index 7072dd7c03..d985ce6dd6 100644 --- a/backend/internal/service/setting_public.go +++ b/backend/internal/service/setting_public.go @@ -232,6 +232,7 @@ func (s *SettingService) GetPublicSettings(ctx context.Context) (*PublicSettings SettingKeyChannelMonitorDefaultIntervalSeconds, SettingKeyChannelMonitorHideThroughput, SettingKeyChannelMonitorShowQuota, + SettingKeyChannelMonitorHideUserRanking, SettingKeyAvailableChannelsEnabled, SettingKeyModelPlazaEnabled, SettingKeyModelPlazaRequireAuth, @@ -358,6 +359,7 @@ func (s *SettingService) GetPublicSettings(ctx context.Context) (*PublicSettings ChannelMonitorDefaultIntervalSeconds: parseChannelMonitorInterval(settings[SettingKeyChannelMonitorDefaultIntervalSeconds]), ChannelMonitorHideThroughput: !isFalseSettingValue(settings[SettingKeyChannelMonitorHideThroughput]), ChannelMonitorShowQuota: settings[SettingKeyChannelMonitorShowQuota] == "true", + ChannelMonitorHideUserRanking: isTrueSettingValue(settings[SettingKeyChannelMonitorHideUserRanking]), AvailableChannelsEnabled: settings[SettingKeyAvailableChannelsEnabled] == "true", @@ -430,6 +432,9 @@ type ChannelMonitorRuntime struct { // snapshots; otherwise the user handler strips them server-side. // Parsed fail-closed (only literal "true" enables). Admin always sees them. ShowQuota bool + // HideUserRanking: when true, user-facing V2 views hide the user ranking tab + // and the /users payload. Parsed fail-open (only literal "true" hides it). + HideUserRanking bool } // ActiveProbesAllowed reports whether V1 active provider probes may run. @@ -459,6 +464,7 @@ func (s *SettingService) GetChannelMonitorRuntime(ctx context.Context) ChannelMo SettingKeyChannelMonitorDefaultIntervalSeconds, SettingKeyChannelMonitorHideThroughput, SettingKeyChannelMonitorShowQuota, + SettingKeyChannelMonitorHideUserRanking, }) if err != nil { return ChannelMonitorRuntime{ @@ -474,6 +480,7 @@ func (s *SettingService) GetChannelMonitorRuntime(ctx context.Context) ChannelMo DefaultIntervalSeconds: parseChannelMonitorInterval(vals[SettingKeyChannelMonitorDefaultIntervalSeconds]), HideThroughput: !isFalseSettingValue(vals[SettingKeyChannelMonitorHideThroughput]), ShowQuota: vals[SettingKeyChannelMonitorShowQuota] == "true", + HideUserRanking: isTrueSettingValue(vals[SettingKeyChannelMonitorHideUserRanking]), } } @@ -618,14 +625,17 @@ type PublicSettingsInjectionPayload struct { ChannelMonitorHideThroughput bool `json:"channel_monitor_hide_throughput"` // ChannelMonitorShowQuota gates the user-facing quota/balance display on // monitors; fail-closed (absent/false = hidden). Admin UI always shows it. - ChannelMonitorShowQuota bool `json:"channel_monitor_show_quota"` - AvailableChannelsEnabled bool `json:"available_channels_enabled"` - ModelPlazaEnabled bool `json:"model_plaza_enabled"` - ModelPlazaRequireAuth bool `json:"model_plaza_require_auth"` - PluginManagementEnabled bool `json:"plugin_management_enabled"` - AffiliateEnabled bool `json:"affiliate_enabled"` - RiskControlEnabled bool `json:"risk_control_enabled"` - AllowUserViewErrorRequests bool `json:"allow_user_view_error_requests"` + // ChannelMonitorHideUserRanking hides the user ranking tab and /users payload + // from non-admin channel-monitor v2 viewers; default false (visible). + ChannelMonitorHideUserRanking bool `json:"channel_monitor_hide_user_ranking"` + ChannelMonitorShowQuota bool `json:"channel_monitor_show_quota"` + AvailableChannelsEnabled bool `json:"available_channels_enabled"` + ModelPlazaEnabled bool `json:"model_plaza_enabled"` + ModelPlazaRequireAuth bool `json:"model_plaza_require_auth"` + PluginManagementEnabled bool `json:"plugin_management_enabled"` + AffiliateEnabled bool `json:"affiliate_enabled"` + RiskControlEnabled bool `json:"risk_control_enabled"` + AllowUserViewErrorRequests bool `json:"allow_user_view_error_requests"` } // GetPublicSettingsForInjection returns public settings in a format suitable for HTML injection. @@ -700,6 +710,7 @@ func (s *SettingService) GetPublicSettingsForInjection(ctx context.Context) (any ChannelMonitorDefaultIntervalSeconds: settings.ChannelMonitorDefaultIntervalSeconds, ChannelMonitorHideThroughput: settings.ChannelMonitorHideThroughput, ChannelMonitorShowQuota: settings.ChannelMonitorShowQuota, + ChannelMonitorHideUserRanking: settings.ChannelMonitorHideUserRanking, AvailableChannelsEnabled: settings.AvailableChannelsEnabled, ModelPlazaEnabled: settings.ModelPlazaEnabled, ModelPlazaRequireAuth: settings.ModelPlazaRequireAuth, diff --git a/backend/internal/service/setting_service_public_test.go b/backend/internal/service/setting_service_public_test.go index 3fa0d40426..316df8ffda 100644 --- a/backend/internal/service/setting_service_public_test.go +++ b/backend/internal/service/setting_service_public_test.go @@ -139,6 +139,21 @@ func TestSettingService_ChannelMonitorShowQuotaFailsClosed(t *testing.T) { } } +func TestSettingService_ChannelMonitorHideUserRankingDefaultsToVisible(t *testing.T) { + missing := NewSettingService(&settingPublicRepoStub{values: map[string]string{}}, &config.Config{}).GetChannelMonitorRuntime(context.Background()) + require.False(t, missing.HideUserRanking) + public, err := NewSettingService(&settingPublicRepoStub{values: map[string]string{}}, &config.Config{}).GetPublicSettings(context.Background()) + require.NoError(t, err) + require.False(t, public.ChannelMonitorHideUserRanking) + + for _, value := range []string{"true", "1", "on", "enabled"} { + runtime := NewSettingService(&settingPublicRepoStub{values: map[string]string{ + SettingKeyChannelMonitorHideUserRanking: value, + }}, &config.Config{}).GetChannelMonitorRuntime(context.Background()) + require.True(t, runtime.HideUserRanking, "value=%q", value) + } +} + func TestSettingService_GetPublicSettings_ExposesForceEmailOnThirdPartySignup(t *testing.T) { repo := &settingPublicRepoStub{ values: map[string]string{ diff --git a/backend/internal/service/setting_update.go b/backend/internal/service/setting_update.go index 8928a63534..1a4793aa55 100644 --- a/backend/internal/service/setting_update.go +++ b/backend/internal/service/setting_update.go @@ -418,6 +418,7 @@ func (s *SettingService) buildSystemSettingsUpdates(ctx context.Context, setting } updates[SettingKeyChannelMonitorHideThroughput] = strconv.FormatBool(settings.ChannelMonitorHideThroughput) updates[SettingKeyChannelMonitorShowQuota] = strconv.FormatBool(settings.ChannelMonitorShowQuota) + updates[SettingKeyChannelMonitorHideUserRanking] = strconv.FormatBool(settings.ChannelMonitorHideUserRanking) // Grok model mapping policy if v := strings.TrimSpace(settings.GrokDefaultTextModel); v != "" { diff --git a/backend/internal/service/settings_view.go b/backend/internal/service/settings_view.go index 9b6932934d..8216535309 100644 --- a/backend/internal/service/settings_view.go +++ b/backend/internal/service/settings_view.go @@ -202,6 +202,7 @@ type SystemSettings struct { ChannelMonitorDefaultIntervalSeconds int `json:"channel_monitor_default_interval_seconds"` ChannelMonitorHideThroughput bool `json:"channel_monitor_hide_throughput"` ChannelMonitorShowQuota bool `json:"channel_monitor_show_quota"` + ChannelMonitorHideUserRanking bool `json:"channel_monitor_hide_user_ranking"` // Grok model mapping policy (admin settings; empty mapping falls back to these). GrokDefaultTextModel string `json:"grok_default_text_model"` @@ -382,6 +383,7 @@ type PublicSettings struct { ChannelMonitorDefaultIntervalSeconds int `json:"channel_monitor_default_interval_seconds"` ChannelMonitorHideThroughput bool `json:"channel_monitor_hide_throughput"` ChannelMonitorShowQuota bool `json:"channel_monitor_show_quota"` + ChannelMonitorHideUserRanking bool `json:"channel_monitor_hide_user_ranking"` // Grok model mapping policy (admin settings). GrokDefaultTextModel string `json:"grok_default_text_model"` diff --git a/frontend/src/api/admin/settings.ts b/frontend/src/api/admin/settings.ts index 3c8d608637..9c0eed44a6 100644 --- a/frontend/src/api/admin/settings.ts +++ b/frontend/src/api/admin/settings.ts @@ -720,6 +720,7 @@ export interface SystemSettings { channel_monitor_default_interval_seconds: number; channel_monitor_hide_throughput?: boolean; channel_monitor_show_quota?: boolean; + channel_monitor_hide_user_ranking?: boolean; // Available Channels feature switch available_channels_enabled: boolean; @@ -1021,6 +1022,7 @@ export interface UpdateSettingsRequest { channel_monitor_default_interval_seconds?: number; channel_monitor_hide_throughput?: boolean; channel_monitor_show_quota?: boolean; + channel_monitor_hide_user_ranking?: boolean; // Available Channels feature switch available_channels_enabled?: boolean; diff --git a/frontend/src/features/channel-monitor-v2/MetricCell.vue b/frontend/src/features/channel-monitor-v2/MetricCell.vue index aa3eee65eb..e2fb5a3b52 100644 --- a/frontend/src/features/channel-monitor-v2/MetricCell.vue +++ b/frontend/src/features/channel-monitor-v2/MetricCell.vue @@ -4,7 +4,7 @@ :title="title || undefined" >
-- {{ t('admin.settings.features.channelMonitor.hideThroughput') }} -
-- {{ t('admin.settings.features.channelMonitor.hideThroughputHint') }} -
++ {{ t('admin.settings.features.channelMonitor.hideThroughput') }} +
++ {{ t('admin.settings.features.channelMonitor.hideThroughputHint') }} +
++ {{ t('admin.settings.features.channelMonitor.hideUserRanking') }} +
++ {{ t('admin.settings.features.channelMonitor.hideUserRankingHint') }} +
+