mirror of
https://github.com/SigNoz/signoz.git
synced 2026-11-10 02:10:42 +00:00
Compare commits
71 Commits
refactor/a
...
feat/input
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5fa5b7b5a3 | ||
|
|
26b3c9a55a | ||
|
|
3ba120c6b7 | ||
|
|
a42a285854 | ||
|
|
409ac92858 | ||
|
|
3c19f2ebc3 | ||
|
|
411fd4814d | ||
|
|
a40e07f899 | ||
|
|
52399fa34c | ||
|
|
b367442e1b | ||
|
|
259a11bafa | ||
|
|
7e49616554 | ||
|
|
d00c563e97 | ||
|
|
013ea514b0 | ||
|
|
4c79b5e852 | ||
|
|
00fe373bb1 | ||
|
|
9d94606d69 | ||
|
|
1260bb7aa3 | ||
|
|
e6b8450df7 | ||
|
|
bff80d4ca4 | ||
|
|
32cb8091e6 | ||
|
|
34a11e3f1d | ||
|
|
b24ed1bcce | ||
|
|
016befce89 | ||
|
|
f0d064dae5 | ||
|
|
2b0795ca18 | ||
|
|
3b31064884 | ||
|
|
1fbfe4b6f4 | ||
|
|
271664440f | ||
|
|
aab8becf26 | ||
|
|
a263394170 | ||
|
|
9966f020ee | ||
|
|
91a4cb61e7 | ||
|
|
3b214df25b | ||
|
|
42f16aab76 | ||
|
|
4551144d8f | ||
|
|
2c2f8e6dee | ||
|
|
95826abb34 | ||
|
|
e48d30ae05 | ||
|
|
385a0e3c48 | ||
|
|
c1a3dfcbb9 | ||
|
|
6cfc70675d | ||
|
|
3739b74b94 | ||
|
|
bb353968c3 | ||
|
|
82402a25da | ||
|
|
6a2714dccb | ||
|
|
ed7abc5ee1 | ||
|
|
ee912dea34 | ||
|
|
2a5e8c1b4d | ||
|
|
702ed3e194 | ||
|
|
3badeac4f3 | ||
|
|
fee26ebbbe | ||
|
|
0c41ae06e0 | ||
|
|
465212af07 | ||
|
|
7d5db88ded | ||
|
|
90bbf9bce6 | ||
|
|
ce8f741303 | ||
|
|
00e7cb475f | ||
|
|
b32e6689ef | ||
|
|
5c067d75fc | ||
|
|
4b1b3092d5 | ||
|
|
d6017f88fd | ||
|
|
9d28014664 | ||
|
|
1997901102 | ||
|
|
84268d00c8 | ||
|
|
628e989bc0 | ||
|
|
74d272a945 | ||
|
|
f8421e81e9 | ||
|
|
e938a10ad5 | ||
|
|
93cc1957d4 | ||
|
|
313ec2d657 |
9
.github/CODEOWNERS
vendored
9
.github/CODEOWNERS
vendored
@@ -209,6 +209,10 @@ go.mod @therealpandey
|
||||
/frontend/src/types/api/alerts/ @SigNoz/pulse-frontend
|
||||
/frontend/src/providers/Alert.tsx @SigNoz/pulse-frontend
|
||||
/frontend/src/constants/alerts.ts @SigNoz/pulse-frontend
|
||||
/frontend/src/container/PlannedDowntime/ @SigNoz/pulse-frontend
|
||||
/frontend/src/hooks/alerts/ @SigNoz/pulse-frontend
|
||||
/frontend/src/container/Home/AlertRules/ @SigNoz/pulse-frontend
|
||||
/frontend/src/container/Home/__tests__/AlertRules.test.tsx @SigNoz/pulse-frontend
|
||||
|
||||
## Notification Channels
|
||||
/frontend/src/pages/ChannelsEdit/ @SigNoz/pulse-frontend
|
||||
@@ -217,6 +221,11 @@ go.mod @therealpandey
|
||||
/frontend/src/container/FormAlertChannels/ @SigNoz/pulse-frontend
|
||||
/frontend/src/hooks/notificationChannels/ @SigNoz/pulse-frontend
|
||||
|
||||
## List filter + views (dashboards and alerts lists)
|
||||
/frontend/src/components/ListFilter/ @SigNoz/pulse-frontend
|
||||
/frontend/src/components/ListViews/ @SigNoz/pulse-frontend
|
||||
/frontend/src/hooks/listViews/ @SigNoz/pulse-frontend
|
||||
|
||||
## OpenAPI Schema - Generated
|
||||
/frontend/src/api/generated/services/ @therealpandey @vikrantgupta25 @srikanthccv
|
||||
/docs/api/openapi.yml @therealpandey @vikrantgupta25 @srikanthccv
|
||||
|
||||
2
.github/workflows/build-community.yaml
vendored
2
.github/workflows/build-community.yaml
vendored
@@ -63,7 +63,7 @@ jobs:
|
||||
secrets: inherit
|
||||
with:
|
||||
PRIMUS_REF: main
|
||||
GO_VERSION: 1.24
|
||||
GO_VERSION: 1.25
|
||||
GO_NAME: signoz-community
|
||||
GO_INPUT_ARTIFACT_CACHE_KEY: community-jsbuild-${{ github.sha }}
|
||||
GO_INPUT_ARTIFACT_PATH: frontend/build
|
||||
|
||||
2
.github/workflows/build-enterprise.yaml
vendored
2
.github/workflows/build-enterprise.yaml
vendored
@@ -89,7 +89,7 @@ jobs:
|
||||
secrets: inherit
|
||||
with:
|
||||
PRIMUS_REF: main
|
||||
GO_VERSION: 1.24
|
||||
GO_VERSION: 1.25
|
||||
GO_INPUT_ARTIFACT_CACHE_KEY: enterprise-jsbuild-${{ github.sha }}
|
||||
GO_INPUT_ARTIFACT_PATH: frontend/build
|
||||
GO_BUILD_CONTEXT: ./cmd/enterprise
|
||||
|
||||
2
.github/workflows/build-staging.yaml
vendored
2
.github/workflows/build-staging.yaml
vendored
@@ -95,7 +95,7 @@ jobs:
|
||||
secrets: inherit
|
||||
with:
|
||||
PRIMUS_REF: main
|
||||
GO_VERSION: 1.24
|
||||
GO_VERSION: 1.25
|
||||
GO_INPUT_ARTIFACT_CACHE_KEY: staging-jsbuild-${{ github.sha }}
|
||||
GO_INPUT_ARTIFACT_PATH: frontend/build
|
||||
GO_BUILD_CONTEXT: ./cmd/enterprise
|
||||
|
||||
20
.github/workflows/goci.yaml
vendored
20
.github/workflows/goci.yaml
vendored
@@ -22,7 +22,7 @@ jobs:
|
||||
with:
|
||||
PRIMUS_REF: main
|
||||
GO_TEST_CONTEXT: ./...
|
||||
GO_VERSION: 1.24
|
||||
GO_VERSION: 1.25
|
||||
fmt:
|
||||
if: |
|
||||
github.event_name == 'merge_group' ||
|
||||
@@ -32,7 +32,7 @@ jobs:
|
||||
secrets: inherit
|
||||
with:
|
||||
PRIMUS_REF: main
|
||||
GO_VERSION: 1.24
|
||||
GO_VERSION: 1.25
|
||||
lint:
|
||||
if: |
|
||||
github.event_name == 'merge_group' ||
|
||||
@@ -42,7 +42,7 @@ jobs:
|
||||
secrets: inherit
|
||||
with:
|
||||
PRIMUS_REF: main
|
||||
GO_VERSION: 1.24
|
||||
GO_VERSION: 1.25
|
||||
deps:
|
||||
if: |
|
||||
github.event_name == 'merge_group' ||
|
||||
@@ -52,7 +52,7 @@ jobs:
|
||||
secrets: inherit
|
||||
with:
|
||||
PRIMUS_REF: main
|
||||
GO_VERSION: 1.24
|
||||
GO_VERSION: 1.25
|
||||
semconv-generated:
|
||||
if: |
|
||||
github.event_name == 'merge_group' ||
|
||||
@@ -65,7 +65,7 @@ jobs:
|
||||
- name: go-install
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.24"
|
||||
go-version: "1.25"
|
||||
- name: check-semconv-generated-files
|
||||
run: make semconv-check
|
||||
build:
|
||||
@@ -80,7 +80,7 @@ jobs:
|
||||
- name: go-install
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.24"
|
||||
go-version: "1.25"
|
||||
- name: qemu-install
|
||||
uses: docker/setup-qemu-action@v3
|
||||
- name: aarch64-install
|
||||
@@ -116,7 +116,7 @@ jobs:
|
||||
- name: go-install
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.24"
|
||||
go-version: "1.25"
|
||||
- name: generate-openapi
|
||||
run: |
|
||||
go run cmd/enterprise/*.go generate openapi
|
||||
@@ -133,7 +133,7 @@ jobs:
|
||||
- name: go-install
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.24"
|
||||
go-version: "1.25"
|
||||
- name: generate-authz
|
||||
run: |
|
||||
go run cmd/enterprise/*.go generate authz
|
||||
@@ -150,7 +150,7 @@ jobs:
|
||||
- name: go-install
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.24"
|
||||
go-version: "1.25"
|
||||
- name: generate-web-settings
|
||||
run: |
|
||||
go run cmd/enterprise/*.go generate config web-settings
|
||||
@@ -167,7 +167,7 @@ jobs:
|
||||
- name: go-install
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.24"
|
||||
go-version: "1.25"
|
||||
- name: generate-transaction-groups
|
||||
run: |
|
||||
go run cmd/enterprise/*.go generate config transaction-groups
|
||||
|
||||
4
.github/workflows/gor-signoz-community.yaml
vendored
4
.github/workflows/gor-signoz-community.yaml
vendored
@@ -66,7 +66,7 @@ jobs:
|
||||
- name: setup-go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.24"
|
||||
go-version: "1.25"
|
||||
- name: cross-compilation-tools
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: |
|
||||
@@ -130,7 +130,7 @@ jobs:
|
||||
- name: setup-go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.24"
|
||||
go-version: "1.25"
|
||||
|
||||
# copy the caches from build
|
||||
- name: get-sha
|
||||
|
||||
4
.github/workflows/gor-signoz.yaml
vendored
4
.github/workflows/gor-signoz.yaml
vendored
@@ -75,7 +75,7 @@ jobs:
|
||||
- name: setup-go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.24"
|
||||
go-version: "1.25"
|
||||
- name: cross-compilation-tools
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: |
|
||||
@@ -138,7 +138,7 @@ jobs:
|
||||
- name: setup-go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.24"
|
||||
go-version: "1.25"
|
||||
|
||||
# copy the caches from build
|
||||
- name: get-sha
|
||||
|
||||
@@ -10231,8 +10231,10 @@ components:
|
||||
having:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/Querybuildertypesv5Having'
|
||||
description: Accepted but currently ignored; the computed values are returned
|
||||
unfiltered.
|
||||
description: A filter on the formula values, referencing the formula by
|
||||
its name or as __result, for example F1 > 0. A time series keeps only
|
||||
the points that match and drops a series with none left; a scalar result
|
||||
keeps only the rows that match.
|
||||
legend:
|
||||
description: The display name template for each series; {{field}} placeholders
|
||||
are replaced by label values.
|
||||
@@ -32729,6 +32731,305 @@ paths:
|
||||
requestType: time_series
|
||||
schemaVersion: v1
|
||||
start: 1640995200000
|
||||
function_anomaly_request_rate:
|
||||
description: 'anomaly is the one function with named arguments. seasonality
|
||||
says which past periods predict the current value: hourly, daily
|
||||
or weekly, daily when absent. z_score_threshold is how many standard
|
||||
deviations from the prediction count as an anomaly. It has no default:
|
||||
a request without it gets bounds of zero width, so send it. 3 is
|
||||
the value the UI sends. The response carries, next to the values,
|
||||
a predicted series, an upper and a lower bound series and an anomaly
|
||||
score series. The scoring runs in the enterprise build; the community
|
||||
build returns the series unchanged.'
|
||||
summary: 'anomaly: request rate scored against its daily pattern'
|
||||
value:
|
||||
compositeQuery:
|
||||
queries:
|
||||
- spec:
|
||||
aggregations:
|
||||
- metricName: http.server.duration.count
|
||||
spaceAggregation: sum
|
||||
timeAggregation: rate
|
||||
functions:
|
||||
- args:
|
||||
- name: seasonality
|
||||
value: daily
|
||||
- name: z_score_threshold
|
||||
value: 3
|
||||
name: anomaly
|
||||
groupBy:
|
||||
- fieldContext: resource
|
||||
name: service.name
|
||||
name: A
|
||||
signal: metrics
|
||||
stepInterval: 300s
|
||||
type: builder_query
|
||||
end: 1640998800000
|
||||
requestType: time_series
|
||||
schemaVersion: v1
|
||||
start: 1640995200000
|
||||
function_cumulativesum_errors_so_far:
|
||||
description: cumulativeSum replaces each point with the sum of every
|
||||
point up to and including it, so a per-step count becomes a running
|
||||
total whose first point is the first count. It takes no arguments.
|
||||
The total resets with the window, so a longer time range gives a
|
||||
larger final value; it is a view of the window, not a counter.
|
||||
summary: 'cumulativeSum: 5xx responses since the start of the window'
|
||||
value:
|
||||
compositeQuery:
|
||||
queries:
|
||||
- spec:
|
||||
aggregations:
|
||||
- alias: server_errors
|
||||
expression: count()
|
||||
filter:
|
||||
expression: http.response.status_code >= 500
|
||||
functions:
|
||||
- name: cumulativeSum
|
||||
groupBy:
|
||||
- fieldContext: resource
|
||||
name: service.name
|
||||
name: A
|
||||
signal: traces
|
||||
stepInterval: 60s
|
||||
type: builder_query
|
||||
end: 1640998800000
|
||||
requestType: time_series
|
||||
schemaVersion: v1
|
||||
start: 1640995200000
|
||||
function_cutoffmin_hide_idle_hosts:
|
||||
description: The four threshold functions take one number. cutOffMin
|
||||
and cutOffMax remove the points below or above the threshold, so
|
||||
an idle host disappears from the chart instead of drawing a line
|
||||
along the bottom. clampMin and clampMax keep the points and replace
|
||||
the value with the threshold, which caps spikes without hiding them.
|
||||
system.cpu.utilization is a ratio, so 0.05 is 5%.
|
||||
summary: 'cutOffMin: drop hosts below 5% CPU from the chart'
|
||||
value:
|
||||
compositeQuery:
|
||||
queries:
|
||||
- spec:
|
||||
aggregations:
|
||||
- metricName: system.cpu.utilization
|
||||
spaceAggregation: avg
|
||||
timeAggregation: avg
|
||||
functions:
|
||||
- args:
|
||||
- value: 0.05
|
||||
name: cutOffMin
|
||||
groupBy:
|
||||
- fieldContext: resource
|
||||
name: host.name
|
||||
name: A
|
||||
signal: metrics
|
||||
stepInterval: 60s
|
||||
type: builder_query
|
||||
end: 1640998800000
|
||||
requestType: time_series
|
||||
schemaVersion: v1
|
||||
start: 1640995200000
|
||||
function_ewma_smoothed_latency:
|
||||
description: 'ewma3, ewma5 and ewma7 replace each point with an exponentially
|
||||
weighted average of the point and the points before it. The number
|
||||
in the name is the window the default weight is derived from: alpha
|
||||
= 2 / (n + 1), so 0.5, 0.333 and 0.25. One optional argument overrides
|
||||
alpha; a value close to 1 follows the raw series, a value close
|
||||
to 0 smooths harder. 0.3 here smooths a little more than ewma5''s
|
||||
default.'
|
||||
summary: 'ewma5: smoothed p95 latency'
|
||||
value:
|
||||
compositeQuery:
|
||||
queries:
|
||||
- spec:
|
||||
aggregations:
|
||||
- metricName: http.server.duration.bucket
|
||||
spaceAggregation: p95
|
||||
functions:
|
||||
- args:
|
||||
- value: 0.3
|
||||
name: ewma5
|
||||
groupBy:
|
||||
- fieldContext: resource
|
||||
name: service.name
|
||||
name: A
|
||||
signal: metrics
|
||||
stepInterval: 60s
|
||||
type: builder_query
|
||||
end: 1640998800000
|
||||
requestType: time_series
|
||||
schemaVersion: v1
|
||||
start: 1640995200000
|
||||
function_fillzero_sparse_errors:
|
||||
description: A count over sparse events returns a point only for steps
|
||||
that had a match, so a chart of failed spans has gaps where nothing
|
||||
failed. fillZero adds a zero at every missing step. With no arguments
|
||||
the server fills the request window at the query's step, which is
|
||||
the common case. The three optional arguments are start, end and
|
||||
step in milliseconds, for callers that want to fill a different
|
||||
grid.
|
||||
summary: 'fillZero: a zero at every step with no failed spans'
|
||||
value:
|
||||
compositeQuery:
|
||||
queries:
|
||||
- spec:
|
||||
aggregations:
|
||||
- alias: failed_spans
|
||||
expression: count()
|
||||
filter:
|
||||
expression: has_error = true
|
||||
functions:
|
||||
- name: fillZero
|
||||
groupBy:
|
||||
- fieldContext: resource
|
||||
name: service.name
|
||||
name: A
|
||||
signal: traces
|
||||
stepInterval: 60s
|
||||
type: builder_query
|
||||
end: 1640998800000
|
||||
requestType: time_series
|
||||
schemaVersion: v1
|
||||
start: 1640995200000
|
||||
function_log10_network_bytes:
|
||||
description: log2 and log10 replace each value with its logarithm,
|
||||
so a loopback device moving kilobytes and an uplink moving gigabytes
|
||||
fit on one chart. They take no arguments. Zero and negative values
|
||||
have no logarithm and are not drawn. absolute, also without arguments,
|
||||
is the usual companion for a signed series before a logarithm.
|
||||
summary: 'log10: network throughput across devices that differ by
|
||||
orders of magnitude'
|
||||
value:
|
||||
compositeQuery:
|
||||
queries:
|
||||
- spec:
|
||||
aggregations:
|
||||
- metricName: system.network.io
|
||||
spaceAggregation: sum
|
||||
timeAggregation: rate
|
||||
filter:
|
||||
expression: direction = 'transmit'
|
||||
functions:
|
||||
- name: log10
|
||||
groupBy:
|
||||
- fieldContext: attribute
|
||||
name: device
|
||||
name: A
|
||||
signal: metrics
|
||||
stepInterval: 60s
|
||||
type: builder_query
|
||||
end: 1640998800000
|
||||
requestType: time_series
|
||||
schemaVersion: v1
|
||||
start: 1640995200000
|
||||
function_median_despiked_request_rate:
|
||||
description: median3, median5 and median7 replace each point with
|
||||
the median of the surrounding 3, 5 or 7 points. A spike that lasts
|
||||
one step vanishes, a shift that lasts longer than half the window
|
||||
stays. The first and last half window, 2 points each for median5,
|
||||
have no full window and keep their values, so the newest spike stays
|
||||
visible; a series shorter than the window is returned unchanged.
|
||||
They take no arguments. Use them when the raw series is noisy and
|
||||
the chart is for a trend, not for catching the spike.
|
||||
summary: 'median5: request rate without one-step spikes'
|
||||
value:
|
||||
compositeQuery:
|
||||
queries:
|
||||
- spec:
|
||||
aggregations:
|
||||
- metricName: http.server.duration.count
|
||||
spaceAggregation: sum
|
||||
timeAggregation: rate
|
||||
functions:
|
||||
- name: median5
|
||||
groupBy:
|
||||
- fieldContext: resource
|
||||
name: service.name
|
||||
name: A
|
||||
signal: metrics
|
||||
stepInterval: 60s
|
||||
type: builder_query
|
||||
end: 1640998800000
|
||||
requestType: time_series
|
||||
schemaVersion: v1
|
||||
start: 1640995200000
|
||||
function_runningdiff_disk_growth:
|
||||
description: 'runningDiff turns a gauge into the change between consecutive
|
||||
points, so a filesystem usage gauge becomes bytes written per step.
|
||||
It takes no arguments and drops the first point of the window, since
|
||||
that point has nothing to difference against; the server reads one
|
||||
step before the window to keep the first interval. Functions apply
|
||||
in order: clampMin with 0 runs on the differences and replaces the
|
||||
negative value a cleanup or a volume reset produces with zero.'
|
||||
summary: 'runningDiff then clampMin: bytes written to disk per step'
|
||||
value:
|
||||
compositeQuery:
|
||||
queries:
|
||||
- spec:
|
||||
aggregations:
|
||||
- metricName: system.filesystem.usage
|
||||
spaceAggregation: sum
|
||||
timeAggregation: latest
|
||||
filter:
|
||||
expression: state = 'used'
|
||||
functions:
|
||||
- name: runningDiff
|
||||
- args:
|
||||
- value: 0
|
||||
name: clampMin
|
||||
groupBy:
|
||||
- fieldContext: resource
|
||||
name: host.name
|
||||
name: A
|
||||
signal: metrics
|
||||
stepInterval: 60s
|
||||
type: builder_query
|
||||
end: 1640998800000
|
||||
requestType: time_series
|
||||
schemaVersion: v1
|
||||
start: 1640995200000
|
||||
function_timeshift_week_over_week:
|
||||
description: timeShift moves every point of its query forward by the
|
||||
argument, in seconds, so a series read from the past lands on the
|
||||
current timestamps. B here reads the hour exactly seven days (604800
|
||||
s) before the requested window and comes back on this week's timestamps,
|
||||
so it overlays A on a chart and the formula can subtract it point
|
||||
by point. The argument is positive; it says how far back to read.
|
||||
summary: 'timeShift: this week''s error logs next to last week''s'
|
||||
value:
|
||||
compositeQuery:
|
||||
queries:
|
||||
- spec:
|
||||
aggregations:
|
||||
- alias: errors
|
||||
expression: count()
|
||||
filter:
|
||||
expression: severity_text = 'ERROR'
|
||||
name: A
|
||||
signal: logs
|
||||
stepInterval: 60s
|
||||
type: builder_query
|
||||
- spec:
|
||||
aggregations:
|
||||
- alias: errors
|
||||
expression: count()
|
||||
filter:
|
||||
expression: severity_text = 'ERROR'
|
||||
functions:
|
||||
- args:
|
||||
- value: 604800
|
||||
name: timeShift
|
||||
name: B
|
||||
signal: logs
|
||||
stepInterval: 60s
|
||||
type: builder_query
|
||||
- spec:
|
||||
expression: A - B
|
||||
name: F1
|
||||
type: builder_formula
|
||||
end: 1640998800000
|
||||
requestType: time_series
|
||||
schemaVersion: v1
|
||||
start: 1640995200000
|
||||
logs_raw:
|
||||
summary: 'Raw: fetch raw log records'
|
||||
value:
|
||||
|
||||
@@ -8,5 +8,5 @@
|
||||
|
||||
.title {
|
||||
color: var(--l1-foreground);
|
||||
font-size: var(--font-size-lg);
|
||||
font-size: var(--label-large-400-font-size);
|
||||
}
|
||||
|
||||
@@ -497,7 +497,11 @@
|
||||
// Requires final else in if-else-if chains (was disabled)
|
||||
"signoz/no-conditional-text-nodes-with-siblings": "warn",
|
||||
// Vendored from eslint-plugin-react-google-translate
|
||||
"signoz/no-return-text-nodes": "warn"
|
||||
"signoz/no-return-text-nodes": "warn",
|
||||
"signoz/no-text-css": "warn",
|
||||
// Font and text CSS in style props, CSSProperties objects and styled-components; text style goes through Typography props
|
||||
"signoz/require-disable-reason": "error"
|
||||
// A disable of signoz/no-text-css must say why Typography cannot render the text
|
||||
},
|
||||
"ignorePatterns": [
|
||||
"src/parser/*.ts",
|
||||
|
||||
@@ -45,8 +45,8 @@ const mockAliases = [
|
||||
},
|
||||
{
|
||||
// jest: not replaced, a test asserts on the toast it fires.
|
||||
find: /^@signozhq\/ui\/sonner$/,
|
||||
replacement: `${srcPath}/storybook/mocks/sonner.mock.tsx`,
|
||||
find: /^@signozhq\/ui\/toast$/,
|
||||
replacement: `${srcPath}/storybook/mocks/toast.mock.tsx`,
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
<!--
|
||||
`index.html` links Inter from Google Fonts here. The link is parsed before the
|
||||
msw worker has started, so that one request escapes the iframe while every
|
||||
other font request the app makes is answered with an empty stylesheet; the
|
||||
local faces stand in for both.
|
||||
Every Google Fonts request the app makes is answered with an empty
|
||||
stylesheet; the local faces stand in for them.
|
||||
-->
|
||||
<link rel="stylesheet" href="storybook-fonts.css" />
|
||||
|
||||
|
||||
@@ -1,22 +1,11 @@
|
||||
/*
|
||||
* The five families the app pulls from Google Fonts, served from the files in
|
||||
* The four families the app pulls from Google Fonts, served from the files in
|
||||
* `public/fonts` instead. `msw/appShellHandlers.ts` answers the CDN with an
|
||||
* empty stylesheet so no request leaves the iframe, which without this left
|
||||
* every story on a fallback for the four families `src/styles.scss` imports at
|
||||
* runtime, and on whatever the Inter link in `index.html` happened to fetch
|
||||
* before the worker had started.
|
||||
* runtime. Inter comes from `src/styles.scss` itself.
|
||||
*/
|
||||
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
/* The comma is part of the filename and the dev server serves it raw, so it
|
||||
stays literal inside the quoted url rather than percent-encoded. */
|
||||
src: url('fonts/Inter-VariableFont_opsz,wght.ttf') format('truetype');
|
||||
font-weight: 100 900;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Work Sans';
|
||||
src: url('fonts/WorkSans-VariableFont_wght.ttf') format('truetype');
|
||||
|
||||
@@ -14,9 +14,23 @@ module.exports = {
|
||||
path.join(__dirname, 'stylelint-rules/css-modules/class-name-pattern.js'),
|
||||
],
|
||||
customSyntax: 'postcss-scss',
|
||||
// Disables of property-disallowed-list must say why after "--"
|
||||
reportDescriptionlessDisables: [
|
||||
false,
|
||||
{ except: ['property-disallowed-list'], severity: 'error' },
|
||||
],
|
||||
rules: {
|
||||
// Applies to all SCSS files
|
||||
'local/no-unsupported-asset-url': true,
|
||||
// Text style goes through @signozhq/ui/typography props
|
||||
'property-disallowed-list': [
|
||||
['/^font/', '/^text-/', 'line-height', 'letter-spacing'],
|
||||
{
|
||||
severity: 'warning',
|
||||
message: (property) =>
|
||||
`Do not set "${property}" in CSS. Render the text with @signozhq/ui/typography and use its props, or disable this rule with a reason after "--" saying why Typography cannot.`,
|
||||
},
|
||||
],
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
|
||||
@@ -191,34 +191,30 @@ You can read the ./node_modules/@signozhq/design-tokens/dist/style.css to find c
|
||||
|
||||
### Typography
|
||||
|
||||
Text styles are tokens named `--{variant}-{size}-{weight}-{property}`:
|
||||
|
||||
- Variant: `label` (line height 1), `paragraph` (line height 18, 20 or 26px), `uppercase` (11 and 13px) or `code` (SF Mono, 11 and 13px).
|
||||
- Size: `small` (11px), `base` (13px), `medium` (16px) or `large` (18px). Paragraph has no `large`; uppercase and code have only `small` and `base`.
|
||||
- Weight: `400`, `500` or `600`.
|
||||
- Property: `font-family`, `font-size`, `font-weight`, `line-height`, `letter-spacing` or `text-transform`.
|
||||
|
||||
```scss
|
||||
// Font sizes (preferred)
|
||||
.title {
|
||||
font-size: var(--periscope-font-size-large); // 18px
|
||||
font-size: var(--periscope-font-size-medium); // 16px
|
||||
font-size: var(--periscope-font-size-base); // 13px
|
||||
font-size: var(--periscope-font-size-small); // 11px
|
||||
font-family: var(--label-large-500-font-family);
|
||||
font-size: var(--label-large-500-font-size);
|
||||
font-weight: var(--label-large-500-font-weight);
|
||||
line-height: var(--label-large-500-line-height);
|
||||
letter-spacing: var(--label-large-500-letter-spacing);
|
||||
}
|
||||
|
||||
// Alternative scale (rem-based)
|
||||
.heading {
|
||||
font-size: var(--font-size-xl); // 1.25rem
|
||||
font-size: var(--font-size-lg); // 1.125rem
|
||||
font-size: var(--font-size-base); // 1rem
|
||||
font-size: var(--font-size-sm); // 0.875rem
|
||||
.hint {
|
||||
font-size: var(--paragraph-small-400-font-size); // 11px
|
||||
line-height: var(--paragraph-small-400-line-height); // 18px
|
||||
}
|
||||
|
||||
// Font weights
|
||||
.bold {
|
||||
font-weight: var(--font-weight-semibold); // 600
|
||||
font-weight: var(--font-weight-medium); // 500
|
||||
font-weight: var(--font-weight-normal); // 400
|
||||
}
|
||||
|
||||
// Line heights
|
||||
.text {
|
||||
line-height: var(--line-height-20); // 20px
|
||||
line-height: var(--line-height-24); // 24px
|
||||
.id {
|
||||
font-family: var(--code-base-400-font-family);
|
||||
font-size: var(--code-base-400-font-size); // 13px
|
||||
}
|
||||
```
|
||||
|
||||
@@ -312,7 +308,7 @@ Each component exposes `--<component>-<property>` variables:
|
||||
--button-background: var(--success-background);
|
||||
--button-border-radius: var(--radius-2);
|
||||
--button-padding: var(--spacing-4) var(--spacing-8);
|
||||
--button-font-size: var(--periscope-font-size-base);
|
||||
--button-font-size: var(--label-base-400-font-size);
|
||||
}
|
||||
|
||||
// Override Input
|
||||
@@ -342,7 +338,7 @@ Components use data attributes for variants/states. Target them for state-specif
|
||||
|
||||
// Target size
|
||||
.wrapper :global([data-size="sm"]) {
|
||||
--button-font-size: var(--periscope-font-size-small);
|
||||
--button-font-size: var(--label-small-400-font-size);
|
||||
}
|
||||
|
||||
// Target color
|
||||
|
||||
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`.
|
||||
@@ -14,11 +14,6 @@
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link rel="preconnect" href="https://cdn.vercel.com" crossorigin />
|
||||
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
|
||||
<title data-react-helmet="true">
|
||||
Open source Observability platform | SigNoz
|
||||
</title>
|
||||
|
||||
@@ -53,9 +53,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.6.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",
|
||||
|
||||
170
frontend/plugins/__tests__/no-text-css.test.mjs
Normal file
170
frontend/plugins/__tests__/no-text-css.test.mjs
Normal file
@@ -0,0 +1,170 @@
|
||||
import { ruleTester } from './rule-tester.mjs';
|
||||
|
||||
const message = (property) =>
|
||||
`Do not set '${property}' in CSS. Render the text with @signozhq/ui/typography and use its props, or disable this rule with a reason after \`--\` saying why Typography cannot.`;
|
||||
|
||||
await ruleTester({
|
||||
rule: 'no-text-css',
|
||||
valid: [
|
||||
{
|
||||
name: 'layout properties in a style prop',
|
||||
code: 'export const A = () => <div style={{ width: 10, color: "red" }} />;',
|
||||
},
|
||||
{
|
||||
name: 'font properties in an object that is not a style',
|
||||
code: 'export const options = { fontSize: 12, font: "12px Inter" };',
|
||||
},
|
||||
{
|
||||
name: 'font properties in a prop that is not a style',
|
||||
code: 'export const A = () => <Chart options={{ fontSize: 12 }} />;',
|
||||
},
|
||||
{
|
||||
name: 'computed key',
|
||||
code:
|
||||
'const k = "fontSize";\nexport const A = () => <div style={{ [k]: 12 }} />;',
|
||||
},
|
||||
{
|
||||
name: 'style identifier from a parameter',
|
||||
code: 'export const A = ({ s }) => <div style={s} />;',
|
||||
},
|
||||
{
|
||||
name: 'template without a styled tag',
|
||||
code: 'export const s = html`font-size: 12px;`;',
|
||||
},
|
||||
{
|
||||
name: 'styled template with layout only',
|
||||
code:
|
||||
"import styled from 'styled-components';\nexport const A = styled.div`\n\tdisplay: flex;\n\ttext: none;\n`;",
|
||||
},
|
||||
{
|
||||
name: 'custom property that ends in a banned name',
|
||||
code:
|
||||
"import styled from 'styled-components';\nexport const A = styled.div`\n\t--my-font-size: 12px;\n`;",
|
||||
},
|
||||
],
|
||||
invalid: [
|
||||
{
|
||||
name: 'inline style',
|
||||
code: 'export const A = () => <div style={{ fontSize: 12, width: 1 }} />;',
|
||||
errors: [{ message: message('fontSize'), line: 1, column: 38 }],
|
||||
},
|
||||
{
|
||||
name: 'string key',
|
||||
code: "export const A = () => <div style={{ 'line-height': 1 }} />;",
|
||||
errors: [{ message: message('line-height') }],
|
||||
},
|
||||
{
|
||||
name: 'every banned family',
|
||||
code:
|
||||
'export const A = () => (\n\t<div\n\t\tstyle={{\n\t\t\tfont: "x",\n\t\t\tfontWeight: 500,\n\t\t\tfontFamily: "x",\n\t\t\ttextAlign: "center",\n\t\t\ttextTransform: "none",\n\t\t\tlineHeight: 1,\n\t\t\tletterSpacing: 1,\n\t\t}}\n\t/>\n);',
|
||||
errors: [
|
||||
{ message: message('font') },
|
||||
{ message: message('fontWeight') },
|
||||
{ message: message('fontFamily') },
|
||||
{ message: message('textAlign') },
|
||||
{ message: message('textTransform') },
|
||||
{ message: message('lineHeight') },
|
||||
{ message: message('letterSpacing') },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'conditional, logical and spread',
|
||||
code:
|
||||
'export const A = ({ a, b }) => (\n\t<div style={{ ...(a ? { fontSize: 1 } : {}), ...(b && { lineHeight: 1 }) }} />\n);',
|
||||
errors: [
|
||||
{ message: message('fontSize') },
|
||||
{ message: message('lineHeight') },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'style prop with a Style suffix',
|
||||
code:
|
||||
'export const A = () => <Card headStyle={{ fontWeight: 600 }} bodyStyle={{ padding: 0 }} />;',
|
||||
errors: [{ message: message('fontWeight') }],
|
||||
},
|
||||
{
|
||||
name: 'antd semantic styles',
|
||||
code:
|
||||
'export const A = () => <Modal styles={{ body: { fontSize: 12 }, header: { padding: 0 } }} />;',
|
||||
errors: [{ message: message('fontSize') }],
|
||||
},
|
||||
{
|
||||
name: 'identifier resolved to a local object',
|
||||
code:
|
||||
'const s = { fontSize: 12 };\nexport const A = () => (\n\t<>\n\t\t<div style={s} />\n\t\t<span style={s} />\n\t</>\n);',
|
||||
errors: [{ message: message('fontSize'), line: 1, column: 13 }],
|
||||
},
|
||||
{
|
||||
name: 'useMemo style',
|
||||
code:
|
||||
"import { useMemo } from 'react';\nexport const A = () => {\n\tconst s = useMemo(() => ({ textAlign: 'left' }), []);\n\treturn <div style={s} />;\n};",
|
||||
errors: [{ message: message('textAlign') }],
|
||||
},
|
||||
{
|
||||
name: 'object typed CSSProperties',
|
||||
code:
|
||||
"import type { CSSProperties } from 'react';\nexport const s: CSSProperties = { fontStyle: 'italic' };",
|
||||
errors: [{ message: message('fontStyle') }],
|
||||
},
|
||||
{
|
||||
name:
|
||||
'object typed React.CSSProperties, reported once when also used as style',
|
||||
code:
|
||||
"import React from 'react';\nconst s: React.CSSProperties = { fontSize: 1 };\nexport const A = () => <div style={s} />;",
|
||||
errors: [{ message: message('fontSize') }],
|
||||
},
|
||||
{
|
||||
name: 'Record of CSSProperties',
|
||||
code:
|
||||
"import type { CSSProperties } from 'react';\nexport const s: Record<string, CSSProperties> = { a: { lineHeight: 1 } };",
|
||||
errors: [{ message: message('lineHeight') }],
|
||||
},
|
||||
{
|
||||
name: 'as and satisfies CSSProperties',
|
||||
code:
|
||||
"import type { CSSProperties } from 'react';\nexport const a = { fontSize: 1 } as CSSProperties;\nexport const b = { fontWeight: 1 } satisfies CSSProperties;",
|
||||
errors: [
|
||||
{ message: message('fontSize') },
|
||||
{ message: message('fontWeight') },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'styled-components template',
|
||||
code:
|
||||
"import styled from 'styled-components';\nexport const A = styled.div`\n\tdisplay: flex;\n\tfont-size: 12px;\n\tcolor: ${(p) => p.c};\n\ttext-transform: none;line-height: 1;\n`;",
|
||||
errors: [
|
||||
{ message: message('font-size'), line: 4, column: 2 },
|
||||
{ message: message('text-transform'), line: 6, column: 2 },
|
||||
{ message: message('line-height'), line: 6, column: 23 },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'function returning CSSProperties',
|
||||
code:
|
||||
"import type { CSSProperties } from 'react';\nexport const a = (): CSSProperties => ({ fontSize: 1 });\nexport function b(): CSSProperties {\n\treturn { lineHeight: 1 };\n}",
|
||||
errors: [
|
||||
{ message: message('fontSize') },
|
||||
{ message: message('lineHeight') },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'strings in styled interpolations',
|
||||
code:
|
||||
"import styled, { css } from 'styled-components';\nexport const A = styled.div`\n\t${(p) => (p.s ? 'font-size: 11px;' : `line-height: 1;`)}\n\t${(p) => p.b && css`font-weight: 500;`}\n`;",
|
||||
errors: [
|
||||
{ message: message('font-size'), line: 3, column: 19 },
|
||||
{ message: message('line-height'), line: 3, column: 40 },
|
||||
{ message: message('font-weight'), line: 4, column: 22 },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'styled(Component) and css helper',
|
||||
code:
|
||||
"import styled, { css } from 'styled-components';\nexport const A = styled(B)`letter-spacing: 1px;`;\nexport const c = css`\n\tfont-weight: 500;\n`;",
|
||||
errors: [
|
||||
{ message: message('letter-spacing'), line: 2, column: 28 },
|
||||
{ message: message('font-weight'), line: 4, column: 2 },
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
49
frontend/plugins/__tests__/require-disable-reason.test.mjs
Normal file
49
frontend/plugins/__tests__/require-disable-reason.test.mjs
Normal file
@@ -0,0 +1,49 @@
|
||||
import { ruleTester } from './rule-tester.mjs';
|
||||
|
||||
const message = 'Disabling signoz/no-text-css needs a reason after `--`.';
|
||||
|
||||
await ruleTester({
|
||||
rule: 'require-disable-reason',
|
||||
valid: [
|
||||
{
|
||||
name: 'reason after --',
|
||||
code:
|
||||
'// oxlint-disable-next-line signoz/no-text-css -- canvas label\nexport const a = 1;',
|
||||
},
|
||||
{
|
||||
name: 'block comment with a reason',
|
||||
code:
|
||||
'/* eslint-disable signoz/no-text-css -- email HTML */\nexport const a = 1;',
|
||||
},
|
||||
{
|
||||
name: 'another rule without a reason',
|
||||
code: '// oxlint-disable-next-line no-console\nexport const a = 1;',
|
||||
},
|
||||
{
|
||||
name: 'disable of every rule',
|
||||
code: '// eslint-disable-next-line\nexport const a = 1;',
|
||||
},
|
||||
{
|
||||
name: 'plain comment naming the rule',
|
||||
code: '// signoz/no-text-css flags font CSS\nexport const a = 1;',
|
||||
},
|
||||
],
|
||||
invalid: [
|
||||
{
|
||||
name: 'next-line without a reason',
|
||||
code: '// oxlint-disable-next-line signoz/no-text-css\nexport const a = 1;',
|
||||
errors: [{ message, line: 1, column: 1 }],
|
||||
},
|
||||
{
|
||||
name: 'empty reason',
|
||||
code: '// eslint-disable-line signoz/no-text-css --\nexport const a = 1;',
|
||||
errors: [{ message }],
|
||||
},
|
||||
{
|
||||
name: 'among other rules',
|
||||
code:
|
||||
'/* oxlint-disable no-console, signoz/no-text-css */\nexport const a = 1;',
|
||||
errors: [{ message }],
|
||||
},
|
||||
],
|
||||
});
|
||||
324
frontend/plugins/rules/no-text-css.mjs
Normal file
324
frontend/plugins/rules/no-text-css.mjs
Normal file
@@ -0,0 +1,324 @@
|
||||
/**
|
||||
* Rule: no-text-css
|
||||
*
|
||||
* Text style (font, line height, letter spacing, case, alignment, overflow)
|
||||
* belongs to Typography props, not to CSS written in a component.
|
||||
*
|
||||
* This rule catches font and text properties in:
|
||||
* <div style={{ fontSize: 12 }} /> style and *Style props
|
||||
* <Modal styles={{ body: { fontWeight: 500 } }} />
|
||||
* const s: CSSProperties = { lineHeight: 1 } objects typed CSSProperties
|
||||
* ({ ... }) as CSSProperties, satisfies CSSProperties
|
||||
* (): CSSProperties => ({ ... }) functions returning CSSProperties
|
||||
* styled.div`font-size: 12px;` styled-components templates,
|
||||
* ${(p) => p.small && 'font-size: 11px;'} strings in their interpolations
|
||||
*
|
||||
* Identifiers passed to a style prop are followed to a local object literal.
|
||||
* SCSS is covered by the stylelint config.
|
||||
*/
|
||||
|
||||
const MESSAGE = (property) =>
|
||||
`Do not set '${property}' in CSS. Render the text with @signozhq/ui/typography and use its props, or disable this rule with a reason after \`--\` saying why Typography cannot.`;
|
||||
|
||||
const BANNED_JS = /^(font([A-Z]\w*)?|text[A-Z]\w*|lineHeight|letterSpacing)$/;
|
||||
const BANNED_CSS =
|
||||
/(^|[\s;{])(font(-[a-z]+)*|text-[a-z-]+|line-height|letter-spacing)\s*:/g;
|
||||
|
||||
const STYLE_TAGS = new Set(['css', 'createGlobalStyle', 'keyframes']);
|
||||
|
||||
const FUNCTIONS = new Set([
|
||||
'ArrowFunctionExpression',
|
||||
'FunctionExpression',
|
||||
'FunctionDeclaration',
|
||||
]);
|
||||
|
||||
const camelCase = (name) =>
|
||||
name.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
|
||||
|
||||
function propertyName(property) {
|
||||
if (property.computed) {
|
||||
return null;
|
||||
}
|
||||
if (property.key.type === 'Identifier') {
|
||||
return property.key.name;
|
||||
}
|
||||
if (
|
||||
property.key.type === 'Literal' &&
|
||||
typeof property.key.value === 'string'
|
||||
) {
|
||||
return property.key.value;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function isCssPropertiesType(typeNode) {
|
||||
if (!typeNode || typeNode.type !== 'TSTypeReference') {
|
||||
return false;
|
||||
}
|
||||
const name = typeNode.typeName;
|
||||
if (name.type === 'Identifier') {
|
||||
return name.name === 'CSSProperties';
|
||||
}
|
||||
return name.type === 'TSQualifiedName' && name.right.name === 'CSSProperties';
|
||||
}
|
||||
|
||||
// Record<K, CSSProperties> and { [key: string]: CSSProperties }
|
||||
function isCssPropertiesMapType(typeNode) {
|
||||
if (!typeNode) {
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
typeNode.type === 'TSTypeReference' &&
|
||||
typeNode.typeName.type === 'Identifier' &&
|
||||
typeNode.typeName.name === 'Record'
|
||||
) {
|
||||
const params = (typeNode.typeArguments ?? typeNode.typeParameters)?.params;
|
||||
return isCssPropertiesType(params?.[1]);
|
||||
}
|
||||
if (typeNode.type === 'TSTypeLiteral') {
|
||||
return typeNode.members.some(
|
||||
(member) =>
|
||||
member.type === 'TSIndexSignature' &&
|
||||
isCssPropertiesType(member.typeAnnotation?.typeAnnotation),
|
||||
);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function rootIdentifier(node) {
|
||||
let current = node;
|
||||
while (current) {
|
||||
if (current.type === 'Identifier') {
|
||||
return current.name;
|
||||
}
|
||||
if (current.type === 'MemberExpression') {
|
||||
current = current.object;
|
||||
} else if (current.type === 'CallExpression') {
|
||||
current = current.callee;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function isStyleTag(tag) {
|
||||
if (tag.type === 'Identifier') {
|
||||
return tag.name === 'styled' || STYLE_TAGS.has(tag.name);
|
||||
}
|
||||
return rootIdentifier(tag) === 'styled';
|
||||
}
|
||||
|
||||
export default {
|
||||
create(context) {
|
||||
const reported = new Set();
|
||||
const followed = new Set();
|
||||
const reportedCss = new Set();
|
||||
|
||||
function report(property, name) {
|
||||
if (reported.has(property)) {
|
||||
return;
|
||||
}
|
||||
reported.add(property);
|
||||
context.report({ node: property.key, message: MESSAGE(name) });
|
||||
}
|
||||
|
||||
function resolveIdentifier(identifier) {
|
||||
const scope = context.sourceCode.getScope(identifier);
|
||||
let current = scope;
|
||||
while (current) {
|
||||
const variable = current.set.get(identifier.name);
|
||||
if (variable) {
|
||||
const definition = variable.defs[0];
|
||||
if (
|
||||
definition?.node?.type === 'VariableDeclarator' &&
|
||||
definition.node.init
|
||||
) {
|
||||
return definition.node.init;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
current = current.upper;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function checkObject(object) {
|
||||
for (const property of object.properties) {
|
||||
if (property.type === 'SpreadElement') {
|
||||
checkStyle(property.argument);
|
||||
continue;
|
||||
}
|
||||
const name = propertyName(property);
|
||||
if (name && BANNED_JS.test(camelCase(name))) {
|
||||
report(property, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function checkStyle(expression) {
|
||||
if (!expression) {
|
||||
return;
|
||||
}
|
||||
switch (expression.type) {
|
||||
case 'ObjectExpression':
|
||||
checkObject(expression);
|
||||
return;
|
||||
case 'ConditionalExpression':
|
||||
checkStyle(expression.consequent);
|
||||
checkStyle(expression.alternate);
|
||||
return;
|
||||
case 'LogicalExpression':
|
||||
checkStyle(expression.left);
|
||||
checkStyle(expression.right);
|
||||
return;
|
||||
case 'TSAsExpression':
|
||||
case 'TSSatisfiesExpression':
|
||||
case 'TSNonNullExpression':
|
||||
case 'ParenthesizedExpression':
|
||||
checkStyle(expression.expression);
|
||||
return;
|
||||
case 'CallExpression':
|
||||
// useMemo(() => ({ ... }), deps)
|
||||
if (
|
||||
expression.callee.type === 'Identifier' &&
|
||||
expression.callee.name === 'useMemo'
|
||||
) {
|
||||
checkReturned(expression.arguments[0]);
|
||||
}
|
||||
return;
|
||||
case 'Identifier': {
|
||||
const init = resolveIdentifier(expression);
|
||||
if (!init || followed.has(init)) {
|
||||
return;
|
||||
}
|
||||
followed.add(init);
|
||||
checkStyle(init);
|
||||
return;
|
||||
}
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
function checkReturned(fn) {
|
||||
if (!fn || !FUNCTIONS.has(fn.type)) {
|
||||
return;
|
||||
}
|
||||
if (fn.body.type !== 'BlockStatement') {
|
||||
checkStyle(fn.body);
|
||||
return;
|
||||
}
|
||||
for (const statement of fn.body.body) {
|
||||
if (statement.type === 'ReturnStatement') {
|
||||
checkStyle(statement.argument);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function checkStyleMap(expression) {
|
||||
if (expression?.type !== 'ObjectExpression') {
|
||||
return;
|
||||
}
|
||||
for (const property of expression.properties) {
|
||||
if (property.type === 'Property') {
|
||||
checkStyle(property.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// `start` is the index of the first character after the opening quote.
|
||||
function checkCss(text, start) {
|
||||
for (const match of text.matchAll(BANNED_CSS)) {
|
||||
const index = start + match.index + match[1].length;
|
||||
if (reportedCss.has(index)) {
|
||||
continue;
|
||||
}
|
||||
reportedCss.add(index);
|
||||
context.report({
|
||||
loc: {
|
||||
start: context.sourceCode.getLocFromIndex(index),
|
||||
end: context.sourceCode.getLocFromIndex(index + match[2].length),
|
||||
},
|
||||
message: MESSAGE(match[2]),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// CSS returned from interpolations: ${(p) => p.small && 'font-size: 11px;'}
|
||||
function checkCssStrings(node) {
|
||||
if (!node || typeof node.type !== 'string') {
|
||||
return;
|
||||
}
|
||||
if (node.type === 'TemplateLiteral') {
|
||||
for (const quasi of node.quasis) {
|
||||
checkCss(quasi.value.raw, quasi.range[0] + 1);
|
||||
}
|
||||
} else if (node.type === 'Literal' && typeof node.value === 'string') {
|
||||
checkCss(node.raw.slice(1, -1), node.range[0] + 1);
|
||||
return;
|
||||
}
|
||||
for (const [key, value] of Object.entries(node)) {
|
||||
if (key === 'parent') {
|
||||
continue;
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
value.forEach(checkCssStrings);
|
||||
} else if (value && typeof value === 'object') {
|
||||
checkCssStrings(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function checkFunction(node) {
|
||||
if (isCssPropertiesType(node.returnType?.typeAnnotation)) {
|
||||
checkReturned(node);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
JSXAttribute(node) {
|
||||
if (
|
||||
node.name.type !== 'JSXIdentifier' ||
|
||||
node.value?.type !== 'JSXExpressionContainer'
|
||||
) {
|
||||
return;
|
||||
}
|
||||
const name = node.name.name;
|
||||
if (name === 'styles') {
|
||||
checkStyleMap(node.value.expression);
|
||||
} else if (name === 'style' || name.endsWith('Style')) {
|
||||
checkStyle(node.value.expression);
|
||||
}
|
||||
},
|
||||
VariableDeclarator(node) {
|
||||
const type = node.id.typeAnnotation?.typeAnnotation;
|
||||
if (isCssPropertiesType(type)) {
|
||||
checkStyle(node.init);
|
||||
} else if (isCssPropertiesMapType(type)) {
|
||||
checkStyleMap(node.init);
|
||||
}
|
||||
},
|
||||
TSAsExpression(node) {
|
||||
if (isCssPropertiesType(node.typeAnnotation)) {
|
||||
checkStyle(node.expression);
|
||||
}
|
||||
},
|
||||
TSSatisfiesExpression(node) {
|
||||
if (isCssPropertiesType(node.typeAnnotation)) {
|
||||
checkStyle(node.expression);
|
||||
} else if (isCssPropertiesMapType(node.typeAnnotation)) {
|
||||
checkStyleMap(node.expression);
|
||||
}
|
||||
},
|
||||
TaggedTemplateExpression(node) {
|
||||
if (!isStyleTag(node.tag)) {
|
||||
return;
|
||||
}
|
||||
checkCssStrings(node.quasi);
|
||||
},
|
||||
ArrowFunctionExpression: checkFunction,
|
||||
FunctionExpression: checkFunction,
|
||||
FunctionDeclaration: checkFunction,
|
||||
};
|
||||
},
|
||||
};
|
||||
38
frontend/plugins/rules/require-disable-reason.mjs
Normal file
38
frontend/plugins/rules/require-disable-reason.mjs
Normal file
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Rule: require-disable-reason
|
||||
*
|
||||
* A disable directive that names one of RULES must say why after `--`:
|
||||
* // oxlint-disable-next-line signoz/no-text-css -- canvas text, not DOM
|
||||
*
|
||||
* Directives that disable every rule are not checked.
|
||||
*/
|
||||
|
||||
const RULES = new Set(['signoz/no-text-css']);
|
||||
|
||||
const DIRECTIVE = /^\s*(?:eslint|oxlint)-disable(?:-next-line|-line)?\s+(.*)$/s;
|
||||
|
||||
export default {
|
||||
create(context) {
|
||||
return {
|
||||
Program() {
|
||||
for (const comment of context.sourceCode.getAllComments()) {
|
||||
const match = comment.value.match(DIRECTIVE);
|
||||
if (!match) {
|
||||
continue;
|
||||
}
|
||||
const [rules, reason] = match[1].split(/\s--\s?/);
|
||||
const named = rules
|
||||
.split(',')
|
||||
.map((rule) => rule.trim())
|
||||
.filter((rule) => RULES.has(rule));
|
||||
if (named.length > 0 && !reason?.trim()) {
|
||||
context.report({
|
||||
node: comment,
|
||||
message: `Disabling ${named.join(', ')} needs a reason after \`--\`.`,
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
@@ -17,6 +17,8 @@ import noConditionalTextNodesWithSiblings from './rules/no-conditional-text-node
|
||||
import noReturnTextNodes from './rules/no-return-text-nodes.mjs';
|
||||
import noMswInStoryFile from './rules/no-msw-in-story-file.mjs';
|
||||
import noDirectReactRouterImport from './rules/no-direct-react-router-import.mjs';
|
||||
import noTextCss from './rules/no-text-css.mjs';
|
||||
import requireDisableReason from './rules/require-disable-reason.mjs';
|
||||
|
||||
export default {
|
||||
meta: {
|
||||
@@ -35,5 +37,7 @@ export default {
|
||||
'no-return-text-nodes': noReturnTextNodes,
|
||||
'no-msw-in-story-file': noMswInStoryFile,
|
||||
'no-direct-react-router-import': noDirectReactRouterImport,
|
||||
'no-text-css': noTextCss,
|
||||
'require-disable-reason': requireDisableReason,
|
||||
},
|
||||
};
|
||||
|
||||
450
frontend/pnpm-lock.yaml
generated
450
frontend/pnpm-lock.yaml
generated
@@ -74,14 +74,14 @@ importers:
|
||||
specifier: 5.3.0
|
||||
version: 5.3.0
|
||||
'@signozhq/design-tokens':
|
||||
specifier: 2.2.1
|
||||
version: 2.2.1
|
||||
specifier: 2.6.0
|
||||
version: 2.6.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)
|
||||
@@ -2909,9 +2909,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==}
|
||||
|
||||
@@ -2928,19 +2925,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:
|
||||
@@ -2972,15 +2956,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:
|
||||
@@ -3095,71 +3070,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:
|
||||
@@ -3169,19 +3079,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:
|
||||
@@ -3209,15 +3106,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:
|
||||
@@ -3227,15 +3115,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:
|
||||
@@ -3254,19 +3133,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==}
|
||||
|
||||
@@ -3585,14 +3451,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.6.0':
|
||||
resolution: {integrity: sha512-7+lBxC3N4r1A2M8awhop7mtYMMd5yOg4BKfRAN3hnle4lclZHlxm7b3gdplfNN10JA1mcwRSQvWrjQPmA/euqw==}
|
||||
|
||||
'@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
|
||||
@@ -4925,12 +4791,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'}
|
||||
@@ -7459,12 +7319,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
|
||||
|
||||
node-addon-api@7.1.1:
|
||||
resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==}
|
||||
|
||||
@@ -8433,8 +8287,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
|
||||
@@ -8934,12 +8788,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'}
|
||||
@@ -9950,7 +9798,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
|
||||
@@ -9966,7 +9814,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
|
||||
@@ -9974,7 +9822,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
|
||||
@@ -12498,8 +12346,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)':
|
||||
@@ -12511,18 +12357,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
|
||||
@@ -12558,12 +12392,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
|
||||
@@ -12674,101 +12502,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)
|
||||
@@ -12776,22 +12509,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
|
||||
@@ -12814,24 +12531,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
|
||||
@@ -12846,20 +12551,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)
|
||||
@@ -12879,7 +12575,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
|
||||
@@ -12895,7 +12591,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
|
||||
@@ -12905,7 +12601,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)
|
||||
@@ -13150,41 +12846,33 @@ snapshots:
|
||||
|
||||
'@sideway/pinpoint@2.0.0': {}
|
||||
|
||||
'@signozhq/design-tokens@2.2.1': {}
|
||||
'@signozhq/design-tokens@2.6.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'
|
||||
@@ -14434,7 +14122,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
|
||||
|
||||
@@ -14471,7 +14159,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
|
||||
@@ -14680,18 +14368,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:
|
||||
@@ -17240,7 +16916,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: {}
|
||||
@@ -17750,11 +17426,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)
|
||||
|
||||
node-addon-api@7.1.1:
|
||||
optional: true
|
||||
|
||||
@@ -18378,7 +18049,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)
|
||||
@@ -18389,7 +18060,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
|
||||
@@ -18397,7 +18068,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)
|
||||
@@ -18406,7 +18077,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)
|
||||
@@ -18416,7 +18087,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)
|
||||
@@ -18426,7 +18097,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)
|
||||
@@ -18435,7 +18106,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
|
||||
@@ -18443,7 +18114,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)
|
||||
@@ -18454,7 +18125,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)
|
||||
@@ -18464,7 +18135,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
|
||||
@@ -18472,7 +18143,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)
|
||||
@@ -18484,7 +18155,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)
|
||||
@@ -18495,7 +18166,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
|
||||
@@ -18503,7 +18174,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)
|
||||
@@ -18512,7 +18183,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)
|
||||
@@ -18521,7 +18192,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
|
||||
@@ -18529,7 +18200,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)
|
||||
@@ -18542,7 +18213,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
|
||||
@@ -18550,7 +18221,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
|
||||
@@ -18558,7 +18229,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
|
||||
@@ -18567,7 +18238,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)
|
||||
@@ -18588,7 +18259,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
|
||||
@@ -18596,7 +18267,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
|
||||
@@ -18604,7 +18275,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
|
||||
@@ -18612,7 +18283,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)
|
||||
@@ -18623,7 +18294,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)
|
||||
@@ -18635,7 +18306,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)
|
||||
@@ -18645,7 +18316,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
|
||||
@@ -18653,7 +18324,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)
|
||||
@@ -18663,7 +18334,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)
|
||||
@@ -18673,7 +18344,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
|
||||
@@ -18681,14 +18352,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)
|
||||
@@ -18914,7 +18585,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)
|
||||
@@ -18965,7 +18636,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
|
||||
@@ -19505,11 +19176,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: {}
|
||||
|
||||
92
frontend/public/fonts/GeistMono-LICENSE.txt
Normal file
92
frontend/public/fonts/GeistMono-LICENSE.txt
Normal file
@@ -0,0 +1,92 @@
|
||||
Copyright (c) 2023 Vercel, in collaboration with basement.studio
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
http://scripts.sil.org/OFL
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION AND CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
Binary file not shown.
@@ -7079,7 +7079,7 @@ export interface Querybuildertypesv5QueryBuilderFormulaDTO {
|
||||
*/
|
||||
functions?: Querybuildertypesv5FunctionDTO[] | null;
|
||||
/**
|
||||
* @description Accepted but currently ignored; the computed values are returned unfiltered.
|
||||
* @description A filter on the formula values, referencing the formula by its name or as __result, for example F1 > 0. A time series keeps only the points that match and drops a series with none left; a scalar result keeps only the rows that match.
|
||||
*/
|
||||
having?: Querybuildertypesv5HavingDTO;
|
||||
/**
|
||||
|
||||
@@ -90,10 +90,17 @@ function AddCreditCardModal({
|
||||
</AuthZTooltip>,
|
||||
]}
|
||||
>
|
||||
<Typography.Text className="add-credit-card-text">
|
||||
<Typography
|
||||
variant="paragraph"
|
||||
size="base"
|
||||
weight="regular"
|
||||
color="inherit"
|
||||
as="span"
|
||||
textOverflow="wrap"
|
||||
>
|
||||
You're currently on <span className="highlight-text">Trial plan</span>.
|
||||
Add a credit card to access SigNoz chat support to your workspace.
|
||||
</Typography.Text>
|
||||
</Typography>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
.breadcrumb {
|
||||
padding: var(--spacing-4) 16px var(--spacing-2);
|
||||
|
||||
// The antd breadcrumb provides the item spacing.
|
||||
--button-padding: 0;
|
||||
|
||||
ol {
|
||||
align-items: center;
|
||||
}
|
||||
@@ -8,13 +11,4 @@
|
||||
:global(.ant-breadcrumb-separator) {
|
||||
color: var(--muted-foreground);
|
||||
}
|
||||
|
||||
[data-slot='button'] {
|
||||
padding: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
.divider {
|
||||
--divider-color: var(--l1-border);
|
||||
--divider-margin: 10px 0 16px 0;
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ function AlertBreadcrumb({
|
||||
items={breadcrumbItems}
|
||||
data-testid={testId}
|
||||
/>
|
||||
{showDivider && <Divider className={styles.divider} />}
|
||||
{showDivider && <Divider spacing="10px 16px" />}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
.itemLast {
|
||||
color: var(--button-secondary-label);
|
||||
font-size: var(--button-font-size, var(--periscope-font-size-base));
|
||||
line-height: var(--button-size-line-height, 16px);
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -26,8 +26,6 @@
|
||||
}
|
||||
|
||||
.brand-title {
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
color: var(--l1-foreground); // Primary text
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
@@ -28,15 +28,29 @@ function AppLoading(): JSX.Element {
|
||||
<div className="brand">
|
||||
<img src={signozBrandLogoUrl} alt="SigNoz" className="brand-logo" />
|
||||
|
||||
<Typography.Title level={2} className="brand-title">
|
||||
<Typography
|
||||
variant="label"
|
||||
size="large"
|
||||
weight="bold"
|
||||
color="main"
|
||||
as="h2"
|
||||
textOverflow="wrap"
|
||||
>
|
||||
SigNoz
|
||||
</Typography.Title>
|
||||
</Typography>
|
||||
</div>
|
||||
|
||||
<div className="brand-tagline">
|
||||
<Typography.Text>
|
||||
<Typography
|
||||
variant="paragraph"
|
||||
size="base"
|
||||
weight="regular"
|
||||
color="inherit"
|
||||
as="span"
|
||||
textOverflow="wrap"
|
||||
>
|
||||
OpenTelemetry-Native Logs, Metrics and Traces in a single pane
|
||||
</Typography.Text>
|
||||
</Typography>
|
||||
</div>
|
||||
|
||||
<div className="loader" />
|
||||
|
||||
11
frontend/src/components/AppToaster/AppToaster.module.scss
Normal file
11
frontend/src/components/AppToaster/AppToaster.module.scss
Normal file
@@ -0,0 +1,11 @@
|
||||
// Clears the pagination bars and footers that sit above the bottom strip, and the 56px footer of
|
||||
// a drawer or dialog panel, which sits 16px from the edge of the window.
|
||||
.toaster[data-slot='toaster'][data-position^='bottom'] {
|
||||
bottom: calc(var(--bottom-strip-height, 0px) + var(--spacing-48));
|
||||
}
|
||||
|
||||
// Clears the header of a drawer or dialog panel, 16px from the edge plus 48px or 56px.
|
||||
:global(body):has([data-slot='drawer-popup'], [data-slot='dialog-popup'])
|
||||
.toaster[data-slot='toaster'][data-position^='top'] {
|
||||
top: var(--spacing-40);
|
||||
}
|
||||
11
frontend/src/components/AppToaster/AppToaster.tsx
Normal file
11
frontend/src/components/AppToaster/AppToaster.tsx
Normal file
@@ -0,0 +1,11 @@
|
||||
import { Toaster } from '@signozhq/ui/toast';
|
||||
|
||||
import styles from './AppToaster.module.scss';
|
||||
|
||||
function AppToaster(): JSX.Element {
|
||||
return (
|
||||
<Toaster position="bottom-right" offset={24} className={styles.toaster} />
|
||||
);
|
||||
}
|
||||
|
||||
export default AppToaster;
|
||||
@@ -35,7 +35,6 @@
|
||||
&__error-code {
|
||||
color: var(--bg-cherry-100);
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
line-height: 1;
|
||||
letter-spacing: -0.065px;
|
||||
}
|
||||
@@ -43,8 +42,6 @@
|
||||
&__error-message {
|
||||
color: var(--bg-cherry-300);
|
||||
font-size: 13px;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
letter-spacing: -0.065px;
|
||||
}
|
||||
|
||||
@@ -75,7 +72,6 @@
|
||||
&__message-item {
|
||||
color: var(--bg-cherry-300);
|
||||
font-size: 13px;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
letter-spacing: -0.065px;
|
||||
|
||||
|
||||
@@ -45,9 +45,8 @@
|
||||
}
|
||||
|
||||
.auth-footer-text {
|
||||
font-family: var(--font-family-inter, Inter, sans-serif);
|
||||
font-family: var(--label-small-400-font-family);
|
||||
font-size: 11px;
|
||||
font-weight: 400;
|
||||
line-height: 1;
|
||||
color: var(--l2-foreground);
|
||||
text-align: center;
|
||||
@@ -57,7 +56,6 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
text-decoration: none;
|
||||
transition: opacity 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4.9px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.auth-header-logo-icon {
|
||||
@@ -26,7 +25,7 @@
|
||||
}
|
||||
|
||||
.auth-header-logo-text {
|
||||
font-family: Satoshi, var(--font-family-inter, Inter), sans-serif;
|
||||
font-family: Satoshi, var(--label-medium-500-font-family), sans-serif;
|
||||
font-size: 15.4px;
|
||||
font-weight: 500;
|
||||
line-height: 17.5px;
|
||||
|
||||
@@ -17,8 +17,3 @@
|
||||
letter-spacing: -0.01em;
|
||||
color: var(--l1-foreground);
|
||||
}
|
||||
|
||||
.divider {
|
||||
--divider-color: var(--l1-border);
|
||||
--divider-margin: 10px 0 16px 0;
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ function BackHeader({
|
||||
</Button>
|
||||
<div className={styles.title}>{title}</div>
|
||||
</div>
|
||||
<Divider className={styles.divider} />
|
||||
<Divider spacing="10px 16px" />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
.ant-table {
|
||||
.ant-table-thead > tr > th {
|
||||
padding: 12px;
|
||||
font-weight: 500;
|
||||
font-size: 12px;
|
||||
line-height: 18px;
|
||||
|
||||
@@ -31,10 +30,7 @@
|
||||
border-bottom: none;
|
||||
|
||||
color: var(--l2-foreground);
|
||||
font-family: Inter;
|
||||
font-size: 11px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: 18px; /* 163.636% */
|
||||
letter-spacing: 0.44px;
|
||||
|
||||
@@ -45,7 +41,6 @@
|
||||
|
||||
.ant-table-cell {
|
||||
padding: 12px;
|
||||
font-size: 13px;
|
||||
line-height: 20px;
|
||||
color: var(--l1-foreground);
|
||||
background: var(--l1-background);
|
||||
@@ -67,12 +62,6 @@
|
||||
.ant-table-cell:nth-child(n + 3) {
|
||||
padding-right: 24px;
|
||||
}
|
||||
.column-header-right {
|
||||
text-align: right;
|
||||
}
|
||||
.column-header-left {
|
||||
text-align: left;
|
||||
}
|
||||
.ant-table-tbody > tr > td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
@@ -40,10 +40,27 @@ const INITIAL_PAGE_SIZE = 20;
|
||||
|
||||
const showPaginationItem = (total: number, range: number[]): JSX.Element => (
|
||||
<>
|
||||
<Typography.Text className="numbers">
|
||||
<Typography
|
||||
variant="paragraph"
|
||||
size="base"
|
||||
weight="regular"
|
||||
color="inherit"
|
||||
as="span"
|
||||
textOverflow="wrap"
|
||||
>
|
||||
{range[0]} — {range[1]}
|
||||
</Typography.Text>
|
||||
<Typography.Text className="total"> of {total}</Typography.Text>
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="paragraph"
|
||||
size="base"
|
||||
weight="regular"
|
||||
color="inherit"
|
||||
as="span"
|
||||
textOverflow="wrap"
|
||||
>
|
||||
{' '}
|
||||
of {total}
|
||||
</Typography>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -56,22 +73,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>
|
||||
);
|
||||
}
|
||||
@@ -526,7 +536,18 @@ export default function CeleryOverviewTable({
|
||||
),
|
||||
}}
|
||||
locale={{
|
||||
emptyText: isLoading ? null : <Typography.Text>No data</Typography.Text>,
|
||||
emptyText: isLoading ? null : (
|
||||
<Typography
|
||||
variant="paragraph"
|
||||
size="base"
|
||||
weight="regular"
|
||||
color="inherit"
|
||||
as="span"
|
||||
textOverflow="wrap"
|
||||
>
|
||||
No data
|
||||
</Typography>
|
||||
),
|
||||
}}
|
||||
scroll={{ x: 'max-content' }}
|
||||
showSorterTooltip
|
||||
|
||||
@@ -24,9 +24,16 @@ function CeleryTaskConfigOptions(): JSX.Element {
|
||||
return (
|
||||
<div className="celery-task-filters">
|
||||
<div className="celery-filters">
|
||||
<Typography.Text style={{ whiteSpace: 'nowrap' }}>
|
||||
<Typography
|
||||
variant="paragraph"
|
||||
size="base"
|
||||
weight="regular"
|
||||
color="inherit"
|
||||
as="span"
|
||||
textOverflow="visible"
|
||||
>
|
||||
Task Name
|
||||
</Typography.Text>
|
||||
</Typography>
|
||||
<Select
|
||||
data-testid="celery-task-name-select"
|
||||
placeholder="Task Name"
|
||||
|
||||
@@ -49,11 +49,7 @@
|
||||
|
||||
.subtitle {
|
||||
color: var(--l2-foreground);
|
||||
font-family: Inter;
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -64,11 +60,6 @@
|
||||
|
||||
.footer-text {
|
||||
color: var(--l2-foreground);
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,15 +65,38 @@ export default function CeleryTaskDetail({
|
||||
width="45%"
|
||||
title={
|
||||
<div>
|
||||
<Typography.Text className="title">{`Details - ${taskData.entity}`}</Typography.Text>
|
||||
<Typography
|
||||
variant="label"
|
||||
size="large"
|
||||
weight="bold"
|
||||
color="main"
|
||||
as="span"
|
||||
textOverflow="wrap"
|
||||
>{`Details - ${taskData.entity}`}</Typography>
|
||||
<div>
|
||||
<Typography.Text className="subtitle">
|
||||
<Typography
|
||||
variant="paragraph"
|
||||
size="small"
|
||||
weight="regular"
|
||||
color="secondary"
|
||||
as="span"
|
||||
textOverflow="wrap"
|
||||
>
|
||||
{`${formatTimestamp(startTime)} ${
|
||||
endTime ? `- ${formatTimestamp(endTime)}` : ''
|
||||
}`}
|
||||
</Typography.Text>
|
||||
<Divider type="vertical" />
|
||||
<Typography.Text className="subtitle">{taskData.value}</Typography.Text>
|
||||
</Typography>
|
||||
<Divider orientation="vertical" />
|
||||
<Typography
|
||||
variant="paragraph"
|
||||
size="small"
|
||||
weight="regular"
|
||||
color="secondary"
|
||||
as="span"
|
||||
textOverflow="wrap"
|
||||
>
|
||||
{taskData.value}
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@@ -88,7 +111,14 @@ export default function CeleryTaskDetail({
|
||||
destroyOnClose
|
||||
closeIcon={<X size={16} style={{ marginTop: Spacing.MARGIN_1 }} />}
|
||||
footer={
|
||||
<Typography.Text className="footer-text">{`Total Task: ${totalTask}`}</Typography.Text>
|
||||
<Typography
|
||||
variant="paragraph"
|
||||
size="base"
|
||||
weight="regular"
|
||||
color="secondary"
|
||||
as="span"
|
||||
textOverflow="wrap"
|
||||
>{`Total Task: ${totalTask}`}</Typography>
|
||||
}
|
||||
>
|
||||
<CeleryTaskGraph
|
||||
|
||||
@@ -52,11 +52,6 @@
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
.celery-task-graph-worker-count-text {
|
||||
font-size: 2.5vw;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.worker-count-header {
|
||||
@@ -148,8 +143,6 @@
|
||||
|
||||
.configure-option-Info-text {
|
||||
color: var(--l2-foreground);
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,7 +210,6 @@
|
||||
}
|
||||
|
||||
&__label {
|
||||
font-family: 'Inter';
|
||||
font-size: 14px;
|
||||
color: var(--l2-foreground);
|
||||
line-height: 20px;
|
||||
@@ -225,7 +217,7 @@
|
||||
}
|
||||
|
||||
&__value {
|
||||
font-family: 'Geist Mono';
|
||||
font-family: var(--code-base-400-font-family);
|
||||
font-size: 24px;
|
||||
color: var(--l1-foreground);
|
||||
line-height: 32px;
|
||||
|
||||
@@ -113,9 +113,16 @@ export default function CeleryTaskGraphGrid({
|
||||
<CardContainer className="row-card" isDarkMode={isDarkMode}>
|
||||
<div className="row-panel">
|
||||
<div className="row-panel-section">
|
||||
<Typography.Text className="section-title">
|
||||
<Typography
|
||||
variant="paragraph"
|
||||
size="base"
|
||||
weight="regular"
|
||||
color="secondary"
|
||||
as="span"
|
||||
textOverflow="wrap"
|
||||
>
|
||||
Flower Metrics
|
||||
</Typography.Text>
|
||||
</Typography>
|
||||
{collapsedSections.metricBasedGraphs ? (
|
||||
<ChevronDown
|
||||
size={14}
|
||||
@@ -146,14 +153,30 @@ export default function CeleryTaskGraphGrid({
|
||||
/>
|
||||
<Card className="celery-task-graph-worker-count">
|
||||
<div className="worker-count-header">
|
||||
<Typography.Text className="worker-count-header-text">
|
||||
<Typography
|
||||
variant="paragraph"
|
||||
size="base"
|
||||
weight="regular"
|
||||
color="inherit"
|
||||
as="span"
|
||||
textOverflow="wrap"
|
||||
>
|
||||
Worker Online
|
||||
</Typography.Text>
|
||||
</Typography>
|
||||
</div>
|
||||
<div className="worker-count-text-container">
|
||||
<Typography.Text className="celery-task-graph-worker-count-text">
|
||||
<Typography
|
||||
/* TODO(typography): not lifted from className: font-size: 2.5vw */ variant="paragraph"
|
||||
size="base"
|
||||
weight="regular"
|
||||
color="inherit"
|
||||
as="span"
|
||||
display="inline-block"
|
||||
align="center"
|
||||
textOverflow="wrap"
|
||||
>
|
||||
{options.filter((option) => option.value).length}
|
||||
</Typography.Text>
|
||||
</Typography>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
@@ -164,9 +187,16 @@ export default function CeleryTaskGraphGrid({
|
||||
<CardContainer className="row-card" isDarkMode={isDarkMode}>
|
||||
<div className="row-panel">
|
||||
<div className="row-panel-section">
|
||||
<Typography.Text className="section-title">
|
||||
<Typography
|
||||
variant="paragraph"
|
||||
size="base"
|
||||
weight="regular"
|
||||
color="secondary"
|
||||
as="span"
|
||||
textOverflow="wrap"
|
||||
>
|
||||
Span Based Stats
|
||||
</Typography.Text>
|
||||
</Typography>
|
||||
{collapsedSections.traceBasedGraphs ? (
|
||||
<ChevronDown
|
||||
size={14}
|
||||
@@ -185,9 +215,16 @@ export default function CeleryTaskGraphGrid({
|
||||
</CardContainer>
|
||||
<div className="configure-option-Info">
|
||||
{configureOptionComponent}
|
||||
<Typography.Text className="configure-option-Info-text">
|
||||
<Typography
|
||||
variant="paragraph"
|
||||
size="small"
|
||||
weight="regular"
|
||||
color="secondary"
|
||||
as="span"
|
||||
textOverflow="wrap"
|
||||
>
|
||||
Click on a graph co-ordinate to see more details
|
||||
</Typography.Text>
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
{!collapsedSections.traceBasedGraphs && (
|
||||
|
||||
@@ -11,9 +11,7 @@
|
||||
justify-content: center;
|
||||
}
|
||||
&__label {
|
||||
font-family: 'Geist Mono';
|
||||
font-size: 12px;
|
||||
line-height: normal;
|
||||
font-family: var(--code-base-400-font-family);
|
||||
}
|
||||
&--positive {
|
||||
.change-percentage-pill {
|
||||
|
||||
@@ -17,10 +17,6 @@
|
||||
color: var(--l2-foreground);
|
||||
|
||||
/* Bifrost (Ancient)/Content/sm */
|
||||
font-family: Inter;
|
||||
font-size: 13px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 20px; /* 142.857% */
|
||||
letter-spacing: -0.07px;
|
||||
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
|
||||
pre {
|
||||
color: var(--l2-foreground) !important;
|
||||
font-family: 'Geist Mono' !important;
|
||||
font-family: var(--code-small-400-font-family) !important;
|
||||
font-size: 12px !important;
|
||||
}
|
||||
|
||||
code {
|
||||
color: var(--l1-foreground) !important;
|
||||
font-family: 'Geist Mono' !important;
|
||||
font-family: var(--code-small-400-font-family) !important;
|
||||
font-size: 12px !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,36 +1,3 @@
|
||||
.create-sa-modal {
|
||||
max-width: 530px;
|
||||
background: var(--popover);
|
||||
border: 1px solid var(--secondary);
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 4px 9px 0 rgba(0, 0, 0, 0.04);
|
||||
|
||||
[data-slot='dialog-header'] {
|
||||
padding: var(--padding-4);
|
||||
border-bottom: 1px solid var(--secondary);
|
||||
flex-shrink: 0;
|
||||
background: transparent;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
[data-slot='dialog-title'] {
|
||||
font-size: var(--label-base-400-font-size);
|
||||
font-weight: var(--label-base-400-font-weight);
|
||||
line-height: var(--label-base-400-line-height);
|
||||
letter-spacing: -0.065px;
|
||||
color: var(--l1-foreground);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
[data-slot='dialog-description'] {
|
||||
padding: 0;
|
||||
|
||||
.create-sa-modal__content {
|
||||
padding: var(--padding-4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.create-sa-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -43,32 +10,11 @@
|
||||
margin-bottom: var(--spacing-4);
|
||||
|
||||
> label {
|
||||
font-size: var(--paragraph-base-400-font-size);
|
||||
font-weight: var(--paragraph-base-400-font-weight);
|
||||
color: var(--foreground);
|
||||
letter-spacing: -0.07px;
|
||||
}
|
||||
}
|
||||
|
||||
&__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);
|
||||
@@ -77,22 +23,7 @@
|
||||
}
|
||||
|
||||
&__helper {
|
||||
font-size: var(--paragraph-small-400-font-size);
|
||||
color: var(--l3-foreground);
|
||||
margin: calc(var(--spacing-2) * -1) 0 var(--spacing-4) 0;
|
||||
line-height: var(--paragraph-small-400-line-height);
|
||||
}
|
||||
}
|
||||
|
||||
.create-sa-modal__footer {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
padding: 0 var(--padding-4);
|
||||
height: 56px;
|
||||
min-height: 56px;
|
||||
border-top: 1px solid var(--secondary);
|
||||
gap: var(--spacing-4);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
@@ -5,9 +5,9 @@ import { Button } from '@signozhq/ui/button';
|
||||
import AuthZButton from 'lib/authz/components/AuthZButton/AuthZButton';
|
||||
import { AuthZGuardContent } from 'lib/authz/components/AuthZGuard/AuthZGuardContent';
|
||||
import { SACreatePermission } from 'lib/authz/hooks/useAuthZ/permissions/service-account.permissions';
|
||||
import { DialogFooter, DialogWrapper } from '@signozhq/ui/dialog';
|
||||
import { Dialog } 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,
|
||||
@@ -35,7 +35,7 @@ function CreateServiceAccountModal(): JSX.Element {
|
||||
);
|
||||
const [, setSelectedAccountId] = useQueryState(SA_QUERY_PARAMS.ACCOUNT);
|
||||
|
||||
const { showErrorModal, isErrorModalVisible } = useErrorModal();
|
||||
const { showErrorModal } = useErrorModal();
|
||||
|
||||
const {
|
||||
control,
|
||||
@@ -82,84 +82,75 @@ function CreateServiceAccountModal(): JSX.Element {
|
||||
}
|
||||
|
||||
return (
|
||||
<DialogWrapper
|
||||
<Dialog
|
||||
title="New Service Account"
|
||||
open={isOpen}
|
||||
onOpenChange={(open): void => {
|
||||
if (!open) {
|
||||
handleClose();
|
||||
}
|
||||
}}
|
||||
showCloseButton
|
||||
width="narrow"
|
||||
className="create-sa-modal"
|
||||
disableOutsideClick={isErrorModalVisible}
|
||||
onClose={handleClose}
|
||||
width="base"
|
||||
testId="create-service-account-modal"
|
||||
>
|
||||
<div className="create-sa-modal__content">
|
||||
<AuthZGuardContent
|
||||
checks={[SACreatePermission]}
|
||||
fallbackOnLoading={<Skeleton active paragraph={{ rows: 1 }} />}
|
||||
>
|
||||
<form
|
||||
id="create-sa-form"
|
||||
className="create-sa-form"
|
||||
onSubmit={handleSubmit(handleCreate)}
|
||||
footerSuffix={
|
||||
<>
|
||||
<Button
|
||||
size="md"
|
||||
type="button"
|
||||
variant="solid"
|
||||
color="secondary"
|
||||
onClick={handleClose}
|
||||
testId="create-sa-cancel-btn"
|
||||
prefix={<X size={12} />}
|
||||
>
|
||||
<div className="create-sa-form__item">
|
||||
<label htmlFor="sa-name">Name</label>
|
||||
<Controller
|
||||
name="name"
|
||||
control={control}
|
||||
rules={{ required: 'Name is required' }}
|
||||
render={({ field }): 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"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
{errors.name && (
|
||||
<p className="create-sa-form__error">{errors.name.message}</p>
|
||||
Cancel
|
||||
</Button>
|
||||
|
||||
<AuthZButton
|
||||
size="md"
|
||||
checks={[SACreatePermission]}
|
||||
type="submit"
|
||||
form="create-sa-form"
|
||||
variant="solid"
|
||||
color="primary"
|
||||
loading={isSubmitting}
|
||||
disabled={!isValid}
|
||||
testId="create-sa-submit-btn"
|
||||
>
|
||||
Create Service Account
|
||||
</AuthZButton>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<AuthZGuardContent
|
||||
checks={[SACreatePermission]}
|
||||
fallbackOnLoading={<Skeleton active paragraph={{ rows: 1 }} />}
|
||||
>
|
||||
<form
|
||||
id="create-sa-form"
|
||||
className="create-sa-form"
|
||||
onSubmit={handleSubmit(handleCreate)}
|
||||
>
|
||||
<div className="create-sa-form__item">
|
||||
<label htmlFor="sa-name">Name</label>
|
||||
<Controller
|
||||
name="name"
|
||||
control={control}
|
||||
rules={{ required: 'Name is required' }}
|
||||
render={({ field }): JSX.Element => (
|
||||
<Input
|
||||
id="sa-name"
|
||||
placeholder="Enter a name"
|
||||
value={field.value}
|
||||
onChange={field.onChange}
|
||||
onBlur={field.onBlur}
|
||||
testId="create-sa-name-input"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</form>
|
||||
</AuthZGuardContent>
|
||||
</div>
|
||||
|
||||
<DialogFooter className="create-sa-modal__footer">
|
||||
<Button
|
||||
size="md"
|
||||
type="button"
|
||||
variant="solid"
|
||||
color="secondary"
|
||||
onClick={handleClose}
|
||||
testId="create-sa-cancel-btn"
|
||||
prefix={<X size={12} />}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
|
||||
<AuthZButton
|
||||
size="md"
|
||||
checks={[SACreatePermission]}
|
||||
type="submit"
|
||||
form="create-sa-form"
|
||||
variant="solid"
|
||||
color="primary"
|
||||
loading={isSubmitting}
|
||||
disabled={!isValid}
|
||||
testId="create-sa-submit-btn"
|
||||
>
|
||||
Create Service Account
|
||||
</AuthZButton>
|
||||
</DialogFooter>
|
||||
</DialogWrapper>
|
||||
/>
|
||||
{errors.name && (
|
||||
<p className="create-sa-form__error">{errors.name.message}</p>
|
||||
)}
|
||||
</div>
|
||||
</form>
|
||||
</AuthZGuardContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
@@ -23,7 +23,6 @@ jest.mock('providers/ErrorModalProvider', () => ({
|
||||
...jest.requireActual('providers/ErrorModalProvider'),
|
||||
useErrorModal: jest.fn(() => ({
|
||||
showErrorModal,
|
||||
isErrorModalVisible: false,
|
||||
})),
|
||||
}));
|
||||
|
||||
@@ -58,7 +57,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 +71,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 +89,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 +127,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 +159,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 +208,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 +245,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);
|
||||
|
||||
|
||||
@@ -65,8 +65,6 @@
|
||||
.valid-format-error {
|
||||
margin-top: 4px;
|
||||
color: var(--bg-cherry-400) !important;
|
||||
font-size: 13px !important;
|
||||
font-weight: 400 !important;
|
||||
}
|
||||
|
||||
.info-text {
|
||||
@@ -76,11 +74,6 @@
|
||||
padding: 4px;
|
||||
cursor: default;
|
||||
color: var(--l2-foreground) !important;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: normal;
|
||||
letter-spacing: 0.14px;
|
||||
}
|
||||
|
||||
.info-text:hover {
|
||||
@@ -104,15 +97,11 @@
|
||||
gap: 6px;
|
||||
|
||||
.timezone__name {
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
color: var(--bg-robin-400);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.timezone__separator {
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
color: var(--bg-robin-300);
|
||||
font-weight: 500;
|
||||
}
|
||||
@@ -228,10 +217,6 @@
|
||||
border-radius: 2px;
|
||||
background: color-mix(in srgb, var(--bg-robin-200) 4%, transparent);
|
||||
color: var(--l1-foreground);
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
line-height: 16px;
|
||||
letter-spacing: -0.06px;
|
||||
cursor: pointer;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
|
||||
@@ -531,7 +531,7 @@ function CustomTimePicker({
|
||||
// Use setTimeout to wait for React to update the DOM and make input editable
|
||||
setTimeout(() => {
|
||||
const inputElement = inputRef.current?.input;
|
||||
if (inputElement) {
|
||||
if (inputElement && !document.activeElement?.closest('.timezone-picker')) {
|
||||
inputElement.focus();
|
||||
inputElement.select();
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ $item-spacing: 8px;
|
||||
.timezone-picker {
|
||||
width: 532px;
|
||||
color: var(--l2-foreground);
|
||||
font-family: $font-family;
|
||||
|
||||
&__search {
|
||||
@include flex-center;
|
||||
@@ -36,34 +35,6 @@ $item-spacing: 8px;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
&__esc-key {
|
||||
@include text-style-base;
|
||||
font-size: 8px;
|
||||
|
||||
@@ -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)}
|
||||
|
||||
@@ -12,5 +12,4 @@
|
||||
|
||||
.name {
|
||||
color: var(--danger-background);
|
||||
font-weight: var(--font-weight-medium);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
.details-header {
|
||||
// ghost + secondary missing hover bg token in @signozhq/button
|
||||
--button-ghost-hover-background: var(--l3-background);
|
||||
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -12,7 +9,6 @@
|
||||
background: var(--l2-background);
|
||||
|
||||
&__title {
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: var(--l1-foreground);
|
||||
flex: 1;
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
.drawer {
|
||||
overflow: hidden;
|
||||
border-color: var(--l2-border);
|
||||
background: var(--l2-background);
|
||||
box-shadow: -4px 10px 16px 2px var(--l1-shadow);
|
||||
|
||||
&:focus,
|
||||
&:focus-visible {
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
.body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
@@ -1,56 +1,49 @@
|
||||
import { Drawer, DrawerContent } from '@signozhq/ui/drawer';
|
||||
import cx from 'classnames';
|
||||
import { AnimatePresence } from 'motion/react';
|
||||
|
||||
import styles from './DetailsPanelDrawer.module.scss';
|
||||
import { ReactNode } from 'react';
|
||||
import { Drawer } from '@signozhq/ui/drawer';
|
||||
|
||||
interface DetailsPanelDrawerProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
title: string;
|
||||
children: ReactNode;
|
||||
headerPrefix?: ReactNode;
|
||||
headerSuffix?: ReactNode;
|
||||
loading?: boolean;
|
||||
testId?: string;
|
||||
}
|
||||
|
||||
function DetailsPanelDrawer({
|
||||
isOpen,
|
||||
onClose,
|
||||
title,
|
||||
children,
|
||||
className,
|
||||
headerPrefix,
|
||||
headerSuffix,
|
||||
loading,
|
||||
testId,
|
||||
}: DetailsPanelDrawerProps): JSX.Element {
|
||||
return (
|
||||
<Drawer
|
||||
type="inspector"
|
||||
stack="overlay"
|
||||
open={isOpen}
|
||||
onOpenChange={(open): void => {
|
||||
if (!open) {
|
||||
onClose();
|
||||
}
|
||||
}}
|
||||
modal={false}
|
||||
onClose={onClose}
|
||||
title={title}
|
||||
width="extra-wide"
|
||||
headerPrefix={headerPrefix}
|
||||
headerSuffix={headerSuffix}
|
||||
loading={loading}
|
||||
testId={testId}
|
||||
>
|
||||
<AnimatePresence>
|
||||
{isOpen && (
|
||||
<DrawerContent
|
||||
forceMount
|
||||
direction="right"
|
||||
width="extra-wide"
|
||||
showOverlay={false}
|
||||
// Outside presses are left to the page, which knows what counts as inside.
|
||||
onInteractOutside={(e): void => e.preventDefault()}
|
||||
className={cx(styles.drawer, className)}
|
||||
testId={testId}
|
||||
>
|
||||
<div className={styles.body}>{children}</div>
|
||||
</DrawerContent>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
{children}
|
||||
</Drawer>
|
||||
);
|
||||
}
|
||||
|
||||
DetailsPanelDrawer.defaultProps = {
|
||||
className: undefined,
|
||||
headerPrefix: undefined,
|
||||
headerSuffix: undefined,
|
||||
loading: false,
|
||||
testId: undefined,
|
||||
};
|
||||
|
||||
|
||||
@@ -68,7 +68,17 @@ export default function DownloadOptionsMenu({
|
||||
aria-modal="true"
|
||||
>
|
||||
<div className="export-format">
|
||||
<Typography.Text className="title">FORMAT</Typography.Text>
|
||||
<Typography
|
||||
variant="uppercase"
|
||||
size="small"
|
||||
weight="medium"
|
||||
color="main"
|
||||
as="p"
|
||||
marginBottom={4}
|
||||
textOverflow="wrap"
|
||||
>
|
||||
FORMAT
|
||||
</Typography>
|
||||
<RadioGroup
|
||||
color="primary"
|
||||
value={exportFormat}
|
||||
@@ -83,7 +93,17 @@ export default function DownloadOptionsMenu({
|
||||
<div className="horizontal-line" />
|
||||
|
||||
<div className="row-limit">
|
||||
<Typography.Text className="title">Number of Rows</Typography.Text>
|
||||
<Typography
|
||||
variant="uppercase"
|
||||
size="small"
|
||||
weight="medium"
|
||||
color="main"
|
||||
as="p"
|
||||
marginBottom={4}
|
||||
textOverflow="wrap"
|
||||
>
|
||||
Number of Rows
|
||||
</Typography>
|
||||
<RadioGroup
|
||||
color="primary"
|
||||
value={String(rowLimit)}
|
||||
@@ -101,7 +121,17 @@ export default function DownloadOptionsMenu({
|
||||
<div className="horizontal-line" />
|
||||
|
||||
<div className="columns-scope">
|
||||
<Typography.Text className="title">Columns</Typography.Text>
|
||||
<Typography
|
||||
variant="uppercase"
|
||||
size="small"
|
||||
weight="medium"
|
||||
color="main"
|
||||
as="p"
|
||||
marginBottom={4}
|
||||
textOverflow="wrap"
|
||||
>
|
||||
Columns
|
||||
</Typography>
|
||||
<RadioGroup
|
||||
color="primary"
|
||||
value={columnsScope}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Trash2, X } from '@signozhq/icons';
|
||||
import { Button } from '@signozhq/ui/button';
|
||||
import { DialogWrapper } from '@signozhq/ui/dialog';
|
||||
import { AlertDialog } from '@signozhq/ui/alert-dialog';
|
||||
import { MemberRow } from 'components/MembersTable/MembersTable';
|
||||
|
||||
interface DeleteMemberDialogProps {
|
||||
@@ -9,7 +8,7 @@ interface DeleteMemberDialogProps {
|
||||
member: MemberRow | null;
|
||||
isDeleting: boolean;
|
||||
onClose: () => void;
|
||||
onConfirm: () => void;
|
||||
onConfirm: () => Promise<void>;
|
||||
}
|
||||
|
||||
function DeleteMemberDialog({
|
||||
@@ -36,47 +35,20 @@ function DeleteMemberDialog({
|
||||
</>
|
||||
);
|
||||
|
||||
const footer = (
|
||||
<>
|
||||
<Button
|
||||
size="md"
|
||||
variant="solid"
|
||||
color="secondary"
|
||||
onClick={onClose}
|
||||
prefix={<X size={12} />}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
size="md"
|
||||
variant="solid"
|
||||
color="danger"
|
||||
onClick={onConfirm}
|
||||
testId="member-delete-confirm-btn"
|
||||
loading={isDeleting}
|
||||
prefix={<Trash2 size={12} />}
|
||||
>
|
||||
{isDeleting ? 'Processing...' : title}
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
|
||||
return (
|
||||
<DialogWrapper
|
||||
<AlertDialog
|
||||
open={open}
|
||||
onOpenChange={(isOpen): void => {
|
||||
if (!isOpen) {
|
||||
onClose();
|
||||
}
|
||||
}}
|
||||
title={title}
|
||||
width="narrow"
|
||||
showCloseButton={false}
|
||||
disableOutsideClick={false}
|
||||
footer={footer}
|
||||
color="danger"
|
||||
confirmText={isDeleting ? 'Processing...' : title}
|
||||
confirmIcon={<Trash2 size={12} />}
|
||||
cancelIcon={<X size={12} />}
|
||||
onConfirm={onConfirm}
|
||||
onCancel={onClose}
|
||||
testId="member-delete"
|
||||
>
|
||||
{body}
|
||||
</DialogWrapper>
|
||||
</AlertDialog>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -20,10 +20,9 @@
|
||||
}
|
||||
|
||||
&__label {
|
||||
font-size: var(--font-size-sm);
|
||||
font-weight: var(--font-weight-normal);
|
||||
font-size: var(--paragraph-base-400-font-size);
|
||||
color: var(--foreground);
|
||||
line-height: var(--line-height-20);
|
||||
line-height: var(--paragraph-base-400-line-height);
|
||||
letter-spacing: -0.07px;
|
||||
cursor: default;
|
||||
}
|
||||
@@ -53,10 +52,8 @@
|
||||
}
|
||||
|
||||
&__email-text {
|
||||
font-size: var(--paragraph-base-400-font-size);
|
||||
font-weight: var(--paragraph-base-400-font-weight);
|
||||
color: var(--foreground);
|
||||
line-height: var(--line-height-18);
|
||||
line-height: var(--paragraph-small-400-line-height);
|
||||
letter-spacing: -0.07px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
@@ -86,41 +83,26 @@
|
||||
[data-slot='badge'] {
|
||||
padding: var(--padding-1) var(--padding-2);
|
||||
align-items: center;
|
||||
font-size: var(--uppercase-small-500-font-size);
|
||||
font-weight: var(--uppercase-small-500-font-weight);
|
||||
line-height: 100%;
|
||||
letter-spacing: 0.44px;
|
||||
}
|
||||
}
|
||||
|
||||
&__meta-label {
|
||||
font-size: var(--font-size-xs);
|
||||
font-weight: var(--font-weight-medium);
|
||||
font-size: var(--uppercase-small-500-font-size);
|
||||
font-weight: var(--uppercase-small-500-font-weight);
|
||||
color: var(--foreground);
|
||||
line-height: var(--line-height-20);
|
||||
line-height: var(--uppercase-small-500-line-height);
|
||||
letter-spacing: 0.48px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
&__footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&__footer-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-4);
|
||||
}
|
||||
|
||||
&__footer-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-6);
|
||||
}
|
||||
|
||||
&__footer-divider {
|
||||
width: 1px;
|
||||
height: 21px;
|
||||
@@ -163,14 +145,6 @@
|
||||
}
|
||||
|
||||
.reset-link-dialog {
|
||||
background: var(--l2-background);
|
||||
border: 1px solid var(--l1-border);
|
||||
|
||||
[data-slot='dialog-header'] {
|
||||
border-color: var(--l1-border);
|
||||
color: var(--l1-foreground);
|
||||
}
|
||||
|
||||
&__content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -178,8 +152,6 @@
|
||||
}
|
||||
|
||||
&__description {
|
||||
font-size: var(--paragraph-base-400-font-size);
|
||||
font-weight: var(--paragraph-base-400-font-weight);
|
||||
color: var(--l2-foreground);
|
||||
line-height: var(--paragraph-base-400-line-height);
|
||||
letter-spacing: -0.065px;
|
||||
@@ -211,10 +183,8 @@
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
padding: 0 var(--padding-2);
|
||||
font-size: var(--paragraph-base-400-font-size);
|
||||
font-weight: var(--paragraph-base-400-font-weight);
|
||||
color: var(--l2-foreground);
|
||||
line-height: var(--line-height-18);
|
||||
line-height: var(--paragraph-small-400-line-height);
|
||||
letter-spacing: -0.07px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,9 +3,9 @@ import { useCopyToClipboard } from 'react-use';
|
||||
import { LockKeyhole, RefreshCw, Trash2, X } from '@signozhq/icons';
|
||||
import { Badge } from '@signozhq/ui/badge';
|
||||
import { Button } from '@signozhq/ui/button';
|
||||
import { DrawerWrapper } from '@signozhq/ui/drawer';
|
||||
import { Drawer } 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';
|
||||
@@ -210,7 +210,7 @@ function EditMemberDrawer({
|
||||
const { mutateAsync: updateMyUser } = useUpdateMyUserV2();
|
||||
const { mutateAsync: updateUser } = useUpdateUser();
|
||||
|
||||
const { mutate: deleteUser, isLoading: isDeleting } = useDeleteUser({
|
||||
const { mutateAsync: deleteUser, isLoading: isDeleting } = useDeleteUser({
|
||||
mutation: {
|
||||
onSuccess: (): void => {
|
||||
toast.success(
|
||||
@@ -378,11 +378,15 @@ function EditMemberDrawer({
|
||||
onComplete,
|
||||
]);
|
||||
|
||||
const handleDelete = useCallback((): void => {
|
||||
const handleDelete = useCallback(async (): Promise<void> => {
|
||||
if (!member) {
|
||||
return;
|
||||
}
|
||||
deleteUser({ pathParams: { id: member.id } });
|
||||
try {
|
||||
await deleteUser({ pathParams: { id: member.id } });
|
||||
} catch {
|
||||
// `onError` already shows the error modal, and the confirmation stays open.
|
||||
}
|
||||
}, [member, deleteUser]);
|
||||
|
||||
const handleGenerateResetLink = useCallback(async (): Promise<void> => {
|
||||
@@ -409,7 +413,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 +443,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 +476,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">
|
||||
@@ -615,96 +619,88 @@ function EditMemberDrawer({
|
||||
</div>
|
||||
);
|
||||
|
||||
const footer = (
|
||||
<div className="edit-member-drawer__footer">
|
||||
{!isDeleted && (
|
||||
<>
|
||||
<div className="edit-member-drawer__footer-left">
|
||||
<Button
|
||||
disabledTooltip={getDeleteTooltip(isRootUser, isSelf)}
|
||||
size="md"
|
||||
onClick={(): void => setShowDeleteConfirm(true)}
|
||||
testId="member-delete-btn"
|
||||
disabled={isRootUser || isSelf}
|
||||
variant="link"
|
||||
color="danger"
|
||||
prefix={<Trash2 size={12} />}
|
||||
>
|
||||
{isInvited ? 'Revoke Invite' : 'Delete Member'}
|
||||
</Button>
|
||||
const footerStart = isDeleted ? null : (
|
||||
<div className="edit-member-drawer__footer-left">
|
||||
<Button
|
||||
disabledTooltip={getDeleteTooltip(isRootUser, isSelf)}
|
||||
size="md"
|
||||
onClick={(): void => setShowDeleteConfirm(true)}
|
||||
testId="member-delete-btn"
|
||||
disabled={isRootUser || isSelf}
|
||||
variant="link"
|
||||
color="danger"
|
||||
prefix={<Trash2 size={12} />}
|
||||
>
|
||||
{isInvited ? 'Revoke Invite' : 'Delete Member'}
|
||||
</Button>
|
||||
|
||||
<div className="edit-member-drawer__footer-divider" />
|
||||
<Button
|
||||
disabledTooltip={ROOT_USER_TOOLTIP}
|
||||
size="md"
|
||||
onClick={handleGenerateResetLink}
|
||||
testId="member-reset-link-btn"
|
||||
disabled={isRootUser}
|
||||
loading={isGeneratingLink || isLoadingTokenStatus}
|
||||
variant="link"
|
||||
color="warning"
|
||||
prefix={<RefreshCw size={12} />}
|
||||
>
|
||||
{isGeneratingLink
|
||||
? 'Generating...'
|
||||
: isInvited
|
||||
? getInviteButtonLabel(
|
||||
isLoadingTokenStatus,
|
||||
existingToken,
|
||||
isTokenExpired,
|
||||
tokenNotFound,
|
||||
)
|
||||
: 'Generate Password Reset Link'}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="edit-member-drawer__footer-right">
|
||||
<Button
|
||||
size="md"
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
onClick={handleClose}
|
||||
prefix={<X size={14} />}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
disabledTooltip={isRootUser ? ROOT_USER_TOOLTIP : 'No changes to save'}
|
||||
size="md"
|
||||
variant="solid"
|
||||
color="primary"
|
||||
disabled={!isDirty || isRootUser}
|
||||
onClick={handleSave}
|
||||
testId="member-save-btn"
|
||||
loading={isSaving}
|
||||
>
|
||||
{isSaving ? 'Saving...' : 'Save Member Details'}
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<div className="edit-member-drawer__footer-divider" />
|
||||
<Button
|
||||
disabledTooltip={ROOT_USER_TOOLTIP}
|
||||
size="md"
|
||||
onClick={handleGenerateResetLink}
|
||||
testId="member-reset-link-btn"
|
||||
disabled={isRootUser}
|
||||
loading={isGeneratingLink || isLoadingTokenStatus}
|
||||
variant="link"
|
||||
color="warning"
|
||||
prefix={<RefreshCw size={12} />}
|
||||
>
|
||||
{isGeneratingLink
|
||||
? 'Generating...'
|
||||
: isInvited
|
||||
? getInviteButtonLabel(
|
||||
isLoadingTokenStatus,
|
||||
existingToken,
|
||||
isTokenExpired,
|
||||
tokenNotFound,
|
||||
)
|
||||
: 'Generate Password Reset Link'}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
|
||||
const footerActions = isDeleted ? null : (
|
||||
<>
|
||||
<Button
|
||||
size="md"
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
onClick={handleClose}
|
||||
prefix={<X size={14} />}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
disabledTooltip={isRootUser ? ROOT_USER_TOOLTIP : 'No changes to save'}
|
||||
size="md"
|
||||
variant="solid"
|
||||
color="primary"
|
||||
disabled={!isDirty || isRootUser}
|
||||
onClick={handleSave}
|
||||
testId="member-save-btn"
|
||||
loading={isSaving}
|
||||
>
|
||||
{isSaving ? 'Saving...' : 'Save Member Details'}
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<DrawerWrapper
|
||||
<Drawer
|
||||
type="task"
|
||||
stack="overlay"
|
||||
open={open}
|
||||
onOpenChange={(isOpen): void => {
|
||||
if (!isOpen) {
|
||||
handleClose();
|
||||
}
|
||||
}}
|
||||
direction="right"
|
||||
showCloseButton
|
||||
showOverlay={false}
|
||||
onClose={handleClose}
|
||||
title="Member Details"
|
||||
footer={footer}
|
||||
width="wide"
|
||||
footerPrefix={footerStart}
|
||||
footerSuffix={footerActions}
|
||||
>
|
||||
{drawerContent}
|
||||
</DrawerWrapper>
|
||||
</Drawer>
|
||||
|
||||
<ResetLinkDialog
|
||||
open={showResetLinkDialog}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Check, Copy } from '@signozhq/icons';
|
||||
import { Button } from '@signozhq/ui/button';
|
||||
import { DialogWrapper } from '@signozhq/ui/dialog';
|
||||
import { Dialog } from '@signozhq/ui/dialog';
|
||||
|
||||
interface ResetLinkDialogProps {
|
||||
open: boolean;
|
||||
@@ -22,17 +22,11 @@ function ResetLinkDialog({
|
||||
onCopy,
|
||||
}: ResetLinkDialogProps): JSX.Element {
|
||||
return (
|
||||
<DialogWrapper
|
||||
<Dialog
|
||||
open={open}
|
||||
onOpenChange={(isOpen): void => {
|
||||
if (!isOpen) {
|
||||
onClose();
|
||||
}
|
||||
}}
|
||||
onClose={onClose}
|
||||
title={linkType === 'invite' ? 'Invite Link' : 'Password Reset Link'}
|
||||
showCloseButton
|
||||
width="base"
|
||||
className="reset-link-dialog"
|
||||
>
|
||||
<div className="reset-link-dialog__content">
|
||||
<p className="reset-link-dialog__description">
|
||||
@@ -65,7 +59,7 @@ function ResetLinkDialog({
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</DialogWrapper>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
@@ -41,52 +41,31 @@ jest.mock('api/ErrorResponseHandlerForGeneratedAPIs', () => ({
|
||||
|
||||
jest.mock('@signozhq/ui/drawer', () => ({
|
||||
...jest.requireActual('@signozhq/ui/drawer'),
|
||||
DrawerWrapper: ({
|
||||
Drawer: ({
|
||||
children,
|
||||
footer,
|
||||
footerPrefix,
|
||||
footerSuffix,
|
||||
open,
|
||||
}: {
|
||||
children?: ReactNode;
|
||||
footer?: ReactNode;
|
||||
footerPrefix?: ReactNode;
|
||||
footerSuffix?: ReactNode;
|
||||
open: boolean;
|
||||
}): JSX.Element | null =>
|
||||
open ? (
|
||||
<div>
|
||||
{children}
|
||||
{footer}
|
||||
{footerPrefix}
|
||||
{footerSuffix}
|
||||
</div>
|
||||
) : null,
|
||||
}));
|
||||
|
||||
jest.mock('@signozhq/ui/dialog', () => ({
|
||||
...jest.requireActual('@signozhq/ui/dialog'),
|
||||
DialogWrapper: ({
|
||||
children,
|
||||
footer,
|
||||
open,
|
||||
title,
|
||||
}: {
|
||||
children?: ReactNode;
|
||||
footer?: ReactNode;
|
||||
open: boolean;
|
||||
title?: string;
|
||||
}): JSX.Element | null =>
|
||||
open ? (
|
||||
<div role="dialog" aria-label={title}>
|
||||
{children}
|
||||
{footer}
|
||||
</div>
|
||||
) : null,
|
||||
DialogFooter: ({ children }: { children?: ReactNode }): JSX.Element => (
|
||||
<div>{children}</div>
|
||||
),
|
||||
}));
|
||||
|
||||
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(),
|
||||
},
|
||||
}));
|
||||
|
||||
@@ -209,7 +188,7 @@ describe('EditMemberDrawer', () => {
|
||||
isLoading: false,
|
||||
});
|
||||
(useDeleteUser as jest.Mock).mockReturnValue({
|
||||
mutate: mockDeleteMutate,
|
||||
mutateAsync: mockDeleteMutate,
|
||||
isLoading: false,
|
||||
});
|
||||
// Token query: valid token for invited members
|
||||
@@ -373,7 +352,7 @@ describe('EditMemberDrawer', () => {
|
||||
const user = userEvent.setup({ pointerEventsCheck: 0 });
|
||||
|
||||
(useDeleteUser as jest.Mock).mockImplementation((options) => ({
|
||||
mutate: mockDeleteMutate.mockImplementation(() => {
|
||||
mutateAsync: mockDeleteMutate.mockImplementation(() => {
|
||||
options?.mutation?.onSuccess?.();
|
||||
}),
|
||||
isLoading: false,
|
||||
@@ -453,7 +432,7 @@ describe('EditMemberDrawer', () => {
|
||||
const user = userEvent.setup({ pointerEventsCheck: 0 });
|
||||
|
||||
(useDeleteUser as jest.Mock).mockImplementation((options) => ({
|
||||
mutate: mockDeleteMutate.mockImplementation(() => {
|
||||
mutateAsync: mockDeleteMutate.mockImplementation(() => {
|
||||
options?.mutation?.onSuccess?.();
|
||||
}),
|
||||
isLoading: false,
|
||||
@@ -567,7 +546,7 @@ describe('EditMemberDrawer', () => {
|
||||
const user = userEvent.setup({ pointerEventsCheck: 0 });
|
||||
|
||||
(useDeleteUser as jest.Mock).mockImplementation((options) => ({
|
||||
mutate: mockDeleteMutate.mockImplementation(() => {
|
||||
mutateAsync: mockDeleteMutate.mockImplementation(() => {
|
||||
options?.mutation?.onError?.({});
|
||||
}),
|
||||
isLoading: false,
|
||||
@@ -598,7 +577,7 @@ describe('EditMemberDrawer', () => {
|
||||
const user = userEvent.setup({ pointerEventsCheck: 0 });
|
||||
|
||||
(useDeleteUser as jest.Mock).mockImplementation((options) => ({
|
||||
mutate: mockDeleteMutate.mockImplementation(() => {
|
||||
mutateAsync: mockDeleteMutate.mockImplementation(() => {
|
||||
options?.mutation?.onError?.({});
|
||||
}),
|
||||
isLoading: false,
|
||||
|
||||
@@ -1,22 +1,16 @@
|
||||
// The mask of the error modal blurs the page and a dialog opened under it, and the
|
||||
// dialog's own backdrop would otherwise dim the error modal that sits above the page.
|
||||
body:has(.error-modal__wrap:not([style*='display: none'])) {
|
||||
--dialog-backdrop-color: transparent;
|
||||
--alert-dialog-backdrop-color: transparent;
|
||||
}
|
||||
|
||||
.error-modal {
|
||||
&__trigger {
|
||||
width: fit-content;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
border-radius: 20px;
|
||||
background: color-mix(in srgb, var(--danger-background) 20%, transparent);
|
||||
padding-left: 3px;
|
||||
padding-right: 8px;
|
||||
cursor: pointer;
|
||||
span {
|
||||
color: var(--danger-background);
|
||||
font-size: 10px;
|
||||
font-weight: 500;
|
||||
line-height: 20px; /* 200% */
|
||||
letter-spacing: 0.4px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
}
|
||||
&__wrap {
|
||||
background: linear-gradient(
|
||||
@@ -65,7 +59,6 @@
|
||||
&__value {
|
||||
padding: 4px 8px;
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 16px;
|
||||
letter-spacing: 0.48px;
|
||||
|
||||
@@ -53,8 +53,13 @@ function ErrorModal({
|
||||
onClick={(): void => setVisible(true)}
|
||||
onKeyDown={undefined}
|
||||
>
|
||||
<Badge variant="solid" color="danger">
|
||||
<CircleAlert size={14} color={Color.BG_CHERRY_500} /> error
|
||||
<Badge
|
||||
variant="solid"
|
||||
color="danger"
|
||||
textTransform="uppercase"
|
||||
prefix={<CircleAlert size={14} />}
|
||||
>
|
||||
error
|
||||
</Badge>
|
||||
</span>
|
||||
) : (
|
||||
|
||||
@@ -38,7 +38,6 @@
|
||||
margin: 0;
|
||||
color: var(--l2-foreground);
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 20px; /* 142.857% */
|
||||
letter-spacing: -0.07px;
|
||||
}
|
||||
@@ -49,8 +48,6 @@
|
||||
gap: 6px;
|
||||
padding: 9px 12.5px;
|
||||
color: var(--l2-foreground);
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
line-height: 18px; /* 150% */
|
||||
letter-spacing: 0.12px;
|
||||
border-radius: 2px;
|
||||
@@ -79,7 +76,6 @@
|
||||
color: var(--l2-foreground);
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
line-height: 18px; /* 150% */
|
||||
letter-spacing: 0.48px;
|
||||
pointer-events: none;
|
||||
}
|
||||
@@ -98,7 +94,6 @@
|
||||
color: var(--l1-foreground);
|
||||
font-size: 10px;
|
||||
font-weight: 500;
|
||||
line-height: 18px; /* 180% */
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
}
|
||||
@@ -133,16 +128,12 @@
|
||||
position: relative;
|
||||
margin-bottom: 4px;
|
||||
color: var(--l2-foreground);
|
||||
font-family: Geist Mono;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
line-height: 18px;
|
||||
font-family: var(--code-base-400-font-family);
|
||||
padding: 3px 12px;
|
||||
padding-left: 26px;
|
||||
}
|
||||
|
||||
&__message-item::before {
|
||||
font-family: unset;
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 12px;
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
.export-menu-popover {
|
||||
width: 240px;
|
||||
padding: 0 12px 12px 12px;
|
||||
// The popover shell carries no padding of its own, so the content owns it.
|
||||
padding: var(--spacing-8);
|
||||
|
||||
.title {
|
||||
display: flex;
|
||||
color: var(--l1-foreground);
|
||||
font-family: Inter;
|
||||
font-size: var(--periscope-font-size-small);
|
||||
font-size: var(--uppercase-small-500-font-size);
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 18px;
|
||||
@@ -23,7 +23,7 @@
|
||||
// radio option labels — radix popover content inherits the root font
|
||||
// size; pin to the app's 13px base the antd popover used to impose
|
||||
label {
|
||||
font-size: var(--periscope-font-size-base);
|
||||
font-size: var(--label-base-400-font-size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Download } from '@signozhq/icons';
|
||||
import { Button } from '@signozhq/ui/button';
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '@signozhq/ui/popover';
|
||||
import { Popover, usePopoverHandle } from '@signozhq/ui/popover';
|
||||
import { RadioGroup } from '@signozhq/ui/radio-group';
|
||||
import { Tooltip } from '@signozhq/ui/tooltip';
|
||||
import { Typography } from '@signozhq/ui/typography';
|
||||
@@ -36,6 +36,10 @@ export default function ExportMenu({
|
||||
}: ExportMenuProps): JSX.Element {
|
||||
const [exportFormat, setExportFormat] = useState<string>(ExportFormat.Csv);
|
||||
const [isPopoverOpen, setIsPopoverOpen] = useState<boolean>(false);
|
||||
// Driven by a handle rather than `trigger`: the button carries a Tooltip, and a Tooltip
|
||||
// routes every prop it does not recognise to its own popup, so a popover's press handlers
|
||||
// would never reach the button underneath.
|
||||
const menu = usePopoverHandle(setIsPopoverOpen);
|
||||
|
||||
const { isExporting, handleExport: handleClientExport } = useClientExport({
|
||||
data,
|
||||
@@ -45,53 +49,72 @@ export default function ExportMenu({
|
||||
});
|
||||
|
||||
const handleExport = useCallback((): void => {
|
||||
setIsPopoverOpen(false);
|
||||
menu.close();
|
||||
handleClientExport({ format: exportFormat as ExportFormat });
|
||||
}, [exportFormat, handleClientExport]);
|
||||
}, [menu, exportFormat, handleClientExport]);
|
||||
|
||||
return (
|
||||
<Popover open={isPopoverOpen} onOpenChange={setIsPopoverOpen}>
|
||||
<>
|
||||
<Tooltip title="Download">
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
color="secondary"
|
||||
size="sm"
|
||||
icon
|
||||
aria-label="Download"
|
||||
testId={`export-menu-${dataSource}`}
|
||||
loading={isExporting}
|
||||
>
|
||||
<Download size={14} />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
</Tooltip>
|
||||
<PopoverContent align="end" className="export-menu-popover">
|
||||
<div className="export-format">
|
||||
<Typography.Text className="title">FORMAT</Typography.Text>
|
||||
<RadioGroup
|
||||
color="primary"
|
||||
value={exportFormat}
|
||||
onChange={setExportFormat}
|
||||
items={[
|
||||
{ value: ExportFormat.Csv, label: 'csv' },
|
||||
{ value: ExportFormat.Jsonl, label: 'jsonl' },
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
size="md"
|
||||
variant="solid"
|
||||
color="primary"
|
||||
width="100%"
|
||||
onClick={handleExport}
|
||||
variant="ghost"
|
||||
color="secondary"
|
||||
size="sm"
|
||||
icon
|
||||
aria-label="Download"
|
||||
aria-haspopup="dialog"
|
||||
aria-expanded={isPopoverOpen}
|
||||
testId={`export-menu-${dataSource}`}
|
||||
loading={isExporting}
|
||||
prefix={<Download size={16} />}
|
||||
onClick={(event): void => menu.showAt(event.currentTarget)}
|
||||
>
|
||||
Export
|
||||
<Download size={14} />
|
||||
</Button>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</Tooltip>
|
||||
<Popover
|
||||
side="bottom"
|
||||
align="end"
|
||||
width={240}
|
||||
handle={menu}
|
||||
aria-label="Export format"
|
||||
>
|
||||
<div className="export-menu-popover">
|
||||
<div className="export-format">
|
||||
<Typography
|
||||
variant="uppercase"
|
||||
size="small"
|
||||
weight="medium"
|
||||
color="main"
|
||||
as="p"
|
||||
marginBottom={4}
|
||||
textOverflow="wrap"
|
||||
>
|
||||
FORMAT
|
||||
</Typography>
|
||||
<RadioGroup
|
||||
color="primary"
|
||||
value={exportFormat}
|
||||
onChange={setExportFormat}
|
||||
items={[
|
||||
{ value: ExportFormat.Csv, label: 'csv' },
|
||||
{ value: ExportFormat.Jsonl, label: 'jsonl' },
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
size="md"
|
||||
variant="solid"
|
||||
color="primary"
|
||||
width="100%"
|
||||
onClick={handleExport}
|
||||
loading={isExporting}
|
||||
prefix={<Download size={16} />}
|
||||
>
|
||||
Export
|
||||
</Button>
|
||||
</div>
|
||||
</Popover>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -117,9 +117,16 @@ function AddedFields({
|
||||
<div className={styles.sectionHeader}>
|
||||
<span>ADDED FIELDS</span>
|
||||
{maxFields !== undefined && (
|
||||
<Typography.Text size="sm" weight="medium" color="muted">
|
||||
<Typography
|
||||
variant="paragraph"
|
||||
size="small"
|
||||
weight="medium"
|
||||
color="secondary"
|
||||
as="span"
|
||||
textOverflow="wrap"
|
||||
>
|
||||
Max Allowed: {maxFields}
|
||||
</Typography.Text>
|
||||
</Typography>
|
||||
)}
|
||||
</div>
|
||||
<div className={styles.addedList}>
|
||||
|
||||
@@ -32,12 +32,15 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Full-bleed search bar: only top/bottom borders, square corners.
|
||||
// Per-side widths cannot be expressed via --input-border-width.
|
||||
.searchInput {
|
||||
background-color: var(--l1-background);
|
||||
height: 40px;
|
||||
border-radius: 0;
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
--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
|
||||
|
||||
@@ -4,9 +4,16 @@ function AnnouncementsModal(): JSX.Element {
|
||||
return (
|
||||
<div className="announcements-modal-container">
|
||||
<div className="announcements-modal-container-header">
|
||||
<Typography.Text className="announcements-modal-title">
|
||||
<Typography
|
||||
variant="paragraph"
|
||||
size="base"
|
||||
weight="regular"
|
||||
color="inherit"
|
||||
as="span"
|
||||
textOverflow="wrap"
|
||||
>
|
||||
Announcements
|
||||
</Typography.Text>
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { useAppLocation } from 'lib/router/useAppLocation';
|
||||
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 { Button as SignozButton } from '@signozhq/ui/button';
|
||||
import { Typography } from '@signozhq/ui/typography';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import { handleContactSupport } from 'container/Integrations/utils';
|
||||
@@ -39,7 +40,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',
|
||||
});
|
||||
})
|
||||
@@ -136,14 +138,23 @@ function FeedbackModal({ onClose }: { onClose: () => void }): JSX.Element {
|
||||
Submit
|
||||
</Button>
|
||||
<div className="feedback-modal-content-footer-info-text">
|
||||
<Typography.Text>
|
||||
<Typography
|
||||
variant="paragraph"
|
||||
size="base"
|
||||
weight="regular"
|
||||
color="inherit"
|
||||
as="span"
|
||||
textOverflow="wrap"
|
||||
>
|
||||
Have a specific issue?{' '}
|
||||
<Typography.Link
|
||||
className="contact-support-link"
|
||||
<SignozButton
|
||||
variant="link"
|
||||
color="primary"
|
||||
size="sm"
|
||||
onClick={handleContactSupportClick}
|
||||
>
|
||||
Contact Support{' '}
|
||||
</Typography.Link>
|
||||
</SignozButton>
|
||||
or{' '}
|
||||
<a
|
||||
href="https://signoz.io/docs/introduction/"
|
||||
@@ -153,7 +164,7 @@ function FeedbackModal({ onClose }: { onClose: () => void }): JSX.Element {
|
||||
>
|
||||
Read our docs
|
||||
</a>
|
||||
</Typography.Text>
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -68,11 +68,6 @@
|
||||
|
||||
.absolute-relative-time-toggler-label {
|
||||
color: var(--l1-foreground);
|
||||
font-size: 13px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 20px; /* 142.857% */
|
||||
letter-spacing: -0.07px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -128,7 +128,16 @@ function HeaderRightSection({
|
||||
}
|
||||
prefix={<Noz size={20} />}
|
||||
>
|
||||
<Typography.Text>Noz</Typography.Text>
|
||||
<Typography
|
||||
variant="paragraph"
|
||||
size="base"
|
||||
weight="regular"
|
||||
color="inherit"
|
||||
as="span"
|
||||
textOverflow="wrap"
|
||||
>
|
||||
Noz
|
||||
</Typography>
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</span>
|
||||
|
||||
@@ -141,9 +141,16 @@ function ShareURLModal({ extraOption }: ShareURLModalProps): JSX.Element {
|
||||
{(shareURLWithTime || isRouteToBeSharedWithTime) && (
|
||||
<>
|
||||
<div className="absolute-relative-time-toggler-container">
|
||||
<Typography.Text className="absolute-relative-time-toggler-label">
|
||||
<Typography
|
||||
variant="paragraph"
|
||||
size="base"
|
||||
weight="medium"
|
||||
color="main"
|
||||
as="span"
|
||||
textOverflow="wrap"
|
||||
>
|
||||
Enable absolute time
|
||||
</Typography.Text>
|
||||
</Typography>
|
||||
|
||||
<div className="absolute-relative-time-toggler">
|
||||
{!isValidateRelativeTime && (
|
||||
@@ -172,9 +179,16 @@ function ShareURLModal({ extraOption }: ShareURLModalProps): JSX.Element {
|
||||
|
||||
{extraOption && (
|
||||
<div className="absolute-relative-time-toggler-container">
|
||||
<Typography.Text className="absolute-relative-time-toggler-label">
|
||||
<Typography
|
||||
variant="paragraph"
|
||||
size="base"
|
||||
weight="medium"
|
||||
color="main"
|
||||
as="span"
|
||||
textOverflow="wrap"
|
||||
>
|
||||
{extraOption.label}
|
||||
</Typography.Text>
|
||||
</Typography>
|
||||
<div className="absolute-relative-time-toggler">
|
||||
<Switch
|
||||
color="primary"
|
||||
@@ -189,12 +203,26 @@ function ShareURLModal({ extraOption }: ShareURLModalProps): JSX.Element {
|
||||
<div className="share-link">
|
||||
<div className="url-share-container">
|
||||
<div className="url-share-container-header">
|
||||
<Typography.Text className="url-share-title">
|
||||
<Typography
|
||||
variant="paragraph"
|
||||
size="base"
|
||||
weight="medium"
|
||||
color="main"
|
||||
as="span"
|
||||
textOverflow="wrap"
|
||||
>
|
||||
Share page link
|
||||
</Typography.Text>
|
||||
<Typography.Text className="url-share-sub-title">
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="paragraph"
|
||||
size="small"
|
||||
weight="regular"
|
||||
color="tertiary"
|
||||
as="span"
|
||||
textOverflow="wrap"
|
||||
>
|
||||
Share the current page link with your team member
|
||||
</Typography.Text>
|
||||
</Typography>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Mock dependencies before imports
|
||||
import { useAppLocation } from 'lib/router/useAppLocation';
|
||||
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';
|
||||
@@ -13,11 +13,11 @@ jest.mock('lib/router/useAppLocation', () => ({
|
||||
useAppLocation: 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(),
|
||||
},
|
||||
}));
|
||||
|
||||
@@ -49,7 +49,7 @@ describe('FeedbackModal', () => {
|
||||
isCloudUser: false,
|
||||
});
|
||||
mockToast.success.mockClear();
|
||||
mockToast.error.mockClear();
|
||||
mockToast.danger.mockClear();
|
||||
});
|
||||
|
||||
it('should render feedback modal with all tabs', () => {
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
}
|
||||
|
||||
.infra-container-card-text {
|
||||
font-size: var(--font-size-sm);
|
||||
font-size: var(--paragraph-base-400-font-size);
|
||||
color: var(--l2-foreground);
|
||||
line-height: 20px;
|
||||
letter-spacing: -0.07px;
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
}
|
||||
|
||||
.infra-container-card-text {
|
||||
font-size: var(--font-size-sm);
|
||||
font-size: var(--paragraph-base-400-font-size);
|
||||
color: var(--l2-foreground);
|
||||
line-height: 20px;
|
||||
letter-spacing: -0.07px;
|
||||
|
||||
@@ -52,9 +52,16 @@ export default function WaitlistFragment({
|
||||
|
||||
return (
|
||||
<div className="wait-list-container">
|
||||
<Typography.Text className="wait-list-text">
|
||||
<Typography
|
||||
variant="paragraph"
|
||||
size="base"
|
||||
weight="regular"
|
||||
color="inherit"
|
||||
as="span"
|
||||
textOverflow="wrap"
|
||||
>
|
||||
{t('waitlist_message')}
|
||||
</Typography.Text>
|
||||
</Typography>
|
||||
|
||||
<Button
|
||||
className="periscope-btn join-waitlist-btn"
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
|
||||
border-radius: 2px 0px 0px 2px;
|
||||
|
||||
// The label chip and the Input share the --input-with-label-* theming
|
||||
// contract (set by e.g. FormAlertRules, WidgetFullView) so the joined
|
||||
// group recolors as one.
|
||||
.label {
|
||||
// Typography.Text takes its display from this token; at its `inline` default
|
||||
// the label blockifies as a flex item and the text rides the top of the row.
|
||||
@@ -31,66 +34,41 @@
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
font-weight: var(--font-weight-light);
|
||||
}
|
||||
|
||||
.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;
|
||||
|
||||
// Square corners: the input sits mid-group between label and close button.
|
||||
--input-border-radius: 0px;
|
||||
--input-height: 36px;
|
||||
--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));
|
||||
|
||||
&: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 {
|
||||
// The Input has no token for the native number spinners.
|
||||
input[type='number']::-webkit-inner-spin-button,
|
||||
input[type='number']::-webkit-outer-spin-button {
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
appearance: none;
|
||||
@@ -114,16 +92,7 @@
|
||||
|
||||
&.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-border-radius: 2px 0 0 2px;
|
||||
}
|
||||
|
||||
.label {
|
||||
|
||||
@@ -49,21 +49,51 @@ function InputWithLabel({
|
||||
labelAfter,
|
||||
})}
|
||||
>
|
||||
{!labelAfter && <Typography.Text className="label">{label}</Typography.Text>}
|
||||
<Input
|
||||
{!labelAfter && (
|
||||
<div className="label">
|
||||
<Typography
|
||||
variant="paragraph"
|
||||
size="small"
|
||||
weight="regular"
|
||||
color="secondary"
|
||||
as="p"
|
||||
textOverflow="ellipsis"
|
||||
>
|
||||
{label}
|
||||
</Typography>
|
||||
</div>
|
||||
)}
|
||||
<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}`}
|
||||
/>
|
||||
{labelAfter && <Typography.Text className="label">{label}</Typography.Text>}
|
||||
>
|
||||
<Input
|
||||
placeholder={placeholder}
|
||||
type={type}
|
||||
value={inputValue}
|
||||
onChange={handleChange}
|
||||
disabled={disabled}
|
||||
disabledTooltip={undefined}
|
||||
name={label.toLowerCase()}
|
||||
testId={`input-${label}`}
|
||||
/>
|
||||
</div>
|
||||
{labelAfter && (
|
||||
<div className="label">
|
||||
<Typography
|
||||
variant="paragraph"
|
||||
size="small"
|
||||
weight="regular"
|
||||
color="secondary"
|
||||
as="p"
|
||||
textOverflow="ellipsis"
|
||||
>
|
||||
{label}
|
||||
</Typography>
|
||||
</div>
|
||||
)}
|
||||
{onClose && (
|
||||
<Button
|
||||
className="periscope-btn ghost close-btn"
|
||||
|
||||
@@ -55,26 +55,12 @@
|
||||
flex: 1 1 0;
|
||||
min-width: 0;
|
||||
|
||||
// Matches the antd RolesSelect in the same row, which is forced to the
|
||||
// same field background below.
|
||||
--input-background: var(
|
||||
--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);
|
||||
}
|
||||
}
|
||||
|
||||
.cellRole {
|
||||
|
||||
@@ -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';
|
||||
@@ -68,20 +68,30 @@ function InviteMembers({
|
||||
<div className={styles.table}>
|
||||
{showHeader && (
|
||||
<div className={styles.header}>
|
||||
<Typography.Text
|
||||
size="base"
|
||||
weight="semibold"
|
||||
className={styles.headerCellEmail}
|
||||
>
|
||||
Email address
|
||||
</Typography.Text>
|
||||
<Typography.Text
|
||||
size="base"
|
||||
weight="semibold"
|
||||
className={styles.headerCellRole}
|
||||
>
|
||||
Roles
|
||||
</Typography.Text>
|
||||
<span className={styles.headerCellEmail}>
|
||||
<Typography
|
||||
variant="paragraph"
|
||||
size="base"
|
||||
weight="bold"
|
||||
color="inherit"
|
||||
as="span"
|
||||
textOverflow="wrap"
|
||||
>
|
||||
Email address
|
||||
</Typography>
|
||||
</span>
|
||||
<span className={styles.headerCellRole}>
|
||||
<Typography
|
||||
variant="paragraph"
|
||||
size="base"
|
||||
weight="bold"
|
||||
color="inherit"
|
||||
as="span"
|
||||
textOverflow="wrap"
|
||||
>
|
||||
Roles
|
||||
</Typography>
|
||||
</span>
|
||||
<div className={styles.headerCellAction} />
|
||||
</div>
|
||||
)}
|
||||
@@ -97,12 +107,19 @@ 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}>
|
||||
<Typography
|
||||
variant="paragraph"
|
||||
size="small"
|
||||
weight="regular"
|
||||
color="danger"
|
||||
as="span"
|
||||
textOverflow="wrap"
|
||||
>
|
||||
Invalid email address
|
||||
</Typography.Text>
|
||||
</Typography>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -152,61 +169,90 @@ 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 && (
|
||||
<Typography.Text size="small">
|
||||
{successResults.length} invite(s) sent successfully.
|
||||
</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>
|
||||
<div className={styles.callout}>
|
||||
<Callout
|
||||
color="danger"
|
||||
size="sm"
|
||||
icon={<CircleAlert />}
|
||||
testId="invite-api-error"
|
||||
>
|
||||
<div className={styles.results}>
|
||||
{hasSuccesses && (
|
||||
<Typography
|
||||
variant="paragraph"
|
||||
size="small"
|
||||
weight="regular"
|
||||
color="inherit"
|
||||
as="span"
|
||||
textOverflow="wrap"
|
||||
>
|
||||
{successResults.length} invite(s) sent successfully.
|
||||
</Typography>
|
||||
)}
|
||||
<Typography
|
||||
variant="paragraph"
|
||||
size="small"
|
||||
weight="regular"
|
||||
color="inherit"
|
||||
as="span"
|
||||
textOverflow="wrap"
|
||||
>
|
||||
{failedResults.length} invite(s) failed:
|
||||
</Typography>
|
||||
<ul className={styles.resultsList}>
|
||||
{failedResults.map((result) => (
|
||||
<li key={result.email}>
|
||||
<Typography
|
||||
variant="paragraph"
|
||||
size="small"
|
||||
weight="regular"
|
||||
color="inherit"
|
||||
as="span"
|
||||
textOverflow="wrap"
|
||||
>
|
||||
{result.email}: {result.error}
|
||||
</Typography>
|
||||
</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
|
||||
variant="paragraph"
|
||||
size="small"
|
||||
weight="regular"
|
||||
color="inherit"
|
||||
as="span"
|
||||
textOverflow="wrap"
|
||||
>
|
||||
{successResults.length} invite(s) sent successfully!
|
||||
</Typography>
|
||||
</Callout>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{renderFooter?.({
|
||||
|
||||
@@ -25,10 +25,6 @@
|
||||
}
|
||||
|
||||
.filtersLabel {
|
||||
font-size: var(--font-size-xs);
|
||||
font-weight: var(--font-weight-medium);
|
||||
letter-spacing: 0.06em;
|
||||
text-transform: uppercase;
|
||||
color: var(--l2-foreground);
|
||||
margin-right: 2px;
|
||||
}
|
||||
@@ -107,6 +103,6 @@
|
||||
}
|
||||
|
||||
.error {
|
||||
font-size: var(--font-size-xs);
|
||||
font-size: var(--label-small-400-font-size);
|
||||
color: var(--danger-foreground);
|
||||
}
|
||||
|
||||
@@ -116,15 +116,31 @@ function FilterZone({
|
||||
{rightSlot}
|
||||
</div>
|
||||
{error && (
|
||||
<Typography.Text
|
||||
className={styles.error}
|
||||
<Typography
|
||||
variant="paragraph"
|
||||
size="small"
|
||||
weight="regular"
|
||||
color="danger"
|
||||
as="span"
|
||||
textOverflow="wrap"
|
||||
testId={`${testIdPrefix}-query-error`}
|
||||
>
|
||||
{error}
|
||||
</Typography.Text>
|
||||
</Typography>
|
||||
)}
|
||||
<div className={styles.filtersRow}>
|
||||
<Typography.Text className={styles.filtersLabel}>Filters</Typography.Text>
|
||||
<Typography
|
||||
variant="uppercase"
|
||||
size="small"
|
||||
weight="medium"
|
||||
color="secondary"
|
||||
as="span"
|
||||
display="inline-block"
|
||||
marginRight={1}
|
||||
textOverflow="wrap"
|
||||
>
|
||||
Filters
|
||||
</Typography>
|
||||
{wrapControl(
|
||||
<div
|
||||
className={styles.filterControls}
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
:global(.cm-dsl-logic) {
|
||||
color: var(--bg-sakura-400);
|
||||
font-weight: var(--font-weight-medium);
|
||||
font-weight: var(--label-base-500-font-weight);
|
||||
}
|
||||
|
||||
:global(.cm-dsl-string) {
|
||||
@@ -59,7 +59,7 @@
|
||||
content: 'SUGGESTIONS';
|
||||
padding: 12px 14px 8px;
|
||||
font-size: 11px;
|
||||
font-weight: var(--font-weight-medium);
|
||||
font-weight: var(--uppercase-small-500-font-weight);
|
||||
line-height: 18px;
|
||||
letter-spacing: 0.88px;
|
||||
text-transform: uppercase;
|
||||
@@ -71,7 +71,7 @@
|
||||
padding: 10px 14px;
|
||||
border-top: 1px solid var(--l1-border);
|
||||
background: var(--l2-background);
|
||||
font-size: var(--font-size-xs);
|
||||
font-size: var(--label-small-400-font-size);
|
||||
color: var(--l2-foreground);
|
||||
}
|
||||
|
||||
@@ -131,7 +131,7 @@
|
||||
border-radius: 50px;
|
||||
background: color-mix(in srgb, var(--l1-foreground) 10%, transparent);
|
||||
font-style: normal;
|
||||
font-size: var(--font-size-xs);
|
||||
font-size: var(--label-small-400-font-size);
|
||||
color: var(--l2-foreground);
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
@@ -29,15 +29,15 @@
|
||||
background: var(--l2-background);
|
||||
border: 1px solid var(--l2-border);
|
||||
color: var(--l2-foreground);
|
||||
font-size: var(--font-size-xs);
|
||||
font-size: var(--label-small-400-font-size);
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
.title {
|
||||
color: var(--l1-foreground);
|
||||
font-size: var(--font-size-lg);
|
||||
font-size: var(--label-large-500-font-size);
|
||||
font-style: normal;
|
||||
font-weight: var(--font-weight-medium);
|
||||
font-weight: var(--label-large-500-font-weight);
|
||||
line-height: 28px;
|
||||
letter-spacing: -0.09px;
|
||||
overflow: hidden;
|
||||
|
||||
@@ -22,12 +22,18 @@ function ListCommandHeader({
|
||||
return (
|
||||
<div className={styles.commandHeader}>
|
||||
<div className={styles.headingBlock}>
|
||||
<Typography.Title
|
||||
className={styles.title}
|
||||
<Typography
|
||||
variant="label"
|
||||
size="large"
|
||||
weight="medium"
|
||||
color="main"
|
||||
as="h1"
|
||||
marginY={4}
|
||||
textOverflow="ellipsis"
|
||||
testId={testIdPrefix && `${testIdPrefix}-list-heading`}
|
||||
>
|
||||
{label}
|
||||
</Typography.Title>
|
||||
</Typography>
|
||||
{isModified && <span className={styles.dirtyDot} title="Unsaved changes" />}
|
||||
<span className={styles.countPill}>{count}</span>
|
||||
</div>
|
||||
|
||||
@@ -10,6 +10,6 @@
|
||||
}
|
||||
|
||||
.count {
|
||||
font-size: var(--font-size-xs);
|
||||
font-size: var(--label-small-400-font-size);
|
||||
color: var(--l3-foreground);
|
||||
}
|
||||
|
||||
@@ -36,9 +36,16 @@ function ListStatusBar({
|
||||
>
|
||||
{collapsed ? 'Expand' : 'Collapse'}
|
||||
</Button>
|
||||
<Typography.Text className={styles.count}>
|
||||
<Typography
|
||||
variant="paragraph"
|
||||
size="small"
|
||||
weight="regular"
|
||||
color="tertiary"
|
||||
as="span"
|
||||
textOverflow="wrap"
|
||||
>
|
||||
{count} of {total} {noun}
|
||||
</Typography.Text>
|
||||
</Typography>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -24,8 +24,8 @@
|
||||
|
||||
.headerTitle {
|
||||
margin: 0;
|
||||
font-size: var(--font-size-base);
|
||||
font-weight: var(--font-weight-semibold);
|
||||
font-size: var(--label-medium-600-font-size);
|
||||
font-weight: var(--label-medium-600-font-weight);
|
||||
letter-spacing: -0.01em;
|
||||
color: var(--l1-foreground);
|
||||
text-align: left;
|
||||
@@ -38,7 +38,7 @@
|
||||
.searchEmpty {
|
||||
padding: 12px;
|
||||
color: var(--l3-foreground);
|
||||
font-size: var(--font-size-xs);
|
||||
font-size: var(--label-small-400-font-size);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@@ -54,8 +54,8 @@
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 16px 8px 8px;
|
||||
font-size: var(--font-size-xs);
|
||||
font-weight: var(--font-weight-semibold);
|
||||
font-size: var(--uppercase-small-600-font-size);
|
||||
font-weight: var(--uppercase-small-600-font-weight);
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
color: var(--l3-foreground);
|
||||
@@ -68,9 +68,6 @@
|
||||
}
|
||||
|
||||
.groupCount {
|
||||
font-size: var(--font-size-xs);
|
||||
font-weight: var(--font-weight-normal);
|
||||
letter-spacing: 0;
|
||||
color: var(--l3-foreground);
|
||||
background: var(--l2-background);
|
||||
border-radius: 10px;
|
||||
@@ -79,7 +76,6 @@
|
||||
|
||||
.deleteName {
|
||||
color: var(--danger-background);
|
||||
font-weight: var(--font-weight-medium);
|
||||
}
|
||||
|
||||
.row {
|
||||
@@ -161,7 +157,7 @@
|
||||
border: 1px dashed var(--l2-border);
|
||||
border-radius: 8px;
|
||||
color: var(--l3-foreground);
|
||||
font-size: var(--font-size-xs);
|
||||
font-size: var(--label-small-400-font-size);
|
||||
line-height: 1.5;
|
||||
text-align: center;
|
||||
}
|
||||
@@ -178,8 +174,8 @@
|
||||
}
|
||||
|
||||
.dirtyTitle {
|
||||
font-size: var(--font-size-xs);
|
||||
font-weight: var(--font-weight-semibold);
|
||||
font-size: var(--uppercase-small-600-font-size);
|
||||
font-weight: var(--uppercase-small-600-font-weight);
|
||||
letter-spacing: 0.04em;
|
||||
text-transform: uppercase;
|
||||
color: var(--l2-foreground);
|
||||
|
||||
@@ -72,7 +72,6 @@ function ViewsRail({
|
||||
onViewRenamed,
|
||||
onViewDeleted,
|
||||
}: Props): JSX.Element {
|
||||
const [saveOpen, setSaveOpen] = useState(false);
|
||||
const [query, setQuery] = useState('');
|
||||
const { contextHolder, confirmDelete } = useDeleteConfirm();
|
||||
|
||||
@@ -94,11 +93,28 @@ function ViewsRail({
|
||||
(id: string, label: string): void => {
|
||||
confirmDelete({
|
||||
title: (
|
||||
<Typography.Title level={5}>
|
||||
<Typography
|
||||
variant="paragraph"
|
||||
size="medium"
|
||||
weight="bold"
|
||||
color="inherit"
|
||||
as="h5"
|
||||
marginY={4}
|
||||
textOverflow="wrap"
|
||||
>
|
||||
Delete the{' '}
|
||||
<Typography.Text className={styles.deleteName}>{label}</Typography.Text>{' '}
|
||||
<Typography
|
||||
variant="paragraph"
|
||||
size="base"
|
||||
weight="medium"
|
||||
color="danger"
|
||||
as="span"
|
||||
textOverflow="wrap"
|
||||
>
|
||||
{label}
|
||||
</Typography>{' '}
|
||||
view?
|
||||
</Typography.Title>
|
||||
</Typography>
|
||||
),
|
||||
content: deleteDescription,
|
||||
// Return the delete promise so the modal's Delete button stays loading
|
||||
@@ -149,14 +165,12 @@ function ViewsRail({
|
||||
<div className={styles.header}>
|
||||
<h4 className={styles.headerTitle}>Views</h4>
|
||||
<ViewNamePopover
|
||||
open={saveOpen}
|
||||
onOpenChange={setSaveOpen}
|
||||
onSubmit={handleSaveAsView}
|
||||
title="Save as view"
|
||||
confirmLabel="Save view"
|
||||
testIdPrefix={saveTestIdPrefix}
|
||||
tooltip="Save current filters as a view"
|
||||
trigger={
|
||||
trigger={(open): JSX.Element => (
|
||||
<Button
|
||||
aria-label="Save current filters as a view"
|
||||
variant="ghost"
|
||||
@@ -164,10 +178,11 @@ function ViewsRail({
|
||||
size="sm"
|
||||
icon
|
||||
testId={`${testIdPrefix}-view-save-trigger`}
|
||||
onClick={(e): void => open(e.currentTarget)}
|
||||
>
|
||||
<Plus size={14} />
|
||||
</Button>
|
||||
}
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -204,9 +219,18 @@ function ViewsRail({
|
||||
<>
|
||||
<div className={cx(styles.groupLabel, styles.groupLabelSpaced)}>
|
||||
{customSectionLabel}
|
||||
<Typography.Text className={styles.groupCount}>
|
||||
{customViews.length}
|
||||
</Typography.Text>
|
||||
<span className={styles.groupCount}>
|
||||
<Typography
|
||||
variant="paragraph"
|
||||
size="small"
|
||||
weight="regular"
|
||||
color="tertiary"
|
||||
as="span"
|
||||
textOverflow="wrap"
|
||||
>
|
||||
{customViews.length}
|
||||
</Typography>
|
||||
</span>
|
||||
</div>
|
||||
{customViewsLoading ? (
|
||||
<div className={styles.empty}>Loading views…</div>
|
||||
@@ -245,8 +269,9 @@ function ViewsRail({
|
||||
<ViewsRailDirtyPanel
|
||||
isCustomActive={isCustomActive}
|
||||
testIdPrefix={testIdPrefix}
|
||||
saveTestIdPrefix={saveTestIdPrefix}
|
||||
onSaveChanges={handleSaveViewChanges}
|
||||
onSaveAs={(): void => setSaveOpen(true)}
|
||||
onSaveAs={handleSaveAsView}
|
||||
onReset={onReset}
|
||||
/>
|
||||
)}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user