mirror of
https://github.com/SigNoz/signoz.git
synced 2026-10-13 13:40:41 +01:00
Compare commits
4 Commits
feat/compo
...
feat/input
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e8fba3e0b6 | ||
|
|
fd0f5cddb3 | ||
|
|
693401dbbb | ||
|
|
c9f0cc18d2 |
191
frontend/docs/popover-migration-analysis.md
Normal file
191
frontend/docs/popover-migration-analysis.md
Normal file
@@ -0,0 +1,191 @@
|
||||
# Popover migration analysis
|
||||
|
||||
The new `@signozhq/ui` Popover is click-only. This note maps the current app onto that API so a later migration does not break open state, focus, or the global `.ant-popover` overrides. It does not change any call site.
|
||||
|
||||
The component this describes lives in the design-system repo (`packages/ui/src/popover`). Storybook stories for it are `Playground`, `SaveViewForm`, `ControlledOpen`, `InsideDialog`, `MaxHeightScrolling`, and `AnchoredTypeahead`.
|
||||
|
||||
## The API to migrate onto
|
||||
|
||||
```tsx
|
||||
<Popover
|
||||
nativeButton
|
||||
side="bottom"
|
||||
align="start"
|
||||
trigger={<button type="button">Open</button>}
|
||||
open={open}
|
||||
onOpenChange={setOpen}
|
||||
>
|
||||
{content}
|
||||
</Popover>
|
||||
```
|
||||
|
||||
| Today | New Popover |
|
||||
| --- | --- |
|
||||
| antd `children` (the trigger) | `trigger` (required) |
|
||||
| antd `content`, or Radix `<PopoverContent>` | `children` |
|
||||
| antd `trigger="click"` | the only mode; there is no `openOn` |
|
||||
| antd `trigger="hover"` | not this component; a Tooltip, or a click popover when the body is interactive |
|
||||
| `placement="bottomRight"` | `side="bottom"` `align="end"` |
|
||||
| `placement="bottomLeft"` | `side="bottom"` `align="start"` |
|
||||
| `open` / `onOpenChange` | kept; `onOpenChange` receives Base UI's reason and `details.cancel()` |
|
||||
| `defaultOpen` | removed; nothing in the app starts open |
|
||||
| `overlayClassName`, `overlayStyle`, `overlayInnerStyle`, `className` | rejected on the shell; size is `width` / `maxWidth` / `maxHeight` |
|
||||
| `getPopupContainer` | `container` |
|
||||
| `destroyTooltipOnHide` | the only behavior; the popup unmounts on close |
|
||||
| `arrow` / `showArrow` | rejected; the shell has no arrow |
|
||||
| Radix `PopoverAnchor` | `anchor`, and the trigger is still required |
|
||||
| Radix `withPortal={false}` | `container` (inside a Dialog the popup already portals into the panel) |
|
||||
| Radix `onInteractOutside` | `onOpenChange` + `details.cancel()` when `reason === 'outside-press'` |
|
||||
| `sideOffset` / `alignOffset` | not props; fixed at 4px and 0 |
|
||||
|
||||
`nativeButton` is required. `true` when `trigger` renders a `<button>` (including `Button`). `false` for anything else.
|
||||
|
||||
A node that renders nothing (`null`, `false`, `''`) renders the trigger alone, with no listeners and no portal.
|
||||
|
||||
## What is in the app today
|
||||
|
||||
Counted from `frontend/src` on this branch:
|
||||
|
||||
- **29 files** import `Popover` from `antd`.
|
||||
- **7 files** import `Popover` from `@signozhq/ui` (the Radix wrapper this rework replaces). Those seven break as soon as the package ships, because `children` stops being the trigger.
|
||||
- **18 SCSS files** override `.ant-popover*`.
|
||||
|
||||
antd `Dropdown` is already on `@signozhq/ui/dropdown`. antd `Tooltip` (on the order of 100 importers) is a separate migration.
|
||||
|
||||
### antd files
|
||||
|
||||
`src/components/CustomTimePicker/CustomTimePicker.tsx`
|
||||
`src/components/DownloadOptionsMenu/DownloadOptionsMenu.tsx`
|
||||
`src/components/ErrorPopover/ErrorPopover.tsx`
|
||||
`src/components/ExplorerCard/ExplorerCard.tsx`
|
||||
`src/components/HeaderRightSection/HeaderRightSection.tsx`
|
||||
`src/components/Logs/AddToQueryHOC.tsx`
|
||||
`src/components/LogsFormatOptionsMenu/LogsFormatOptionsMenu.tsx`
|
||||
`src/components/QueryBuilderV2/QueryV2/QueryAggregation/QueryAggregationSelect.tsx`
|
||||
`src/components/QueryBuilderV2/QueryV2/QuerySearch/QuerySearch.tsx`
|
||||
`src/components/QueryBuilderV2/QueryV2/TraceOperator/TraceOperatorEditor.tsx`
|
||||
`src/components/TableRenderer/LabelColumn.tsx`
|
||||
`src/components/WarningPopover/WarningPopover.tsx`
|
||||
`src/container/AlertHistory/AlertPopover/AlertPopover.tsx`
|
||||
`src/container/CreateAlertV2/EvaluationSettings/EvaluationSettings.tsx`
|
||||
`src/container/Home/Home.tsx`
|
||||
`src/container/MetricsExplorer/MetricDetails/AllAttributes.tsx`
|
||||
`src/container/MetricsExplorer/MetricDetails/AllAttributesValue.tsx`
|
||||
`src/container/MetricsExplorer/Summary/MetricNameSearch.tsx`
|
||||
`src/container/OptionsMenu/index.tsx`
|
||||
`src/container/TopNav/AutoRefreshV2/index.tsx`
|
||||
`src/pages/DashboardsListPage/components/ListHeader/ListHeader.tsx`
|
||||
`src/pages/MetricsApplication/ApDex/ApDexApplication.tsx`
|
||||
`src/pages/PublicDashboard/PublicDashboardView/PublicAutoRefresh/PublicAutoRefresh.tsx`
|
||||
`src/pages/TraceDetailsV3/SpanDetailsPanel/Events/components/AttributeWithExpandablePopover.tsx`
|
||||
`src/pages/TracesFunnelDetails/components/FunnelConfiguration/FunnelStepPopover.tsx`
|
||||
`src/pages/TracesFunnels/components/FunnelsList/FunnelItemPopover.tsx`
|
||||
`src/pages/TracesFunnels/components/SearchBar/SearchBar.tsx`
|
||||
`src/periscope/components/ContextMenu/index.tsx`
|
||||
`src/periscope/components/SeeMore/SeeMore.tsx`
|
||||
|
||||
### Existing `@signozhq/ui` popovers
|
||||
|
||||
`src/components/ExportMenu/ExportMenu.tsx`
|
||||
`src/components/MarkdownEditor/MarkdownHelp.tsx`
|
||||
`src/components/TanStackTableView/TanStackHeaderRow.tsx`
|
||||
`src/container/AIAssistant/components/ChatInput/ChatInput.tsx`
|
||||
`src/container/ListAlertRules/components/ColumnSelector.tsx`
|
||||
`src/pages/DashboardPage/DashboardContainer/PanelEditor/ConfigPane/sections/ContextLinksSection/VariablesPopover.tsx`
|
||||
`src/pages/DashboardsListPage/components/ViewsRail/ViewNamePopover.tsx`
|
||||
|
||||
### SCSS that overrides `.ant-popover`
|
||||
|
||||
`src/components/DownloadOptionsMenu/DownloadOptionsMenu.styles.scss`
|
||||
`src/components/LogsFormatOptionsMenu/LogsFormatOptionsMenu.styles.scss`
|
||||
`src/components/QueryBuilderV2/QueryV2/QueryAggregation/QueryAggregation.styles.scss`
|
||||
`src/components/QueryBuilderV2/QueryV2/QuerySearch/QuerySearch.styles.scss`
|
||||
`src/container/AlertHistory/AlertPopover/AlertPopover.styles.scss`
|
||||
`src/container/AlertHistory/Statistics/TopContributorsCard/TopContributorsCard.styles.scss`
|
||||
`src/container/CreateAlertV2/EvaluationSettings/styles.scss`
|
||||
`src/container/Home/Home.styles.scss`
|
||||
`src/container/MetricsExplorer/MetricDetails/MetricDetails.styles.scss`
|
||||
`src/container/MetricsExplorer/Summary/Summary.styles.scss`
|
||||
`src/container/TopNav/AutoRefreshV2/AutoRefreshV2.styles.scss`
|
||||
`src/container/TopNav/DateTimeSelectionV2/DateTimeSelectionV2.styles.scss`
|
||||
`src/container/WidgetCard/WidgetCard.styles.scss`
|
||||
`src/pages/DashboardsListPage/components/ListHeader/ListHeader.module.scss`
|
||||
`src/pages/TracesFunnels/TracesFunnels.styles.scss`
|
||||
`src/pages/TracesFunnels/components/FunnelsList/FunnelsList.styles.scss`
|
||||
`src/pages/TracesFunnels/components/SearchBar/SearchBar.styles.scss`
|
||||
`src/periscope/components/ContextMenu/styles.scss`
|
||||
|
||||
Three of these are unscoped and restyle every antd popover on the page, not just their own:
|
||||
|
||||
- `src/container/MetricsExplorer/Summary/Summary.styles.scss`
|
||||
- `src/container/CreateAlertV2/EvaluationSettings/styles.scss`
|
||||
- `src/container/AlertHistory/Statistics/TopContributorsCard/TopContributorsCard.styles.scss`
|
||||
|
||||
Scope or delete those three before moving any nearby popover, or a migrated popup and a leftover antd popup will share rules that only one of them understands.
|
||||
|
||||
Most of the other files repeat one recipe: padding 0, 4px radius, a 1px border, a translucent background, the arrow hidden. The new shell already has zero padding, `--radius-2`, and no arrow. Per-site size (fixed widths around 187–500px, max-width 600px, max-height 254px) becomes `width` / `maxWidth` / `maxHeight`. Do not re-apply the recipe as a `className`; the component rejects it.
|
||||
|
||||
## What should not become this Popover
|
||||
|
||||
**Hover descriptions.** `AddToQueryHOC`, `LabelColumn`, the three Query Builder validation popovers, `SeeMore`, and `AttributeWithExpandablePopover` open on hover. Those are Tooltips. `AttributeWithExpandablePopover` shows a value the user copies; if Tooltip cannot hold that, make it a click popover instead of keeping hover.
|
||||
|
||||
**WarningPopover** (imported from about 10 files) is hover today and contains docs links. A tooltip cannot host those links. Either the links move out and the summary becomes a Tooltip, or the wrapper becomes a click Popover with `maxWidth` around 600.
|
||||
|
||||
**Menus.** Funnel item and funnel step action lists, the sort lists in `ListHeader` and Traces Funnels `SearchBar`, `AutoRefreshV2` / `PublicAutoRefresh` interval lists, and `AlertPopover`'s two links are rows that close when one is picked. Those are Dropdowns. `AllAttributes` per-row actions are the same shape.
|
||||
|
||||
**ContextMenu** (`src/periscope/components/ContextMenu/index.tsx`, imported across the dashboard drilldown) opens at chart-click coordinates. There is no trigger element. The new Popover requires `trigger`, so this stays a context menu. Do not fold the phantom span, `position: fixed` overlay, `zIndex: 10000`, or `calculatePopoverPosition.ts` into `anchor`. `anchor` is for a popover that already has a trigger and should position against something else (the typeahead input row).
|
||||
|
||||
**Inspect charts.** `MetricsExplorer/Inspect` `GraphPopover` and `HoverPopover` are absolutely positioned cards, not antd Popover. Leave them until there is a context-menu or anchor design that matches them.
|
||||
|
||||
**PanelStatusPopover** is a Tooltip despite the name.
|
||||
|
||||
## What should become this Popover
|
||||
|
||||
Click panels whose content is a form, a picker, or a composed layout:
|
||||
|
||||
- `CustomTimePicker` — the most visible one. Controlled `open`, `container` for `getPopupContainer`, and it already unmounts on hide. It opens from both input focus and click; that logic stays in the caller, with `onOpenChange`.
|
||||
- `EvaluationSettings` / `EvaluationWindowPopover` — controlled, wide (`width` / `maxWidth`), and sitting on a global `.ant-popover` override. Migrate the override in the same change.
|
||||
- `HeaderRightSection` — three controlled forms (feedback, announcements, share).
|
||||
- `ExplorerCard` save-view form and `ViewNamePopover` — close only after the save succeeds, so `open` stays controlled. `ViewNamePopover` is already on the library and will not compile against the new props.
|
||||
- `ApDex` threshold form.
|
||||
- `Home` welcome checklist — drop the transparent-shell override; the shell is the component's.
|
||||
- `OptionsMenu`, `DownloadOptionsMenu`, `LogsFormatOptionsMenu` — click panels. `LogsFormatOptionsMenu` nests a submenu; the inner popup must be portalled, and the outer `onOpenChange` must `cancel()` an outside press that lands in the inner popup.
|
||||
- `MetricNameSearch` — controlled search panel. Scope the global padding override in `Summary.styles.scss` in the same change.
|
||||
- `AllAttributesValue` — one popover open at a time in a list. That is controlled `open` keyed by row, which the API still allows.
|
||||
- `ChatInput` context picker — already on the library; `side="top"` `align="end"` plus `width`.
|
||||
- `VariablesPopover` — already on the library. Keep `anchor` on the input row, `initialFocus={false}` so the caret stays, and `details.cancel()` when the outside press is the anchor. `withPortal={false}` becomes the default dialog `container`, not an unportalled popup.
|
||||
- `ColumnSelector`, `MarkdownHelp`, `TanStackHeaderRow`, `ExportMenu` — Radix `asChild` trigger plus content. The trigger moves to the `trigger` prop, content moves to `children`, and `className` on the shell becomes `width` / `maxWidth`.
|
||||
|
||||
`ErrorPopover` is a thin wrapper. Re-point it at the new props and its consumer follows.
|
||||
|
||||
## Order
|
||||
|
||||
Before any change: screenshot every current popover, in both themes, so each call site's pattern is on record and a component swap is reviewable against it. Flag the misusages to design as they turn up — links inside hover surfaces above all. Swapping the component during migration is expected where the old call site was on the wrong one; antd call sites stay untouched until their turn.
|
||||
|
||||
1. Scope the three global `.ant-popover` files so later migrations cannot restyle leftover antd popovers.
|
||||
2. Move the seven existing `@signozhq/ui` call sites first. They break at the package bump; the 29 antd files do not, until each one is switched.
|
||||
3. Migrate shared wrappers that are actually popovers (`ErrorPopover`, then `WarningPopover` once the links decision is made). Leave `ContextMenu` on antd until a context menu exists.
|
||||
4. Migrate click forms (`CustomTimePicker`, evaluation window, header menus, save-view, ApDex).
|
||||
5. Send hover descriptions to Tooltip and row menus to Dropdown, instead of forcing them through Popover.
|
||||
6. Delete the matching `.ant-popover` rules in the same change as each call site, so a half-migrated page is not styled twice.
|
||||
|
||||
## Risks
|
||||
|
||||
- **Controlled `open` is load-bearing.** Save forms close from inside the popup only when the mutation succeeds. A list keeps one popover open at a time. Dropping `open` and driving everything from `onOpenChange` alone cannot express either. Keep the controlled prop at those sites.
|
||||
- **Focus moves in.** Click popovers focus the first tabbable child. Typeaheads (`VariablesPopover`, and any filter that must keep the caret) need `initialFocus={false}`.
|
||||
- **Outside press and anchors.** A press on the anchor looks outside. Call `details.cancel()` or the typeahead closes when the user clicks the input it is anchored to.
|
||||
- **Dialogs.** Inside a Dialog or Drawer the popup portals into the panel so the focus trap can reach it. Do not set `container={document.body}` there.
|
||||
- **Z-index.** The shell starts at `--popover-z-index` (50) and lifts above the layer its trigger sits in. A Tooltip inside a row must still paint above the popup. The app ladder today is tooltip 1070, dropdown 1050, popover 1030, modal 1000. Check a truncated row tooltip inside a popover after the first real migration.
|
||||
- **Scroll locking.** Chart popovers measure the wrong box when a modal has locked scroll. This popover does not lock scroll (`modal` is rejected). Re-check Infrastructure Monitoring charts if a surrounding modal is open.
|
||||
- **`nativeButton` mismatches.** Base UI logs an error when the flag does not match the element. Every non-button trigger (a badge, a custom row, an input) needs `nativeButton={false}`.
|
||||
|
||||
## `--popover-foreground` audit
|
||||
|
||||
The shell reads `--popover-background`, `--popover-foreground`, `--popover-border`, and `--popover-shadow`. Those semantic values belong to the token package, not to call sites.
|
||||
|
||||
Two legacy shadcn variables predate those hooks:
|
||||
|
||||
- `--popover-foreground` is the foreground hook the shell uses. It should stay pointed at the same foreground as the rest of the light and dark themes (`--l1-foreground` today).
|
||||
- `--popover` (no suffix) is the old background. `--calendar-select-background` still reads it. Do not repoint `--popover` at `--popover-background` or at `surface-3` until the calendar token is audited, or date selection changes color with the popover shell.
|
||||
|
||||
Call sites that pass antd's `color` (a gradient string on `AlertPopover`, a raw token on `SeeMore`) do not map to a prop. The shell color comes from the tokens. Content that needs its own background paints it inside `children`.
|
||||
@@ -52,9 +52,9 @@
|
||||
"@monaco-editor/react": "^4.7.0",
|
||||
"@sentry/react": "10.57.0",
|
||||
"@sentry/vite-plugin": "5.3.0",
|
||||
"@signozhq/design-tokens": "2.2.1",
|
||||
"@signozhq/design-tokens": "2.3.0",
|
||||
"@signozhq/icons": "0.4.0",
|
||||
"@signozhq/ui": "0.2.0",
|
||||
"@signozhq/ui": "0.3.0",
|
||||
"@tanstack/react-table": "8.21.3",
|
||||
"@tanstack/react-virtual": "3.13.22",
|
||||
"@uiw/codemirror-theme-copilot": "4.23.11",
|
||||
|
||||
450
frontend/pnpm-lock.yaml
generated
450
frontend/pnpm-lock.yaml
generated
@@ -72,14 +72,14 @@ importers:
|
||||
specifier: 5.3.0
|
||||
version: 5.3.0
|
||||
'@signozhq/design-tokens':
|
||||
specifier: 2.2.1
|
||||
version: 2.2.1
|
||||
specifier: 2.3.0
|
||||
version: 2.3.0
|
||||
'@signozhq/icons':
|
||||
specifier: 0.4.0
|
||||
version: 0.4.0
|
||||
'@signozhq/ui':
|
||||
specifier: 0.2.0
|
||||
version: 0.2.0(@date-fns/tz@1.4.1)(@emotion/is-prop-valid@1.2.0)(@signozhq/icons@0.4.0)(@types/react-dom@18.0.10)(@types/react@18.0.26)(date-fns@4.1.0)(react-dom@18.2.0(react@18.2.0))(react-router-dom@5.3.4(react@18.2.0))(react-router@6.30.6(react@18.2.0))(react@18.2.0)
|
||||
specifier: 0.3.0
|
||||
version: 0.3.0(@date-fns/tz@1.4.1)(@emotion/is-prop-valid@1.2.0)(@signozhq/icons@0.4.0)(@types/react-dom@18.0.10)(@types/react@18.0.26)(date-fns@4.1.0)(react-dom@18.2.0(react@18.2.0))(react-router-dom@5.3.4(react@18.2.0))(react-router@6.30.6(react@18.2.0))(react@18.2.0)
|
||||
'@tanstack/react-table':
|
||||
specifier: 8.21.3
|
||||
version: 8.21.3(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
@@ -2928,9 +2928,6 @@ packages:
|
||||
'@posthog/core@1.6.0':
|
||||
resolution: {integrity: sha512-Tbh8UACwbb7jFdDC7wwXHtfNzO+4wKh3VbyMHmp2UBe6w1jliJixexTJNfkqdGZm+ht3M10mcKvGGPnoZ2zLBg==}
|
||||
|
||||
'@radix-ui/number@1.1.3':
|
||||
resolution: {integrity: sha512-Road2bidD0uu/1BGDOWNdPI06g0lIRy6IF9GZcIrDK2KGItfor8IQwQa+yM2ERgHM1MmHxaxpTzk0/Jp42lNfA==}
|
||||
|
||||
'@radix-ui/primitive@1.1.7':
|
||||
resolution: {integrity: sha512-rqWnm76nYT8HoNNqEjpgJ7Pw/DrBj5iBTrmEPo6HTX5+VJyBNOqTdv4g89G63HuR5g0AaENoAcH7Is5fF2kZ8Q==}
|
||||
|
||||
@@ -2947,19 +2944,6 @@ packages:
|
||||
'@types/react-dom':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-collection@1.1.15':
|
||||
resolution: {integrity: sha512-9W+B9NPF0NaaPh/1NJd3+KqsnlLqU9H7T2rvww+fp+T/evVXdNAyYcnfRQZFOjkR1ajQp3yORlqnI8soawLvNA==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
'@types/react-dom': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
'@types/react-dom':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-compose-refs@1.1.5':
|
||||
resolution: {integrity: sha512-+48PbAAbq3didjJxa+OaWY2ZwgAKsNiRGyeHKszblZMQ+kcpd9pAaT11cMkGEie0vsOi3QdeTE6d5Fe3Gn61kA==}
|
||||
peerDependencies:
|
||||
@@ -2991,15 +2975,6 @@ packages:
|
||||
'@types/react-dom':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-direction@1.1.4':
|
||||
resolution: {integrity: sha512-5pzg4FGQNpExhnhT2zlrP1wZFaYCd1K0nYWoFAdcYoYK868IEigqMX3B3f8yIoRlAhAeDWciLI6ZdCKHF9P4Vg==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-dismissable-layer@1.1.19':
|
||||
resolution: {integrity: sha512-8g4pfOL9HoKKLWGiypT+dphVqjFfmcXO5GBnhsG6zI+lxAx/8feQpr+1LSN8Re3hiZ+XkLNS4O9ztK11/LzQ6w==}
|
||||
peerDependencies:
|
||||
@@ -3114,71 +3089,6 @@ packages:
|
||||
'@types/react-dom':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-progress@1.1.16':
|
||||
resolution: {integrity: sha512-5XnomAsoZZCY+KNTxbIghpGqPruZvKFNlvcAljVAOdDRDsH4/OZQxhtwo5wdtoDM5R6MhJBb2sPnDuRFep3lzg==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
'@types/react-dom': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
'@types/react-dom':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-radio-group@1.4.7':
|
||||
resolution: {integrity: sha512-cgYFEkntCxppHZgtSZ+7vh0wbZQ+IC7PPMw8DSnRG27B6kDd32/Zw0OJt7dGDigCoprMuWHjg2PvUn3PYvPFoQ==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
'@types/react-dom': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
'@types/react-dom':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-roving-focus@1.1.19':
|
||||
resolution: {integrity: sha512-V9jI6hDjT7l3jsCQD9bLNvDLM3tH/gdbOTp7Tefp3hbbgCGQoK7tUvrWiRlcoBHIZ809ElXwNQwVo0B98LuTXQ==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
'@types/react-dom': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
'@types/react-dom':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-select@2.3.7':
|
||||
resolution: {integrity: sha512-WFGImkmbzcfxeIwq/+4HvRN0pizBwbwQUED4I13ezQsDdfl38ZntN6TmR8XaSzPBqoCToe8rF75j6NPNDSzhbg==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
'@types/react-dom': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
'@types/react-dom':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-slider@1.4.7':
|
||||
resolution: {integrity: sha512-mTSLf1GC/C0moWjTbvCM6Qn/gBjvlFt1azuWF2v7MN5C3Zq2U2J2lN3ZEYkpujuOU5Ro7A28wkviSxaKnG0BYg==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
'@types/react-dom': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
'@types/react-dom':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-slot@1.3.3':
|
||||
resolution: {integrity: sha512-qx7oqnYbxnK9kYI9m317qmFmEgo6ywqWvbTogdj7cL9p3/yx4M48p7Rnw5z3H890cL/ow/EeWJsuTykeZVXP5Q==}
|
||||
peerDependencies:
|
||||
@@ -3188,19 +3098,6 @@ packages:
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-tabs@1.1.21':
|
||||
resolution: {integrity: sha512-UKxJlZid7FVtsk/WTxj4i4uSEgj2Au+KBbS7SQyTlzMhhn+86Cz3tISZdTa87bfEfcuvZezf2ZsxD4xuEKtkog==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
'@types/react-dom': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
'@types/react-dom':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-use-callback-ref@1.1.4':
|
||||
resolution: {integrity: sha512-R6OUY2e2fA6Yn6s+VSx5KBV6Nx8LQEhu+cz7LCej18rQ1HLyg9PSC9jP/ZNx0o6FAIK9c0F1kHylzSxKsdlkrQ==}
|
||||
peerDependencies:
|
||||
@@ -3228,15 +3125,6 @@ packages:
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-use-is-hydrated@0.1.3':
|
||||
resolution: {integrity: sha512-umO/aJ+82CpOnhDZUTbILCQf7kU/g0iv+oGs/Q8jw7IkhWBzaEP4sA268PhFAJTFetbwp3ICc6ktpI4TqtxcIw==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-use-layout-effect@1.1.4':
|
||||
resolution: {integrity: sha512-K20DkRkUwDnxEYMBPcg3Y6voLkEy5p5QQmszZgLngKKiC7dzBR/aEuK3w1qlx2JWDUNH6FluahYdgR3BP+QbYw==}
|
||||
peerDependencies:
|
||||
@@ -3246,15 +3134,6 @@ packages:
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-use-previous@1.1.4':
|
||||
resolution: {integrity: sha512-XoSLhbRbqxFtgJoi2fNHA3C6pDlY34x508vUpUGoFZfvePfHXHbE1lC4FYFMnJWgiCRroSTw6fOsXQoVS9RwZg==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-use-rect@1.1.4':
|
||||
resolution: {integrity: sha512-cSOCh6JlkmfjLyNcLiu2nB4v+nm+dkZ+Q5KHWk/soo4U7ZLiEQFKHK9/YmtBHjfCEaU43IBKQOc4/uJmCaiCTQ==}
|
||||
peerDependencies:
|
||||
@@ -3273,19 +3152,6 @@ packages:
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-visually-hidden@1.2.11':
|
||||
resolution: {integrity: sha512-NFS86RYYZb4/exihaESBGOpMJFz8MGLAfu3mOBSGByVnVPC9JPASfYubxd/8KbkQK0sYAv8lVQDEQukDX/qXvQ==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
'@types/react-dom': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
'@types/react-dom':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/rect@1.1.3':
|
||||
resolution: {integrity: sha512-JtyZR+mqgBibTo8xea3B6ZRmzZiM/YeVBtUkas6zMuXjAlfIFIW2FgqeM9eLyvEaYX66vr6DJMK+4U6LV0KhNw==}
|
||||
|
||||
@@ -3608,14 +3474,14 @@ packages:
|
||||
'@sideway/pinpoint@2.0.0':
|
||||
resolution: {integrity: sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==}
|
||||
|
||||
'@signozhq/design-tokens@2.2.1':
|
||||
resolution: {integrity: sha512-ulf6lhWUP6qxqsyemQySUMfOIKRvefvZ5rBRv+wlhOpPfYPk3uatPmPkKEhAjISpX8f9g1Gg8wgRZxUCysf20Q==}
|
||||
'@signozhq/design-tokens@2.3.0':
|
||||
resolution: {integrity: sha512-gVTPc2uJwBsTF9VMzPghgsnv6eioK8oxzES1CLDxedxpcfwbWltuzxVPwhPuoC54E2vItDVZhR9RFI/JZ50ldw==}
|
||||
|
||||
'@signozhq/icons@0.4.0':
|
||||
resolution: {integrity: sha512-QctNvNPlQlsefFdIVmSy/P/7a1IcQ3IryKP0KPMqtoqew3q89FGUmFa5IfEJ7Z3CHhtG1zaXwBKqP72QkLyA3g==}
|
||||
|
||||
'@signozhq/ui@0.2.0':
|
||||
resolution: {integrity: sha512-j20ldiOkR150ON0BCDhlq6fXGoPN++7w6e4t2HtM2XMM4buJO7aHzPzNKvDRZ2UUf/LGPULPAW8w6sg6gUVHyw==}
|
||||
'@signozhq/ui@0.3.0':
|
||||
resolution: {integrity: sha512-Khzr3/YC/QACwBi1zfQwt+h88iyuIu9hm5hkcIkuCTteD9P+r13US/NN4kkbJe8IHyZXCoDkoIpmQxzbrA3/SA==}
|
||||
peerDependencies:
|
||||
'@signozhq/icons': 0.3.0
|
||||
react: ^18.2.0
|
||||
@@ -4985,12 +4851,6 @@ packages:
|
||||
resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
cmdk@1.1.1:
|
||||
resolution: {integrity: sha512-Vsv7kFaXm+ptHDMZ7izaRsP70GgrW9NBNGswt9OZaVBLlE0SNpDq8eu/VGXyF9r7M0azK3Wy7OlYXsuyYLFzHg==}
|
||||
peerDependencies:
|
||||
react: ^18 || ^19 || ^19.0.0-rc
|
||||
react-dom: ^18 || ^19 || ^19.0.0-rc
|
||||
|
||||
co@4.6.0:
|
||||
resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==}
|
||||
engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'}
|
||||
@@ -7593,12 +7453,6 @@ packages:
|
||||
neo-async@2.6.2:
|
||||
resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==}
|
||||
|
||||
next-themes@0.4.6:
|
||||
resolution: {integrity: sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==}
|
||||
peerDependencies:
|
||||
react: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc
|
||||
react-dom: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc
|
||||
|
||||
no-case@3.0.4:
|
||||
resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==}
|
||||
|
||||
@@ -8589,8 +8443,8 @@ packages:
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
react-resizable-panels@4.12.3:
|
||||
resolution: {integrity: sha512-GHMJWnDXui/3RX4bT+cgBP+N3N2nkwcoZATr/2xLFpqQQe7TlBrE0cGM0dUa9ceT7A90tUrSRsiqgRG+g0/2AA==}
|
||||
react-resizable-panels@4.12.2:
|
||||
resolution: {integrity: sha512-NwY5LCo4WrxVvDh0xoMML6EMLPONP/8ckKcIdpnojxexoatZdjLiRqLJQjQK5CPkd4SYiB/2M5BVrjZBQtOO7Q==}
|
||||
peerDependencies:
|
||||
react: ^18.0.0 || ^19.0.0
|
||||
react-dom: ^18.0.0 || ^19.0.0
|
||||
@@ -9093,12 +8947,6 @@ packages:
|
||||
resolution: {integrity: sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg==}
|
||||
engines: {node: '>=20'}
|
||||
|
||||
sonner@2.0.7:
|
||||
resolution: {integrity: sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w==}
|
||||
peerDependencies:
|
||||
react: ^18.0.0 || ^19.0.0 || ^19.0.0-rc
|
||||
react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-rc
|
||||
|
||||
sort-asc@0.1.0:
|
||||
resolution: {integrity: sha512-jBgdDd+rQ+HkZF2/OHCmace5dvpos/aWQpcxuyRs9QUbPRnkEJmYVo81PIGpjIdpOcsnJ4rGjStfDHsbn+UVyw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -10120,7 +9968,7 @@ snapshots:
|
||||
|
||||
'@ant-design/cssinjs@1.17.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.2
|
||||
'@babel/runtime': 7.29.2
|
||||
'@emotion/hash': 0.8.0
|
||||
'@emotion/unitless': 0.7.5
|
||||
classnames: 2.3.2
|
||||
@@ -10136,7 +9984,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@ant-design/colors': 7.0.0
|
||||
'@ant-design/icons-svg': 4.3.1
|
||||
'@babel/runtime': 7.28.2
|
||||
'@babel/runtime': 7.29.2
|
||||
classnames: 2.3.2
|
||||
rc-util: 5.38.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
react: 18.2.0
|
||||
@@ -10144,7 +9992,7 @@ snapshots:
|
||||
|
||||
'@ant-design/react-slick@1.0.2(react@18.2.0)':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.2
|
||||
'@babel/runtime': 7.29.2
|
||||
classnames: 2.3.2
|
||||
json2mq: 0.2.0
|
||||
react: 18.2.0
|
||||
@@ -12670,8 +12518,6 @@ snapshots:
|
||||
dependencies:
|
||||
cross-spawn: 7.0.6
|
||||
|
||||
'@radix-ui/number@1.1.3': {}
|
||||
|
||||
'@radix-ui/primitive@1.1.7': {}
|
||||
|
||||
'@radix-ui/react-arrow@1.1.15(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
|
||||
@@ -12683,18 +12529,6 @@ snapshots:
|
||||
'@types/react': 18.0.26
|
||||
'@types/react-dom': 18.0.10
|
||||
|
||||
'@radix-ui/react-collection@1.1.15(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
|
||||
dependencies:
|
||||
'@radix-ui/react-compose-refs': 1.1.5(@types/react@18.0.26)(react@18.2.0)
|
||||
'@radix-ui/react-context': 1.2.2(@types/react@18.0.26)(react@18.2.0)
|
||||
'@radix-ui/react-primitive': 2.1.10(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
'@radix-ui/react-slot': 1.3.3(@types/react@18.0.26)(react@18.2.0)
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
optionalDependencies:
|
||||
'@types/react': 18.0.26
|
||||
'@types/react-dom': 18.0.10
|
||||
|
||||
'@radix-ui/react-compose-refs@1.1.5(@types/react@18.0.26)(react@18.2.0)':
|
||||
dependencies:
|
||||
react: 18.2.0
|
||||
@@ -12730,12 +12564,6 @@ snapshots:
|
||||
'@types/react': 18.0.26
|
||||
'@types/react-dom': 18.0.10
|
||||
|
||||
'@radix-ui/react-direction@1.1.4(@types/react@18.0.26)(react@18.2.0)':
|
||||
dependencies:
|
||||
react: 18.2.0
|
||||
optionalDependencies:
|
||||
'@types/react': 18.0.26
|
||||
|
||||
'@radix-ui/react-dismissable-layer@1.1.19(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
|
||||
dependencies:
|
||||
'@radix-ui/primitive': 1.1.7
|
||||
@@ -12846,101 +12674,6 @@ snapshots:
|
||||
'@types/react': 18.0.26
|
||||
'@types/react-dom': 18.0.10
|
||||
|
||||
'@radix-ui/react-progress@1.1.16(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
|
||||
dependencies:
|
||||
'@radix-ui/react-context': 1.2.2(@types/react@18.0.26)(react@18.2.0)
|
||||
'@radix-ui/react-primitive': 2.1.10(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
optionalDependencies:
|
||||
'@types/react': 18.0.26
|
||||
'@types/react-dom': 18.0.10
|
||||
|
||||
'@radix-ui/react-radio-group@1.4.7(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
|
||||
dependencies:
|
||||
'@radix-ui/primitive': 1.1.7
|
||||
'@radix-ui/react-compose-refs': 1.1.5(@types/react@18.0.26)(react@18.2.0)
|
||||
'@radix-ui/react-context': 1.2.2(@types/react@18.0.26)(react@18.2.0)
|
||||
'@radix-ui/react-direction': 1.1.4(@types/react@18.0.26)(react@18.2.0)
|
||||
'@radix-ui/react-presence': 1.1.10(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
'@radix-ui/react-primitive': 2.1.10(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
'@radix-ui/react-roving-focus': 1.1.19(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
'@radix-ui/react-use-controllable-state': 1.2.6(@types/react@18.0.26)(react@18.2.0)
|
||||
'@radix-ui/react-use-size': 1.1.4(@types/react@18.0.26)(react@18.2.0)
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
optionalDependencies:
|
||||
'@types/react': 18.0.26
|
||||
'@types/react-dom': 18.0.10
|
||||
|
||||
'@radix-ui/react-roving-focus@1.1.19(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
|
||||
dependencies:
|
||||
'@radix-ui/primitive': 1.1.7
|
||||
'@radix-ui/react-collection': 1.1.15(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
'@radix-ui/react-compose-refs': 1.1.5(@types/react@18.0.26)(react@18.2.0)
|
||||
'@radix-ui/react-context': 1.2.2(@types/react@18.0.26)(react@18.2.0)
|
||||
'@radix-ui/react-direction': 1.1.4(@types/react@18.0.26)(react@18.2.0)
|
||||
'@radix-ui/react-id': 1.1.4(@types/react@18.0.26)(react@18.2.0)
|
||||
'@radix-ui/react-primitive': 2.1.10(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
'@radix-ui/react-use-callback-ref': 1.1.4(@types/react@18.0.26)(react@18.2.0)
|
||||
'@radix-ui/react-use-controllable-state': 1.2.6(@types/react@18.0.26)(react@18.2.0)
|
||||
'@radix-ui/react-use-is-hydrated': 0.1.3(@types/react@18.0.26)(react@18.2.0)
|
||||
'@radix-ui/react-use-layout-effect': 1.1.4(@types/react@18.0.26)(react@18.2.0)
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
optionalDependencies:
|
||||
'@types/react': 18.0.26
|
||||
'@types/react-dom': 18.0.10
|
||||
|
||||
'@radix-ui/react-select@2.3.7(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
|
||||
dependencies:
|
||||
'@radix-ui/number': 1.1.3
|
||||
'@radix-ui/primitive': 1.1.7
|
||||
'@radix-ui/react-collection': 1.1.15(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
'@radix-ui/react-compose-refs': 1.1.5(@types/react@18.0.26)(react@18.2.0)
|
||||
'@radix-ui/react-context': 1.2.2(@types/react@18.0.26)(react@18.2.0)
|
||||
'@radix-ui/react-direction': 1.1.4(@types/react@18.0.26)(react@18.2.0)
|
||||
'@radix-ui/react-dismissable-layer': 1.1.19(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
'@radix-ui/react-focus-guards': 1.1.6(@types/react@18.0.26)(react@18.2.0)
|
||||
'@radix-ui/react-focus-scope': 1.1.16(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
'@radix-ui/react-id': 1.1.4(@types/react@18.0.26)(react@18.2.0)
|
||||
'@radix-ui/react-popper': 1.3.7(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
'@radix-ui/react-portal': 1.1.17(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
'@radix-ui/react-presence': 1.1.10(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
'@radix-ui/react-primitive': 2.1.10(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
'@radix-ui/react-slot': 1.3.3(@types/react@18.0.26)(react@18.2.0)
|
||||
'@radix-ui/react-use-callback-ref': 1.1.4(@types/react@18.0.26)(react@18.2.0)
|
||||
'@radix-ui/react-use-controllable-state': 1.2.6(@types/react@18.0.26)(react@18.2.0)
|
||||
'@radix-ui/react-use-layout-effect': 1.1.4(@types/react@18.0.26)(react@18.2.0)
|
||||
'@radix-ui/react-use-previous': 1.1.4(@types/react@18.0.26)(react@18.2.0)
|
||||
'@radix-ui/react-visually-hidden': 1.2.11(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
aria-hidden: 1.2.6
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
react-remove-scroll: 2.7.2(@types/react@18.0.26)(react@18.2.0)
|
||||
optionalDependencies:
|
||||
'@types/react': 18.0.26
|
||||
'@types/react-dom': 18.0.10
|
||||
|
||||
'@radix-ui/react-slider@1.4.7(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
|
||||
dependencies:
|
||||
'@radix-ui/number': 1.1.3
|
||||
'@radix-ui/primitive': 1.1.7
|
||||
'@radix-ui/react-collection': 1.1.15(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
'@radix-ui/react-compose-refs': 1.1.5(@types/react@18.0.26)(react@18.2.0)
|
||||
'@radix-ui/react-context': 1.2.2(@types/react@18.0.26)(react@18.2.0)
|
||||
'@radix-ui/react-direction': 1.1.4(@types/react@18.0.26)(react@18.2.0)
|
||||
'@radix-ui/react-primitive': 2.1.10(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
'@radix-ui/react-use-controllable-state': 1.2.6(@types/react@18.0.26)(react@18.2.0)
|
||||
'@radix-ui/react-use-layout-effect': 1.1.4(@types/react@18.0.26)(react@18.2.0)
|
||||
'@radix-ui/react-use-previous': 1.1.4(@types/react@18.0.26)(react@18.2.0)
|
||||
'@radix-ui/react-use-size': 1.1.4(@types/react@18.0.26)(react@18.2.0)
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
optionalDependencies:
|
||||
'@types/react': 18.0.26
|
||||
'@types/react-dom': 18.0.10
|
||||
|
||||
'@radix-ui/react-slot@1.3.3(@types/react@18.0.26)(react@18.2.0)':
|
||||
dependencies:
|
||||
'@radix-ui/react-compose-refs': 1.1.5(@types/react@18.0.26)(react@18.2.0)
|
||||
@@ -12948,22 +12681,6 @@ snapshots:
|
||||
optionalDependencies:
|
||||
'@types/react': 18.0.26
|
||||
|
||||
'@radix-ui/react-tabs@1.1.21(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
|
||||
dependencies:
|
||||
'@radix-ui/primitive': 1.1.7
|
||||
'@radix-ui/react-context': 1.2.2(@types/react@18.0.26)(react@18.2.0)
|
||||
'@radix-ui/react-direction': 1.1.4(@types/react@18.0.26)(react@18.2.0)
|
||||
'@radix-ui/react-id': 1.1.4(@types/react@18.0.26)(react@18.2.0)
|
||||
'@radix-ui/react-presence': 1.1.10(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
'@radix-ui/react-primitive': 2.1.10(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
'@radix-ui/react-roving-focus': 1.1.19(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
'@radix-ui/react-use-controllable-state': 1.2.6(@types/react@18.0.26)(react@18.2.0)
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
optionalDependencies:
|
||||
'@types/react': 18.0.26
|
||||
'@types/react-dom': 18.0.10
|
||||
|
||||
'@radix-ui/react-use-callback-ref@1.1.4(@types/react@18.0.26)(react@18.2.0)':
|
||||
dependencies:
|
||||
react: 18.2.0
|
||||
@@ -12986,24 +12703,12 @@ snapshots:
|
||||
optionalDependencies:
|
||||
'@types/react': 18.0.26
|
||||
|
||||
'@radix-ui/react-use-is-hydrated@0.1.3(@types/react@18.0.26)(react@18.2.0)':
|
||||
dependencies:
|
||||
react: 18.2.0
|
||||
optionalDependencies:
|
||||
'@types/react': 18.0.26
|
||||
|
||||
'@radix-ui/react-use-layout-effect@1.1.4(@types/react@18.0.26)(react@18.2.0)':
|
||||
dependencies:
|
||||
react: 18.2.0
|
||||
optionalDependencies:
|
||||
'@types/react': 18.0.26
|
||||
|
||||
'@radix-ui/react-use-previous@1.1.4(@types/react@18.0.26)(react@18.2.0)':
|
||||
dependencies:
|
||||
react: 18.2.0
|
||||
optionalDependencies:
|
||||
'@types/react': 18.0.26
|
||||
|
||||
'@radix-ui/react-use-rect@1.1.4(@types/react@18.0.26)(react@18.2.0)':
|
||||
dependencies:
|
||||
'@radix-ui/rect': 1.1.3
|
||||
@@ -13018,20 +12723,11 @@ snapshots:
|
||||
optionalDependencies:
|
||||
'@types/react': 18.0.26
|
||||
|
||||
'@radix-ui/react-visually-hidden@1.2.11(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
|
||||
dependencies:
|
||||
'@radix-ui/react-primitive': 2.1.10(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
optionalDependencies:
|
||||
'@types/react': 18.0.26
|
||||
'@types/react-dom': 18.0.10
|
||||
|
||||
'@radix-ui/rect@1.1.3': {}
|
||||
|
||||
'@rc-component/color-picker@1.4.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.2
|
||||
'@babel/runtime': 7.29.2
|
||||
'@ctrl/tinycolor': 3.6.1
|
||||
classnames: 2.3.2
|
||||
rc-util: 5.38.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
@@ -13051,7 +12747,7 @@ snapshots:
|
||||
|
||||
'@rc-component/mutate-observer@1.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.2
|
||||
'@babel/runtime': 7.29.2
|
||||
classnames: 2.3.2
|
||||
rc-util: 5.38.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
react: 18.2.0
|
||||
@@ -13067,7 +12763,7 @@ snapshots:
|
||||
|
||||
'@rc-component/tour@1.10.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.2
|
||||
'@babel/runtime': 7.29.2
|
||||
'@rc-component/portal': 1.1.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
'@rc-component/trigger': 1.18.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
classnames: 2.3.2
|
||||
@@ -13077,7 +12773,7 @@ snapshots:
|
||||
|
||||
'@rc-component/trigger@1.18.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.2
|
||||
'@babel/runtime': 7.29.2
|
||||
'@rc-component/portal': 1.1.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
classnames: 2.3.2
|
||||
rc-motion: 2.9.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
@@ -13327,41 +13023,33 @@ snapshots:
|
||||
|
||||
'@sideway/pinpoint@2.0.0': {}
|
||||
|
||||
'@signozhq/design-tokens@2.2.1': {}
|
||||
'@signozhq/design-tokens@2.3.0': {}
|
||||
|
||||
'@signozhq/icons@0.4.0': {}
|
||||
|
||||
'@signozhq/ui@0.2.0(@date-fns/tz@1.4.1)(@emotion/is-prop-valid@1.2.0)(@signozhq/icons@0.4.0)(@types/react-dom@18.0.10)(@types/react@18.0.26)(date-fns@4.1.0)(react-dom@18.2.0(react@18.2.0))(react-router-dom@5.3.4(react@18.2.0))(react-router@6.30.6(react@18.2.0))(react@18.2.0)':
|
||||
'@signozhq/ui@0.3.0(@date-fns/tz@1.4.1)(@emotion/is-prop-valid@1.2.0)(@signozhq/icons@0.4.0)(@types/react-dom@18.0.10)(@types/react@18.0.26)(date-fns@4.1.0)(react-dom@18.2.0(react@18.2.0))(react-router-dom@5.3.4(react@18.2.0))(react-router@6.30.6(react@18.2.0))(react@18.2.0)':
|
||||
dependencies:
|
||||
'@base-ui/react': 1.7.0(@date-fns/tz@1.4.1)(@types/react@18.0.26)(date-fns@4.1.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
'@chenglou/pretext': 0.0.8
|
||||
'@radix-ui/react-dialog': 1.1.23(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
'@radix-ui/react-icons': 1.3.2(react@18.2.0)
|
||||
'@radix-ui/react-popover': 1.1.23(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
'@radix-ui/react-progress': 1.1.16(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
'@radix-ui/react-radio-group': 1.4.7(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
'@radix-ui/react-select': 2.3.7(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
'@radix-ui/react-slider': 1.4.7(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
'@radix-ui/react-slot': 1.3.3(@types/react@18.0.26)(react@18.2.0)
|
||||
'@radix-ui/react-tabs': 1.1.21(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
'@signozhq/icons': 0.4.0
|
||||
'@tanstack/react-table': 8.21.3(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
'@tanstack/react-virtual': 3.14.9(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
'@types/lodash-es': 4.17.12
|
||||
clsx: 2.1.1
|
||||
cmdk: 1.1.1(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
copy-text-to-clipboard: 3.2.2
|
||||
dayjs: 1.11.23
|
||||
lodash-es: 4.18.1
|
||||
motion: 11.18.2(@emotion/is-prop-valid@1.2.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
next-themes: 0.4.6(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
nuqs: 2.9.6(react-router-dom@5.3.4(react@18.2.0))(react-router@6.30.6(react@18.2.0))(react@18.2.0)
|
||||
react: 18.2.0
|
||||
react-compiler-runtime: 1.0.0(react@18.2.0)
|
||||
react-day-picker: 9.9.0(react@18.2.0)
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
react-resizable-panels: 4.12.3(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
sonner: 2.0.7(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
react-resizable-panels: 4.12.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
transitivePeerDependencies:
|
||||
- '@date-fns/tz'
|
||||
- '@emotion/is-prop-valid'
|
||||
@@ -14638,7 +14326,7 @@ snapshots:
|
||||
|
||||
better-xlsx@0.7.6:
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.2
|
||||
'@babel/runtime': 7.29.2
|
||||
jszip: 3.10.1
|
||||
kind-of: 6.0.3
|
||||
|
||||
@@ -14675,7 +14363,7 @@ snapshots:
|
||||
|
||||
broadcast-channel@3.7.0:
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.2
|
||||
'@babel/runtime': 7.29.2
|
||||
detect-node: 2.1.0
|
||||
js-sha3: 0.8.0
|
||||
microseconds: 0.2.0
|
||||
@@ -14893,18 +14581,6 @@ snapshots:
|
||||
|
||||
clsx@2.1.1: {}
|
||||
|
||||
cmdk@1.1.1(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
|
||||
dependencies:
|
||||
'@radix-ui/react-compose-refs': 1.1.5(@types/react@18.0.26)(react@18.2.0)
|
||||
'@radix-ui/react-dialog': 1.1.23(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
'@radix-ui/react-id': 1.1.4(@types/react@18.0.26)(react@18.2.0)
|
||||
'@radix-ui/react-primitive': 2.1.10(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
transitivePeerDependencies:
|
||||
- '@types/react'
|
||||
- '@types/react-dom'
|
||||
|
||||
co@4.6.0: {}
|
||||
|
||||
codemirror@6.0.1:
|
||||
@@ -17528,7 +17204,7 @@ snapshots:
|
||||
|
||||
match-sorter@6.3.1:
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.2
|
||||
'@babel/runtime': 7.29.2
|
||||
remove-accents: 0.4.2
|
||||
|
||||
math-intrinsics@1.1.0: {}
|
||||
@@ -18044,11 +17720,6 @@ snapshots:
|
||||
|
||||
neo-async@2.6.2: {}
|
||||
|
||||
next-themes@0.4.6(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
|
||||
dependencies:
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
|
||||
no-case@3.0.4:
|
||||
dependencies:
|
||||
lower-case: 2.0.2
|
||||
@@ -18696,7 +18367,7 @@ snapshots:
|
||||
|
||||
rc-cascader@3.20.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.2
|
||||
'@babel/runtime': 7.29.2
|
||||
array-tree-filter: 2.1.0
|
||||
classnames: 2.3.2
|
||||
rc-select: 14.10.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
@@ -18707,7 +18378,7 @@ snapshots:
|
||||
|
||||
rc-checkbox@3.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.2
|
||||
'@babel/runtime': 7.29.2
|
||||
classnames: 2.3.2
|
||||
rc-util: 5.38.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
react: 18.2.0
|
||||
@@ -18715,7 +18386,7 @@ snapshots:
|
||||
|
||||
rc-collapse@3.7.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.2
|
||||
'@babel/runtime': 7.29.2
|
||||
classnames: 2.3.2
|
||||
rc-motion: 2.9.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
rc-util: 5.38.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
@@ -18724,7 +18395,7 @@ snapshots:
|
||||
|
||||
rc-dialog@9.3.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.2
|
||||
'@babel/runtime': 7.29.2
|
||||
'@rc-component/portal': 1.1.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
classnames: 2.3.2
|
||||
rc-motion: 2.9.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
@@ -18734,7 +18405,7 @@ snapshots:
|
||||
|
||||
rc-drawer@6.5.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.2
|
||||
'@babel/runtime': 7.29.2
|
||||
'@rc-component/portal': 1.1.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
classnames: 2.3.2
|
||||
rc-motion: 2.9.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
@@ -18744,7 +18415,7 @@ snapshots:
|
||||
|
||||
rc-dropdown@4.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.2
|
||||
'@babel/runtime': 7.29.2
|
||||
'@rc-component/trigger': 1.18.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
classnames: 2.3.2
|
||||
rc-util: 5.38.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
@@ -18753,7 +18424,7 @@ snapshots:
|
||||
|
||||
rc-field-form@1.40.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.2
|
||||
'@babel/runtime': 7.29.2
|
||||
async-validator: 4.2.5
|
||||
rc-util: 5.38.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
react: 18.2.0
|
||||
@@ -18761,7 +18432,7 @@ snapshots:
|
||||
|
||||
rc-image@7.3.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.2
|
||||
'@babel/runtime': 7.29.2
|
||||
'@rc-component/portal': 1.1.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
classnames: 2.3.2
|
||||
rc-dialog: 9.3.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
@@ -18772,7 +18443,7 @@ snapshots:
|
||||
|
||||
rc-input-number@8.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.2
|
||||
'@babel/runtime': 7.29.2
|
||||
'@rc-component/mini-decimal': 1.0.1
|
||||
classnames: 2.3.2
|
||||
rc-input: 1.3.5(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
@@ -18782,7 +18453,7 @@ snapshots:
|
||||
|
||||
rc-input@1.3.5(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.2
|
||||
'@babel/runtime': 7.29.2
|
||||
classnames: 2.3.2
|
||||
rc-util: 5.38.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
react: 18.2.0
|
||||
@@ -18790,7 +18461,7 @@ snapshots:
|
||||
|
||||
rc-mentions@2.9.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.2
|
||||
'@babel/runtime': 7.29.2
|
||||
'@rc-component/trigger': 1.18.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
classnames: 2.3.2
|
||||
rc-input: 1.3.5(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
@@ -18802,7 +18473,7 @@ snapshots:
|
||||
|
||||
rc-menu@9.12.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.2
|
||||
'@babel/runtime': 7.29.2
|
||||
'@rc-component/trigger': 1.18.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
classnames: 2.3.2
|
||||
rc-motion: 2.9.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
@@ -18813,7 +18484,7 @@ snapshots:
|
||||
|
||||
rc-motion@2.9.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.2
|
||||
'@babel/runtime': 7.29.2
|
||||
classnames: 2.3.2
|
||||
rc-util: 5.38.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
react: 18.2.0
|
||||
@@ -18821,7 +18492,7 @@ snapshots:
|
||||
|
||||
rc-notification@5.3.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.2
|
||||
'@babel/runtime': 7.29.2
|
||||
classnames: 2.3.2
|
||||
rc-motion: 2.9.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
rc-util: 5.38.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
@@ -18830,7 +18501,7 @@ snapshots:
|
||||
|
||||
rc-overflow@1.3.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.2
|
||||
'@babel/runtime': 7.29.2
|
||||
classnames: 2.3.2
|
||||
rc-resize-observer: 1.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
rc-util: 5.38.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
@@ -18839,7 +18510,7 @@ snapshots:
|
||||
|
||||
rc-pagination@3.7.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.2
|
||||
'@babel/runtime': 7.29.2
|
||||
classnames: 2.3.2
|
||||
rc-util: 5.38.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
react: 18.2.0
|
||||
@@ -18847,7 +18518,7 @@ snapshots:
|
||||
|
||||
rc-picker@3.14.6(date-fns@4.1.0)(dayjs@1.11.20)(moment@2.30.1)(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.2
|
||||
'@babel/runtime': 7.29.2
|
||||
'@rc-component/trigger': 1.18.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
classnames: 2.3.2
|
||||
rc-util: 5.38.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
@@ -18860,7 +18531,7 @@ snapshots:
|
||||
|
||||
rc-progress@3.5.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.2
|
||||
'@babel/runtime': 7.29.2
|
||||
classnames: 2.3.2
|
||||
rc-util: 5.38.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
react: 18.2.0
|
||||
@@ -18868,7 +18539,7 @@ snapshots:
|
||||
|
||||
rc-rate@2.12.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.2
|
||||
'@babel/runtime': 7.29.2
|
||||
classnames: 2.3.2
|
||||
rc-util: 5.38.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
react: 18.2.0
|
||||
@@ -18876,7 +18547,7 @@ snapshots:
|
||||
|
||||
rc-resize-observer@1.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.2
|
||||
'@babel/runtime': 7.29.2
|
||||
classnames: 2.3.2
|
||||
rc-util: 5.38.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
react: 18.2.0
|
||||
@@ -18885,7 +18556,7 @@ snapshots:
|
||||
|
||||
rc-segmented@2.2.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.2
|
||||
'@babel/runtime': 7.29.2
|
||||
classnames: 2.3.2
|
||||
rc-motion: 2.9.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
rc-util: 5.38.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
@@ -18906,7 +18577,7 @@ snapshots:
|
||||
|
||||
rc-slider@10.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.2
|
||||
'@babel/runtime': 7.29.2
|
||||
classnames: 2.3.2
|
||||
rc-util: 5.38.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
react: 18.2.0
|
||||
@@ -18914,7 +18585,7 @@ snapshots:
|
||||
|
||||
rc-steps@6.0.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.2
|
||||
'@babel/runtime': 7.29.2
|
||||
classnames: 2.3.2
|
||||
rc-util: 5.38.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
react: 18.2.0
|
||||
@@ -18922,7 +18593,7 @@ snapshots:
|
||||
|
||||
rc-switch@4.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.2
|
||||
'@babel/runtime': 7.29.2
|
||||
classnames: 2.3.2
|
||||
rc-util: 5.38.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
react: 18.2.0
|
||||
@@ -18930,7 +18601,7 @@ snapshots:
|
||||
|
||||
rc-table@7.35.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.2
|
||||
'@babel/runtime': 7.29.2
|
||||
'@rc-component/context': 1.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
classnames: 2.3.2
|
||||
rc-resize-observer: 1.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
@@ -18941,7 +18612,7 @@ snapshots:
|
||||
|
||||
rc-tabs@12.13.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.2
|
||||
'@babel/runtime': 7.29.2
|
||||
classnames: 2.3.2
|
||||
rc-dropdown: 4.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
rc-menu: 9.12.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
@@ -18953,7 +18624,7 @@ snapshots:
|
||||
|
||||
rc-textarea@1.5.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.2
|
||||
'@babel/runtime': 7.29.2
|
||||
classnames: 2.3.2
|
||||
rc-input: 1.3.5(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
rc-resize-observer: 1.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
@@ -18963,7 +18634,7 @@ snapshots:
|
||||
|
||||
rc-tooltip@6.1.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.2
|
||||
'@babel/runtime': 7.29.2
|
||||
'@rc-component/trigger': 1.18.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
classnames: 2.3.2
|
||||
react: 18.2.0
|
||||
@@ -18971,7 +18642,7 @@ snapshots:
|
||||
|
||||
rc-tree-select@5.15.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.2
|
||||
'@babel/runtime': 7.29.2
|
||||
classnames: 2.3.2
|
||||
rc-select: 14.10.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
rc-tree: 5.8.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
@@ -18981,7 +18652,7 @@ snapshots:
|
||||
|
||||
rc-tree@5.8.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.2
|
||||
'@babel/runtime': 7.29.2
|
||||
classnames: 2.3.2
|
||||
rc-motion: 2.9.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
rc-util: 5.38.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
@@ -18991,7 +18662,7 @@ snapshots:
|
||||
|
||||
rc-upload@4.3.5(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.2
|
||||
'@babel/runtime': 7.29.2
|
||||
classnames: 2.3.2
|
||||
rc-util: 5.38.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
react: 18.2.0
|
||||
@@ -18999,14 +18670,14 @@ snapshots:
|
||||
|
||||
rc-util@5.38.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.2
|
||||
'@babel/runtime': 7.29.2
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
react-is: 18.3.1
|
||||
|
||||
rc-virtual-list@3.11.3(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.2
|
||||
'@babel/runtime': 7.29.2
|
||||
classnames: 2.3.2
|
||||
rc-resize-observer: 1.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
rc-util: 5.38.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
@@ -19246,7 +18917,7 @@ snapshots:
|
||||
optionalDependencies:
|
||||
'@types/react': 18.0.26
|
||||
|
||||
react-resizable-panels@4.12.3(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
|
||||
react-resizable-panels@4.12.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
|
||||
dependencies:
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
@@ -19297,7 +18968,7 @@ snapshots:
|
||||
|
||||
react-router@5.3.4(react@18.2.0):
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.2
|
||||
'@babel/runtime': 7.29.2
|
||||
history: 4.10.1
|
||||
hoist-non-react-statics: 3.3.2
|
||||
loose-envify: 1.4.0
|
||||
@@ -19834,11 +19505,6 @@ snapshots:
|
||||
ansi-styles: 6.2.3
|
||||
is-fullwidth-code-point: 5.1.0
|
||||
|
||||
sonner@2.0.7(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
|
||||
dependencies:
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
|
||||
sort-asc@0.1.0: {}
|
||||
|
||||
sort-desc@0.1.1: {}
|
||||
|
||||
@@ -15,6 +15,5 @@
|
||||
}
|
||||
|
||||
.divider {
|
||||
--divider-color: var(--l1-border);
|
||||
--divider-margin: 10px 0 16px 0;
|
||||
--divider-border: var(--l1-border);
|
||||
}
|
||||
|
||||
@@ -28,7 +28,11 @@ function AlertBreadcrumb({
|
||||
items={breadcrumbItems}
|
||||
data-testid={testId}
|
||||
/>
|
||||
{showDivider && <Divider className={styles.divider} />}
|
||||
{showDivider && (
|
||||
<div className={styles.divider}>
|
||||
<Divider spacing="10px 16px" />
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Copy } from '@signozhq/icons';
|
||||
import { Badge, type BadgeColorType } from '@signozhq/ui/badge';
|
||||
import { toast } from '@signozhq/ui/sonner';
|
||||
import { toast } from '@signozhq/ui/toast';
|
||||
import { Tooltip } from '@signozhq/ui/tooltip';
|
||||
import cx from 'classnames';
|
||||
import { useLayoutEffect, useRef, useState } from 'react';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Copy } from '@signozhq/icons';
|
||||
import { Badge, type BadgeColorType } from '@signozhq/ui/badge';
|
||||
import { toast } from '@signozhq/ui/sonner';
|
||||
import { toast } from '@signozhq/ui/toast';
|
||||
import { Tooltip } from '@signozhq/ui/tooltip';
|
||||
import { useCopyToClipboard } from 'react-use';
|
||||
|
||||
|
||||
@@ -56,22 +56,15 @@ function ProgressRender(item: string | number): JSX.Element {
|
||||
const percent = Number(Number(item).toFixed(1));
|
||||
return (
|
||||
<div className="progress-container">
|
||||
<Progress
|
||||
percent={percent}
|
||||
strokeLinecap="butt"
|
||||
showInfo
|
||||
strokeColor={((): string => {
|
||||
const cpuPercent = percent;
|
||||
if (cpuPercent >= 90) {
|
||||
return Color.BG_SAKURA_500;
|
||||
<div className="progress-bar">
|
||||
<Progress
|
||||
percent={percent}
|
||||
showInfo
|
||||
color={
|
||||
percent >= 90 ? 'highlight-danger' : percent >= 60 ? 'warning' : 'success'
|
||||
}
|
||||
if (cpuPercent >= 60) {
|
||||
return Color.BG_AMBER_500;
|
||||
}
|
||||
return Color.BG_FOREST_500;
|
||||
})()}
|
||||
className="progress-bar"
|
||||
/>
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ export default function CeleryTaskDetail({
|
||||
endTime ? `- ${formatTimestamp(endTime)}` : ''
|
||||
}`}
|
||||
</Typography.Text>
|
||||
<Divider type="vertical" />
|
||||
<Divider orientation="vertical" />
|
||||
<Typography.Text className="subtitle">{taskData.value}</Typography.Text>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -42,6 +42,16 @@
|
||||
gap: var(--spacing-1);
|
||||
margin-bottom: var(--spacing-4);
|
||||
|
||||
// Input restyle (the old .create-sa-form__input class, as --input-* tokens)
|
||||
--input-foreground: var(--l1-foreground);
|
||||
--input-background: var(--l2-background);
|
||||
--input-border: var(--l1-border);
|
||||
--input-font-size: var(--paragraph-base-400-font-size);
|
||||
--input-border-radius: 2px;
|
||||
--input-placeholder: var(--l3-foreground);
|
||||
--input-placeholder-opacity: 1;
|
||||
--input-focus-ring: var(--primary);
|
||||
|
||||
> label {
|
||||
font-size: var(--paragraph-base-400-font-size);
|
||||
font-weight: var(--paragraph-base-400-font-weight);
|
||||
@@ -50,25 +60,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
&__input {
|
||||
height: 32px;
|
||||
color: var(--l1-foreground);
|
||||
background-color: var(--l2-background);
|
||||
border-color: var(--l1-border);
|
||||
font-size: var(--paragraph-base-400-font-size);
|
||||
border-radius: 2px;
|
||||
width: 100%;
|
||||
|
||||
&::placeholder {
|
||||
color: var(--l3-foreground);
|
||||
}
|
||||
|
||||
&:focus {
|
||||
border-color: var(--primary);
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
&__error {
|
||||
font-size: var(--paragraph-small-400-font-size);
|
||||
color: var(--destructive);
|
||||
|
||||
@@ -7,7 +7,7 @@ import { AuthZGuardContent } from 'lib/authz/components/AuthZGuard/AuthZGuardCon
|
||||
import { SACreatePermission } from 'lib/authz/hooks/useAuthZ/permissions/service-account.permissions';
|
||||
import { DialogFooter, DialogWrapper } from '@signozhq/ui/dialog';
|
||||
import { Input } from '@signozhq/ui/input';
|
||||
import { toast } from '@signozhq/ui/sonner';
|
||||
import { toast } from '@signozhq/ui/toast';
|
||||
import { convertToApiError } from 'api/ErrorResponseHandlerForGeneratedAPIs';
|
||||
import {
|
||||
invalidateListServiceAccounts,
|
||||
@@ -116,11 +116,10 @@ function CreateServiceAccountModal(): JSX.Element {
|
||||
<Input
|
||||
id="sa-name"
|
||||
placeholder="Enter a name"
|
||||
className="create-sa-form__input"
|
||||
value={field.value}
|
||||
onChange={field.onChange}
|
||||
onBlur={field.onBlur}
|
||||
data-testid="create-sa-name-input"
|
||||
testId="create-sa-name-input"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { toast } from '@signozhq/ui/sonner';
|
||||
import { toast } from '@signozhq/ui/toast';
|
||||
import {
|
||||
setupAuthzAdmin,
|
||||
setupAuthzDenyAll,
|
||||
@@ -10,9 +10,9 @@ import { render, screen, userEvent, waitFor } from 'tests/test-utils';
|
||||
|
||||
import CreateServiceAccountModal from '../CreateServiceAccountModal';
|
||||
|
||||
jest.mock('@signozhq/ui/sonner', () => ({
|
||||
...jest.requireActual('@signozhq/ui/sonner'),
|
||||
toast: { success: jest.fn(), error: jest.fn() },
|
||||
jest.mock('@signozhq/ui/toast', () => ({
|
||||
...jest.requireActual('@signozhq/ui/toast'),
|
||||
toast: { success: jest.fn(), danger: jest.fn() },
|
||||
}));
|
||||
|
||||
const mockToast = jest.mocked(toast);
|
||||
@@ -58,7 +58,7 @@ describe('CreateServiceAccountModal', () => {
|
||||
// The form only renders once the create check resolves, and the name field
|
||||
// registers its `required` rule on mount, so the empty-form invalid state
|
||||
// settles a tick later.
|
||||
await screen.findByTestId('create-sa-name-input');
|
||||
await screen.findByTestId('create-sa-name-input-field');
|
||||
|
||||
await waitFor(() =>
|
||||
expect(screen.getByTestId('create-sa-submit-btn')).toHaveAttribute(
|
||||
@@ -72,7 +72,7 @@ describe('CreateServiceAccountModal', () => {
|
||||
const user = userEvent.setup({ pointerEventsCheck: 0 });
|
||||
renderModal();
|
||||
|
||||
const nameInput = await screen.findByTestId('create-sa-name-input');
|
||||
const nameInput = await screen.findByTestId('create-sa-name-input-field');
|
||||
const submitBtn = await screen.findByTestId('create-sa-submit-btn');
|
||||
|
||||
await user.type(nameInput, 'test');
|
||||
@@ -90,7 +90,7 @@ describe('CreateServiceAccountModal', () => {
|
||||
const user = userEvent.setup({ pointerEventsCheck: 0 });
|
||||
renderModal();
|
||||
|
||||
const nameInput = await screen.findByTestId('create-sa-name-input');
|
||||
const nameInput = await screen.findByTestId('create-sa-name-input-field');
|
||||
await user.type(nameInput, 'Deploy Bot');
|
||||
|
||||
const submitBtn = screen.getByTestId('create-sa-submit-btn');
|
||||
@@ -128,7 +128,7 @@ describe('CreateServiceAccountModal', () => {
|
||||
renderModal();
|
||||
|
||||
const nameInput = await screen.findByTestId<HTMLInputElement>(
|
||||
'create-sa-name-input',
|
||||
'create-sa-name-input-field',
|
||||
);
|
||||
await user.type(nameInput, 'Deploy Bot');
|
||||
const submitBtn = screen.getByTestId('create-sa-submit-btn');
|
||||
@@ -160,7 +160,7 @@ describe('CreateServiceAccountModal', () => {
|
||||
|
||||
renderModal();
|
||||
|
||||
const nameInput = await screen.findByTestId('create-sa-name-input');
|
||||
const nameInput = await screen.findByTestId('create-sa-name-input-field');
|
||||
await user.type(nameInput, 'Dupe Bot');
|
||||
|
||||
const submitBtn = screen.getByTestId('create-sa-submit-btn');
|
||||
@@ -209,7 +209,9 @@ describe('CreateServiceAccountModal', () => {
|
||||
screen.findByText(/is not authorized to perform/i),
|
||||
).resolves.toBeInTheDocument();
|
||||
|
||||
expect(screen.queryByTestId('create-sa-name-input')).not.toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByTestId('create-sa-name-input-field'),
|
||||
).not.toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByTestId('create-service-account-modal'),
|
||||
).toBeInTheDocument();
|
||||
@@ -244,7 +246,7 @@ describe('CreateServiceAccountModal', () => {
|
||||
const user = userEvent.setup({ pointerEventsCheck: 0 });
|
||||
renderModal();
|
||||
|
||||
const nameInput = await screen.findByTestId('create-sa-name-input');
|
||||
const nameInput = await screen.findByTestId('create-sa-name-input-field');
|
||||
await user.type(nameInput, 'Bot');
|
||||
await user.clear(nameInput);
|
||||
|
||||
|
||||
@@ -34,34 +34,19 @@ $item-spacing: 8px;
|
||||
@include flex-center;
|
||||
gap: 6px;
|
||||
width: -webkit-fill-available;
|
||||
}
|
||||
|
||||
&__input {
|
||||
@include text-style-base;
|
||||
width: 100%;
|
||||
background: transparent;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
outline: none;
|
||||
height: auto;
|
||||
color: var(--l1-foreground);
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
letter-spacing: -0.07px;
|
||||
padding: 0;
|
||||
|
||||
&:focus,
|
||||
&:focus-visible,
|
||||
&:hover {
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
&::placeholder {
|
||||
color: var(--l2-foreground);
|
||||
}
|
||||
// Restyle the unstyled search Input (old .timezone-picker__input rules,
|
||||
// as --input-* tokens picked up by the Input frame inside)
|
||||
--input-height: auto;
|
||||
--input-padding: 0;
|
||||
--input-border-width: 0;
|
||||
--input-border-radius: 0;
|
||||
--input-foreground: var(--l1-foreground);
|
||||
--input-font-size: 14px;
|
||||
--input-line-height: 20px;
|
||||
--input-letter-spacing: -0.07px;
|
||||
--input-placeholder: var(--l2-foreground);
|
||||
--input-placeholder-opacity: 1;
|
||||
}
|
||||
|
||||
&__esc-key {
|
||||
|
||||
@@ -65,7 +65,8 @@ function SearchBar({
|
||||
/>
|
||||
<Input
|
||||
type="text"
|
||||
className="timezone-picker__input"
|
||||
variant="unstyled"
|
||||
noFocusRing
|
||||
placeholder="Search timezones..."
|
||||
value={value}
|
||||
onChange={(e): void => onChange(e.target.value)}
|
||||
|
||||
@@ -5,7 +5,7 @@ import { Badge } from '@signozhq/ui/badge';
|
||||
import { Button } from '@signozhq/ui/button';
|
||||
import { DrawerWrapper } from '@signozhq/ui/drawer';
|
||||
import { Input } from '@signozhq/ui/input';
|
||||
import { toast } from '@signozhq/ui/sonner';
|
||||
import { toast } from '@signozhq/ui/toast';
|
||||
import { Skeleton, Tooltip } from 'antd';
|
||||
import { convertToApiError } from 'api/ErrorResponseHandlerForGeneratedAPIs';
|
||||
import type { RenderErrorResponseDTO } from 'api/generated/services/sigNoz.schemas';
|
||||
@@ -409,7 +409,8 @@ function EditMemberDrawer({
|
||||
setShowResetLinkDialog(true);
|
||||
onClose();
|
||||
} else {
|
||||
toast.error('Failed to generate password reset link', {
|
||||
toast.danger('Failed to generate password reset link', {
|
||||
action: { label: 'Dismiss' },
|
||||
position: 'top-right',
|
||||
});
|
||||
}
|
||||
@@ -438,7 +439,7 @@ function EditMemberDrawer({
|
||||
|
||||
useEffect(() => {
|
||||
if (copyState.error) {
|
||||
toast.error('Failed to copy link');
|
||||
toast.danger('Failed to copy link', { action: { label: 'Dismiss' } });
|
||||
}
|
||||
}, [copyState.error]);
|
||||
|
||||
@@ -471,20 +472,19 @@ function EditMemberDrawer({
|
||||
<label className="edit-member-drawer__label" htmlFor="member-name">
|
||||
Name
|
||||
</label>
|
||||
<Tooltip title={isRootUser ? ROOT_USER_TOOLTIP : undefined}>
|
||||
<Input
|
||||
id="member-name"
|
||||
value={localDisplayName}
|
||||
onChange={(e): void => {
|
||||
setLocalDisplayName(e.target.value);
|
||||
setSaveErrors((prev) =>
|
||||
prev.filter((err) => err.context !== 'Name update'),
|
||||
);
|
||||
}}
|
||||
placeholder="Enter name"
|
||||
disabled={isRootUser || isDeleted}
|
||||
/>
|
||||
</Tooltip>
|
||||
<Input
|
||||
id="member-name"
|
||||
value={localDisplayName}
|
||||
onChange={(e): void => {
|
||||
setLocalDisplayName(e.target.value);
|
||||
setSaveErrors((prev) =>
|
||||
prev.filter((err) => err.context !== 'Name update'),
|
||||
);
|
||||
}}
|
||||
placeholder="Enter name"
|
||||
disabled={isRootUser || isDeleted}
|
||||
disabledTooltip={isRootUser ? ROOT_USER_TOOLTIP : undefined}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="edit-member-drawer__field">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { ReactNode } from 'react';
|
||||
import { toast } from '@signozhq/ui/sonner';
|
||||
import { toast } from '@signozhq/ui/toast';
|
||||
import { convertToApiError } from 'api/ErrorResponseHandlerForGeneratedAPIs';
|
||||
import {
|
||||
useCreateResetPasswordToken,
|
||||
@@ -82,11 +82,11 @@ jest.mock('@signozhq/ui/dialog', () => ({
|
||||
),
|
||||
}));
|
||||
|
||||
jest.mock('@signozhq/ui/sonner', () => ({
|
||||
...jest.requireActual('@signozhq/ui/sonner'),
|
||||
jest.mock('@signozhq/ui/toast', () => ({
|
||||
...jest.requireActual('@signozhq/ui/toast'),
|
||||
toast: {
|
||||
success: jest.fn(),
|
||||
error: jest.fn(),
|
||||
danger: jest.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
|
||||
@@ -32,12 +32,16 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Wraps the search Input; restyles it via its --input-* tokens
|
||||
// (the Input is size="large" for the 40px height).
|
||||
.searchInput {
|
||||
background-color: var(--l1-background);
|
||||
height: 40px;
|
||||
border-radius: 0;
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
--input-background: var(--l1-background);
|
||||
--input-border-radius: 0;
|
||||
|
||||
[data-slot='input'] {
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
}
|
||||
}
|
||||
|
||||
.section {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { toast } from '@signozhq/ui/sonner';
|
||||
import { toast } from '@signozhq/ui/toast';
|
||||
import { Button } from '@signozhq/ui/button';
|
||||
import { Input } from '@signozhq/ui/input';
|
||||
import useDebouncedFn from 'hooks/useDebouncedFunction';
|
||||
@@ -140,10 +140,10 @@ function FieldsSelectorContent({
|
||||
<X className={styles.closeIcon} size={16} onClick={onClose} />
|
||||
</div>
|
||||
|
||||
<section>
|
||||
<section className={styles.searchInput}>
|
||||
<Input
|
||||
className={styles.searchInput}
|
||||
type="text"
|
||||
size="large"
|
||||
value={inputValue}
|
||||
placeholder="Search for a field..."
|
||||
onChange={handleInputChange}
|
||||
|
||||
@@ -13,9 +13,9 @@ jest.mock('hooks/querySuggestions/useFieldKeysSuggestion', () => ({
|
||||
})),
|
||||
}));
|
||||
|
||||
jest.mock('@signozhq/ui/sonner', () => ({
|
||||
...jest.requireActual('@signozhq/ui/sonner'),
|
||||
toast: { success: jest.fn(), error: jest.fn() },
|
||||
jest.mock('@signozhq/ui/toast', () => ({
|
||||
...jest.requireActual('@signozhq/ui/toast'),
|
||||
toast: { success: jest.fn(), danger: jest.fn() },
|
||||
}));
|
||||
|
||||
// FloatingPanel is a react-rnd/portal shell — presentation only. Render its
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { toast } from '@signozhq/ui/sonner';
|
||||
import { toast } from '@signozhq/ui/toast';
|
||||
import { Button, Input } from 'antd';
|
||||
import { ToggleGroup } from '@signozhq/ui/toggle-group';
|
||||
import { Typography } from '@signozhq/ui/typography';
|
||||
@@ -39,7 +39,8 @@ function FeedbackModal({ onClose }: { onClose: () => void }): JSX.Element {
|
||||
})
|
||||
.catch(() => {
|
||||
console.error(`Failed to submit ${entityName}`);
|
||||
toast.error(`Failed to submit ${entityName}`, {
|
||||
toast.danger(`Failed to submit ${entityName}`, {
|
||||
action: { label: 'Dismiss' },
|
||||
position: 'top-right',
|
||||
});
|
||||
})
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Mock dependencies before imports
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { toast } from '@signozhq/ui/sonner';
|
||||
import { toast } from '@signozhq/ui/toast';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { logEventMock } from '__tests__/logEventMock';
|
||||
@@ -14,11 +14,11 @@ jest.mock('react-router-dom', () => ({
|
||||
useLocation: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('@signozhq/ui/sonner', () => ({
|
||||
...jest.requireActual('@signozhq/ui/sonner'),
|
||||
jest.mock('@signozhq/ui/toast', () => ({
|
||||
...jest.requireActual('@signozhq/ui/toast'),
|
||||
toast: {
|
||||
success: jest.fn(),
|
||||
error: jest.fn(),
|
||||
danger: jest.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
@@ -50,7 +50,7 @@ describe('FeedbackModal', () => {
|
||||
isCloudUser: false,
|
||||
});
|
||||
mockToast.success.mockClear();
|
||||
mockToast.error.mockClear();
|
||||
mockToast.danger.mockClear();
|
||||
});
|
||||
|
||||
it('should render feedback modal with all tabs', () => {
|
||||
|
||||
@@ -30,63 +30,41 @@
|
||||
font-weight: var(--font-weight-light);
|
||||
}
|
||||
|
||||
// Wrapper around the Input; restyles the frame inside via --input-* tokens.
|
||||
.input {
|
||||
flex: 1;
|
||||
min-width: 150px;
|
||||
font-family: 'Space Mono', monospace !important;
|
||||
|
||||
--input-border-radius: 0px;
|
||||
border: 1px solid var(--input-with-label-border-color, var(--l2-border));
|
||||
background: var(--input-with-label-background-color, var(--l2-background));
|
||||
color: var(--input-with-label-color, var(--l2-foreground));
|
||||
|
||||
font-size: 12px !important;
|
||||
line-height: 25px;
|
||||
|
||||
position: relative;
|
||||
|
||||
--input-font-family: 'Space Mono', monospace;
|
||||
--input-font-size: 12px;
|
||||
--input-border-radius: 0px;
|
||||
--input-border: var(--input-with-label-border-color, var(--l2-border));
|
||||
--input-background: var(
|
||||
--input-with-label-background-color,
|
||||
var(--l2-background)
|
||||
);
|
||||
--input-foreground: var(--input-with-label-color, var(--l2-foreground));
|
||||
--input-placeholder: var(--input-with-label-color, var(--l3-foreground));
|
||||
--input-placeholder-opacity: 1;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.ant-select-selector {
|
||||
position: relative;
|
||||
border-radius: inherit;
|
||||
}
|
||||
|
||||
.ant-select:hover .ant-select-selector,
|
||||
.ant-select-focused .ant-select-selector {
|
||||
&:focus-within {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
&.input__has-label-after {
|
||||
margin-left: -1px;
|
||||
|
||||
.ant-select-selector {
|
||||
margin-left: -1px;
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.input__has-close-button {
|
||||
.ant-select-selector {
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
|
||||
~ .close-btn {
|
||||
margin-left: -1px;
|
||||
}
|
||||
}
|
||||
|
||||
&::placeholder {
|
||||
color: var(--input-with-label-color, var(--l3-foreground)) !important;
|
||||
font-size: 12px !important;
|
||||
}
|
||||
&[type='number']::-webkit-inner-spin-button,
|
||||
&[type='number']::-webkit-outer-spin-button {
|
||||
input[type='number']::-webkit-inner-spin-button,
|
||||
input[type='number']::-webkit-outer-spin-button {
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
appearance: none;
|
||||
@@ -109,17 +87,8 @@
|
||||
}
|
||||
|
||||
&.labelAfter {
|
||||
.input {
|
||||
border-radius: 2px;
|
||||
border: 1px solid var(--input-with-label-border-color, var(--l2-border));
|
||||
background: var(--input-with-label-background-color, var(--l2-background));
|
||||
border-top-right-radius: 0px;
|
||||
border-bottom-right-radius: 0px;
|
||||
|
||||
.ant-select-selector {
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
.input [data-slot='input'] {
|
||||
border-radius: 2px 0 0 2px;
|
||||
}
|
||||
|
||||
.label {
|
||||
|
||||
@@ -46,19 +46,23 @@ function InputWithLabel({
|
||||
})}
|
||||
>
|
||||
{!labelAfter && <Typography.Text className="label">{label}</Typography.Text>}
|
||||
<Input
|
||||
<div
|
||||
className={cx('input', {
|
||||
'input__has-label-after': !labelAfter,
|
||||
'input__has-close-button': !!onClose,
|
||||
})}
|
||||
placeholder={placeholder}
|
||||
type={type}
|
||||
value={inputValue}
|
||||
onChange={handleChange}
|
||||
disabled={disabled}
|
||||
name={label.toLowerCase()}
|
||||
data-testid={`input-${label}`}
|
||||
/>
|
||||
>
|
||||
<Input
|
||||
placeholder={placeholder}
|
||||
type={type}
|
||||
value={inputValue}
|
||||
onChange={handleChange}
|
||||
disabled={disabled}
|
||||
disabledTooltip={undefined}
|
||||
name={label.toLowerCase()}
|
||||
testId={`input-${label}`}
|
||||
/>
|
||||
</div>
|
||||
{labelAfter && <Typography.Text className="label">{label}</Typography.Text>}
|
||||
{onClose && (
|
||||
<Button
|
||||
|
||||
@@ -59,18 +59,6 @@
|
||||
--invite-members-field-background,
|
||||
var(--l2-background)
|
||||
);
|
||||
--input-hover-background: var(
|
||||
--invite-members-field-background,
|
||||
var(--l2-background)
|
||||
);
|
||||
--input-focus-background: var(
|
||||
--invite-members-field-background,
|
||||
var(--l2-background)
|
||||
);
|
||||
--input-disabled-background: var(
|
||||
--invite-members-field-background,
|
||||
var(--l2-background)
|
||||
);
|
||||
|
||||
input::placeholder {
|
||||
color: var(--l3-foreground);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { CircleAlert, Plus, Trash2 } from '@signozhq/icons';
|
||||
import { CircleAlert, Plus, SolidCheckCircle2, Trash2 } from '@signozhq/icons';
|
||||
import { Button } from '@signozhq/ui/button';
|
||||
import { Callout } from '@signozhq/ui/callout';
|
||||
import { Input } from '@signozhq/ui/input';
|
||||
@@ -97,7 +97,7 @@ function InviteMembers({
|
||||
onChange={(e): void => updateEmail(row.id, e.target.value)}
|
||||
name={`invite-email-${row.id}`}
|
||||
autoComplete="email"
|
||||
data-testid={`invite-email-${row.id}`}
|
||||
testId={`invite-email-${row.id}`}
|
||||
/>
|
||||
{emailValidity[row.id] === false && row.email.trim() !== '' && (
|
||||
<Typography.Text size="small" className={styles.errorText}>
|
||||
@@ -152,61 +152,62 @@ function InviteMembers({
|
||||
</div>
|
||||
|
||||
{hasValidationErrors && (
|
||||
<Callout
|
||||
type="error"
|
||||
size="small"
|
||||
showIcon
|
||||
icon={<CircleAlert size={12} />}
|
||||
className={styles.callout}
|
||||
data-testid="invite-validation-error"
|
||||
>
|
||||
{getValidationErrorMessage()}
|
||||
</Callout>
|
||||
<div className={styles.callout}>
|
||||
<Callout
|
||||
color="danger"
|
||||
size="sm"
|
||||
icon={<CircleAlert />}
|
||||
testId="invite-validation-error"
|
||||
>
|
||||
{getValidationErrorMessage()}
|
||||
</Callout>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{hasResults && hasFailures && (
|
||||
<Callout
|
||||
type="error"
|
||||
size="small"
|
||||
showIcon
|
||||
icon={<CircleAlert size={12} />}
|
||||
className={styles.callout}
|
||||
data-testid="invite-api-error"
|
||||
>
|
||||
<div className={styles.results}>
|
||||
{hasSuccesses && (
|
||||
<div className={styles.callout}>
|
||||
<Callout
|
||||
color="danger"
|
||||
size="sm"
|
||||
icon={<CircleAlert />}
|
||||
testId="invite-api-error"
|
||||
>
|
||||
<div className={styles.results}>
|
||||
{hasSuccesses && (
|
||||
<Typography.Text size="small">
|
||||
{successResults.length} invite(s) sent successfully.
|
||||
</Typography.Text>
|
||||
)}
|
||||
<Typography.Text size="small">
|
||||
{successResults.length} invite(s) sent successfully.
|
||||
{failedResults.length} invite(s) failed:
|
||||
</Typography.Text>
|
||||
)}
|
||||
<Typography.Text size="small">
|
||||
{failedResults.length} invite(s) failed:
|
||||
</Typography.Text>
|
||||
<ul className={styles.resultsList}>
|
||||
{failedResults.map((result) => (
|
||||
<li key={result.email}>
|
||||
<Typography.Text size="small">
|
||||
{result.email}: {result.error}
|
||||
</Typography.Text>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</Callout>
|
||||
<ul className={styles.resultsList}>
|
||||
{failedResults.map((result) => (
|
||||
<li key={result.email}>
|
||||
<Typography.Text size="small">
|
||||
{result.email}: {result.error}
|
||||
</Typography.Text>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</Callout>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{hasResults && !hasFailures && hasSuccesses && (
|
||||
<Callout
|
||||
type="success"
|
||||
size="small"
|
||||
showIcon
|
||||
className={styles.callout}
|
||||
data-testid="invite-success"
|
||||
>
|
||||
<Typography.Text size="small">
|
||||
{successResults.length} invite(s) sent successfully!
|
||||
</Typography.Text>
|
||||
</Callout>
|
||||
<div className={styles.callout}>
|
||||
<Callout
|
||||
color="success"
|
||||
size="sm"
|
||||
icon={<SolidCheckCircle2 />}
|
||||
testId="invite-success"
|
||||
>
|
||||
<Typography.Text size="small">
|
||||
{successResults.length} invite(s) sent successfully!
|
||||
</Typography.Text>
|
||||
</Callout>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{renderFooter?.({
|
||||
|
||||
@@ -12,11 +12,6 @@
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.divider {
|
||||
height: 16px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.timestamp {
|
||||
font-family: 'Geist Mono', monospace;
|
||||
font-size: var(--font-size-sm);
|
||||
|
||||
@@ -5,7 +5,7 @@ import { Typography } from '@signozhq/ui/typography';
|
||||
import { Tooltip } from '@signozhq/ui/tooltip';
|
||||
import { DATE_TIME_FORMATS } from 'constants/dateTimeFormats';
|
||||
import { aggregateAttributesResourcesToString } from 'container/LogDetailedView/utils';
|
||||
import { toast } from '@signozhq/ui/sonner';
|
||||
import { toast } from '@signozhq/ui/toast';
|
||||
import { useCopyLogLink } from 'hooks/logs/useCopyLogLink';
|
||||
import {
|
||||
ChevronDown,
|
||||
@@ -76,7 +76,7 @@ function LogDetailsHeader({
|
||||
return (
|
||||
<div className={styles.header} data-log-detail-ignore="true">
|
||||
<div className={styles.leftSection}>
|
||||
<Divider type="vertical" className={styles.divider} />
|
||||
<Divider orientation="vertical" height={16} spacing={0} />
|
||||
<Typography.Text
|
||||
className={styles.timestamp}
|
||||
data-testid="log-details-header-timestamp"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { toast } from '@signozhq/ui/sonner';
|
||||
import { toast } from '@signozhq/ui/toast';
|
||||
import { LOCALSTORAGE } from 'constants/localStorage';
|
||||
import { render, screen, userEvent } from 'tests/test-utils';
|
||||
import { ILog } from 'types/api/logs/log';
|
||||
@@ -7,8 +7,8 @@ import LogDetail from '..';
|
||||
import { VIEW_TYPES } from '../constants';
|
||||
import { LogDetailProps } from '../LogDetail.interfaces';
|
||||
|
||||
jest.mock('@signozhq/ui/sonner', () => ({
|
||||
toast: { success: jest.fn(), error: jest.fn() },
|
||||
jest.mock('@signozhq/ui/toast', () => ({
|
||||
toast: { success: jest.fn(), danger: jest.fn() },
|
||||
}));
|
||||
|
||||
// DataViewer pulls in react-json-tree (ESM) + Monaco; mock it (as trace's tests
|
||||
|
||||
@@ -202,46 +202,19 @@
|
||||
.max-lines-per-row-input {
|
||||
display: flex;
|
||||
|
||||
.ant-input-number-handler-wrap {
|
||||
display: none;
|
||||
}
|
||||
--input-height: 26px;
|
||||
--input-border-radius: 0;
|
||||
--input-border: var(--l1-border);
|
||||
|
||||
.ant-input-number {
|
||||
[data-testid='max-lines-per-row-field'] {
|
||||
min-width: 36px;
|
||||
width: auto;
|
||||
border-right: none;
|
||||
border-left: none;
|
||||
border-top: 1px solid var(--l1-border);
|
||||
border-bottom: 1px solid var(--l1-border);
|
||||
text-align: center;
|
||||
height: 26px;
|
||||
border-radius: 0;
|
||||
|
||||
&:active,
|
||||
&:focus {
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-input-number-focused {
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
.ant-input-number-input-wrap {
|
||||
input {
|
||||
text-align: center;
|
||||
font-size: 13px;
|
||||
|
||||
&:active,
|
||||
&:focus {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
&:active,
|
||||
&:focus {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { Button, InputNumber, Popover, Tooltip } from 'antd';
|
||||
import { Input } from '@signozhq/ui/input';
|
||||
import { Button, Popover, Tooltip } from 'antd';
|
||||
import { Typography } from '@signozhq/ui/typography';
|
||||
import cx from 'classnames';
|
||||
import { LogViewMode } from 'container/OptionsMenu/types';
|
||||
@@ -207,11 +208,14 @@ function OptionsMenu({
|
||||
{' '}
|
||||
<Minus size={12} />{' '}
|
||||
</button>
|
||||
<InputNumber
|
||||
<Input.Number
|
||||
min={1}
|
||||
max={10}
|
||||
value={maxLinesPerRow}
|
||||
onChange={handleLinesPerRowChange}
|
||||
controls={false}
|
||||
noFocusRing
|
||||
testId="max-lines-per-row-field"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
.banner {
|
||||
height: var(--spacing-20);
|
||||
|
||||
a {
|
||||
color: var(--callout-warning-title);
|
||||
text-decoration: underline;
|
||||
|
||||
&:hover {
|
||||
color: var(--callout-warning-title);
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,25 +1,22 @@
|
||||
import { PersistedAnnouncementBanner } from '@signozhq/ui/announcement-banner';
|
||||
|
||||
import styles from './NoAuthBanner.module.scss';
|
||||
import { AlertStrip } from '@signozhq/ui/alert-strip';
|
||||
|
||||
export function NoAuthBanner(): JSX.Element {
|
||||
return (
|
||||
<PersistedAnnouncementBanner
|
||||
type="warning"
|
||||
<AlertStrip.CloseablePersisted
|
||||
color="warning"
|
||||
side="top"
|
||||
storageKey="no-auth-banner-v1"
|
||||
testId="no-auth-banner"
|
||||
className={styles.banner}
|
||||
>
|
||||
Impersonation mode: authentication is disabled. Anyone with access to this
|
||||
instance has admin privileges.{' '}
|
||||
<a
|
||||
<AlertStrip.Link
|
||||
href="https://signoz.io/docs/manage/administrator-guide/configuration/impersonation-mode/"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
Learn more
|
||||
</a>
|
||||
</PersistedAnnouncementBanner>
|
||||
</AlertStrip.Link>
|
||||
</AlertStrip.CloseablePersisted>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,6 @@ describe('NoAuthBanner', () => {
|
||||
render(<NoAuthBanner />);
|
||||
const link = screen.getByRole('link', { name: /learn more/i });
|
||||
expect(link).toHaveAttribute('target', '_blank');
|
||||
expect(link).toHaveAttribute('rel', 'noreferrer');
|
||||
expect(link).toHaveAttribute('rel', expect.stringContaining('noreferrer'));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -68,11 +68,7 @@
|
||||
|
||||
// Meant to fix the query builder colors
|
||||
--input-background: var(--l2-background);
|
||||
--input-hover-background: var(--l2-background);
|
||||
--input-focus-background: var(--l2-background);
|
||||
--input-border-color: var(--l2-border);
|
||||
--input-hover-border-color: var(--internal-ant-border-color-hover);
|
||||
--input-focus-border-color: var(--internal-ant-border-color-hover);
|
||||
}
|
||||
|
||||
&-aggregation-container {
|
||||
|
||||
@@ -168,7 +168,7 @@ describe('QueryBuilderV2 + QueryV2 - base render', () => {
|
||||
expect(limitInput).toBeInTheDocument();
|
||||
expect(limitInput).toHaveAttribute('type', 'number');
|
||||
expect(limitInput).toHaveAttribute('name', 'limit');
|
||||
expect(limitInput).toHaveAttribute('data-testid', 'input-Limit');
|
||||
expect(limitInput).toHaveAttribute('data-testid', 'input-Limit-field');
|
||||
});
|
||||
|
||||
it('Cmd+Enter on an input triggers handleRunQuery via container handler', async () => {
|
||||
|
||||
@@ -159,7 +159,7 @@ describe('QueryAddOns', () => {
|
||||
/>,
|
||||
);
|
||||
|
||||
const input = screen.getByTestId('input-Limit') as HTMLInputElement;
|
||||
const input = screen.getByTestId('input-Limit-field') as HTMLInputElement;
|
||||
expect(screen.getByTestId('limit-content')).toBeInTheDocument();
|
||||
expect(input.value).toBe('5');
|
||||
|
||||
@@ -185,7 +185,9 @@ describe('QueryAddOns', () => {
|
||||
);
|
||||
|
||||
expect(screen.getByTestId('order-by-content')).toBeInTheDocument();
|
||||
const limitInput = screen.getByTestId('input-Limit') as HTMLInputElement;
|
||||
const limitInput = screen.getByTestId(
|
||||
'input-Limit-field',
|
||||
) as HTMLInputElement;
|
||||
expect(screen.getByTestId('limit-content')).toBeInTheDocument();
|
||||
expect(limitInput.value).toBe('7');
|
||||
});
|
||||
|
||||
@@ -6,18 +6,15 @@
|
||||
border-bottom: 1px solid var(--l1-border);
|
||||
|
||||
.search {
|
||||
input {
|
||||
--input-background: var(--l2-background);
|
||||
--input-hover-background: var(--l2-background);
|
||||
--input-focus-background: var(--l2-background);
|
||||
&::placeholder {
|
||||
color: var(--l3-foreground);
|
||||
}
|
||||
--input-font-size: 14px;
|
||||
--input-border-color: var(--l1-border);
|
||||
--input-focus-border-color: var(--primary-background);
|
||||
--input-focus-outline-width: 0;
|
||||
--input-focus-outline-offset: 0;
|
||||
// Set on the wrapper so the Input frame inside picks them up.
|
||||
--input-background: var(--l2-background);
|
||||
--input-font-size: 14px;
|
||||
--input-border-color: var(--l1-border);
|
||||
--input-focus-ring-color: var(--primary-background);
|
||||
--input-focus-outline-offset: 0;
|
||||
|
||||
input::placeholder {
|
||||
color: var(--l3-foreground);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -116,6 +116,7 @@ export default function CheckboxFilter(props: ICheckboxProps): JSX.Element {
|
||||
placeholder="Filter values"
|
||||
onChange={(e): void => setSearchTextDebounced(e.target.value)}
|
||||
disabled={isFilterDisabled}
|
||||
disabledTooltip={FILTER_DISABLED_REASON}
|
||||
/>
|
||||
</section>
|
||||
)}
|
||||
|
||||
@@ -46,10 +46,6 @@
|
||||
|
||||
.search {
|
||||
--input-background: var(--l2-background);
|
||||
--input-hover-background: var(--l2-background);
|
||||
--input-focus-background: var(--l2-background);
|
||||
--input-border-color: var(--l2-border);
|
||||
--input-focus-border-color: var(--l2-border);
|
||||
}
|
||||
|
||||
.searchSpinner {
|
||||
|
||||
@@ -19,7 +19,9 @@ import { NON_SELECTED_OPERATORS } from '../checkboxFilterQuery';
|
||||
import useActiveQueryIndex from 'components/QuickFilters/hooks/useActiveQueryIndex';
|
||||
import useCheckboxDisclosure from '../useCheckboxDisclosure';
|
||||
import useCheckboxFilterActions from '../useCheckboxFilterActions';
|
||||
import useCheckboxFilterState from '../useCheckboxFilterState';
|
||||
import useCheckboxFilterState, {
|
||||
FILTER_DISABLED_REASON,
|
||||
} from '../useCheckboxFilterState';
|
||||
import { useFieldValues } from './useFieldValues';
|
||||
import { useExistingQuery } from './useExistingQuery';
|
||||
import { isKeyMatch } from '../utils';
|
||||
@@ -195,7 +197,8 @@ export default function CheckboxFilterV2(
|
||||
placeholder="Filter values"
|
||||
onChange={(e): void => setSearchTextDebounced(e.target.value)}
|
||||
disabled={isFilterDisabled}
|
||||
data-testid="checkbox-filter-search"
|
||||
disabledTooltip={FILTER_DISABLED_REASON}
|
||||
testId="checkbox-filter-search"
|
||||
suffix={
|
||||
isFetching ? (
|
||||
<LoaderCircle
|
||||
|
||||
@@ -61,7 +61,7 @@ describe('CheckboxFilterV2 - interactions', () => {
|
||||
expect(screen.getByTestId('checkbox-value-row-staging')).toBeInTheDocument();
|
||||
|
||||
await user.click(screen.getByTestId('checkbox-filter-search-toggle'));
|
||||
const searchInput = screen.getByTestId('checkbox-filter-search');
|
||||
const searchInput = screen.getByTestId('checkbox-filter-search-field');
|
||||
await user.type(searchInput, 'prod');
|
||||
|
||||
await waitFor(() => {
|
||||
@@ -145,7 +145,7 @@ describe('CheckboxFilterV2 - interactions', () => {
|
||||
expect(screen.getByTestId('section-divider-related')).toBeInTheDocument();
|
||||
|
||||
await user.click(screen.getByTestId('checkbox-filter-search-toggle'));
|
||||
const searchInput = screen.getByTestId('checkbox-filter-search');
|
||||
const searchInput = screen.getByTestId('checkbox-filter-search-field');
|
||||
await user.type(searchInput, 'prod');
|
||||
|
||||
await waitFor(() => {
|
||||
@@ -195,7 +195,7 @@ describe('CheckboxFilterV2 - interactions', () => {
|
||||
await screen.findByTestId('checkbox-value-row-prod');
|
||||
|
||||
await user.click(screen.getByTestId('checkbox-filter-search-toggle'));
|
||||
const searchInput = screen.getByTestId('checkbox-filter-search');
|
||||
const searchInput = screen.getByTestId('checkbox-filter-search-field');
|
||||
await user.type(searchInput, 'prod');
|
||||
|
||||
await waitFor(() => {
|
||||
@@ -240,7 +240,7 @@ describe('CheckboxFilterV2 - interactions', () => {
|
||||
await screen.findByTestId('checkbox-value-row-prod');
|
||||
|
||||
await user.click(screen.getByTestId('checkbox-filter-search-toggle'));
|
||||
const searchInput = screen.getByTestId('checkbox-filter-search');
|
||||
const searchInput = screen.getByTestId('checkbox-filter-search-field');
|
||||
await user.type(searchInput, 'xyz-no-match');
|
||||
|
||||
await waitFor(() => {
|
||||
@@ -348,7 +348,7 @@ describe('CheckboxFilterV2 - interactions', () => {
|
||||
await screen.findByTestId('checkbox-value-row-pod-a-v1');
|
||||
|
||||
await user.click(screen.getByTestId('checkbox-filter-search-toggle'));
|
||||
const searchInput = screen.getByTestId('checkbox-filter-search');
|
||||
const searchInput = screen.getByTestId('checkbox-filter-search-field');
|
||||
await user.type(searchInput, 'pod-a');
|
||||
|
||||
// After search, both sections should still appear with filtered results
|
||||
|
||||
@@ -111,7 +111,7 @@ describe('CheckboxFilterV2 - states', () => {
|
||||
await screen.findByTestId('checkbox-value-row-production');
|
||||
|
||||
await user.click(screen.getByTestId('checkbox-filter-search-toggle'));
|
||||
const searchInput = screen.getByTestId('checkbox-filter-search');
|
||||
const searchInput = screen.getByTestId('checkbox-filter-search-field');
|
||||
await user.type(searchInput, 'prod');
|
||||
|
||||
await waitFor(() => {
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
useState,
|
||||
} from 'react';
|
||||
import { Input } from 'antd';
|
||||
import { Slider } from '@signozhq/ui/slider';
|
||||
import { Slider, type SliderRangeValue } from '@signozhq/ui/slider';
|
||||
import { getMs } from 'utils/timeUtils';
|
||||
import useDebouncedFn from 'hooks/useDebouncedFunction';
|
||||
|
||||
@@ -90,13 +90,13 @@ export function DurationSection(props: DurationProps): JSX.Element {
|
||||
debouncedFunction(min, max);
|
||||
};
|
||||
|
||||
const onRangeHandler = (value: number | number[]): void => {
|
||||
const [min, max] = value as number[];
|
||||
const onRangeHandler = (value: SliderRangeValue): void => {
|
||||
const [min, max] = value;
|
||||
updateDurationFilter(min.toString(), max.toString());
|
||||
};
|
||||
|
||||
const TipComponent = useCallback(
|
||||
(value: number) => <div>{`${value.toString()}ms`}</div>,
|
||||
const formatTooltipValue = useCallback(
|
||||
(value: number): string => `${value.toString()}ms`,
|
||||
[],
|
||||
);
|
||||
|
||||
@@ -125,13 +125,14 @@ export function DurationSection(props: DurationProps): JSX.Element {
|
||||
/>
|
||||
</div>
|
||||
<div className="duration-input-slider">
|
||||
<Slider
|
||||
<Slider.Range
|
||||
min={0}
|
||||
max={100000}
|
||||
range
|
||||
tooltip={{ formatter: TipComponent }}
|
||||
color="primary"
|
||||
tooltip
|
||||
formatValue={formatTooltipValue}
|
||||
onChange={(value): void => {
|
||||
const [min, max] = value as number[];
|
||||
const [min, max] = value;
|
||||
onRangeSliderHandler([String(min), String(max)]);
|
||||
}}
|
||||
onAfterChange={onRangeHandler}
|
||||
|
||||
@@ -55,8 +55,7 @@
|
||||
min-width: 70px;
|
||||
}
|
||||
|
||||
.sync-tag,
|
||||
.select-box {
|
||||
.sync-tag {
|
||||
display: flex;
|
||||
padding: 5px 9px;
|
||||
flex-direction: column;
|
||||
@@ -77,17 +76,21 @@
|
||||
}
|
||||
|
||||
.select-box {
|
||||
display: flex;
|
||||
padding: 2px 5px;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 3px;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
.select-box-item {
|
||||
min-width: 60px;
|
||||
text-transform: uppercase;
|
||||
--combobox-trigger-background: var(--primary-background);
|
||||
--combobox-trigger-border: color-mix(
|
||||
in srgb,
|
||||
var(--primary-background) 20%,
|
||||
transparent
|
||||
);
|
||||
--combobox-trigger-border-hover: var(--primary-background);
|
||||
--combobox-trigger-label: var(--primary-foreground);
|
||||
--combobox-trigger-icon: var(--primary-foreground);
|
||||
--combobox-trigger-icon-hover: var(--primary-foreground);
|
||||
--combobox-trigger-font-family: 'Geist Mono';
|
||||
--combobox-trigger-border-radius: 2px;
|
||||
--combobox-trigger-min-block-size: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,14 +6,7 @@ import {
|
||||
RefreshCw,
|
||||
Settings2 as SettingsIcon,
|
||||
} from '@signozhq/icons';
|
||||
import {
|
||||
Combobox,
|
||||
ComboboxCommand,
|
||||
ComboboxContent,
|
||||
ComboboxItem,
|
||||
ComboboxList,
|
||||
ComboboxTrigger,
|
||||
} from '@signozhq/ui/combobox';
|
||||
import { Combobox, type ComboboxItemType } from '@signozhq/ui/combobox';
|
||||
import { Skeleton, Tooltip } from 'antd';
|
||||
import { Button } from '@signozhq/ui/button';
|
||||
import { Switch } from '@signozhq/ui/switch';
|
||||
@@ -83,7 +76,6 @@ export default function QuickFilters(props: IQuickFiltersProps): JSX.Element {
|
||||
redirectWithQueryBuilderData,
|
||||
panelType,
|
||||
} = useQueryBuilder();
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
// Sync lastUsedQuery when queries change (e.g., after deletion)
|
||||
const validQueryIndex = useMemo(
|
||||
@@ -110,6 +102,16 @@ export default function QuickFilters(props: IQuickFiltersProps): JSX.Element {
|
||||
}));
|
||||
}, [currentQuery?.builder?.queryData]);
|
||||
|
||||
const queryItems = useMemo<ComboboxItemType[]>(
|
||||
() =>
|
||||
queryOptions.map((option) => ({
|
||||
type: 'item',
|
||||
value: String(option.value),
|
||||
label: option.label,
|
||||
})),
|
||||
[queryOptions],
|
||||
);
|
||||
|
||||
// Show dropdown in ListView only for TRACES_EXPLORER source
|
||||
const shouldShowDropdownInListView =
|
||||
isListView && source === QuickFiltersSource.TRACES_EXPLORER;
|
||||
@@ -186,34 +188,20 @@ export default function QuickFilters(props: IQuickFiltersProps): JSX.Element {
|
||||
{queryOptions.length > 1 &&
|
||||
!isAIObservabilityRowView &&
|
||||
(!isListView || shouldShowDropdownInListView) ? (
|
||||
<Combobox open={open} onOpenChange={setOpen}>
|
||||
<ComboboxTrigger
|
||||
<div className="select-box">
|
||||
<Combobox
|
||||
aria-label="Select a query"
|
||||
placeholder="Select a query"
|
||||
value={queryOptions.find((f) => f.value === validQueryIndex)?.label || ''}
|
||||
className="select-box"
|
||||
searchInputProps={{ placeholder: 'Search queries' }}
|
||||
items={queryItems}
|
||||
value={String(validQueryIndex)}
|
||||
onChange={(value): void => {
|
||||
if (value !== undefined) {
|
||||
handleQueryChange(Number(value));
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{open && (
|
||||
<ComboboxContent>
|
||||
<ComboboxCommand>
|
||||
<ComboboxList>
|
||||
{queryOptions.map((option) => (
|
||||
<ComboboxItem
|
||||
key={option.value}
|
||||
value={String(option.value)}
|
||||
onSelect={(): void => {
|
||||
handleQueryChange(option.value);
|
||||
setOpen(false);
|
||||
}}
|
||||
isSelected={validQueryIndex === option.value}
|
||||
>
|
||||
{option.label}
|
||||
</ComboboxItem>
|
||||
))}
|
||||
</ComboboxList>
|
||||
</ComboboxCommand>
|
||||
</ComboboxContent>
|
||||
)}
|
||||
</Combobox>
|
||||
</div>
|
||||
) : (
|
||||
displayedQueryName && (
|
||||
<Tooltip
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react-vite';
|
||||
import type { ComponentProps, ComponentType } from 'react';
|
||||
import { expect, screen, userEvent, waitFor, within } from 'storybook/test';
|
||||
import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||
|
||||
import { withCanvas } from '@/storybook/decorators/withCanvas';
|
||||
import type { GlobalMockArgs } from '@/storybook/globals';
|
||||
@@ -15,7 +16,7 @@ import {
|
||||
queryBuilder,
|
||||
selectedServiceQueryBuilder,
|
||||
} from './QuickFilters.stories.mocks';
|
||||
import { QuickFiltersSource, SignalType } from '../types';
|
||||
import { FiltersType, QuickFiltersSource, SignalType } from '../types';
|
||||
|
||||
const meta = {
|
||||
title: 'Components/Quick Filters',
|
||||
@@ -56,10 +57,10 @@ type TooltipsStory = StoryObj<
|
||||
* silence.
|
||||
*/
|
||||
const settingsControl = (canvasElement: HTMLElement): Promise<HTMLElement> =>
|
||||
waitFor(() => {
|
||||
waitFor(async () => {
|
||||
const control = within(canvasElement).getByTestId('settings-icon-container');
|
||||
|
||||
expect(control).not.toHaveAttribute('aria-disabled', 'true');
|
||||
await expect(control).not.toHaveAttribute('aria-disabled', 'true');
|
||||
|
||||
return control;
|
||||
});
|
||||
@@ -105,6 +106,27 @@ export const SelectedExpandedCheckbox: Story = {
|
||||
},
|
||||
};
|
||||
|
||||
/** The duration min/max row, which a checkbox-only config never reaches. */
|
||||
export const DurationFilter: Story = {
|
||||
args: {
|
||||
signal: undefined,
|
||||
source: QuickFiltersSource.TRACES_EXPLORER,
|
||||
config: [
|
||||
...checkboxConfig,
|
||||
{
|
||||
attributeKey: {
|
||||
dataType: DataTypes.Float64,
|
||||
key: 'duration_nano',
|
||||
type: '',
|
||||
},
|
||||
defaultOpen: true,
|
||||
title: 'Duration',
|
||||
type: FiltersType.DURATION,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Every tooltip the panel renders, held open: the reveal on each truncated
|
||||
* filter value. Nothing bounds those values, so the panel is answered with
|
||||
|
||||
@@ -202,15 +202,29 @@ describe('Quick Filters', () => {
|
||||
expect(screen.getByRole('option', { name: 'Query C' })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// Select Query B
|
||||
const queryBOption = screen.getByRole('option', { name: 'Query B' });
|
||||
await user.click(queryBOption);
|
||||
// Select Query B with the keyboard: jsdom cannot deliver the pointer
|
||||
// click Base UI expects, so filter through the search row and pick with
|
||||
// Enter instead
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
screen.getByRole('combobox', { name: 'Search queries' }),
|
||||
).toHaveFocus();
|
||||
});
|
||||
await user.keyboard('Query B');
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
screen.queryByRole('option', { name: 'Query A' }),
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
await user.keyboard('{ArrowDown}{Enter}');
|
||||
|
||||
// Verify setLastUsedQuery was called with index 1
|
||||
await waitFor(() => {
|
||||
expect(setLastUsedQuery).toHaveBeenCalledWith(1);
|
||||
});
|
||||
});
|
||||
// filtering and picking through the search row takes Base UI past the
|
||||
// default 5s under jsdom
|
||||
}, 20000);
|
||||
|
||||
it('should not display query dropdown in ListView', () => {
|
||||
(useQueryBuilder as jest.Mock).mockReturnValue({
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Check, Copy } from '@signozhq/icons';
|
||||
import { Check, Copy, SolidInfoCircle } from '@signozhq/icons';
|
||||
import { Badge } from '@signozhq/ui/badge';
|
||||
import { Button } from '@signozhq/ui/button';
|
||||
import { Callout } from '@signozhq/ui/callout';
|
||||
@@ -44,11 +44,9 @@ function KeyCreatedPhase({
|
||||
</div>
|
||||
|
||||
<div className="add-key-modal__callout-wrapper">
|
||||
<Callout
|
||||
type="info"
|
||||
showIcon
|
||||
title="Store the key securely. This is the only time it will be displayed."
|
||||
/>
|
||||
<Callout color="info" size="sm" icon={<SolidInfoCircle />}>
|
||||
Store the key securely. This is the only time it will be displayed.
|
||||
</Callout>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -53,7 +53,6 @@ function KeyFormPhase({
|
||||
<Input
|
||||
id="key-name"
|
||||
placeholder="Enter key name e.g.: Service Owner"
|
||||
className="add-key-modal__input"
|
||||
testId="add-key-name-input"
|
||||
{...register('keyName', {
|
||||
required: true,
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useForm } from 'react-hook-form';
|
||||
import { useQueryClient } from 'react-query';
|
||||
import { useCopyToClipboard } from 'react-use';
|
||||
import { DialogWrapper } from '@signozhq/ui/dialog';
|
||||
import { toast } from '@signozhq/ui/sonner';
|
||||
import { toast } from '@signozhq/ui/toast';
|
||||
import { convertToApiError } from 'api/ErrorResponseHandlerForGeneratedAPIs';
|
||||
import {
|
||||
invalidateListServiceAccountKeys,
|
||||
@@ -119,7 +119,7 @@ function AddKeyModal(): JSX.Element {
|
||||
|
||||
useEffect(() => {
|
||||
if (copyState.error) {
|
||||
toast.error('Failed to copy key');
|
||||
toast.danger('Failed to copy key', { action: { label: 'Dismiss' } });
|
||||
}
|
||||
}, [copyState.error]);
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import { Button } from '@signozhq/ui/button';
|
||||
import AuthZButton from 'lib/authz/components/AuthZButton/AuthZButton';
|
||||
import { buildSADeletePermission } from 'lib/authz/hooks/useAuthZ/permissions/service-account.permissions';
|
||||
import { DialogWrapper } from '@signozhq/ui/dialog';
|
||||
import { toast } from '@signozhq/ui/sonner';
|
||||
import { toast } from '@signozhq/ui/toast';
|
||||
import { convertToApiError } from 'api/ErrorResponseHandlerForGeneratedAPIs';
|
||||
import {
|
||||
getGetServiceAccountQueryKey,
|
||||
|
||||
@@ -70,7 +70,6 @@ function EditKeyForm({
|
||||
) : (
|
||||
<Input
|
||||
id="edit-key-name"
|
||||
className="edit-key-modal__input"
|
||||
placeholder="Enter key name"
|
||||
{...register('name')}
|
||||
/>
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useEffect, useState } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { useQueryClient } from 'react-query';
|
||||
import { DialogWrapper } from '@signozhq/ui/dialog';
|
||||
import { toast } from '@signozhq/ui/sonner';
|
||||
import { toast } from '@signozhq/ui/toast';
|
||||
import { convertToApiError } from 'api/ErrorResponseHandlerForGeneratedAPIs';
|
||||
import {
|
||||
invalidateListServiceAccountKeys,
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
buildSADetachPermission,
|
||||
} from 'lib/authz/hooks/useAuthZ/permissions/service-account.permissions';
|
||||
import { DialogWrapper } from '@signozhq/ui/dialog';
|
||||
import { toast } from '@signozhq/ui/sonner';
|
||||
import { toast } from '@signozhq/ui/toast';
|
||||
import { convertToApiError } from 'api/ErrorResponseHandlerForGeneratedAPIs';
|
||||
import {
|
||||
getListServiceAccountKeysQueryKey,
|
||||
|
||||
@@ -9,7 +9,7 @@ import { useQueryClient } from 'react-query';
|
||||
import { Key, LayoutGrid, Plus, Trash2, X } from '@signozhq/icons';
|
||||
import { Button } from '@signozhq/ui/button';
|
||||
import { DrawerWrapper } from '@signozhq/ui/drawer';
|
||||
import { toast } from '@signozhq/ui/sonner';
|
||||
import { toast } from '@signozhq/ui/toast';
|
||||
import { ToggleGroup } from '@signozhq/ui/toggle-group';
|
||||
import { Skeleton } from 'antd';
|
||||
import { convertToApiError } from 'api/ErrorResponseHandlerForGeneratedAPIs';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { toast } from '@signozhq/ui/sonner';
|
||||
import { toast } from '@signozhq/ui/toast';
|
||||
import { buildSAAttachPermission } from 'lib/authz/hooks/useAuthZ/permissions/service-account.permissions';
|
||||
import {
|
||||
setupAuthzAdmin,
|
||||
@@ -12,9 +12,9 @@ import { render, screen, userEvent, waitFor } from 'tests/test-utils';
|
||||
|
||||
import AddKeyModal from '../AddKeyModal';
|
||||
|
||||
jest.mock('@signozhq/ui/sonner', () => ({
|
||||
...jest.requireActual('@signozhq/ui/sonner'),
|
||||
toast: { success: jest.fn(), error: jest.fn() },
|
||||
jest.mock('@signozhq/ui/toast', () => ({
|
||||
...jest.requireActual('@signozhq/ui/toast'),
|
||||
toast: { success: jest.fn(), danger: jest.fn() },
|
||||
}));
|
||||
|
||||
const mockCopyToClipboard = jest.fn();
|
||||
@@ -74,7 +74,8 @@ describe('AddKeyModal', () => {
|
||||
|
||||
// The form only renders once the checks resolve, so waiting for it also
|
||||
// guarantees the button is no longer in its authz-loading state.
|
||||
const nameInput = await screen.findByTestId('add-key-name-input');
|
||||
// The Input's testId lands on its frame; the native input is `-field`.
|
||||
const nameInput = await screen.findByTestId('add-key-name-input-field');
|
||||
const createBtn = await screen.findByTestId('add-key-submit-btn');
|
||||
|
||||
expect(createBtn).toHaveAttribute('aria-disabled', 'true');
|
||||
@@ -94,7 +95,7 @@ describe('AddKeyModal', () => {
|
||||
const user = userEvent.setup({ pointerEventsCheck: 0 });
|
||||
renderModal();
|
||||
|
||||
const nameInput = await screen.findByTestId('add-key-name-input');
|
||||
const nameInput = await screen.findByTestId('add-key-name-input-field');
|
||||
const submitBtn = await screen.findByTestId('add-key-submit-btn');
|
||||
await user.type(nameInput, 'Deploy Key');
|
||||
await waitFor(() =>
|
||||
@@ -118,8 +119,9 @@ describe('AddKeyModal', () => {
|
||||
);
|
||||
renderModal();
|
||||
|
||||
const nameInput =
|
||||
await screen.findByTestId<HTMLInputElement>('add-key-name-input');
|
||||
const nameInput = await screen.findByTestId<HTMLInputElement>(
|
||||
'add-key-name-input-field',
|
||||
);
|
||||
const submitBtn = await screen.findByTestId('add-key-submit-btn');
|
||||
await user.type(nameInput, 'Deploy Key');
|
||||
await waitFor(() =>
|
||||
@@ -139,7 +141,7 @@ describe('AddKeyModal', () => {
|
||||
|
||||
renderModal();
|
||||
|
||||
const nameInput = await screen.findByTestId('add-key-name-input');
|
||||
const nameInput = await screen.findByTestId('add-key-name-input-field');
|
||||
const submitBtn = await screen.findByTestId('add-key-submit-btn');
|
||||
await user.type(nameInput, 'Deploy Key');
|
||||
await waitFor(() =>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { toast } from '@signozhq/ui/sonner';
|
||||
import { toast } from '@signozhq/ui/toast';
|
||||
import type { ServiceaccounttypesGettableFactorAPIKeyDTO } from 'api/generated/services/sigNoz.schemas';
|
||||
import { setupAuthzAdmin } from 'lib/authz/utils/authz-test-utils';
|
||||
import { rest, server } from 'mocks-server/server';
|
||||
@@ -17,9 +17,9 @@ jest.mock('lib/authz/components/AuthZTooltip/AuthZTooltip', () => ({
|
||||
}): React.ReactElement => children,
|
||||
}));
|
||||
|
||||
jest.mock('@signozhq/ui/sonner', () => ({
|
||||
...jest.requireActual('@signozhq/ui/sonner'),
|
||||
toast: { success: jest.fn(), error: jest.fn() },
|
||||
jest.mock('@signozhq/ui/toast', () => ({
|
||||
...jest.requireActual('@signozhq/ui/toast'),
|
||||
toast: { success: jest.fn(), danger: jest.fn() },
|
||||
}));
|
||||
|
||||
const mockToast = jest.mocked(toast);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { toast } from '@signozhq/ui/sonner';
|
||||
import { toast } from '@signozhq/ui/toast';
|
||||
import { ServiceaccounttypesGettableFactorAPIKeyDTO } from 'api/generated/services/sigNoz.schemas';
|
||||
import { setupAuthzAdmin } from 'lib/authz/utils/authz-test-utils';
|
||||
import { rest, server } from 'mocks-server/server';
|
||||
@@ -16,9 +16,9 @@ jest.mock('lib/authz/components/AuthZTooltip/AuthZTooltip', () => ({
|
||||
}): React.ReactElement => children,
|
||||
}));
|
||||
|
||||
jest.mock('@signozhq/ui/sonner', () => ({
|
||||
...jest.requireActual('@signozhq/ui/sonner'),
|
||||
toast: { success: jest.fn(), error: jest.fn() },
|
||||
jest.mock('@signozhq/ui/toast', () => ({
|
||||
...jest.requireActual('@signozhq/ui/toast'),
|
||||
toast: { success: jest.fn(), danger: jest.fn() },
|
||||
}));
|
||||
|
||||
const mockToast = jest.mocked(toast);
|
||||
|
||||
@@ -15,20 +15,18 @@
|
||||
border: 1px solid var(--l2-border);
|
||||
}
|
||||
|
||||
// Wrapper around the chromeless new-tag Input (the Input itself is
|
||||
// variant="unstyled"); the placeholder colour comes via the --input-* tokens.
|
||||
.input {
|
||||
flex: 1;
|
||||
min-width: 120px;
|
||||
border: none;
|
||||
background: transparent;
|
||||
|
||||
&::placeholder {
|
||||
color: var(--l3-foreground);
|
||||
}
|
||||
--input-placeholder: var(--l3-foreground);
|
||||
--input-placeholder-opacity: 1;
|
||||
}
|
||||
|
||||
// Wrapper around the inline chip-edit Input; width lives on the Input prop.
|
||||
.editInput {
|
||||
width: 160px;
|
||||
height: 24px;
|
||||
--input-height: 24px;
|
||||
}
|
||||
|
||||
.error {
|
||||
|
||||
@@ -9,7 +9,8 @@ type User = ReturnType<typeof userEvent.setup>;
|
||||
|
||||
const startEditingFirstChip = async (user: User): Promise<HTMLElement> => {
|
||||
await user.click(screen.getAllByTestId(`${TID}-chip`)[0]);
|
||||
return screen.getByTestId(`${TID}-edit`);
|
||||
// The Input's testId lands on its frame; the native input is `${testId}-field`.
|
||||
return screen.getByTestId(`${TID}-edit-field`);
|
||||
};
|
||||
|
||||
describe('TagKeyValueInput — inline chip edit', () => {
|
||||
@@ -101,7 +102,7 @@ describe('TagKeyValueInput — backend-rule validation', () => {
|
||||
const onTagsChange = jest.fn();
|
||||
render(<TagKeyValueInput tags={[]} onTagsChange={onTagsChange} />);
|
||||
|
||||
const input = screen.getByTestId(TID);
|
||||
const input = screen.getByTestId(`${TID}-field`);
|
||||
await user.type(input, 'env:pro d{Enter}');
|
||||
|
||||
expect(screen.getByTestId(`${TID}-error`)).toHaveTextContent(
|
||||
|
||||
@@ -115,21 +115,22 @@ function TagKeyValueInput({
|
||||
<div className={styles.field}>
|
||||
{tags.map((tag, index) =>
|
||||
index === editIndex ? (
|
||||
<Input
|
||||
key={tag}
|
||||
className={styles.editInput}
|
||||
value={editValue}
|
||||
autoFocus
|
||||
testId={`${testId}-edit`}
|
||||
onChange={(e: ChangeEvent<HTMLInputElement>): void => {
|
||||
setEditValue(e.target.value);
|
||||
if (error) {
|
||||
setError('');
|
||||
}
|
||||
}}
|
||||
onKeyDown={handleEditKeyDown}
|
||||
onBlur={(): void => commitEdit(true)}
|
||||
/>
|
||||
<span key={tag} className={styles.editInput}>
|
||||
<Input
|
||||
width={160}
|
||||
value={editValue}
|
||||
autoFocus
|
||||
testId={`${testId}-edit`}
|
||||
onChange={(e: ChangeEvent<HTMLInputElement>): void => {
|
||||
setEditValue(e.target.value);
|
||||
if (error) {
|
||||
setError('');
|
||||
}
|
||||
}}
|
||||
onKeyDown={handleEditKeyDown}
|
||||
onBlur={(): void => commitEdit(true)}
|
||||
/>
|
||||
</span>
|
||||
) : (
|
||||
<Pill.Closeable
|
||||
key={tag}
|
||||
@@ -142,14 +143,17 @@ function TagKeyValueInput({
|
||||
</Pill.Closeable>
|
||||
),
|
||||
)}
|
||||
<Input
|
||||
className={styles.input}
|
||||
value={inputValue}
|
||||
placeholder={placeholder}
|
||||
testId={testId}
|
||||
onChange={handleChange}
|
||||
onKeyDown={handleKeyDown}
|
||||
/>
|
||||
<div className={styles.input}>
|
||||
<Input
|
||||
variant="unstyled"
|
||||
width="100%"
|
||||
value={inputValue}
|
||||
placeholder={placeholder}
|
||||
testId={testId}
|
||||
onChange={handleChange}
|
||||
onKeyDown={handleKeyDown}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{error && (
|
||||
<Typography className={styles.error} data-testid={`${testId}-error`}>
|
||||
|
||||
@@ -17,7 +17,7 @@ import {
|
||||
horizontalListSortingStrategy,
|
||||
SortableContext,
|
||||
} from '@dnd-kit/sortable';
|
||||
import { ComboboxSimple } from '@signozhq/ui/combobox';
|
||||
import { Combobox } from '@signozhq/ui/combobox';
|
||||
import { Pagination } from '@signozhq/ui/pagination';
|
||||
import type { Row } from '@tanstack/react-table';
|
||||
import {
|
||||
@@ -668,14 +668,16 @@ function TanStackTableInner<TData, TItemKey = string>(
|
||||
/>
|
||||
{pagination.showPageSize !== false && (
|
||||
<div className={viewStyles.paginationPageSize}>
|
||||
<ComboboxSimple
|
||||
<Combobox
|
||||
testId="pagination-page-size"
|
||||
aria-label="Rows per page"
|
||||
placeholder="Page size"
|
||||
searchInputProps={{ placeholder: 'Search page sizes' }}
|
||||
value={limit?.toString()}
|
||||
defaultValue="10"
|
||||
onChange={(value): void => {
|
||||
value ??= '10';
|
||||
setLimit(+value);
|
||||
pagination.onLimitChange?.(+value);
|
||||
onChange={(value: string | undefined): void => {
|
||||
const nextValue = value ?? '10';
|
||||
setLimit(+nextValue);
|
||||
pagination.onLimitChange?.(+nextValue);
|
||||
if (page !== 1) {
|
||||
setPage(1);
|
||||
pagination.onPageChange?.(1);
|
||||
|
||||
@@ -125,7 +125,7 @@
|
||||
|
||||
.paginationPageSize {
|
||||
width: 80px;
|
||||
--combobox-trigger-height: 2rem;
|
||||
--combobox-trigger-min-block-size: 2rem;
|
||||
}
|
||||
|
||||
.paginationTotalCount {
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { CSSProperties, ReactNode } from 'react';
|
||||
import type { ColumnDef } from '@tanstack/react-table';
|
||||
|
||||
import { RowKeyData, TableColumnDef } from './types';
|
||||
import { ComboboxSimpleItem } from '@signozhq/ui/combobox';
|
||||
import type { ComboboxItemType } from '@signozhq/ui/combobox';
|
||||
|
||||
export const getColumnId = <TData>(column: TableColumnDef<TData>): string =>
|
||||
column.id;
|
||||
@@ -151,11 +151,12 @@ const DEFAULT_PAGE_SIZES = [10, 20, 30, 50, 100];
|
||||
|
||||
export function buildPageSizeItems(
|
||||
calculatedSize?: number | null,
|
||||
): ComboboxSimpleItem[] {
|
||||
const items: ComboboxSimpleItem[] = [];
|
||||
): ComboboxItemType[] {
|
||||
const items: ComboboxItemType[] = [];
|
||||
|
||||
if (calculatedSize) {
|
||||
items.push({
|
||||
type: 'item',
|
||||
value: calculatedSize.toString(),
|
||||
label: `Auto (${calculatedSize})`,
|
||||
displayValue: calculatedSize.toString(),
|
||||
@@ -165,6 +166,7 @@ export function buildPageSizeItems(
|
||||
for (const size of DEFAULT_PAGE_SIZES) {
|
||||
if (size !== calculatedSize) {
|
||||
items.push({
|
||||
type: 'item',
|
||||
value: size.toString(),
|
||||
label: size.toString(),
|
||||
displayValue: size.toString(),
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import cx from 'classnames';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import {
|
||||
CommandDialog,
|
||||
CommandEmpty,
|
||||
CommandGroup,
|
||||
CommandInput,
|
||||
CommandItem,
|
||||
CommandList,
|
||||
CommandShortcut,
|
||||
Command,
|
||||
type CommandActionItemType,
|
||||
type CommandItemType,
|
||||
} from '@signozhq/ui/command';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import {
|
||||
@@ -26,7 +21,10 @@ import { IS_DEV } from 'lib/env';
|
||||
import history from 'lib/history';
|
||||
import { ROLES as UserRole } from 'types/roles';
|
||||
|
||||
import { createShortcutActions } from '../../constants/shortcutActions';
|
||||
import {
|
||||
type CmdAction,
|
||||
createShortcutActions,
|
||||
} from '../../constants/shortcutActions';
|
||||
import { useCmdK } from '../../providers/cmdKProvider';
|
||||
|
||||
import './cmdKPalette.scss';
|
||||
@@ -58,17 +56,6 @@ const openAuthZDevModal = IS_DEV
|
||||
}
|
||||
: undefined;
|
||||
|
||||
type CmdAction = {
|
||||
id: string;
|
||||
name: string;
|
||||
shortcut?: string[];
|
||||
keywords?: string;
|
||||
section?: string;
|
||||
icon?: React.ReactNode;
|
||||
roles?: UserRole[];
|
||||
perform: () => void;
|
||||
};
|
||||
|
||||
export function CmdKPalette({
|
||||
userRole,
|
||||
}: {
|
||||
@@ -76,7 +63,7 @@ export function CmdKPalette({
|
||||
}): JSX.Element | null {
|
||||
const { open, setOpen } = useCmdK();
|
||||
|
||||
const { setAutoSwitch, setTheme, theme } = useThemeMode();
|
||||
const { setAutoSwitch, setTheme } = useThemeMode();
|
||||
const location = useLocation();
|
||||
const isAIAssistantEnabled = useIsAIAssistantEnabled();
|
||||
const startNewConversation = useAIAssistantStore(
|
||||
@@ -146,8 +133,28 @@ export function CmdKPalette({
|
||||
(a) => !a.roles || a.roles.includes(userRole),
|
||||
);
|
||||
|
||||
const toCommandItem = (action: CmdAction): CommandActionItemType => ({
|
||||
type: 'item',
|
||||
value: action.id,
|
||||
label: action.name,
|
||||
prefix: action.icon,
|
||||
searchMetadata: action.keywords,
|
||||
shortcut:
|
||||
action.shortcut && action.shortcut.length > 0
|
||||
? action.shortcut.join(' • ')
|
||||
: undefined,
|
||||
onClick: (): void => {
|
||||
// the palette closes itself after a pick, also when this throws
|
||||
try {
|
||||
action.perform();
|
||||
} catch (e) {
|
||||
console.error('Error invoking action', e);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// group permitted actions by section
|
||||
const grouped: [string, CmdAction[]][] = ((): [string, CmdAction[]][] => {
|
||||
const items: CommandItemType[] = ((): CommandItemType[] => {
|
||||
const map = new Map<string, CmdAction[]>();
|
||||
|
||||
permitted.forEach((a) => {
|
||||
@@ -161,61 +168,25 @@ export function CmdKPalette({
|
||||
}
|
||||
});
|
||||
|
||||
return Array.from(map.entries());
|
||||
return Array.from(map.entries()).map(([section, sectionActions]) => ({
|
||||
type: 'group',
|
||||
value: section.toLowerCase().replace(/\s+/g, '-'),
|
||||
label: section,
|
||||
items: sectionActions.map(toCommandItem),
|
||||
}));
|
||||
})();
|
||||
|
||||
const handleInvoke = (action: CmdAction): void => {
|
||||
try {
|
||||
action.perform();
|
||||
} catch (e) {
|
||||
console.error('Error invoking action', e);
|
||||
} finally {
|
||||
setOpen(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<CommandDialog
|
||||
<Command
|
||||
label="Command palette"
|
||||
open={open}
|
||||
onOpenChange={setOpen}
|
||||
position="top"
|
||||
offset={110}
|
||||
>
|
||||
<CommandInput placeholder="Search…" className="cmdk-input-wrapper" />
|
||||
<CommandList className="cmdk-list-scroll">
|
||||
<CommandEmpty>No results</CommandEmpty>
|
||||
{grouped.map(([section, items]) => (
|
||||
<CommandGroup
|
||||
key={section}
|
||||
heading={section}
|
||||
className="cmdk-section-heading"
|
||||
>
|
||||
{items.map((it) => (
|
||||
<CommandItem
|
||||
key={it.id}
|
||||
onSelect={(): void => handleInvoke(it)}
|
||||
value={it.name}
|
||||
className={theme === 'light' ? 'cmdk-item-light' : 'cmdk-item'}
|
||||
>
|
||||
<span
|
||||
className={cx(
|
||||
'cmd-item-icon',
|
||||
it.id === 'ai-assistant' && 'noz-icon',
|
||||
)}
|
||||
>
|
||||
{it.icon}
|
||||
</span>
|
||||
{it.name}
|
||||
{it.shortcut && it.shortcut.length > 0 && (
|
||||
<CommandShortcut>{it.shortcut.join(' • ')}</CommandShortcut>
|
||||
)}
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
))}
|
||||
</CommandList>
|
||||
</CommandDialog>
|
||||
items={items}
|
||||
searchInputProps={{ placeholder: 'Search…' }}
|
||||
noContent="No results"
|
||||
testId="cmdk-palette"
|
||||
/>
|
||||
{IS_DEV && AuthZDevModal && (
|
||||
<React.Suspense fallback={null}>
|
||||
<AuthZDevModal />
|
||||
|
||||
@@ -27,7 +27,7 @@ export type CmdAction = {
|
||||
shortcut?: string[];
|
||||
keywords?: string;
|
||||
section?: string;
|
||||
icon?: React.ReactNode;
|
||||
icon?: React.ReactElement;
|
||||
roles?: ROLES[];
|
||||
perform: () => void;
|
||||
};
|
||||
|
||||
@@ -255,11 +255,6 @@
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
.contextPopoverSearchInput {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.contextPopoverEntities {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@@ -11,7 +11,7 @@ import { Badge } from '@signozhq/ui/badge';
|
||||
import { Button } from '@signozhq/ui/button';
|
||||
import { Input } from '@signozhq/ui/input';
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '@signozhq/ui/popover';
|
||||
import { toast } from '@signozhq/ui/sonner';
|
||||
import { toast } from '@signozhq/ui/toast';
|
||||
import { Tooltip } from '@signozhq/ui/tooltip';
|
||||
import type { UploadFile } from 'antd';
|
||||
import getSessionStorage from 'api/browser/sessionstorage/get';
|
||||
@@ -591,19 +591,23 @@ export default function ChatInput({
|
||||
});
|
||||
if (error === 'network') {
|
||||
markVoiceUnavailable();
|
||||
toast.error('Voice input unavailable in this browser', {
|
||||
toast.danger('Voice input unavailable in this browser', {
|
||||
action: { label: 'Dismiss' },
|
||||
description:
|
||||
'This browser cannot reach the speech recognition service. Try Google Chrome or Microsoft Edge.',
|
||||
});
|
||||
} else if (error === 'not-allowed') {
|
||||
markVoiceUnavailable();
|
||||
toast.error('Microphone access denied', {
|
||||
toast.danger('Microphone access denied', {
|
||||
action: { label: 'Dismiss' },
|
||||
description:
|
||||
'Grant microphone permission in your browser settings to use voice input.',
|
||||
});
|
||||
} else if (error === 'not-supported') {
|
||||
markVoiceUnavailable();
|
||||
toast.error('Voice input is not supported in this browser.');
|
||||
toast.danger('Voice input is not supported in this browser.', {
|
||||
action: { label: 'Dismiss' },
|
||||
});
|
||||
}
|
||||
// `no-speech` is benign (just silence) — don't toast or hide.
|
||||
},
|
||||
@@ -1093,7 +1097,6 @@ export default function ChatInput({
|
||||
<Input
|
||||
type="text"
|
||||
placeholder={`Search ${activeContextCategory.toLowerCase()}…`}
|
||||
className={styles.contextPopoverSearchInput}
|
||||
value={pickerSearchQuery}
|
||||
onChange={(e): void => setPickerSearchQuery(e.target.value)}
|
||||
prefix={<Search size={12} />}
|
||||
|
||||
@@ -88,7 +88,20 @@
|
||||
margin-left: 2px;
|
||||
}
|
||||
|
||||
.input,
|
||||
// The free-text Inputs inside .field / .multiSelectFieldset pick up the old
|
||||
// `.input` look through the Input component's CSS tokens.
|
||||
.field,
|
||||
.multiSelectFieldset {
|
||||
--input-background: var(--l2-background);
|
||||
--input-border: var(--l2-border);
|
||||
--input-border-radius: var(--radius-2);
|
||||
--input-height: 28px;
|
||||
--input-padding: 5px 8px;
|
||||
--input-font-size: 13px;
|
||||
--input-foreground: var(--l1-foreground);
|
||||
--input-focus-ring: var(--accent-primary);
|
||||
}
|
||||
|
||||
.select {
|
||||
background: var(--l2-background);
|
||||
border: 1px solid var(--l2-border);
|
||||
@@ -108,14 +121,6 @@
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
// Constrain the Radix-based SelectContent popover so it never grows wider
|
||||
// than the trigger button. `--radix-select-trigger-width` is set by Radix
|
||||
// at the popper layer when `position: 'popper'` (the default).
|
||||
.selectContent {
|
||||
width: var(--radix-select-trigger-width);
|
||||
min-width: var(--radix-select-trigger-width);
|
||||
}
|
||||
|
||||
.radioGroup,
|
||||
.checkboxGroup {
|
||||
display: flex;
|
||||
|
||||
@@ -3,12 +3,8 @@ import cx from 'classnames';
|
||||
import { Button } from '@signozhq/ui/button';
|
||||
import { Checkbox } from '@signozhq/ui/checkbox';
|
||||
import { Input } from '@signozhq/ui/input';
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
} from '@signozhq/ui/select';
|
||||
import { Select } from '@signozhq/ui/select';
|
||||
import type { SelectItemType } from '@signozhq/ui/select';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import { ClarificationFieldTypeDTO } from 'api/ai-assistant/sigNozAIAssistantAPI.schemas';
|
||||
import type {
|
||||
@@ -247,10 +243,7 @@ function FieldInput({ field, value, onChange }: FieldInputProps): JSX.Element {
|
||||
// "Other (type your own)" entry — a plain `options: null` would
|
||||
// otherwise fall through to the bare text-input renderer).
|
||||
if (type === ClarificationFieldTypeDTO.select && (options || allowCustom)) {
|
||||
const handleSelectChange = (next: string | string[]): void => {
|
||||
// `multiple` is off → callback receives a single string. The wider
|
||||
// `string | string[]` typing comes from the shared Select root.
|
||||
const picked = Array.isArray(next) ? (next[0] ?? '') : next;
|
||||
const handleSelectChange = (picked: string): void => {
|
||||
if (picked === CUSTOM_OPTION_SENTINEL) {
|
||||
setIsCustom(true);
|
||||
onChange(customValue);
|
||||
@@ -260,6 +253,23 @@ function FieldInput({ field, value, onChange }: FieldInputProps): JSX.Element {
|
||||
}
|
||||
};
|
||||
|
||||
const selectItems: SelectItemType[] = [
|
||||
...(options ?? []).map(
|
||||
(opt): SelectItemType => ({ type: 'item', value: opt, label: opt }),
|
||||
),
|
||||
...(allowCustom
|
||||
? [
|
||||
{
|
||||
type: 'item',
|
||||
value: CUSTOM_OPTION_SENTINEL,
|
||||
label: CUSTOM_OPTION_LABEL,
|
||||
} as SelectItemType,
|
||||
]
|
||||
: []),
|
||||
];
|
||||
|
||||
const selectedValue = isCustom ? CUSTOM_OPTION_SENTINEL : String(value ?? '');
|
||||
|
||||
return (
|
||||
<div className={styles.field}>
|
||||
<label className={styles.label} htmlFor={id}>
|
||||
@@ -271,34 +281,16 @@ function FieldInput({ field, value, onChange }: FieldInputProps): JSX.Element {
|
||||
)}
|
||||
</label>
|
||||
<Select
|
||||
value={isCustom ? CUSTOM_OPTION_SENTINEL : String(value ?? '')}
|
||||
id={id}
|
||||
items={selectItems}
|
||||
placeholder="Select…"
|
||||
aria-required={required || undefined}
|
||||
value={selectedValue || undefined}
|
||||
onChange={handleSelectChange}
|
||||
>
|
||||
<SelectTrigger
|
||||
id={id}
|
||||
placeholder="Select…"
|
||||
aria-required={required || undefined}
|
||||
/>
|
||||
{/* Pin the dropdown width to the trigger via Radix's
|
||||
`--radix-select-trigger-width`; otherwise the popover
|
||||
sizes to its widest item and looks misaligned. */}
|
||||
<SelectContent className={styles.selectContent}>
|
||||
{options?.map((opt) => (
|
||||
<SelectItem key={opt} value={opt}>
|
||||
{opt}
|
||||
</SelectItem>
|
||||
))}
|
||||
{allowCustom && (
|
||||
<SelectItem value={CUSTOM_OPTION_SENTINEL}>
|
||||
{CUSTOM_OPTION_LABEL}
|
||||
</SelectItem>
|
||||
)}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
/>
|
||||
{isCustom && (
|
||||
<Input
|
||||
type="text"
|
||||
className={styles.input}
|
||||
placeholder="Enter a custom value"
|
||||
value={customValue}
|
||||
onChange={(e): void => {
|
||||
@@ -407,7 +399,6 @@ function FieldInput({ field, value, onChange }: FieldInputProps): JSX.Element {
|
||||
{isCustom && (
|
||||
<Input
|
||||
type="text"
|
||||
className={styles.input}
|
||||
placeholder="Enter a custom value"
|
||||
value={customValue}
|
||||
onChange={(e): void => updateCustomValue(e.target.value)}
|
||||
@@ -431,7 +422,6 @@ function FieldInput({ field, value, onChange }: FieldInputProps): JSX.Element {
|
||||
<Input
|
||||
id={id}
|
||||
type={type === 'number' ? 'number' : 'text'}
|
||||
className={styles.input}
|
||||
value={String(value ?? '')}
|
||||
aria-required={required || undefined}
|
||||
onChange={(e): void => {
|
||||
|
||||
@@ -62,16 +62,16 @@
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
// Wrapper around the chromeless rename Input (variant="unstyled"); the accent
|
||||
// bottom border marks the edit state, and the --input-* tokens keep the field
|
||||
// as compact as the title row it replaces.
|
||||
.input {
|
||||
width: 100%;
|
||||
background: transparent;
|
||||
border: none;
|
||||
border-bottom: 1px solid var(--accent-primary);
|
||||
outline: none;
|
||||
color: var(--l1-foreground);
|
||||
font-size: 12px;
|
||||
font-family: inherit;
|
||||
padding: 1px 0;
|
||||
--input-height: 20px;
|
||||
--input-padding: 1px 0;
|
||||
--input-font-size: 12px;
|
||||
--input-foreground: var(--l1-foreground);
|
||||
}
|
||||
|
||||
.actions {
|
||||
|
||||
@@ -5,7 +5,7 @@ import ROUTES from 'constants/routes';
|
||||
import { getAbsoluteUrl } from 'utils/basePath';
|
||||
import { Button } from '@signozhq/ui/button';
|
||||
import { Input } from '@signozhq/ui/input';
|
||||
import { toast } from '@signozhq/ui/sonner';
|
||||
import { toast } from '@signozhq/ui/toast';
|
||||
import { Dropdown, type DropdownItemType } from '@signozhq/ui/dropdown';
|
||||
|
||||
import {
|
||||
@@ -176,16 +176,21 @@ export default function ConversationItem({
|
||||
|
||||
<div className={styles.body}>
|
||||
{isEditing ? (
|
||||
<Input
|
||||
ref={inputRef}
|
||||
className={styles.input}
|
||||
value={editValue}
|
||||
onChange={(e): void => setEditValue(e.target.value)}
|
||||
onKeyDown={handleKeyDown}
|
||||
onBlur={commitEdit}
|
||||
onClick={(e): void => e.stopPropagation()}
|
||||
maxLength={80}
|
||||
/>
|
||||
<div className={styles.input}>
|
||||
<Input
|
||||
ref={inputRef}
|
||||
// The wrapper's accent bottom border is the edit/focus
|
||||
// treatment, so the Input renders chromeless.
|
||||
variant="unstyled"
|
||||
noFocusRing
|
||||
value={editValue}
|
||||
onChange={(e): void => setEditValue(e.target.value)}
|
||||
onKeyDown={handleKeyDown}
|
||||
onBlur={commitEdit}
|
||||
onClick={(e): void => e.stopPropagation()}
|
||||
maxLength={80}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<span className={styles.title} title={displayTitle}>
|
||||
|
||||
@@ -43,11 +43,9 @@
|
||||
.searchBar {
|
||||
padding: 0px 8px 12px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.search {
|
||||
width: 100%;
|
||||
font-size: 12px;
|
||||
// The search Input fills its parent by default; its text size comes from
|
||||
// the component's CSS token.
|
||||
--input-font-size: 12px;
|
||||
}
|
||||
|
||||
.list {
|
||||
|
||||
@@ -193,7 +193,6 @@ export default function ConversationsList({
|
||||
onChange={(e): void => setSearchQuery(e.target.value)}
|
||||
placeholder="Search conversations…"
|
||||
prefix={<Search size={12} />}
|
||||
className={styles.search}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
height: 80px;
|
||||
}
|
||||
.top-contributors-progress {
|
||||
--progress-background: transparent;
|
||||
--progress-track-background-color: transparent;
|
||||
}
|
||||
|
||||
&__content {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { HTMLAttributes } from 'react';
|
||||
import { Color } from '@signozhq/design-tokens';
|
||||
import { Table, TableColumnsType as ColumnsType } from 'antd';
|
||||
import { Progress } from '@signozhq/ui/progress';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
@@ -49,12 +48,13 @@ function TopContributorsRows({
|
||||
relatedTracesLink={record.relatedTracesLink}
|
||||
relatedLogsLink={record.relatedLogsLink}
|
||||
>
|
||||
<Progress
|
||||
percent={(count / totalCurrentTriggers) * 100}
|
||||
showInfo={false}
|
||||
strokeColor={Color.BG_ROBIN_500}
|
||||
className="top-contributors-progress"
|
||||
/>
|
||||
<div className="top-contributors-progress">
|
||||
<Progress
|
||||
percent={(count / totalCurrentTriggers) * 100}
|
||||
showInfo={false}
|
||||
color="primary"
|
||||
/>
|
||||
</div>
|
||||
</ConditionalAlertPopover>
|
||||
),
|
||||
},
|
||||
|
||||
@@ -387,7 +387,7 @@ describe('Create Alert Channel', () => {
|
||||
it('Should check if API key label, required, info(help_email_to), and textbox are displayed properly', () => {
|
||||
testLabelInputAndHelpValue({
|
||||
labelText: 'field_email_to',
|
||||
testId: 'email-to-textbox',
|
||||
testId: 'email-to-textbox-field',
|
||||
helpText: 'help_email_to',
|
||||
required: true,
|
||||
});
|
||||
@@ -479,7 +479,7 @@ describe('Create Alert Channel', () => {
|
||||
it('Should check if saving with a webhook url outside chat.googleapis.com displays error notification', async () => {
|
||||
const user = userEvent.setup();
|
||||
|
||||
await fillField(user, 'channel-name-textbox', 'gchat-channel');
|
||||
await fillField(user, 'channel-name-textbox-field', 'gchat-channel');
|
||||
await fillField(user, 'webhook-url-textbox', 'https://example.com/webhook');
|
||||
|
||||
await user.click(screen.getByTestId('save-channel-button'));
|
||||
@@ -506,7 +506,7 @@ describe('Create Alert Channel', () => {
|
||||
|
||||
const user = userEvent.setup();
|
||||
|
||||
await fillField(user, 'channel-name-textbox', 'gchat-channel');
|
||||
await fillField(user, 'channel-name-textbox-field', 'gchat-channel');
|
||||
await fillField(user, 'webhook-url-textbox', validWebhookUrl);
|
||||
|
||||
await user.click(screen.getByTestId('save-channel-button'));
|
||||
@@ -538,7 +538,10 @@ describe('Create Alert Channel', () => {
|
||||
user: ReturnType<typeof userEvent.setup>,
|
||||
site: string,
|
||||
): Promise<void> => {
|
||||
await user.type(screen.getByTestId('channel-name-textbox'), 'jira-channel');
|
||||
await user.type(
|
||||
screen.getByTestId('channel-name-textbox-field'),
|
||||
'jira-channel',
|
||||
);
|
||||
await user.type(screen.getByTestId('jira-site-textbox'), site);
|
||||
await user.type(screen.getByTestId('jira-email-textbox'), 'me@acme.com');
|
||||
await user.type(screen.getByTestId('jira-api-token-textbox'), 'tok123');
|
||||
@@ -687,7 +690,7 @@ describe('Create Alert Channel', () => {
|
||||
it('Should block save when the API key is missing', async () => {
|
||||
const user = userEvent.setup();
|
||||
await user.type(
|
||||
screen.getByTestId('channel-name-textbox'),
|
||||
screen.getByTestId('channel-name-textbox-field'),
|
||||
'jsmops-channel',
|
||||
);
|
||||
|
||||
@@ -715,7 +718,7 @@ describe('Create Alert Channel', () => {
|
||||
|
||||
const user = userEvent.setup();
|
||||
await user.type(
|
||||
screen.getByTestId('channel-name-textbox'),
|
||||
screen.getByTestId('channel-name-textbox-field'),
|
||||
'jsmops-channel',
|
||||
);
|
||||
await user.type(screen.getByTestId('jsmops-api-key-textbox'), 'key-abc');
|
||||
@@ -773,7 +776,7 @@ describe('Create Alert Channel', () => {
|
||||
it('Should block save when the URL or token is missing', async () => {
|
||||
const user = userEvent.setup();
|
||||
await user.type(
|
||||
screen.getByTestId('channel-name-textbox'),
|
||||
screen.getByTestId('channel-name-textbox-field'),
|
||||
'incidentio-channel',
|
||||
);
|
||||
|
||||
@@ -790,7 +793,7 @@ describe('Create Alert Channel', () => {
|
||||
it('Should display an error when the URL is not an alert events URL', async () => {
|
||||
const user = userEvent.setup();
|
||||
await user.type(
|
||||
screen.getByTestId('channel-name-textbox'),
|
||||
screen.getByTestId('channel-name-textbox-field'),
|
||||
'incidentio-channel',
|
||||
);
|
||||
await user.type(
|
||||
@@ -823,7 +826,7 @@ describe('Create Alert Channel', () => {
|
||||
|
||||
const user = userEvent.setup();
|
||||
await user.type(
|
||||
screen.getByTestId('channel-name-textbox'),
|
||||
screen.getByTestId('channel-name-textbox-field'),
|
||||
'incidentio-channel',
|
||||
);
|
||||
await user.type(
|
||||
|
||||
@@ -297,7 +297,7 @@ describe('Create Alert Channel (Normal User)', () => {
|
||||
it('Should check if API key label, required, info(help_email_to), and textbox are displayed properly', () => {
|
||||
testLabelInputAndHelpValue({
|
||||
labelText: 'field_email_to',
|
||||
testId: 'email-to-textbox',
|
||||
testId: 'email-to-textbox-field',
|
||||
helpText: 'help_email_to',
|
||||
required: true,
|
||||
});
|
||||
|
||||
@@ -43,7 +43,7 @@ describe('Should check if the edit alert channel is properly displayed', () => {
|
||||
it('Should check if the name label and textbox are displayed properly', () => {
|
||||
testLabelInputAndHelpValue({
|
||||
labelText: 'field_channel_name',
|
||||
testId: 'channel-name-textbox',
|
||||
testId: 'channel-name-textbox-field',
|
||||
value: 'Dummy-Channel',
|
||||
});
|
||||
});
|
||||
|
||||
@@ -158,7 +158,7 @@ function DomainDetails({
|
||||
title={
|
||||
<div className="domain-details-drawer-header">
|
||||
<div className="domain-details-drawer-header-title">
|
||||
<Divider type="vertical" />
|
||||
<Divider orientation="vertical" />
|
||||
|
||||
{domainData?.domainName && (
|
||||
<Typography.Text className="title">
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { useMemo } from 'react';
|
||||
import { useQueries } from 'react-query';
|
||||
import { Color } from '@signozhq/design-tokens';
|
||||
import { Skeleton, Tooltip } from 'antd';
|
||||
import { Progress } from '@signozhq/ui/progress';
|
||||
import { Typography } from '@signozhq/ui/typography';
|
||||
@@ -136,26 +135,21 @@ function DomainMetrics({
|
||||
) : (
|
||||
<Tooltip title={formattedDomainMetricsData.errorRate}>
|
||||
{formattedDomainMetricsData.errorRate !== '-' ? (
|
||||
<Progress
|
||||
percent={Number(
|
||||
Number(formattedDomainMetricsData.errorRate).toFixed(2),
|
||||
)}
|
||||
strokeLinecap="butt"
|
||||
showInfo
|
||||
strokeColor={((): string => {
|
||||
const errorRatePercent = Number(
|
||||
<div className="progress-bar">
|
||||
<Progress
|
||||
percent={Number(
|
||||
Number(formattedDomainMetricsData.errorRate).toFixed(2),
|
||||
);
|
||||
if (errorRatePercent >= 90) {
|
||||
return Color.BG_SAKURA_500;
|
||||
)}
|
||||
showInfo
|
||||
color={
|
||||
Number(formattedDomainMetricsData.errorRate) >= 90
|
||||
? 'highlight-danger'
|
||||
: Number(formattedDomainMetricsData.errorRate) >= 60
|
||||
? 'warning'
|
||||
: 'success'
|
||||
}
|
||||
if (errorRatePercent >= 60) {
|
||||
return Color.BG_AMBER_500;
|
||||
}
|
||||
return Color.BG_FOREST_500;
|
||||
})()}
|
||||
className="progress-bar"
|
||||
/>
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
'-'
|
||||
)}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { useMemo } from 'react';
|
||||
import { UseQueryResult } from 'react-query';
|
||||
import { Color } from '@signozhq/design-tokens';
|
||||
import { Skeleton, Tooltip } from 'antd';
|
||||
import { Progress } from '@signozhq/ui/progress';
|
||||
import { Typography } from '@signozhq/ui/typography';
|
||||
@@ -80,24 +79,19 @@ function EndPointMetrics({
|
||||
) : (
|
||||
<Tooltip title={metricsData?.errorRate}>
|
||||
{metricsData?.errorRate !== '-' ? (
|
||||
<Progress
|
||||
percent={Number(Number(metricsData?.errorRate ?? 0).toFixed(2))}
|
||||
strokeLinecap="butt"
|
||||
showInfo
|
||||
strokeColor={((): string => {
|
||||
const errorRatePercent = Number(
|
||||
Number(metricsData?.errorRate ?? 0).toFixed(2),
|
||||
);
|
||||
if (errorRatePercent >= 90) {
|
||||
return Color.BG_SAKURA_500;
|
||||
<div className="progress-bar">
|
||||
<Progress
|
||||
percent={Number(Number(metricsData?.errorRate ?? 0).toFixed(2))}
|
||||
showInfo
|
||||
color={
|
||||
Number(metricsData?.errorRate ?? 0) >= 90
|
||||
? 'highlight-danger'
|
||||
: Number(metricsData?.errorRate ?? 0) >= 60
|
||||
? 'warning'
|
||||
: 'success'
|
||||
}
|
||||
if (errorRatePercent >= 60) {
|
||||
return Color.BG_AMBER_500;
|
||||
}
|
||||
return Color.BG_FOREST_500;
|
||||
})()}
|
||||
className="progress-bar"
|
||||
/>
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
'-'
|
||||
)}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { ReactNode } from 'react';
|
||||
import { Color } from '@signozhq/design-tokens';
|
||||
import { TableColumnType as ColumnType, Tooltip } from 'antd';
|
||||
import { Progress } from '@signozhq/ui/progress';
|
||||
import { Badge } from '@signozhq/ui/badge';
|
||||
@@ -47,6 +46,18 @@ import {
|
||||
EndPointsResponseRow,
|
||||
} from './types';
|
||||
|
||||
function metricProgressColor(
|
||||
percent: number,
|
||||
): 'success' | 'warning' | 'highlight-danger' {
|
||||
if (percent >= 90) {
|
||||
return 'highlight-danger';
|
||||
}
|
||||
if (percent >= 60) {
|
||||
return 'warning';
|
||||
}
|
||||
return 'success';
|
||||
}
|
||||
|
||||
export const isEmptyFilterValue = (value: unknown): boolean =>
|
||||
value === '' || value === null || value === undefined || value === 'n/a';
|
||||
/**
|
||||
@@ -258,22 +269,13 @@ export const columnsConfig: ColumnType<APIDomainsRowData>[] = [
|
||||
const errorRateValue =
|
||||
errorRate === 'n/a' || errorRate === '-' ? 0 : errorRate;
|
||||
return (
|
||||
<Progress
|
||||
percent={Number((errorRateValue as number).toFixed(2))}
|
||||
strokeLinecap="butt"
|
||||
showInfo
|
||||
strokeColor={((): string => {
|
||||
const errorRatePercent = Number((errorRateValue as number).toFixed(2));
|
||||
if (errorRatePercent >= 90) {
|
||||
return Color.BG_SAKURA_500;
|
||||
}
|
||||
if (errorRatePercent >= 60) {
|
||||
return Color.BG_AMBER_500;
|
||||
}
|
||||
return Color.BG_FOREST_500;
|
||||
})()}
|
||||
className="progress-bar error-rate"
|
||||
/>
|
||||
<div className="progress-bar error-rate">
|
||||
<Progress
|
||||
percent={Number((errorRateValue as number).toFixed(2))}
|
||||
showInfo
|
||||
color={metricProgressColor(Number((errorRateValue as number).toFixed(2)))}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
@@ -1023,26 +1025,23 @@ export const getEndPointsColumnsConfig = (
|
||||
align: 'right',
|
||||
className: `column`,
|
||||
render: (errorRate: number | string): React.ReactNode => (
|
||||
<Progress
|
||||
percent={Number(
|
||||
(
|
||||
(errorRate === 'n/a' || errorRate === '-' ? 0 : errorRate) as number
|
||||
).toFixed(1),
|
||||
)}
|
||||
strokeLinecap="butt"
|
||||
showInfo
|
||||
strokeColor={((): string => {
|
||||
const errorRatePercent = Number((errorRate as number).toFixed(1));
|
||||
if (errorRatePercent >= 90) {
|
||||
return Color.BG_SAKURA_500;
|
||||
}
|
||||
if (errorRatePercent >= 60) {
|
||||
return Color.BG_AMBER_500;
|
||||
}
|
||||
return Color.BG_FOREST_500;
|
||||
})()}
|
||||
className="progress-bar error-rate"
|
||||
/>
|
||||
<div className="progress-bar error-rate">
|
||||
<Progress
|
||||
percent={Number(
|
||||
(
|
||||
(errorRate === 'n/a' || errorRate === '-' ? 0 : errorRate) as number
|
||||
).toFixed(1),
|
||||
)}
|
||||
showInfo
|
||||
color={metricProgressColor(
|
||||
Number(
|
||||
(
|
||||
(errorRate === 'n/a' || errorRate === '-' ? 0 : errorRate) as number
|
||||
).toFixed(1),
|
||||
),
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
@@ -2514,24 +2513,15 @@ export const dependentServicesColumns: ColumnType<DependentServicesData>[] = [
|
||||
align: 'center',
|
||||
render: (errorPercentage: number | string): React.ReactNode =>
|
||||
errorPercentage !== '-' ? (
|
||||
<Progress
|
||||
percent={Number((errorPercentage as number).toFixed(2))}
|
||||
strokeLinecap="butt"
|
||||
showInfo
|
||||
strokeColor={((): string => {
|
||||
const errorPercentagePercent = Number(
|
||||
(errorPercentage as number).toFixed(2),
|
||||
);
|
||||
if (errorPercentagePercent >= 90) {
|
||||
return Color.BG_SAKURA_500;
|
||||
}
|
||||
if (errorPercentagePercent >= 60) {
|
||||
return Color.BG_AMBER_500;
|
||||
}
|
||||
return Color.BG_FOREST_500;
|
||||
})()}
|
||||
className="progress-bar error-rate"
|
||||
/>
|
||||
<div className="progress-bar error-rate">
|
||||
<Progress
|
||||
percent={Number((errorPercentage as number).toFixed(2))}
|
||||
showInfo
|
||||
color={metricProgressColor(
|
||||
Number((errorPercentage as number).toFixed(2)),
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
'-'
|
||||
),
|
||||
@@ -3021,30 +3011,23 @@ export const getAllEndpointsWidgetData = (
|
||||
</span>
|
||||
),
|
||||
F1: (errorRate: any): ReactNode => (
|
||||
<Progress
|
||||
percent={Number(
|
||||
(
|
||||
(errorRate === 'n/a' || errorRate === '-' ? 0 : errorRate) as number
|
||||
).toFixed(2),
|
||||
)}
|
||||
strokeLinecap="butt"
|
||||
showInfo
|
||||
strokeColor={((): string => {
|
||||
const errorRatePercent = Number(
|
||||
<div className="progress-bar error-rate">
|
||||
<Progress
|
||||
percent={Number(
|
||||
(
|
||||
(errorRate === 'n/a' || errorRate === '-' ? 0 : errorRate) as number
|
||||
).toFixed(2),
|
||||
);
|
||||
if (errorRatePercent >= 90) {
|
||||
return Color.BG_SAKURA_500;
|
||||
}
|
||||
if (errorRatePercent >= 60) {
|
||||
return Color.BG_AMBER_500;
|
||||
}
|
||||
return Color.BG_FOREST_500;
|
||||
})()}
|
||||
className="progress-bar error-rate"
|
||||
/>
|
||||
)}
|
||||
showInfo
|
||||
color={metricProgressColor(
|
||||
Number(
|
||||
(
|
||||
(errorRate === 'n/a' || errorRate === '-' ? 0 : errorRate) as number
|
||||
).toFixed(2),
|
||||
),
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
),
|
||||
};
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ import { useMutation, useQueries } from 'react-query';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import * as Sentry from '@sentry/react';
|
||||
import { Toaster } from '@signozhq/ui/sonner';
|
||||
import { Toaster } from '@signozhq/ui/toast';
|
||||
import { TooltipProvider } from '@signozhq/ui/tooltip';
|
||||
import { Flex } from 'antd';
|
||||
import getLocalStorageApi from 'api/browser/localstorage/get';
|
||||
|
||||
@@ -4,7 +4,13 @@ import { Typography } from '@signozhq/ui/typography';
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useMutation } from 'react-query';
|
||||
import { CircleCheck, Landmark, MonitorDown } from '@signozhq/icons';
|
||||
import {
|
||||
CircleCheck,
|
||||
Landmark,
|
||||
MonitorDown,
|
||||
SolidAlertCircle,
|
||||
SolidInfoCircle,
|
||||
} from '@signozhq/icons';
|
||||
import {
|
||||
Card,
|
||||
Col,
|
||||
@@ -480,12 +486,11 @@ export default function BillingContainer(): JSX.Element {
|
||||
|
||||
{!isLoading && !isFetchingBillingData && !showGracePeriodMessage
|
||||
? headerText && (
|
||||
<Callout
|
||||
title={headerText}
|
||||
type="info"
|
||||
showIcon
|
||||
style={{ marginTop: 12 }}
|
||||
/>
|
||||
<div style={{ marginTop: 12 }}>
|
||||
<Callout color="info" size="sm" icon={<SolidInfoCircle />}>
|
||||
{headerText}
|
||||
</Callout>
|
||||
</div>
|
||||
)
|
||||
: null}
|
||||
|
||||
@@ -498,21 +503,22 @@ export default function BillingContainer(): JSX.Element {
|
||||
billingData &&
|
||||
trialInfo?.gracePeriodEnd &&
|
||||
showGracePeriodMessage ? (
|
||||
<Callout
|
||||
title={`Your data is safe with us until ${getFormattedDate(
|
||||
trialInfo?.gracePeriodEnd || Date.now(),
|
||||
)}. Please upgrade plan now to retain your data.`}
|
||||
type="info"
|
||||
showIcon
|
||||
style={{ marginTop: 12 }}
|
||||
/>
|
||||
<div style={{ marginTop: 12 }}>
|
||||
<Callout color="info" size="sm" icon={<SolidInfoCircle />}>
|
||||
{`Your data is safe with us until ${getFormattedDate(
|
||||
trialInfo?.gracePeriodEnd || Date.now(),
|
||||
)}. Please upgrade plan now to retain your data.`}
|
||||
</Callout>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{isSubscriptionPastDue &&
|
||||
(!isLoading && !isFetchingBillingData ? (
|
||||
<Callout type="error" showIcon style={{ marginTop: 12 }}>
|
||||
{subscriptionPastDueMessage()}
|
||||
</Callout>
|
||||
<div style={{ marginTop: 12 }}>
|
||||
<Callout color="danger" size="sm" icon={<SolidAlertCircle />}>
|
||||
{subscriptionPastDueMessage()}
|
||||
</Callout>
|
||||
</div>
|
||||
) : (
|
||||
<Skeleton.Input active style={{ height: 20, marginTop: 20 }} />
|
||||
))}
|
||||
@@ -553,9 +559,11 @@ export default function BillingContainer(): JSX.Element {
|
||||
)}
|
||||
</div>
|
||||
{!isLoading && !isFetchingBillingData && (
|
||||
<Callout type="info" size="small" className={styles.billingUpdateNote}>
|
||||
Billing metrics are updated once every 24 hours.
|
||||
</Callout>
|
||||
<div className={styles.billingUpdateNote}>
|
||||
<Callout color="info" size="sm" icon={<SolidInfoCircle />}>
|
||||
Billing metrics are updated once every 24 hours.
|
||||
</Callout>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className={styles.billingDetails}>
|
||||
|
||||
@@ -92,7 +92,7 @@ describe('CancelSubscriptionBanner', () => {
|
||||
screen.getByText(/Cancelling your subscription would stop your data/i),
|
||||
).toBeInTheDocument();
|
||||
expect(screen.getByText(/Type/i)).toBeInTheDocument();
|
||||
expect(screen.getByTestId('cancel-confirm-input')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('cancel-confirm-input-field')).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: /go back/i })).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByTestId('cancel-subscription-confirm-btn'),
|
||||
@@ -114,7 +114,7 @@ describe('CancelSubscriptionBanner', () => {
|
||||
const confirmButton = screen.getByTestId('cancel-subscription-confirm-btn');
|
||||
expect(confirmButton).toHaveAttribute('aria-disabled', 'true');
|
||||
|
||||
const input = screen.getByTestId('cancel-confirm-input');
|
||||
const input = screen.getByTestId('cancel-confirm-input-field');
|
||||
await user.type(input, 'canc');
|
||||
expect(confirmButton).toHaveAttribute('aria-disabled', 'true');
|
||||
|
||||
@@ -134,7 +134,7 @@ describe('CancelSubscriptionBanner', () => {
|
||||
});
|
||||
await user.click(screen.getByTestId('cancel-subscription-btn'));
|
||||
|
||||
const input = screen.getByTestId('cancel-confirm-input');
|
||||
const input = screen.getByTestId('cancel-confirm-input-field');
|
||||
await user.type(input, 'cancel');
|
||||
|
||||
await user.click(screen.getByRole('button', { name: /go back/i }));
|
||||
@@ -149,7 +149,7 @@ describe('CancelSubscriptionBanner', () => {
|
||||
);
|
||||
});
|
||||
await user.click(screen.getByTestId('cancel-subscription-btn'));
|
||||
expect(screen.getByTestId('cancel-confirm-input')).toHaveValue('');
|
||||
expect(screen.getByTestId('cancel-confirm-input-field')).toHaveValue('');
|
||||
});
|
||||
|
||||
it('fires mailto via DOM-attached anchor and shows fallback view after confirming', async () => {
|
||||
@@ -165,7 +165,7 @@ describe('CancelSubscriptionBanner', () => {
|
||||
);
|
||||
});
|
||||
await user.click(screen.getByTestId('cancel-subscription-btn'));
|
||||
await user.type(screen.getByTestId('cancel-confirm-input'), 'cancel');
|
||||
await user.type(screen.getByTestId('cancel-confirm-input-field'), 'cancel');
|
||||
await user.click(screen.getByTestId('cancel-subscription-confirm-btn'));
|
||||
|
||||
const appendedAnchor = appendSpy.mock.calls
|
||||
@@ -201,7 +201,7 @@ describe('CancelSubscriptionBanner', () => {
|
||||
);
|
||||
});
|
||||
await user.click(screen.getByTestId('cancel-subscription-btn'));
|
||||
await user.type(screen.getByTestId('cancel-confirm-input'), 'cancel');
|
||||
await user.type(screen.getByTestId('cancel-confirm-input-field'), 'cancel');
|
||||
await user.click(screen.getByTestId('cancel-subscription-confirm-btn'));
|
||||
|
||||
await user.click(screen.getByTestId('copy-email-template-btn'));
|
||||
@@ -227,7 +227,7 @@ describe('CancelSubscriptionBanner', () => {
|
||||
);
|
||||
});
|
||||
await user.click(screen.getByTestId('cancel-subscription-btn'));
|
||||
await user.type(screen.getByTestId('cancel-confirm-input'), 'cancel');
|
||||
await user.type(screen.getByTestId('cancel-confirm-input-field'), 'cancel');
|
||||
await user.click(screen.getByTestId('cancel-subscription-confirm-btn'));
|
||||
|
||||
await user.click(screen.getByTestId('retry-mailto-btn'));
|
||||
@@ -251,7 +251,7 @@ describe('CancelSubscriptionBanner', () => {
|
||||
);
|
||||
});
|
||||
await user.click(screen.getByTestId('cancel-subscription-btn'));
|
||||
await user.type(screen.getByTestId('cancel-confirm-input'), 'cancel');
|
||||
await user.type(screen.getByTestId('cancel-confirm-input-field'), 'cancel');
|
||||
await user.click(screen.getByTestId('cancel-subscription-confirm-btn'));
|
||||
|
||||
await user.click(screen.getByRole('button', { name: /close/i }));
|
||||
|
||||
@@ -216,7 +216,7 @@ function CancelSubscriptionBanner(): JSX.Element {
|
||||
placeholder="Enter the word cancel..."
|
||||
value={confirmText}
|
||||
onChange={(e): void => setConfirmText(e.target.value)}
|
||||
data-testid="cancel-confirm-input"
|
||||
testId="cancel-confirm-input"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -101,8 +101,8 @@ function ThresholdItem({
|
||||
onChange={(e): void =>
|
||||
updateThreshold(threshold.id, 'label', e.target.value)
|
||||
}
|
||||
style={{ width: 200 }}
|
||||
data-testid="threshold-name-input"
|
||||
width={200}
|
||||
testId="threshold-name-input"
|
||||
/>
|
||||
<Typography.Text className="sentence-text">on value</Typography.Text>
|
||||
<Typography.Text className="sentence-text highlighted-text">
|
||||
@@ -114,9 +114,9 @@ function ThresholdItem({
|
||||
onChange={(e): void =>
|
||||
updateThreshold(threshold.id, 'thresholdValue', e.target.value)
|
||||
}
|
||||
style={{ width: 100 }}
|
||||
width={100}
|
||||
type="number"
|
||||
data-testid="threshold-value-input"
|
||||
testId="threshold-value-input"
|
||||
/>
|
||||
{yAxisUnitSelect}
|
||||
{!notificationSettings.routingPolicies && (
|
||||
@@ -165,9 +165,9 @@ function ThresholdItem({
|
||||
onChange={(e): void =>
|
||||
updateThreshold(threshold.id, 'recoveryThresholdValue', e.target.value)
|
||||
}
|
||||
style={{ width: 100 }}
|
||||
width={100}
|
||||
type="number"
|
||||
data-testid="recovery-threshold-value-input"
|
||||
testId="recovery-threshold-value-input"
|
||||
/>
|
||||
<Tooltip title="Remove recovery threshold">
|
||||
<Button
|
||||
|
||||
@@ -319,15 +319,12 @@ describe('ThresholdItem', () => {
|
||||
it('renders with correct input widths', () => {
|
||||
renderThresholdItem();
|
||||
|
||||
const labelInput = screen.getByPlaceholderText(
|
||||
TEST_CONSTANTS.ENTER_THRESHOLD_NAME,
|
||||
);
|
||||
const valueInput = screen.getByPlaceholderText(
|
||||
TEST_CONSTANTS.ENTER_THRESHOLD_VALUE,
|
||||
);
|
||||
// The width prop lands on the input frame as a CSS custom property
|
||||
const labelInputFrame = screen.getByTestId('threshold-name-input');
|
||||
const valueInputFrame = screen.getByTestId('threshold-value-input');
|
||||
|
||||
expect(labelInput).toHaveStyle('width: 200px');
|
||||
expect(valueInput).toHaveStyle('width: 100px');
|
||||
expect(labelInputFrame).toHaveStyle('--input-internal-width: 200px');
|
||||
expect(valueInputFrame).toHaveStyle('--input-internal-width: 100px');
|
||||
});
|
||||
|
||||
it('renders channels selector with correct width', () => {
|
||||
|
||||
@@ -73,20 +73,21 @@ function CreateAlertHeader(): JSX.Element {
|
||||
</div>
|
||||
)}
|
||||
<div className="alert-header__content">
|
||||
<Input
|
||||
type="text"
|
||||
value={alertState.name}
|
||||
onChange={(e): void => {
|
||||
const newName = e.target.value;
|
||||
setAlertState({ type: 'SET_ALERT_NAME', payload: newName });
|
||||
if (isEditMode && alertRuleContext?.setAlertRuleName) {
|
||||
alertRuleContext.setAlertRuleName(newName);
|
||||
}
|
||||
}}
|
||||
className="alert-header__input title"
|
||||
placeholder="Enter alert rule name"
|
||||
data-testid="alert-name-input"
|
||||
/>
|
||||
<div className="alert-header__input title">
|
||||
<Input
|
||||
type="text"
|
||||
value={alertState.name}
|
||||
onChange={(e): void => {
|
||||
const newName = e.target.value;
|
||||
setAlertState({ type: 'SET_ALERT_NAME', payload: newName });
|
||||
if (isEditMode && alertRuleContext?.setAlertRuleName) {
|
||||
alertRuleContext.setAlertRuleName(newName);
|
||||
}
|
||||
}}
|
||||
placeholder="Enter alert rule name"
|
||||
testId="alert-name-input"
|
||||
/>
|
||||
</div>
|
||||
<LabelsInput
|
||||
labels={alertState.labels}
|
||||
onLabelsChange={(labels: Labels): void =>
|
||||
|
||||
@@ -38,8 +38,8 @@
|
||||
}
|
||||
|
||||
&__input.title {
|
||||
background-color: transparent;
|
||||
color: var(--l1-foreground);
|
||||
--input-background: transparent;
|
||||
--input-foreground: var(--l1-foreground);
|
||||
width: 100%;
|
||||
min-width: 300px;
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ function AdvancedOptions(): JSX.Element {
|
||||
<Input
|
||||
placeholder="Enter tolerance limit..."
|
||||
type="number"
|
||||
style={{ width: 100 }}
|
||||
width={100}
|
||||
onChange={(e): void =>
|
||||
setAdvancedOptions({
|
||||
type: 'SET_SEND_NOTIFICATION_IF_DATA_IS_MISSING',
|
||||
@@ -56,7 +56,7 @@ function AdvancedOptions(): JSX.Element {
|
||||
<div className="advanced-option-item-input-group">
|
||||
<Input
|
||||
placeholder="Enter minimum datapoints..."
|
||||
style={{ width: 100 }}
|
||||
width={100}
|
||||
type="number"
|
||||
onChange={(e): void =>
|
||||
setAdvancedOptions({
|
||||
|
||||
@@ -217,7 +217,7 @@ function EvaluationWindowDetails({
|
||||
value={evaluationWindow.startingAt.number}
|
||||
onChange={(e): void => handleNumberChange(e.target.value)}
|
||||
placeholder="Enter value"
|
||||
data-testid="evaluation-window-details-custom-rolling-window-duration-input"
|
||||
testId="evaluation-window-details-custom-rolling-window-duration-input"
|
||||
/>
|
||||
</div>
|
||||
<div className="select-group time-select-group">
|
||||
|
||||
@@ -3,42 +3,20 @@
|
||||
align-items: center;
|
||||
gap: 0;
|
||||
|
||||
.time-input-field {
|
||||
width: 40px;
|
||||
height: 32px;
|
||||
background-color: var(--l2-background);
|
||||
border: 1px solid var(--l1-border);
|
||||
color: var(--l1-foreground);
|
||||
font-family: 'Space Mono', monospace;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
// The fields are @signozhq/ui Inputs, restyled through the input tokens.
|
||||
--input-background: var(--l2-background);
|
||||
--input-border: var(--l1-border);
|
||||
--input-border-hover: var(--l1-border);
|
||||
--input-foreground: var(--l1-foreground);
|
||||
--input-placeholder: var(--l2-foreground);
|
||||
--input-font-family: 'Space Mono', monospace;
|
||||
--input-font-size: 13px;
|
||||
--input-font-weight: 600;
|
||||
--input-border-radius: 4px;
|
||||
--input-padding: 0 4px;
|
||||
|
||||
input {
|
||||
text-align: center;
|
||||
border-radius: 4px;
|
||||
|
||||
&::placeholder {
|
||||
color: var(--l2-foreground);
|
||||
font-family: 'Space Mono', monospace;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-color: var(--l1-border);
|
||||
}
|
||||
|
||||
&:focus {
|
||||
border-color: var(--l1-border);
|
||||
box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.1);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
background-color: var(--l2-background);
|
||||
color: var(--l2-foreground);
|
||||
cursor: not-allowed;
|
||||
|
||||
&:hover {
|
||||
border-color: var(--l1-border);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.time-input-separator {
|
||||
|
||||
@@ -131,13 +131,13 @@ function TimeInput({
|
||||
if (e.key === 'ArrowRight' || e.key === 'Tab') {
|
||||
e.preventDefault();
|
||||
const nextField = document.querySelector(
|
||||
`[data-field="${getNextField(currentField)}"]`,
|
||||
`[data-field="${getNextField(currentField)}"] input`,
|
||||
) as HTMLInputElement;
|
||||
nextField?.focus();
|
||||
} else if (e.key === 'ArrowLeft') {
|
||||
e.preventDefault();
|
||||
const prevField = document.querySelector(
|
||||
`[data-field="${getPrevField(currentField)}"]`,
|
||||
`[data-field="${getPrevField(currentField)}"] input`,
|
||||
) as HTMLInputElement;
|
||||
prevField?.focus();
|
||||
}
|
||||
@@ -152,10 +152,11 @@ function TimeInput({
|
||||
onBlur={handleHoursBlur}
|
||||
onKeyDown={(e): void => handleKeyDown(e, 'hours')}
|
||||
disabled={disabled}
|
||||
disabledTooltip={undefined}
|
||||
maxLength={2}
|
||||
className="time-input-field"
|
||||
width={40}
|
||||
placeholder="00"
|
||||
data-testid="time-input-hours"
|
||||
testId="time-input-hours"
|
||||
/>
|
||||
<span className="time-input-separator">:</span>
|
||||
<Input
|
||||
@@ -165,10 +166,11 @@ function TimeInput({
|
||||
onBlur={handleMinutesBlur}
|
||||
onKeyDown={(e): void => handleKeyDown(e, 'minutes')}
|
||||
disabled={disabled}
|
||||
disabledTooltip={undefined}
|
||||
maxLength={2}
|
||||
className="time-input-field"
|
||||
width={40}
|
||||
placeholder="00"
|
||||
data-testid="time-input-minutes"
|
||||
testId="time-input-minutes"
|
||||
/>
|
||||
<span className="time-input-separator">:</span>
|
||||
<Input
|
||||
@@ -178,10 +180,11 @@ function TimeInput({
|
||||
onBlur={handleSecondsBlur}
|
||||
onKeyDown={(e): void => handleKeyDown(e, 'seconds')}
|
||||
disabled={disabled}
|
||||
disabledTooltip={undefined}
|
||||
maxLength={2}
|
||||
className="time-input-field"
|
||||
width={40}
|
||||
placeholder="00"
|
||||
data-testid="time-input-seconds"
|
||||
testId="time-input-seconds"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { Button } from '@signozhq/ui/button';
|
||||
import { toast } from '@signozhq/ui/sonner';
|
||||
import { toast } from '@signozhq/ui/toast';
|
||||
import { convertToApiError } from 'api/ErrorResponseHandlerForGeneratedAPIs';
|
||||
import type { RenderErrorResponseDTO } from 'api/generated/services/sigNoz.schemas';
|
||||
import { AxiosError } from 'axios';
|
||||
@@ -98,8 +98,9 @@ function Footer(): JSX.Element {
|
||||
{
|
||||
onSuccess: (response) => {
|
||||
if (response.data?.alertCount === 0) {
|
||||
toast.error(
|
||||
toast.danger(
|
||||
'No alerts found during the evaluation. This happens when rule condition is unsatisfied. You may adjust the rule threshold and retry.',
|
||||
{ action: { label: 'Dismiss' } },
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ function NotificationSettings(): JSX.Element {
|
||||
value={notificationSettings.reNotification.value}
|
||||
placeholder="Enter time interval..."
|
||||
disabled={!notificationSettings.reNotification.enabled}
|
||||
disabledTooltip={undefined}
|
||||
type="number"
|
||||
onChange={(e): void => {
|
||||
setNotificationSettings({
|
||||
@@ -37,7 +38,7 @@ function NotificationSettings(): JSX.Element {
|
||||
},
|
||||
});
|
||||
}}
|
||||
data-testid="repeat-notifications-time-input"
|
||||
testId="repeat-notifications-time-input"
|
||||
/>
|
||||
<Select
|
||||
value={notificationSettings.reNotification.unit || null}
|
||||
|
||||
@@ -6,13 +6,13 @@ import {
|
||||
ExternalLink,
|
||||
FilePenLine,
|
||||
Link2,
|
||||
SolidAlertCircle,
|
||||
SolidInfoCircle,
|
||||
X,
|
||||
} from '@signozhq/icons';
|
||||
import { Button } from '@signozhq/ui/button';
|
||||
import { Callout } from '@signozhq/ui/callout';
|
||||
import { Dropdown, type DropdownItemType } from '@signozhq/ui/dropdown';
|
||||
import { toast } from '@signozhq/ui/sonner';
|
||||
import { toast } from '@signozhq/ui/toast';
|
||||
import { Skeleton } from 'antd';
|
||||
import {
|
||||
RenderErrorResponseDTO,
|
||||
@@ -31,7 +31,7 @@ function DomainUpdateToast({
|
||||
toastId,
|
||||
url,
|
||||
}: {
|
||||
toastId: string | number;
|
||||
toastId: string;
|
||||
url: string;
|
||||
}): JSX.Element {
|
||||
const displayUrl = url?.split('://')[1] ?? url;
|
||||
@@ -143,10 +143,12 @@ export default function CustomDomainSettings(): JSX.Element {
|
||||
setIsEditModalOpen(false);
|
||||
setCustomDomainSubdomain(subdomain);
|
||||
const newUrl = `https://${subdomain}.${dnsSuffix}`;
|
||||
toast.custom(
|
||||
(toastId) => <DomainUpdateToast toastId={toastId} url={newUrl} />,
|
||||
{ duration: 5000, position: 'bottom-right' }, // this 5 sec is as per design
|
||||
);
|
||||
const toastId = `domain-update:${newUrl}`;
|
||||
toast.info(<DomainUpdateToast toastId={toastId} url={newUrl} />, {
|
||||
id: toastId,
|
||||
timeout: 5000, // this 5 sec is as per design
|
||||
position: 'bottom-right',
|
||||
});
|
||||
},
|
||||
onError: (error: AxiosError<RenderErrorResponseDTO>) => {
|
||||
setUpdateDomainError(error as AxiosError<RenderErrorResponseDTO>);
|
||||
@@ -270,14 +272,11 @@ export default function CustomDomainSettings(): JSX.Element {
|
||||
</div>
|
||||
|
||||
{isPollingEnabled && (
|
||||
<Callout
|
||||
type="info"
|
||||
showIcon
|
||||
className="custom-domain-callout"
|
||||
size="small"
|
||||
icon={<SolidAlertCircle size={13} color="primary" />}
|
||||
title={`Updating your URL to ⎯ ${customDomainSubdomain}.${dnsSuffix}. This may take a few mins.`}
|
||||
/>
|
||||
<div className="custom-domain-callout">
|
||||
<Callout color="info" size="sm" icon={<SolidInfoCircle />}>
|
||||
{`Updating your URL to ⎯ ${customDomainSubdomain}.${dnsSuffix}. This may take a few mins.`}
|
||||
</Callout>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<CustomDomainEditModal
|
||||
|
||||
@@ -12,11 +12,11 @@ jest.mock('components/LaunchChatSupport/LaunchChatSupport', () => ({
|
||||
),
|
||||
}));
|
||||
|
||||
const mockToastCustom = jest.fn();
|
||||
jest.mock('@signozhq/ui/sonner', () => ({
|
||||
...jest.requireActual('@signozhq/ui/sonner'),
|
||||
const mockToastInfo = jest.fn();
|
||||
jest.mock('@signozhq/ui/toast', () => ({
|
||||
...jest.requireActual('@signozhq/ui/toast'),
|
||||
toast: {
|
||||
custom: (...args: unknown[]): unknown => mockToastCustom(...args),
|
||||
info: (...args: unknown[]): unknown => mockToastInfo(...args),
|
||||
dismiss: jest.fn(),
|
||||
},
|
||||
}));
|
||||
@@ -55,7 +55,7 @@ describe('CustomDomainSettings', () => {
|
||||
|
||||
afterEach(() => {
|
||||
server.resetHandlers();
|
||||
mockToastCustom.mockClear();
|
||||
mockToastInfo.mockClear();
|
||||
});
|
||||
|
||||
it('renders active host URL in the trigger button', async () => {
|
||||
@@ -168,7 +168,7 @@ describe('CustomDomainSettings', () => {
|
||||
expect(hrefs).toContain('https://custom-host.test.cloud');
|
||||
});
|
||||
|
||||
it('calls toast.custom with new URL after successful domain update', async () => {
|
||||
it('calls toast.info with new URL after successful domain update', async () => {
|
||||
server.use(
|
||||
rest.put(ZEUS_HOSTS_ENDPOINT, (_, res, ctx) =>
|
||||
res(ctx.status(200), ctx.json({})),
|
||||
@@ -184,17 +184,22 @@ describe('CustomDomainSettings', () => {
|
||||
fireEvent.change(input, { target: { value: 'myteam' } });
|
||||
fireEvent.click(screen.getByRole('button', { name: /apply changes/i }));
|
||||
|
||||
// Verify toast.custom was called
|
||||
// Verify toast.info was called
|
||||
await waitFor(() => {
|
||||
expect(mockToastCustom).toHaveBeenCalledTimes(1);
|
||||
expect(mockToastInfo).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
// Render the toast element to verify its content
|
||||
const toastRenderer = mockToastCustom.mock.calls[0][0] as (
|
||||
id: string,
|
||||
) => JSX.Element;
|
||||
const { container } = render(toastRenderer('test-id'));
|
||||
const toastElement = mockToastInfo.mock.calls[0][0] as JSX.Element;
|
||||
const { container } = render(toastElement);
|
||||
expect(container).toHaveTextContent(/myteam\.test\.cloud/i);
|
||||
expect(mockToastInfo.mock.calls[0][1]).toStrictEqual(
|
||||
expect.objectContaining({
|
||||
id: 'domain-update:https://myteam.test.cloud',
|
||||
timeout: 5000,
|
||||
position: 'bottom-right',
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
describe('Workspace Name rendering', () => {
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
}
|
||||
|
||||
&__divider {
|
||||
--divider-vertical-margin: 0;
|
||||
--divider-vertical-margin-inline: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user