From be23422e5b5969ddea312921aed2cb892d3ca9ef Mon Sep 17 00:00:00 2001 From: ktx-vaidehi <134508096+ktx-vaidehi@users.noreply.github.com> Date: Fri, 11 Sep 2026 14:00:35 +0000 Subject: [PATCH] feat(ui): responsive mobile/tablet support across the app (#13763) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .claude/skills/ui-architect/SKILL.md | 65 ++++- .../references/component-catalog.md | 4 + .../ui-architect/references/conventions.md | 6 +- .../references/core-controls-table.md | 1 + .../ui-architect/references/house-rules.md | 2 +- .../references/overlay-navigation.md | 3 + .../ui-architect/references/page-recipes.md | 47 ++- .../ui-architect/references/responsive.md | 276 ++++++++++++++++++ scripts/translations/.translation_state.json | 4 + web/src/components/DateTime.vue | 45 ++- web/src/components/Header.vue | 30 +- web/src/components/MenuLink.vue | 19 +- web/src/components/O2AIChat.vue | 2 +- web/src/components/OrganizationSelector.vue | 2 +- .../about/FeatureComparisonTable.vue | 2 +- web/src/components/alerts/AddAlert.vue | 14 +- web/src/components/alerts/AddTemplate.vue | 4 +- .../components/alerts/AlertGroupsTable.vue | 2 +- web/src/components/alerts/AlertHistory.vue | 74 ++++- .../components/alerts/AlertHistoryDrawer.vue | 2 +- web/src/components/alerts/AlertInsights.vue | 4 +- web/src/components/alerts/AlertList.vue | 115 ++++++-- .../components/alerts/AlertSectionTabs.vue | 4 +- .../alerts/AlertsDestinationList.vue | 69 ++++- .../components/alerts/DependencyEntityRow.vue | 2 +- .../alerts/ExternalAlertSourcesList.vue | 60 ++++ web/src/components/alerts/ImportAlert.vue | 16 +- .../components/alerts/ImportDestination.vue | 6 +- web/src/components/alerts/ImportTemplate.vue | 6 +- web/src/components/alerts/IncidentList.vue | 54 +++- .../alerts/PipelinesDestinationList.vue | 36 +++ web/src/components/alerts/TemplateList.vue | 89 +++++- web/src/components/common/AppTabs.vue | 9 +- web/src/components/common/BaseImport.vue | 8 +- web/src/components/common/KpiCard.vue | 24 +- web/src/components/common/KpiCardRow.vue | 7 +- .../components/common/sidebar/FolderList.vue | 266 +++++++++++------ .../dashboards/OverrideConfigPopup.vue | 2 +- .../components/dashboards/PanelContainer.vue | 6 +- .../dashboards/PanelEditor/PanelEditor.vue | 141 ++++++++- .../dashboards/addPanel/ChartSelection.vue | 6 +- .../addPanel/DashboardGeoMapsQueryBuilder.vue | 12 +- .../addPanel/DashboardMapsQueryBuilder.vue | 8 +- .../addPanel/DashboardQueryBuilder.vue | 22 +- .../addPanel/DashboardQueryEditor.vue | 6 +- .../addPanel/DashboardSankeyChartBuilder.vue | 12 +- .../dashboards/addPanel/PanelFieldList.vue | 9 +- .../dashboards/addPanel/PanelSidebar.vue | 2 +- .../dashboards/addPanel/QueryTypeSelector.vue | 2 +- .../dashboards/addPanel/ShowLegendsPopup.vue | 2 +- .../dynamicFunction/DynamicFunctionPopUp.vue | 6 +- .../dashboards/panels/ChartRenderer.vue | 36 +++ .../dashboards/panels/TableRenderer.vue | 7 + .../settings/AddSettingVariable.vue | 4 +- .../dashboards/settings/TabsSettings.vue | 38 +++ .../dashboards/settings/VariableSettings.vue | 36 +++ .../components/dashboards/tabs/TabList.vue | 2 +- .../dashboards/viewPanel/ViewPanel.vue | 17 +- web/src/components/dbm/DbmInsightStrip.vue | 2 +- web/src/components/dbm/DbmRefreshButton.vue | 2 +- web/src/components/dbm/DbmRowActions.vue | 31 +- web/src/components/dbm/DbmScopeFilters.vue | 4 +- .../components/dbm/DbmTableToolbar.spec.ts | 4 +- web/src/components/dbm/DbmTableToolbar.vue | 6 +- web/src/components/functions/AddFunction.vue | 13 +- .../functions/EnrichmentTableList.vue | 76 ++++- web/src/components/functions/FunctionList.vue | 48 ++- .../components/functions/FunctionsToolbar.vue | 4 +- web/src/components/iam/IngestionTokens.vue | 2 +- web/src/components/iam/SyntheticsTokens.vue | 2 +- web/src/components/iam/groups/AppGroups.vue | 39 ++- .../iam/organizations/ListOrganizations.vue | 39 ++- web/src/components/iam/quota/Quota.vue | 20 +- web/src/components/iam/roles/RoleTable.vue | 37 ++- .../serviceAccounts/ServiceAccountsList.vue | 49 +++- .../components/iam/users/InvitationList.vue | 36 ++- web/src/components/iam/users/User.vue | 60 +++- .../components/ingestion/AIIntegrations.vue | 115 +++++++- web/src/components/ingestion/Custom.vue | 2 +- .../ingestion/DataSourceSidebarLayout.vue | 120 +++++++- .../ingestion/recommended/AWSQuickSetup.vue | 12 +- .../ingestion/setupCard/SetupCardRenderer.vue | 10 + web/src/components/login/Login.vue | 5 +- web/src/components/logstream/schema.vue | 19 +- .../components/pipeline/ImportPipeline.vue | 2 +- .../pipeline/PipelineSectionTabs.vue | 2 +- web/src/components/pipeline/PipelinesList.vue | 32 +- .../components/pipelines/BackfillJobsList.vue | 76 ++++- .../components/pipelines/PipelineHistory.vue | 4 +- .../promql/components/MetricSelector.vue | 2 +- .../components/PromQLBuilderOptions.vue | 9 +- web/src/components/queries/RunningQueries.vue | 7 +- .../components/queries/RunningQueriesList.vue | 35 +++ web/src/components/reports/CreateReport.vue | 27 +- web/src/components/reports/ReportList.vue | 86 +++++- .../rum/errorTracking/list/ErrorsKpiCards.vue | 22 +- .../sessionReplay/SessionsMetricsStrip.vue | 23 +- web/src/components/settings/AiToolsets.vue | 37 ++- web/src/components/settings/CipherKeys.vue | 37 ++- .../settings/DiscoveredServices.vue | 2 +- .../components/settings/DomainManagement.vue | 4 +- .../settings/GenAiAgentMappingSettings.vue | 5 +- web/src/components/settings/General.vue | 20 +- .../settings/ImportModelPricing.vue | 2 +- .../settings/ImportRegexPattern.vue | 2 +- .../settings/LlmProvidersSettings.vue | 33 +++ .../settings/ModelPricingEditor.vue | 10 +- .../components/settings/ModelPricingList.vue | 66 ++++- web/src/components/settings/Nodes.vue | 17 +- .../settings/OrganizationManagement.vue | 97 +++++- .../settings/OrganizationSettings.vue | 12 +- .../components/settings/RegexPatternList.vue | 46 ++- .../settings/SyntheticsLocationsList.vue | 54 +++- .../components/shared/HomeViewSkeleton.vue | 14 +- .../synthetic-monitoring/MonitorTable.vue | 43 ++- .../synthetics/journey/BrowserJourneyStep.vue | 2 +- .../synthetics/journey/JourneySteps.vue | 58 +++- .../results/ProtocolRunSummary.spec.ts | 1 + .../workflows/WorkflowEditor.spec.ts | 2 +- .../components/workflows/WorkflowEditor.vue | 55 ++-- web/src/components/workflows/WorkflowRuns.vue | 8 +- .../components/workflows/WorkflowsList.vue | 25 +- web/src/composables/useBreakpoint.spec.ts | 84 ++++++ web/src/composables/useBreakpoint.ts | 63 ++++ .../AIObservability/AgentScopeCascade.vue | 11 +- .../components/AIObservability/AiScopeBar.vue | 2 +- .../AIObservability/ExperimentBrowser.vue | 38 +++ web/src/enterprise/components/OnlineEvals.vue | 3 +- .../components/billings/Billing.vue | 10 + .../components/billings/BillingGroup.vue | 34 +++ .../enterprise/components/billings/plans.vue | 2 +- .../enterprise/components/billings/usage.vue | 16 +- .../components/onlineEvals/EvalJobList.vue | 58 +++- .../components/onlineEvals/QualityPage.vue | 2 +- .../onlineEvals/ScoreConfigList.vue | 44 ++- .../components/onlineEvals/ScorerList.vue | 44 ++- .../onlineEvals/quality/QualityKpiCard.vue | 18 +- .../quality/QualityKpiSkeleton.vue | 2 +- .../AIObservability/DatasetDetailPage.vue | 33 +++ .../views/AIObservability/DatasetsPage.vue | 33 +++ .../views/AIObservability/DiscoveryPage.vue | 10 +- .../views/AIObservability/PlaygroundPage.vue | 26 +- .../views/AIObservability/RemoteTasksPage.vue | 34 +++ web/src/layouts/MainLayout.vue | 90 +++++- .../core/EmptyState/EmptyStateActionCard.vue | 2 +- .../EmptyState/EmptyStateIngestionCard.vue | 2 +- .../lib/core/EmptyState/OEmptyState.spec.ts | 15 +- web/src/lib/core/EmptyState/OEmptyState.vue | 10 +- web/src/lib/core/Navbar/ONavGroup.vue | 77 ++++- web/src/lib/core/Navbar/ONavbar.vue | 2 +- web/src/lib/core/PageHeader/OPageHeader.vue | 114 +++++++- web/src/lib/core/PageLayout/OPageLayout.vue | 78 ++++- .../lib/core/PageLayout/pageLayoutSidebar.ts | 25 ++ .../lib/core/RefreshButton/ORefreshButton.vue | 2 +- web/src/lib/core/Table/OTable.vue | 60 ++-- web/src/lib/core/Table/cells/OCodeCell.vue | 2 +- .../core/Table/composables/useTableCore.ts | 43 ++- .../Table/sub-components/OTableBodyCell.vue | 2 +- .../Table/sub-components/OTableHeader.vue | 4 +- .../Table/sub-components/OTablePagination.vue | 11 +- .../core/ToggleGroup/OToggleGroup.types.ts | 12 + web/src/lib/core/ToggleGroup/OToggleGroup.vue | 68 ++++- .../lib/core/ToggleGroup/OToggleGroupItem.vue | 46 ++- .../ToggleGroup/OToggleGroupMenuScope.vue | 38 +++ web/src/lib/data/StatStrip/OStatCard.vue | 18 +- web/src/lib/data/StatStrip/OStatStrip.vue | 4 +- web/src/lib/forms/Color/OColor.vue | 2 +- web/src/lib/forms/Combobox/OCombobox.vue | 5 +- .../forms/DateTimeRange/ODateTimeRange.vue | 4 +- web/src/lib/forms/Select/OSelect.vue | 7 +- web/src/lib/forms/Time/OTime.vue | 1 + web/src/lib/lists/FieldList/OFieldList.vue | 2 +- web/src/lib/lists/FieldList/OFieldRow.vue | 2 +- web/src/lib/navigation/Tabs/OTab.vue | 7 +- web/src/lib/navigation/Tabs/OTabs.types.ts | 4 + web/src/lib/navigation/Tabs/OTabs.vue | 2 + web/src/lib/overlay/Drawer/ODrawer.types.ts | 6 + web/src/lib/overlay/Drawer/ODrawer.vue | 44 ++- web/src/lib/overlay/Dropdown/ODropdown.vue | 7 +- web/src/lib/overlay/Popover/OPopover.vue | 7 +- web/src/locales/languages/ar-SA.json | 4 + web/src/locales/languages/de-DE.json | 4 + web/src/locales/languages/en-US.json | 4 + web/src/locales/languages/es-ES.json | 4 + web/src/locales/languages/fr-FR.json | 4 + web/src/locales/languages/it-IT.json | 4 + web/src/locales/languages/ja-JP.json | 4 + web/src/locales/languages/ko-KR.json | 4 + web/src/locales/languages/nl-NL.json | 4 + web/src/locales/languages/pl-PL.json | 4 + web/src/locales/languages/pt-PT.json | 4 + web/src/locales/languages/ru-RU.json | 4 + web/src/locales/languages/tr-TR.json | 4 + web/src/locales/languages/vi-VN.json | 4 + web/src/locales/languages/zh-CN.json | 4 + web/src/locales/languages/zh-TW.json | 4 + web/src/plugins/logs/DetailTable.vue | 6 +- web/src/plugins/logs/Index.vue | 83 +++++- web/src/plugins/logs/SearchBar.vue | 116 +++++++- web/src/plugins/logs/SearchHistory.vue | 12 +- web/src/plugins/logs/SearchJobInspector.vue | 60 ++-- web/src/plugins/logs/SearchResult.vue | 47 ++- web/src/plugins/logs/SearchSchedulersList.vue | 60 +++- .../plugins/metrics/explorer/MetricCard.vue | 4 +- .../metrics/explorer/MetricsExplorer.vue | 69 ++++- .../plugins/pipelines/PipelineFlow.spec.ts | 65 ++++- web/src/plugins/pipelines/PipelineFlow.vue | 25 +- web/src/plugins/traces/Index.vue | 71 ++++- web/src/plugins/traces/IndexList.vue | 7 + .../plugins/traces/LLMInsightsDashboard.vue | 17 +- web/src/plugins/traces/SearchBar.vue | 26 +- web/src/plugins/traces/SearchResult.spec.ts | 1 + web/src/plugins/traces/SearchResult.vue | 22 +- web/src/plugins/traces/ServiceGraph.vue | 29 +- web/src/plugins/traces/ServicesCatalog.vue | 10 +- web/src/plugins/traces/TraceTree.vue | 7 + web/src/test/__mocks__/global.ts | 3 +- web/src/utils/dashboard/constants.ts | 3 +- web/src/views/About.vue | 14 +- web/src/views/AlertLibrary/AlertLibrary.vue | 40 ++- .../views/Dashboards/DashboardSettings.vue | 26 +- web/src/views/Dashboards/Dashboards.vue | 63 +++- web/src/views/Dashboards/ImportDashboard.vue | 50 +++- .../Dashboards/RenderDashboardCharts.vue | 33 +++ web/src/views/Dashboards/ViewDashboard.vue | 17 +- .../Dashboards/addPanel/AddJoinPopUp.vue | 5 +- .../views/Dashboards/addPanel/AddPanel.vue | 78 +++-- .../views/DatabaseMonitoring/ActivityPage.vue | 4 +- .../DatabaseMonitoring/BlockedQueriesPage.vue | 2 +- .../DatabaseMonitoring/DatabasesPage.vue | 2 +- .../DatabaseMonitoring/DeadlocksPage.vue | 2 +- .../views/DatabaseMonitoring/QueriesPage.vue | 6 +- .../views/DatabaseMonitoring/SamplesPage.vue | 12 +- .../DatabaseMonitoring/TableHealthPage.vue | 4 +- web/src/views/Functions.vue | 2 +- web/src/views/HomeChatHistory.vue | 2 +- web/src/views/HomeView.vue | 54 +++- web/src/views/Ingestion.vue | 21 +- web/src/views/LogStream.vue | 54 +++- web/src/views/OverviewTab.vue | 4 +- web/src/views/RUM/AppErrors.vue | 69 ++++- web/src/views/RUM/AppSessions.vue | 64 +++- web/src/views/SyntheticMonitoring.vue | 38 ++- web/src/views/UsageTab.vue | 3 +- web/src/views/slos/SloList.vue | 68 ++++- web/src/views/synthetics/PrivateLocations.vue | 40 +++ web/src/views/synthetics/RunDetail.vue | 2 +- .../status-pages/StatusPagesList.vue | 12 + 248 files changed, 5621 insertions(+), 860 deletions(-) create mode 100644 .claude/skills/ui-architect/references/responsive.md create mode 100644 web/src/composables/useBreakpoint.spec.ts create mode 100644 web/src/composables/useBreakpoint.ts create mode 100644 web/src/lib/core/PageLayout/pageLayoutSidebar.ts create mode 100644 web/src/lib/core/ToggleGroup/OToggleGroupMenuScope.vue diff --git a/.claude/skills/ui-architect/SKILL.md b/.claude/skills/ui-architect/SKILL.md index c876fd634b..0f2eda21c5 100644 --- a/.claude/skills/ui-architect/SKILL.md +++ b/.claude/skills/ui-architect/SKILL.md @@ -3,7 +3,7 @@ name: ui-architect description: >- Authoring guardrails for building ANY new frontend UI in the OpenObserve web app (web/) — new views, pages, panels, dialogs, feature components, or edits - to existing ones. Enforce six house rules the moment you write Vue/template + to existing ones. Enforce seven house rules the moment you write Vue/template markup: (1) use OPageHeader for every page/module header, (2) build UI from O2 library components in web/src/lib — never bare HTML controls when an O2 equivalent exists, (3) NEVER write px — always rem, including inside Tailwind class @@ -29,7 +29,12 @@ description: >- is banned) with keys added to web/src/locales/languages/en-US.json; text-carrying props/fields are typed I18nText and i18n keys stored as data are typed I18nKey, and the ONLY opt-out for a genuinely non-translatable string is raw() — never an - eslint-disable. It also settles the recurring + eslint-disable, (7) every page is responsive — phones and tablets adapt through + max-md:/max-lg: variants and useBreakpoint() while the laptop (≥lg) view stays + identical; headers, toolbars and stat strips keep one row, filter toggles collapse + to dropdowns (mobile-dropdown), secondary header actions go to #actions-overflow, + side panels become drawers that open from their own row, row actions fold into a + kebab, and popups fit the viewport. It also settles the recurring structural decisions: use OTable for any tabular data, follow the view → service → Vuex/local-ref layering for fetching list data, choose the right form container (ConfirmDialog vs ODialog vs ODrawer vs a full in-page @@ -38,7 +43,8 @@ description: >- v-model/ref mirrors, automatic submit/loading, correct field-array keys). Trigger this whenever the user asks to create, add, build, scaffold, lay out, validate, or restyle any screen, component, header, table, list, dialog, - drawer, form, field, or panel in the web frontend, or asks where a + drawer, form, field, or panel in the web frontend, asks to make any of them + work on mobile / phone / tablet / small screens (responsive), or asks where a form/table/fetch should live, how to validate a form, how to add a keyboard shortcut, how to build a new reusable/common O2 component when nothing existing fits (create one in web/src/lib instead of assembling divs and classes), @@ -61,7 +67,7 @@ token system, one spacing scale. This skill governs **feature/app UI** — views under `web/src/views` and components under `web/src/components` — built from the shared **O2 component library** in -`web/src/lib`. This page is the **contract + map**: the six laws and the +`web/src/lib`. This page is the **contract + map**: the seven laws and the recurring structural decisions, each in a line or two, each pointing to the reference that carries the full rationale, examples, and per-component detail. Open the linked reference before you implement that specific thing — don't guess a @@ -69,10 +75,11 @@ prop, a class string, or a path. --- -## The six house rules +## The seven house rules The always-true laws. Each is stated here in brief; the full **what / why / how + -code** for all six is in [references/house-rules.md](references/house-rules.md) — +code** for rules 1–6 is in [references/house-rules.md](references/house-rules.md), and +rule 7 has its own [references/responsive.md](references/responsive.md) — read it once, it is the backbone of everything below. 1. **Every page/module header is `OPageHeader`** — never a hand-rolled @@ -513,6 +520,33 @@ read it once, it is the backbone of everything below. > Toast/notification copy added by this convention lives under `toastMessages.*`, > grouped by module. +7. **Every page is responsive — and the laptop view does not move.** New pages + are built for 375 px phones and 768 px tablets from the first commit, using the + method in [references/responsive.md](references/responsive.md): + - **Desktop is frozen.** Responsive rules are additive below a breakpoint — + `max-md:` (phone), `max-lg:` / `md:max-lg:` (tablet) — or a JS branch on + `useBreakpoint()`'s `isMobile` / `!lgUp`. An unprefixed class change or a bare + `md:`/`lg:` class that alters ≥1024 px is a blocker. JS is only for structure + (rail → drawer, toggle strip → dropdown, splitter locked shut). + - **One row of chrome.** Header: primaries in `#actions`, secondaries in + `#actions-overflow` (inline on desktop, one ⋮ below md; `overflow-first` when + they precede the CTA). Toolbar: wrapper `max-md:contents`, filter + `OToggleGroup mobile-dropdown`, search `min-w-0 flex-1 max-md:min-w-40`. + Stat tiles: `OStatStrip` / `KpiCard` (compact to icon + value below lg). + - **Side panels open from their own row.** `OPageLayout #sidebar` and + `FolderList` already become drawers; any other rail renders in an + `ODrawer` with `anchor` set to its trigger's row. Only the main nav opens from + the top. + - **Tables keep every column** and scroll within the frame (OTable does it); + inline row actions get `max-md:hidden` plus one `md:hidden` kebab mirroring them + with `-menu` items; the footer count is `max-md:hidden`. + - **Nothing clipped, nothing hover-only.** Popups use library components and + `min(, calc(100vw - 1.5rem))` widths; an `h-full` pane beside a stacked + sibling gets `max-md:h-auto max-md:min-h-0`; hover-revealed controls get + `max-md:opacity-100`. + - **Verify** at 375 / 360 / 768 / 1280 in the in-app browser, and at 1280 compare + against main — identical is the bar. + ## Structural decisions *What* to reach for and *where the code lives* — the recurring calls that @@ -535,6 +569,7 @@ and each domain has its own reference below. | **New page in nav** | a route **+ exactly one** surface (rail item / flyout child / Settings / IAM sub-page) **+** an env/role gate — the route condition, the nav-entry gate, and the SectionRail `visible` all express the same rule | [navigation-menus](references/navigation-menus.md) | | **Keyboard shortcuts** | registry-driven — declare in `shortcutRegistry.ts`, bind with `useShortcuts([{ id, handler }])`; never an ad-hoc `keydown` listener or a hardcoded `⌘N` in a template | [keyboard-shortcuts](references/keyboard-shortcuts.md) | | **Cancel / Save row** | cancel = `variant="outline"`, save = `variant="primary"`, both `size="sm-action"`, spaced with `gap-2` on the parent | [conventions](references/conventions.md) | +| **Responsive** | `max-md:`/`max-lg:` variants + `useBreakpoint()` for structure; desktop unchanged; one row of header/toolbar/stat chrome; rails → anchored drawers; row actions → kebab; popups ≤ viewport | [responsive](references/responsive.md) | | **Nothing fits** | build a reusable component — generic primitive → a new `O*` in `web/src/lib`; app-specific composition → a named component in `web/src/components`. Never hand-assemble `
` + utility classes to fake a component | [creating-components](references/creating-components.md) | **Dark mode is automatic** — every O2 component and token resolves correctly in @@ -709,9 +744,21 @@ considering the UI done: - [ ] `data-test` on every interactive and key output element, pattern `--` (see the project FE rules). - [ ] New component uses ` diff --git a/web/src/components/dbm/DbmScopeFilters.vue b/web/src/components/dbm/DbmScopeFilters.vue index 20e154c7d5..b60d16c514 100644 --- a/web/src/components/dbm/DbmScopeFilters.vue +++ b/web/src/components/dbm/DbmScopeFilters.vue @@ -104,7 +104,7 @@ along with this program. If not, see .