Compare commits
64 Commits
ns/flamegr
...
feature/da
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7e63e612c8 | ||
|
|
8fadd9fb08 | ||
|
|
0748565583 | ||
|
|
dd629a0e97 | ||
|
|
c76f796e39 | ||
|
|
73f2a785d3 | ||
|
|
4c473d3ce2 | ||
|
|
54705e73f2 | ||
|
|
f1d7f727fe | ||
|
|
f963a98953 | ||
|
|
029f7196b2 | ||
|
|
9976f7c95f | ||
|
|
37133429a2 | ||
|
|
f8479e33ba | ||
|
|
bb06867cd7 | ||
|
|
4da5673e12 | ||
|
|
c3db819d8e | ||
|
|
c83578f211 | ||
|
|
04a4d3fe32 | ||
|
|
27dc996fd8 | ||
|
|
83b25f3e9a | ||
|
|
67e4c4611c | ||
|
|
7274421895 | ||
|
|
9c6656d6b9 | ||
|
|
5c54a2537c | ||
|
|
bf201710a7 | ||
|
|
a5adc52276 | ||
|
|
5ddcf33811 | ||
|
|
c0fe996e7a | ||
|
|
1b6bb78ca4 | ||
|
|
0583f30e35 | ||
|
|
fb3e316ce9 | ||
|
|
b753b95a8a | ||
|
|
4757550189 | ||
|
|
96ad37fea9 | ||
|
|
5419e8461c | ||
|
|
e634eb4452 | ||
|
|
a50bc53f4c | ||
|
|
9f60bdf54a | ||
|
|
e41639dea0 | ||
|
|
847bc71f4e | ||
|
|
8d7d3e5c64 | ||
|
|
74c875ec79 | ||
|
|
a27b7d3d8e | ||
|
|
eab8d45611 | ||
|
|
c73fdd1f81 | ||
|
|
6094a5eee2 | ||
|
|
93407d3bcc | ||
|
|
5bd4cabbca | ||
|
|
f9e21cecd8 | ||
|
|
4b98b0bb27 | ||
|
|
b48851e487 | ||
|
|
279a71c5b3 | ||
|
|
7e63e35113 | ||
|
|
d5a50fe456 | ||
|
|
885b41356a | ||
|
|
b653c69e29 | ||
|
|
7d2f8b291e | ||
|
|
3bea4484f9 | ||
|
|
87ceba2d84 | ||
|
|
445dc3b290 | ||
|
|
76b35b9d8f | ||
|
|
b860cce31d | ||
|
|
1bd4ca88de |
7
.github/CODEOWNERS
vendored
@@ -118,6 +118,9 @@ go.mod @therealpandey
|
||||
|
||||
/tests/integration/ @therealpandey
|
||||
|
||||
# e2e tests
|
||||
/tests/e2e/ @AshwinBhatkal
|
||||
|
||||
# Flagger Owners
|
||||
|
||||
/pkg/flagger/ @therealpandey
|
||||
@@ -162,3 +165,7 @@ go.mod @therealpandey
|
||||
/frontend/src/lib/dashboard/ @SigNoz/pulse-frontend
|
||||
/frontend/src/lib/dashboardVariables/ @SigNoz/pulse-frontend
|
||||
/frontend/src/components/NewSelect/ @SigNoz/pulse-frontend
|
||||
|
||||
## Dashboard V2
|
||||
/frontend/src/pages/DashboardPageV2/ @SigNoz/pulse-frontend
|
||||
/frontend/src/pages/DashboardsListPageV2/ @SigNoz/pulse-frontend
|
||||
|
||||
@@ -8,6 +8,14 @@ packages:
|
||||
filename: "alertmanager.go"
|
||||
structname: 'Mock{{.InterfaceName}}'
|
||||
pkgname: '{{.SrcPackageName}}test'
|
||||
github.com/SigNoz/signoz/pkg/types/alertmanagertypes:
|
||||
interfaces:
|
||||
MaintenanceStore:
|
||||
config:
|
||||
dir: '{{.InterfaceDir}}/alertmanagertypestest'
|
||||
filename: "maintenance.go"
|
||||
structname: 'Mock{{.InterfaceName}}'
|
||||
pkgname: '{{.SrcPackageName}}test'
|
||||
github.com/SigNoz/signoz/pkg/tokenizer:
|
||||
config:
|
||||
all: true
|
||||
|
||||
@@ -190,7 +190,7 @@ services:
|
||||
# - ../common/clickhouse/storage.xml:/etc/clickhouse-server/config.d/storage.xml
|
||||
signoz:
|
||||
!!merge <<: *db-depend
|
||||
image: signoz/signoz:v0.124.0
|
||||
image: signoz/signoz:v0.125.1
|
||||
ports:
|
||||
- "8080:8080" # signoz port
|
||||
# - "6060:6060" # pprof port
|
||||
|
||||
@@ -117,7 +117,7 @@ services:
|
||||
# - ../common/clickhouse/storage.xml:/etc/clickhouse-server/config.d/storage.xml
|
||||
signoz:
|
||||
!!merge <<: *db-depend
|
||||
image: signoz/signoz:v0.124.0
|
||||
image: signoz/signoz:v0.125.1
|
||||
ports:
|
||||
- "8080:8080" # signoz port
|
||||
volumes:
|
||||
|
||||
@@ -181,7 +181,7 @@ services:
|
||||
# - ../common/clickhouse/storage.xml:/etc/clickhouse-server/config.d/storage.xml
|
||||
signoz:
|
||||
!!merge <<: *db-depend
|
||||
image: signoz/signoz:${VERSION:-v0.124.0}
|
||||
image: signoz/signoz:${VERSION:-v0.125.1}
|
||||
container_name: signoz
|
||||
ports:
|
||||
- "8080:8080" # signoz port
|
||||
|
||||
@@ -109,7 +109,7 @@ services:
|
||||
# - ../common/clickhouse/storage.xml:/etc/clickhouse-server/config.d/storage.xml
|
||||
signoz:
|
||||
!!merge <<: *db-depend
|
||||
image: signoz/signoz:${VERSION:-v0.124.0}
|
||||
image: signoz/signoz:${VERSION:-v0.125.1}
|
||||
container_name: signoz
|
||||
ports:
|
||||
- "8080:8080" # signoz port
|
||||
|
||||
1681
docs/api/openapi.yml
@@ -49,6 +49,14 @@ func (module *module) CreatePublic(ctx context.Context, orgID valuer.UUID, publi
|
||||
return errors.New(errors.TypeLicenseUnavailable, errors.CodeLicenseUnavailable, "a valid license is not available").WithAdditional("this feature requires a valid license").WithAdditional(err.Error())
|
||||
}
|
||||
|
||||
dashboard, err := module.Get(ctx, orgID, publicDashboard.DashboardID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := dashboard.ErrIfNotPublishable(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
storablePublicDashboard, err := module.store.GetPublic(ctx, publicDashboard.DashboardID.StringValue())
|
||||
if err != nil && !errors.Ast(err, errors.TypeNotFound) {
|
||||
return err
|
||||
@@ -129,6 +137,14 @@ func (module *module) UpdatePublic(ctx context.Context, orgID valuer.UUID, publi
|
||||
return errors.New(errors.TypeLicenseUnavailable, errors.CodeLicenseUnavailable, "a valid license is not available").WithAdditional("this feature requires a valid license").WithAdditional(err.Error())
|
||||
}
|
||||
|
||||
dashboard, err := module.Get(ctx, orgID, publicDashboard.DashboardID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := dashboard.ErrIfNotPublishable(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return module.store.UpdatePublic(ctx, dashboardtypes.NewStorablePublicDashboardFromPublicDashboard(publicDashboard))
|
||||
}
|
||||
|
||||
@@ -138,6 +154,10 @@ func (module *module) Delete(ctx context.Context, orgID valuer.UUID, id valuer.U
|
||||
return err
|
||||
}
|
||||
|
||||
if err := dashboard.ErrIfNotDeletable(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if dashboard.Locked {
|
||||
return errors.New(errors.TypeInvalidInput, errors.CodeInvalidInput, "dashboard is locked, please unlock the dashboard to be delete it")
|
||||
}
|
||||
@@ -168,6 +188,14 @@ func (module *module) DeletePublic(ctx context.Context, orgID valuer.UUID, dashb
|
||||
return errors.New(errors.TypeLicenseUnavailable, errors.CodeLicenseUnavailable, "a valid license is not available").WithAdditional("this feature requires a valid license").WithAdditional(err.Error())
|
||||
}
|
||||
|
||||
dashboard, err := module.Get(ctx, orgID, dashboardID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := dashboard.ErrIfNotPublishable(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = module.store.DeletePublic(ctx, dashboardID.StringValue())
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -80,6 +80,15 @@ func (ah *APIHandler) getFeatureFlags(w http.ResponseWriter, r *http.Request) {
|
||||
Route: "",
|
||||
})
|
||||
|
||||
fineGrainedAuthz := ah.Signoz.Flagger.BooleanOrEmpty(ctx, flagger.FeatureUseFineGrainedAuthz, evalCtx)
|
||||
featureSet = append(featureSet, &licensetypes.Feature{
|
||||
Name: valuer.NewString(flagger.FeatureUseFineGrainedAuthz.String()),
|
||||
Active: fineGrainedAuthz,
|
||||
Usage: 0,
|
||||
UsageLimit: -1,
|
||||
Route: "",
|
||||
})
|
||||
|
||||
if constants.IsDotMetricsEnabled {
|
||||
for idx, feature := range featureSet {
|
||||
if feature.Name == licensetypes.DotMetricsEnabled {
|
||||
|
||||
@@ -13,7 +13,6 @@ import (
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
baserules "github.com/SigNoz/signoz/pkg/query-service/rules"
|
||||
"github.com/SigNoz/signoz/pkg/types/ruletypes"
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
)
|
||||
|
||||
func PrepareTaskFunc(opts baserules.PrepareTaskOptions) (baserules.Task, error) {
|
||||
@@ -49,7 +48,7 @@ func PrepareTaskFunc(opts baserules.PrepareTaskOptions) (baserules.Task, error)
|
||||
rules = append(rules, tr)
|
||||
|
||||
// create ch rule task for evaluation
|
||||
task = newTask(baserules.TaskTypeCh, opts.TaskName, evaluation.GetFrequency().Duration(), rules, opts.ManagerOpts, opts.NotifyFunc, opts.MaintenanceStore, opts.OrgID)
|
||||
task = newTask(baserules.TaskTypeCh, opts.TaskName, evaluation.GetFrequency().Duration(), rules, opts.ManagerOpts, opts.NotifyFunc)
|
||||
|
||||
} else if opts.Rule.RuleType == ruletypes.RuleTypeProm {
|
||||
|
||||
@@ -73,7 +72,7 @@ func PrepareTaskFunc(opts baserules.PrepareTaskOptions) (baserules.Task, error)
|
||||
rules = append(rules, pr)
|
||||
|
||||
// create promql rule task for evaluation
|
||||
task = newTask(baserules.TaskTypeProm, opts.TaskName, evaluation.GetFrequency().Duration(), rules, opts.ManagerOpts, opts.NotifyFunc, opts.MaintenanceStore, opts.OrgID)
|
||||
task = newTask(baserules.TaskTypeProm, opts.TaskName, evaluation.GetFrequency().Duration(), rules, opts.ManagerOpts, opts.NotifyFunc)
|
||||
|
||||
} else if opts.Rule.RuleType == ruletypes.RuleTypeAnomaly {
|
||||
// create anomaly rule
|
||||
@@ -96,7 +95,7 @@ func PrepareTaskFunc(opts baserules.PrepareTaskOptions) (baserules.Task, error)
|
||||
rules = append(rules, ar)
|
||||
|
||||
// create anomaly rule task for evaluation
|
||||
task = newTask(baserules.TaskTypeCh, opts.TaskName, evaluation.GetFrequency().Duration(), rules, opts.ManagerOpts, opts.NotifyFunc, opts.MaintenanceStore, opts.OrgID)
|
||||
task = newTask(baserules.TaskTypeCh, opts.TaskName, evaluation.GetFrequency().Duration(), rules, opts.ManagerOpts, opts.NotifyFunc)
|
||||
|
||||
} else {
|
||||
return nil, errors.NewInvalidInputf(errors.CodeInvalidInput, "unsupported rule type %s. Supported types: %s, %s", opts.Rule.RuleType, ruletypes.RuleTypeProm, ruletypes.RuleTypeThreshold)
|
||||
@@ -210,9 +209,9 @@ func TestNotification(opts baserules.PrepareTestRuleOptions) (int, error) {
|
||||
}
|
||||
|
||||
// newTask returns an appropriate group for the rule type
|
||||
func newTask(taskType baserules.TaskType, name string, frequency time.Duration, rules []baserules.Rule, opts *baserules.ManagerOptions, notify baserules.NotifyFunc, maintenanceStore ruletypes.MaintenanceStore, orgID valuer.UUID) baserules.Task {
|
||||
func newTask(taskType baserules.TaskType, name string, frequency time.Duration, rules []baserules.Rule, opts *baserules.ManagerOptions, notify baserules.NotifyFunc) baserules.Task {
|
||||
if taskType == baserules.TaskTypeCh {
|
||||
return baserules.NewRuleTask(name, "", frequency, rules, opts, notify, maintenanceStore, orgID)
|
||||
return baserules.NewRuleTask(name, "", frequency, rules, opts, notify)
|
||||
}
|
||||
return baserules.NewPromRuleTask(name, "", frequency, rules, opts, notify, maintenanceStore, orgID)
|
||||
return baserules.NewPromRuleTask(name, "", frequency, rules, opts, notify)
|
||||
}
|
||||
|
||||
@@ -10,6 +10,13 @@ export default defineConfig({
|
||||
signoz: {
|
||||
input: {
|
||||
target: '../docs/api/openapi.yml',
|
||||
// Perses' `common.JSONRef` (used by `DashboardGridItem.content`) has a
|
||||
// field tagged `json:"$ref"`, so our spec contains a property literally
|
||||
// named `$ref`.
|
||||
// Orval v8's validator (`@scalar/openapi-parser`) treats every `$ref` key
|
||||
// as a JSON Reference and aborts with `INVALID_REFERENCE` when the value isn't a URI string.
|
||||
// Safe to disable: yes, the spec is generated by `cmd/openapi.go` and gated by backend CI, not hand-edited.
|
||||
unsafeDisableValidation: true,
|
||||
},
|
||||
output: {
|
||||
target: './src/api/generated/services',
|
||||
@@ -27,7 +34,7 @@ export default defineConfig({
|
||||
signal: true,
|
||||
useOperationIdAsQueryKey: false,
|
||||
},
|
||||
useDates: true,
|
||||
useDates: false,
|
||||
useNamedParameters: true,
|
||||
enumGenerationType: 'enum',
|
||||
mutator: {
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
"@signozhq/design-tokens": "2.1.4",
|
||||
"@signozhq/icons": "0.4.0",
|
||||
"@signozhq/resizable": "0.0.2",
|
||||
"@signozhq/ui": "0.0.19",
|
||||
"@signozhq/ui": "0.0.21",
|
||||
"@tanstack/react-table": "8.21.3",
|
||||
"@tanstack/react-virtual": "3.13.22",
|
||||
"@uiw/codemirror-theme-copilot": "4.23.11",
|
||||
|
||||
@@ -14,7 +14,11 @@
|
||||
*/
|
||||
|
||||
const BANNED_COMPONENTS = {
|
||||
Typography: 'Use @signozhq/ui Typography instead of antd Typography.',
|
||||
Typography:
|
||||
'Use @signozhq/ui/typography Typography instead of antd Typography.',
|
||||
Switch: 'Use @signozhq/ui/switch Switch instead of antd Switch.',
|
||||
Badge: 'Use @signozhq/ui/badge instead of antd Badge.',
|
||||
Progress: 'Use @signozhq/ui/progress instead of antd Progress.',
|
||||
};
|
||||
|
||||
export default {
|
||||
|
||||
31
frontend/pnpm-lock.yaml
generated
@@ -77,8 +77,8 @@ importers:
|
||||
specifier: 0.0.2
|
||||
version: 0.0.2(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
'@signozhq/ui':
|
||||
specifier: 0.0.19
|
||||
version: 0.0.19(@emotion/is-prop-valid@1.2.0)(@signozhq/icons@0.4.0)(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react-router-dom@5.3.4(react@18.2.0))(react-router@6.30.3(react@18.2.0))(react@18.2.0)
|
||||
specifier: 0.0.21
|
||||
version: 0.0.21(@emotion/is-prop-valid@1.2.0)(@signozhq/icons@0.4.0)(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react-router-dom@5.3.4(react@18.2.0))(react-router@6.30.3(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)
|
||||
@@ -3269,8 +3269,8 @@ packages:
|
||||
peerDependencies:
|
||||
react: ^18.2.0
|
||||
|
||||
'@signozhq/ui@0.0.19':
|
||||
resolution: {integrity: sha512-2q6aRxN/PR4PlR2xJZAREEuvLPiDFggfFKzCW2Z5vHVVbrgnvZHWD1jPUuwszfEg0ceH3UvkwqceO7wN4uRJAA==}
|
||||
'@signozhq/ui@0.0.21':
|
||||
resolution: {integrity: sha512-uLM3Vqwxlk2USXbwtb3qRLpjZR9b9QSHFQq/jtcfYNMDmIE/sNjSj0nRkEhX4RqqRgsLRt2PVA33aeWxDOLO3g==}
|
||||
peerDependencies:
|
||||
'@signozhq/icons': 0.3.0
|
||||
react: ^18.2.0
|
||||
@@ -3851,27 +3851,6 @@ packages:
|
||||
peerDependencies:
|
||||
vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0
|
||||
|
||||
'@webassemblyjs/ast@1.14.1':
|
||||
resolution: {integrity: sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==}
|
||||
|
||||
'@webassemblyjs/floating-point-hex-parser@1.13.2':
|
||||
resolution: {integrity: sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==}
|
||||
|
||||
'@webassemblyjs/helper-api-error@1.13.2':
|
||||
resolution: {integrity: sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==}
|
||||
|
||||
'@webassemblyjs/helper-buffer@1.14.1':
|
||||
resolution: {integrity: sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==}
|
||||
|
||||
'@webassemblyjs/helper-numbers@1.13.2':
|
||||
resolution: {integrity: sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==}
|
||||
|
||||
'@webassemblyjs/helper-wasm-bytecode@1.13.2':
|
||||
resolution: {integrity: sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==}
|
||||
|
||||
'@webassemblyjs/helper-wasm-section@1.14.1':
|
||||
resolution: {integrity: sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==}
|
||||
|
||||
'@xmldom/xmldom@0.8.13':
|
||||
resolution: {integrity: sha512-KRYzxepc14G/CEpEGc3Yn+JKaAeT63smlDr+vjB8jRfgTBBI9wRj/nkQEO+ucV8p8I9bfKLWp37uHgFrbntPvw==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
@@ -12034,7 +12013,7 @@ snapshots:
|
||||
- react-dom
|
||||
- tailwindcss
|
||||
|
||||
'@signozhq/ui@0.0.19(@emotion/is-prop-valid@1.2.0)(@signozhq/icons@0.4.0)(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react-router-dom@5.3.4(react@18.2.0))(react-router@6.30.3(react@18.2.0))(react@18.2.0)':
|
||||
'@signozhq/ui@0.0.21(@emotion/is-prop-valid@1.2.0)(@signozhq/icons@0.4.0)(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react-router-dom@5.3.4(react@18.2.0))(react-router@6.30.3(react@18.2.0))(react@18.2.0)':
|
||||
dependencies:
|
||||
'@chenglou/pretext': 0.0.5
|
||||
'@radix-ui/react-checkbox': 1.3.3(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
|
||||
@@ -166,6 +166,7 @@ function createMockAppContext(
|
||||
userPreferences: [],
|
||||
hostsData: null,
|
||||
isLoggedIn: true,
|
||||
isPreflightLoading: false,
|
||||
org: [{ createdAt: 0, id: 'org-id', displayName: 'Test Org' }],
|
||||
isFetchingUser: false,
|
||||
isFetchingActiveLicense: false,
|
||||
|
||||
@@ -59,6 +59,7 @@ function App(): JSX.Element {
|
||||
isLoggedIn: isLoggedInState,
|
||||
featureFlags,
|
||||
org,
|
||||
isPreflightLoading,
|
||||
} = useAppContext();
|
||||
const [routes, setRoutes] = useState<AppRoutes[]>(defaultRoutes);
|
||||
const isAIAssistantEnabled = useIsAIAssistantEnabled();
|
||||
@@ -386,6 +387,10 @@ function App(): JSX.Element {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isCloudUser, isEnterpriseSelfHostedUser]);
|
||||
|
||||
if (isPreflightLoading) {
|
||||
return <Spinner tip="Loading..." />;
|
||||
}
|
||||
|
||||
// if the user is in logged in state
|
||||
if (isLoggedInState) {
|
||||
// if the setup calls are loading then return a spinner
|
||||
|
||||
@@ -47,7 +47,6 @@ export const TracesFunnels = Loadable(
|
||||
import(/* webpackChunkName: "Traces Funnels" */ 'pages/TracesModulePage'),
|
||||
);
|
||||
export const TracesFunnelDetails = Loadable(
|
||||
// eslint-disable-next-line sonarjs/no-identical-functions
|
||||
() =>
|
||||
import(
|
||||
/* webpackChunkName: "Traces Funnel Details" */ 'pages/TracesModulePage'
|
||||
@@ -313,13 +312,6 @@ export const PublicDashboardPage = Loadable(
|
||||
),
|
||||
);
|
||||
|
||||
export const AlertTypeSelectionPage = Loadable(
|
||||
() =>
|
||||
import(
|
||||
/* webpackChunkName: "Alert Type Selection Page" */ 'pages/AlertTypeSelection'
|
||||
),
|
||||
);
|
||||
|
||||
export const MeterExplorerPage = Loadable(
|
||||
() =>
|
||||
import(/* webpackChunkName: "Meter Explorer Page" */ 'pages/MeterExplorer'),
|
||||
|
||||
@@ -5,7 +5,6 @@ import {
|
||||
AIAssistantPage,
|
||||
AlertHistory,
|
||||
AlertOverview,
|
||||
AlertTypeSelectionPage,
|
||||
AllAlertChannels,
|
||||
AllErrors,
|
||||
ApiMonitoring,
|
||||
@@ -144,18 +143,18 @@ const routes: AppRoutes[] = [
|
||||
// /trace-old serves V3 (URL-only access). Flip the two `component`
|
||||
// values back to release V3.
|
||||
{
|
||||
path: ROUTES.TRACE_DETAIL,
|
||||
path: ROUTES.TRACE_DETAIL_OLD,
|
||||
exact: true,
|
||||
component: TraceDetail,
|
||||
isPrivate: true,
|
||||
key: 'TRACE_DETAIL',
|
||||
key: 'TRACE_DETAIL_OLD',
|
||||
},
|
||||
{
|
||||
path: ROUTES.TRACE_DETAIL_OLD,
|
||||
path: ROUTES.TRACE_DETAIL,
|
||||
exact: true,
|
||||
component: TraceDetailV3,
|
||||
isPrivate: true,
|
||||
key: 'TRACE_DETAIL_OLD',
|
||||
key: 'TRACE_DETAIL',
|
||||
},
|
||||
{
|
||||
path: ROUTES.SETTINGS,
|
||||
@@ -213,13 +212,6 @@ const routes: AppRoutes[] = [
|
||||
isPrivate: true,
|
||||
key: 'LIST_ALL_ALERT',
|
||||
},
|
||||
{
|
||||
path: ROUTES.ALERT_TYPE_SELECTION,
|
||||
exact: true,
|
||||
component: AlertTypeSelectionPage,
|
||||
isPrivate: true,
|
||||
key: 'ALERT_TYPE_SELECTION',
|
||||
},
|
||||
{
|
||||
path: ROUTES.ALERTS_NEW,
|
||||
exact: true,
|
||||
@@ -533,18 +525,6 @@ export const LIST_LICENSES: AppRoutes = {
|
||||
key: 'LIST_LICENSES',
|
||||
};
|
||||
|
||||
export const oldRoutes = [
|
||||
'/pipelines',
|
||||
'/logs-explorer',
|
||||
'/logs-explorer/live',
|
||||
'/logs-save-views',
|
||||
'/traces-save-views',
|
||||
'/settings/access-tokens',
|
||||
'/settings/api-keys',
|
||||
'/messaging-queues',
|
||||
'/alerts/edit',
|
||||
];
|
||||
|
||||
export const oldNewRoutesMapping: Record<string, string> = {
|
||||
'/pipelines': '/logs/pipelines',
|
||||
'/logs-explorer': '/logs/logs-explorer',
|
||||
@@ -555,7 +535,9 @@ export const oldNewRoutesMapping: Record<string, string> = {
|
||||
'/settings/api-keys': '/settings/service-accounts',
|
||||
'/messaging-queues': '/messaging-queues/overview',
|
||||
'/alerts/edit': '/alerts/overview',
|
||||
'/alerts/type-selection': '/alerts/new',
|
||||
};
|
||||
export const oldRoutes = Object.keys(oldNewRoutesMapping);
|
||||
|
||||
export const ROUTES_NOT_TO_BE_OVERRIDEN: string[] = [
|
||||
ROUTES.WORKSPACE_LOCKED,
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
import axios from 'axios';
|
||||
import { getIsNoAuthMode } from 'utils/noAuthMode';
|
||||
|
||||
import { interceptorRejected } from '../index';
|
||||
|
||||
jest.mock('utils/noAuthMode', () => ({
|
||||
getIsNoAuthMode: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('api/v2/sessions/rotate/post', () => ({
|
||||
__esModule: true,
|
||||
default: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('AppRoutes/utils', () => ({
|
||||
__esModule: true,
|
||||
default: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('../utils', () => ({
|
||||
Logout: jest.fn(),
|
||||
}));
|
||||
|
||||
// oxlint-disable-next-line typescript/no-require-imports typescript/no-var-requires
|
||||
const post = require('api/v2/sessions/rotate/post').default;
|
||||
// oxlint-disable-next-line typescript/no-require-imports typescript/no-var-requires
|
||||
const { Logout } = require('../utils');
|
||||
|
||||
describe('interceptorRejected — no-auth mode', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
jest.spyOn(axios, 'isAxiosError').mockReturnValue(true);
|
||||
});
|
||||
|
||||
it('does NOT call rotate or Logout when no-auth mode is enabled on 401', async () => {
|
||||
(getIsNoAuthMode as jest.Mock).mockReturnValue(true);
|
||||
|
||||
const error = {
|
||||
isAxiosError: true,
|
||||
response: {
|
||||
status: 401,
|
||||
config: { url: '/dashboards', method: 'get' },
|
||||
},
|
||||
config: { url: '/dashboards', headers: {} },
|
||||
};
|
||||
|
||||
await interceptorRejected(error as any).catch(() => {});
|
||||
|
||||
expect(post).not.toHaveBeenCalled();
|
||||
expect(Logout).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('DOES attempt rotate when no-auth mode is disabled on 401', async () => {
|
||||
(getIsNoAuthMode as jest.Mock).mockReturnValue(false);
|
||||
(post as jest.Mock).mockResolvedValue({
|
||||
data: { accessToken: 'a', refreshToken: 'b' },
|
||||
});
|
||||
|
||||
const error = {
|
||||
isAxiosError: true,
|
||||
response: {
|
||||
status: 401,
|
||||
config: { url: '/dashboards', method: 'get' },
|
||||
},
|
||||
config: { url: '/dashboards', headers: {} },
|
||||
};
|
||||
|
||||
await interceptorRejected(error as any).catch(() => {});
|
||||
|
||||
expect(post).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -3,7 +3,6 @@
|
||||
* * The file has been auto-generated using Orval for SigNoz
|
||||
* * regenerate with 'pnpm generate:api'
|
||||
* SigNoz
|
||||
* OpenAPI spec version: 0.0.1
|
||||
*/
|
||||
import { useQuery } from 'react-query';
|
||||
import type {
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
* * The file has been auto-generated using Orval for SigNoz
|
||||
* * regenerate with 'pnpm generate:api'
|
||||
* SigNoz
|
||||
* OpenAPI spec version: 0.0.1
|
||||
*/
|
||||
import { useMutation, useQuery } from 'react-query';
|
||||
import type {
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
* * The file has been auto-generated using Orval for SigNoz
|
||||
* * regenerate with 'pnpm generate:api'
|
||||
* SigNoz
|
||||
* OpenAPI spec version: 0.0.1
|
||||
*/
|
||||
import { useMutation } from 'react-query';
|
||||
import type {
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
* * The file has been auto-generated using Orval for SigNoz
|
||||
* * regenerate with 'pnpm generate:api'
|
||||
* SigNoz
|
||||
* OpenAPI spec version: 0.0.1
|
||||
*/
|
||||
import { useMutation, useQuery } from 'react-query';
|
||||
import type {
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
* * The file has been auto-generated using Orval for SigNoz
|
||||
* * regenerate with 'pnpm generate:api'
|
||||
* SigNoz
|
||||
* OpenAPI spec version: 0.0.1
|
||||
*/
|
||||
import { useMutation, useQuery } from 'react-query';
|
||||
import type {
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
* * The file has been auto-generated using Orval for SigNoz
|
||||
* * regenerate with 'pnpm generate:api'
|
||||
* SigNoz
|
||||
* OpenAPI spec version: 0.0.1
|
||||
*/
|
||||
import { useMutation, useQuery } from 'react-query';
|
||||
import type {
|
||||
@@ -19,18 +18,33 @@ import type {
|
||||
} from 'react-query';
|
||||
|
||||
import type {
|
||||
CreateDashboardV2201,
|
||||
CreatePublicDashboard201,
|
||||
CreatePublicDashboardPathParameters,
|
||||
DashboardtypesJSONPatchDocumentDTO,
|
||||
DashboardtypesPostableDashboardV2DTO,
|
||||
DashboardtypesPostablePublicDashboardDTO,
|
||||
DashboardtypesUpdatablePublicDashboardDTO,
|
||||
DeletePublicDashboardPathParameters,
|
||||
GetDashboardV2200,
|
||||
GetDashboardV2PathParameters,
|
||||
GetPublicDashboard200,
|
||||
GetPublicDashboardData200,
|
||||
GetPublicDashboardDataPathParameters,
|
||||
GetPublicDashboardPathParameters,
|
||||
GetPublicDashboardWidgetQueryRange200,
|
||||
GetPublicDashboardWidgetQueryRangePathParameters,
|
||||
ListDashboardsV2200,
|
||||
ListDashboardsV2Params,
|
||||
LockDashboardV2PathParameters,
|
||||
PatchDashboardV2200,
|
||||
PatchDashboardV2PathParameters,
|
||||
PinDashboardV2PathParameters,
|
||||
RenderErrorResponseDTO,
|
||||
UnlockDashboardV2PathParameters,
|
||||
UnpinDashboardV2PathParameters,
|
||||
UpdateDashboardV2200,
|
||||
UpdateDashboardV2PathParameters,
|
||||
UpdatePublicDashboardPathParameters,
|
||||
} from '../sigNoz.schemas';
|
||||
|
||||
@@ -629,3 +643,799 @@ export const invalidateGetPublicDashboardWidgetQueryRange = async (
|
||||
|
||||
return queryClient;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a page of v2-shape dashboards for the calling user's org. Supports a filter DSL (`query`), sort (`updated_at`/`created_at`/`title`), order (`asc`/`desc`), and offset-based pagination (`limit`/`offset`). Pinned dashboards float to the top of each page.
|
||||
* @summary List dashboards (v2)
|
||||
*/
|
||||
export const listDashboardsV2 = (
|
||||
params?: ListDashboardsV2Params,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<ListDashboardsV2200>({
|
||||
url: `/api/v2/dashboards`,
|
||||
method: 'GET',
|
||||
params,
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getListDashboardsV2QueryKey = (
|
||||
params?: ListDashboardsV2Params,
|
||||
) => {
|
||||
return [`/api/v2/dashboards`, ...(params ? [params] : [])] as const;
|
||||
};
|
||||
|
||||
export const getListDashboardsV2QueryOptions = <
|
||||
TData = Awaited<ReturnType<typeof listDashboardsV2>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
>(
|
||||
params?: ListDashboardsV2Params,
|
||||
options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof listDashboardsV2>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
},
|
||||
) => {
|
||||
const { query: queryOptions } = options ?? {};
|
||||
|
||||
const queryKey = queryOptions?.queryKey ?? getListDashboardsV2QueryKey(params);
|
||||
|
||||
const queryFn: QueryFunction<Awaited<ReturnType<typeof listDashboardsV2>>> = ({
|
||||
signal,
|
||||
}) => listDashboardsV2(params, signal);
|
||||
|
||||
return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof listDashboardsV2>>,
|
||||
TError,
|
||||
TData
|
||||
> & { queryKey: QueryKey };
|
||||
};
|
||||
|
||||
export type ListDashboardsV2QueryResult = NonNullable<
|
||||
Awaited<ReturnType<typeof listDashboardsV2>>
|
||||
>;
|
||||
export type ListDashboardsV2QueryError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @summary List dashboards (v2)
|
||||
*/
|
||||
|
||||
export function useListDashboardsV2<
|
||||
TData = Awaited<ReturnType<typeof listDashboardsV2>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
>(
|
||||
params?: ListDashboardsV2Params,
|
||||
options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof listDashboardsV2>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
},
|
||||
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
|
||||
const queryOptions = getListDashboardsV2QueryOptions(params, options);
|
||||
|
||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary List dashboards (v2)
|
||||
*/
|
||||
export const invalidateListDashboardsV2 = async (
|
||||
queryClient: QueryClient,
|
||||
params?: ListDashboardsV2Params,
|
||||
options?: InvalidateOptions,
|
||||
): Promise<QueryClient> => {
|
||||
await queryClient.invalidateQueries(
|
||||
{ queryKey: getListDashboardsV2QueryKey(params) },
|
||||
options,
|
||||
);
|
||||
|
||||
return queryClient;
|
||||
};
|
||||
|
||||
/**
|
||||
* This endpoint creates a v2-shape dashboard with structured metadata, a typed data tree, and resolved tags.
|
||||
* @summary Create dashboard (v2)
|
||||
*/
|
||||
export const createDashboardV2 = (
|
||||
dashboardtypesPostableDashboardV2DTO?: BodyType<DashboardtypesPostableDashboardV2DTO>,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<CreateDashboardV2201>({
|
||||
url: `/api/v2/dashboards`,
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
data: dashboardtypesPostableDashboardV2DTO,
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getCreateDashboardV2MutationOptions = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof createDashboardV2>>,
|
||||
TError,
|
||||
{ data?: BodyType<DashboardtypesPostableDashboardV2DTO> },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationOptions<
|
||||
Awaited<ReturnType<typeof createDashboardV2>>,
|
||||
TError,
|
||||
{ data?: BodyType<DashboardtypesPostableDashboardV2DTO> },
|
||||
TContext
|
||||
> => {
|
||||
const mutationKey = ['createDashboardV2'];
|
||||
const { mutation: mutationOptions } = options
|
||||
? options.mutation &&
|
||||
'mutationKey' in options.mutation &&
|
||||
options.mutation.mutationKey
|
||||
? options
|
||||
: { ...options, mutation: { ...options.mutation, mutationKey } }
|
||||
: { mutation: { mutationKey } };
|
||||
|
||||
const mutationFn: MutationFunction<
|
||||
Awaited<ReturnType<typeof createDashboardV2>>,
|
||||
{ data?: BodyType<DashboardtypesPostableDashboardV2DTO> }
|
||||
> = (props) => {
|
||||
const { data } = props ?? {};
|
||||
|
||||
return createDashboardV2(data);
|
||||
};
|
||||
|
||||
return { mutationFn, ...mutationOptions };
|
||||
};
|
||||
|
||||
export type CreateDashboardV2MutationResult = NonNullable<
|
||||
Awaited<ReturnType<typeof createDashboardV2>>
|
||||
>;
|
||||
export type CreateDashboardV2MutationBody =
|
||||
| BodyType<DashboardtypesPostableDashboardV2DTO>
|
||||
| undefined;
|
||||
export type CreateDashboardV2MutationError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @summary Create dashboard (v2)
|
||||
*/
|
||||
export const useCreateDashboardV2 = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof createDashboardV2>>,
|
||||
TError,
|
||||
{ data?: BodyType<DashboardtypesPostableDashboardV2DTO> },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationResult<
|
||||
Awaited<ReturnType<typeof createDashboardV2>>,
|
||||
TError,
|
||||
{ data?: BodyType<DashboardtypesPostableDashboardV2DTO> },
|
||||
TContext
|
||||
> => {
|
||||
return useMutation(getCreateDashboardV2MutationOptions(options));
|
||||
};
|
||||
/**
|
||||
* This endpoint returns a v2-shape dashboard with its tags and public sharing config (if any).
|
||||
* @summary Get dashboard (v2)
|
||||
*/
|
||||
export const getDashboardV2 = (
|
||||
{ id }: GetDashboardV2PathParameters,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<GetDashboardV2200>({
|
||||
url: `/api/v2/dashboards/${id}`,
|
||||
method: 'GET',
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getGetDashboardV2QueryKey = ({
|
||||
id,
|
||||
}: GetDashboardV2PathParameters) => {
|
||||
return [`/api/v2/dashboards/${id}`] as const;
|
||||
};
|
||||
|
||||
export const getGetDashboardV2QueryOptions = <
|
||||
TData = Awaited<ReturnType<typeof getDashboardV2>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
>(
|
||||
{ id }: GetDashboardV2PathParameters,
|
||||
options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getDashboardV2>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
},
|
||||
) => {
|
||||
const { query: queryOptions } = options ?? {};
|
||||
|
||||
const queryKey = queryOptions?.queryKey ?? getGetDashboardV2QueryKey({ id });
|
||||
|
||||
const queryFn: QueryFunction<Awaited<ReturnType<typeof getDashboardV2>>> = ({
|
||||
signal,
|
||||
}) => getDashboardV2({ id }, signal);
|
||||
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!id,
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getDashboardV2>>,
|
||||
TError,
|
||||
TData
|
||||
> & { queryKey: QueryKey };
|
||||
};
|
||||
|
||||
export type GetDashboardV2QueryResult = NonNullable<
|
||||
Awaited<ReturnType<typeof getDashboardV2>>
|
||||
>;
|
||||
export type GetDashboardV2QueryError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @summary Get dashboard (v2)
|
||||
*/
|
||||
|
||||
export function useGetDashboardV2<
|
||||
TData = Awaited<ReturnType<typeof getDashboardV2>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
>(
|
||||
{ id }: GetDashboardV2PathParameters,
|
||||
options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getDashboardV2>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
},
|
||||
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
|
||||
const queryOptions = getGetDashboardV2QueryOptions({ id }, options);
|
||||
|
||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Get dashboard (v2)
|
||||
*/
|
||||
export const invalidateGetDashboardV2 = async (
|
||||
queryClient: QueryClient,
|
||||
{ id }: GetDashboardV2PathParameters,
|
||||
options?: InvalidateOptions,
|
||||
): Promise<QueryClient> => {
|
||||
await queryClient.invalidateQueries(
|
||||
{ queryKey: getGetDashboardV2QueryKey({ id }) },
|
||||
options,
|
||||
);
|
||||
|
||||
return queryClient;
|
||||
};
|
||||
|
||||
/**
|
||||
* This endpoint applies an RFC 6902 JSON Patch to a v2-shape dashboard. The patch is applied against the postable view of the dashboard (metadata, data, tags), so individual panels, queries, variables, layouts, or tags can be updated without re-sending the rest of the dashboard. Locked dashboards are rejected.
|
||||
* @summary Patch dashboard (v2)
|
||||
*/
|
||||
export const patchDashboardV2 = (
|
||||
{ id }: PatchDashboardV2PathParameters,
|
||||
dashboardtypesJSONPatchDocumentDTONull?: BodyType<DashboardtypesJSONPatchDocumentDTO | null> | null,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<PatchDashboardV2200>({
|
||||
url: `/api/v2/dashboards/${id}`,
|
||||
method: 'PATCH',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
data: dashboardtypesJSONPatchDocumentDTONull,
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getPatchDashboardV2MutationOptions = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof patchDashboardV2>>,
|
||||
TError,
|
||||
{
|
||||
pathParams: PatchDashboardV2PathParameters;
|
||||
data?: BodyType<DashboardtypesJSONPatchDocumentDTO | null>;
|
||||
},
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationOptions<
|
||||
Awaited<ReturnType<typeof patchDashboardV2>>,
|
||||
TError,
|
||||
{
|
||||
pathParams: PatchDashboardV2PathParameters;
|
||||
data?: BodyType<DashboardtypesJSONPatchDocumentDTO | null>;
|
||||
},
|
||||
TContext
|
||||
> => {
|
||||
const mutationKey = ['patchDashboardV2'];
|
||||
const { mutation: mutationOptions } = options
|
||||
? options.mutation &&
|
||||
'mutationKey' in options.mutation &&
|
||||
options.mutation.mutationKey
|
||||
? options
|
||||
: { ...options, mutation: { ...options.mutation, mutationKey } }
|
||||
: { mutation: { mutationKey } };
|
||||
|
||||
const mutationFn: MutationFunction<
|
||||
Awaited<ReturnType<typeof patchDashboardV2>>,
|
||||
{
|
||||
pathParams: PatchDashboardV2PathParameters;
|
||||
data?: BodyType<DashboardtypesJSONPatchDocumentDTO | null>;
|
||||
}
|
||||
> = (props) => {
|
||||
const { pathParams, data } = props ?? {};
|
||||
|
||||
return patchDashboardV2(pathParams, data);
|
||||
};
|
||||
|
||||
return { mutationFn, ...mutationOptions };
|
||||
};
|
||||
|
||||
export type PatchDashboardV2MutationResult = NonNullable<
|
||||
Awaited<ReturnType<typeof patchDashboardV2>>
|
||||
>;
|
||||
export type PatchDashboardV2MutationBody =
|
||||
| BodyType<DashboardtypesJSONPatchDocumentDTO | null>
|
||||
| undefined;
|
||||
export type PatchDashboardV2MutationError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @summary Patch dashboard (v2)
|
||||
*/
|
||||
export const usePatchDashboardV2 = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof patchDashboardV2>>,
|
||||
TError,
|
||||
{
|
||||
pathParams: PatchDashboardV2PathParameters;
|
||||
data?: BodyType<DashboardtypesJSONPatchDocumentDTO | null>;
|
||||
},
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationResult<
|
||||
Awaited<ReturnType<typeof patchDashboardV2>>,
|
||||
TError,
|
||||
{
|
||||
pathParams: PatchDashboardV2PathParameters;
|
||||
data?: BodyType<DashboardtypesJSONPatchDocumentDTO | null>;
|
||||
},
|
||||
TContext
|
||||
> => {
|
||||
return useMutation(getPatchDashboardV2MutationOptions(options));
|
||||
};
|
||||
/**
|
||||
* This endpoint updates a v2-shape dashboard's metadata, data, and tag set. Locked dashboards are rejected.
|
||||
* @summary Update dashboard (v2)
|
||||
*/
|
||||
export const updateDashboardV2 = (
|
||||
{ id }: UpdateDashboardV2PathParameters,
|
||||
dashboardtypesPostableDashboardV2DTO?: BodyType<DashboardtypesPostableDashboardV2DTO>,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<UpdateDashboardV2200>({
|
||||
url: `/api/v2/dashboards/${id}`,
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
data: dashboardtypesPostableDashboardV2DTO,
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getUpdateDashboardV2MutationOptions = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof updateDashboardV2>>,
|
||||
TError,
|
||||
{
|
||||
pathParams: UpdateDashboardV2PathParameters;
|
||||
data?: BodyType<DashboardtypesPostableDashboardV2DTO>;
|
||||
},
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationOptions<
|
||||
Awaited<ReturnType<typeof updateDashboardV2>>,
|
||||
TError,
|
||||
{
|
||||
pathParams: UpdateDashboardV2PathParameters;
|
||||
data?: BodyType<DashboardtypesPostableDashboardV2DTO>;
|
||||
},
|
||||
TContext
|
||||
> => {
|
||||
const mutationKey = ['updateDashboardV2'];
|
||||
const { mutation: mutationOptions } = options
|
||||
? options.mutation &&
|
||||
'mutationKey' in options.mutation &&
|
||||
options.mutation.mutationKey
|
||||
? options
|
||||
: { ...options, mutation: { ...options.mutation, mutationKey } }
|
||||
: { mutation: { mutationKey } };
|
||||
|
||||
const mutationFn: MutationFunction<
|
||||
Awaited<ReturnType<typeof updateDashboardV2>>,
|
||||
{
|
||||
pathParams: UpdateDashboardV2PathParameters;
|
||||
data?: BodyType<DashboardtypesPostableDashboardV2DTO>;
|
||||
}
|
||||
> = (props) => {
|
||||
const { pathParams, data } = props ?? {};
|
||||
|
||||
return updateDashboardV2(pathParams, data);
|
||||
};
|
||||
|
||||
return { mutationFn, ...mutationOptions };
|
||||
};
|
||||
|
||||
export type UpdateDashboardV2MutationResult = NonNullable<
|
||||
Awaited<ReturnType<typeof updateDashboardV2>>
|
||||
>;
|
||||
export type UpdateDashboardV2MutationBody =
|
||||
| BodyType<DashboardtypesPostableDashboardV2DTO>
|
||||
| undefined;
|
||||
export type UpdateDashboardV2MutationError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @summary Update dashboard (v2)
|
||||
*/
|
||||
export const useUpdateDashboardV2 = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof updateDashboardV2>>,
|
||||
TError,
|
||||
{
|
||||
pathParams: UpdateDashboardV2PathParameters;
|
||||
data?: BodyType<DashboardtypesPostableDashboardV2DTO>;
|
||||
},
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationResult<
|
||||
Awaited<ReturnType<typeof updateDashboardV2>>,
|
||||
TError,
|
||||
{
|
||||
pathParams: UpdateDashboardV2PathParameters;
|
||||
data?: BodyType<DashboardtypesPostableDashboardV2DTO>;
|
||||
},
|
||||
TContext
|
||||
> => {
|
||||
return useMutation(getUpdateDashboardV2MutationOptions(options));
|
||||
};
|
||||
/**
|
||||
* This endpoint unlocks a v2-shape dashboard. Only the dashboard's creator or an org admin may lock or unlock.
|
||||
* @summary Unlock dashboard (v2)
|
||||
*/
|
||||
export const unlockDashboardV2 = (
|
||||
{ id }: UnlockDashboardV2PathParameters,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<string>({
|
||||
url: `/api/v2/dashboards/${id}/lock`,
|
||||
method: 'DELETE',
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getUnlockDashboardV2MutationOptions = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof unlockDashboardV2>>,
|
||||
TError,
|
||||
{ pathParams: UnlockDashboardV2PathParameters },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationOptions<
|
||||
Awaited<ReturnType<typeof unlockDashboardV2>>,
|
||||
TError,
|
||||
{ pathParams: UnlockDashboardV2PathParameters },
|
||||
TContext
|
||||
> => {
|
||||
const mutationKey = ['unlockDashboardV2'];
|
||||
const { mutation: mutationOptions } = options
|
||||
? options.mutation &&
|
||||
'mutationKey' in options.mutation &&
|
||||
options.mutation.mutationKey
|
||||
? options
|
||||
: { ...options, mutation: { ...options.mutation, mutationKey } }
|
||||
: { mutation: { mutationKey } };
|
||||
|
||||
const mutationFn: MutationFunction<
|
||||
Awaited<ReturnType<typeof unlockDashboardV2>>,
|
||||
{ pathParams: UnlockDashboardV2PathParameters }
|
||||
> = (props) => {
|
||||
const { pathParams } = props ?? {};
|
||||
|
||||
return unlockDashboardV2(pathParams);
|
||||
};
|
||||
|
||||
return { mutationFn, ...mutationOptions };
|
||||
};
|
||||
|
||||
export type UnlockDashboardV2MutationResult = NonNullable<
|
||||
Awaited<ReturnType<typeof unlockDashboardV2>>
|
||||
>;
|
||||
|
||||
export type UnlockDashboardV2MutationError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @summary Unlock dashboard (v2)
|
||||
*/
|
||||
export const useUnlockDashboardV2 = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof unlockDashboardV2>>,
|
||||
TError,
|
||||
{ pathParams: UnlockDashboardV2PathParameters },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationResult<
|
||||
Awaited<ReturnType<typeof unlockDashboardV2>>,
|
||||
TError,
|
||||
{ pathParams: UnlockDashboardV2PathParameters },
|
||||
TContext
|
||||
> => {
|
||||
return useMutation(getUnlockDashboardV2MutationOptions(options));
|
||||
};
|
||||
/**
|
||||
* This endpoint locks a v2-shape dashboard. Only the dashboard's creator or an org admin may lock or unlock.
|
||||
* @summary Lock dashboard (v2)
|
||||
*/
|
||||
export const lockDashboardV2 = (
|
||||
{ id }: LockDashboardV2PathParameters,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<string>({
|
||||
url: `/api/v2/dashboards/${id}/lock`,
|
||||
method: 'PUT',
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getLockDashboardV2MutationOptions = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof lockDashboardV2>>,
|
||||
TError,
|
||||
{ pathParams: LockDashboardV2PathParameters },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationOptions<
|
||||
Awaited<ReturnType<typeof lockDashboardV2>>,
|
||||
TError,
|
||||
{ pathParams: LockDashboardV2PathParameters },
|
||||
TContext
|
||||
> => {
|
||||
const mutationKey = ['lockDashboardV2'];
|
||||
const { mutation: mutationOptions } = options
|
||||
? options.mutation &&
|
||||
'mutationKey' in options.mutation &&
|
||||
options.mutation.mutationKey
|
||||
? options
|
||||
: { ...options, mutation: { ...options.mutation, mutationKey } }
|
||||
: { mutation: { mutationKey } };
|
||||
|
||||
const mutationFn: MutationFunction<
|
||||
Awaited<ReturnType<typeof lockDashboardV2>>,
|
||||
{ pathParams: LockDashboardV2PathParameters }
|
||||
> = (props) => {
|
||||
const { pathParams } = props ?? {};
|
||||
|
||||
return lockDashboardV2(pathParams);
|
||||
};
|
||||
|
||||
return { mutationFn, ...mutationOptions };
|
||||
};
|
||||
|
||||
export type LockDashboardV2MutationResult = NonNullable<
|
||||
Awaited<ReturnType<typeof lockDashboardV2>>
|
||||
>;
|
||||
|
||||
export type LockDashboardV2MutationError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @summary Lock dashboard (v2)
|
||||
*/
|
||||
export const useLockDashboardV2 = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof lockDashboardV2>>,
|
||||
TError,
|
||||
{ pathParams: LockDashboardV2PathParameters },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationResult<
|
||||
Awaited<ReturnType<typeof lockDashboardV2>>,
|
||||
TError,
|
||||
{ pathParams: LockDashboardV2PathParameters },
|
||||
TContext
|
||||
> => {
|
||||
return useMutation(getLockDashboardV2MutationOptions(options));
|
||||
};
|
||||
/**
|
||||
* Removes the pin for the calling user. Idempotent — unpinning a dashboard that wasn't pinned still returns 204.
|
||||
* @summary Unpin a dashboard for the current user (v2)
|
||||
*/
|
||||
export const unpinDashboardV2 = (
|
||||
{ id }: UnpinDashboardV2PathParameters,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<string>({
|
||||
url: `/api/v2/dashboards/${id}/pins/me`,
|
||||
method: 'DELETE',
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getUnpinDashboardV2MutationOptions = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof unpinDashboardV2>>,
|
||||
TError,
|
||||
{ pathParams: UnpinDashboardV2PathParameters },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationOptions<
|
||||
Awaited<ReturnType<typeof unpinDashboardV2>>,
|
||||
TError,
|
||||
{ pathParams: UnpinDashboardV2PathParameters },
|
||||
TContext
|
||||
> => {
|
||||
const mutationKey = ['unpinDashboardV2'];
|
||||
const { mutation: mutationOptions } = options
|
||||
? options.mutation &&
|
||||
'mutationKey' in options.mutation &&
|
||||
options.mutation.mutationKey
|
||||
? options
|
||||
: { ...options, mutation: { ...options.mutation, mutationKey } }
|
||||
: { mutation: { mutationKey } };
|
||||
|
||||
const mutationFn: MutationFunction<
|
||||
Awaited<ReturnType<typeof unpinDashboardV2>>,
|
||||
{ pathParams: UnpinDashboardV2PathParameters }
|
||||
> = (props) => {
|
||||
const { pathParams } = props ?? {};
|
||||
|
||||
return unpinDashboardV2(pathParams);
|
||||
};
|
||||
|
||||
return { mutationFn, ...mutationOptions };
|
||||
};
|
||||
|
||||
export type UnpinDashboardV2MutationResult = NonNullable<
|
||||
Awaited<ReturnType<typeof unpinDashboardV2>>
|
||||
>;
|
||||
|
||||
export type UnpinDashboardV2MutationError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @summary Unpin a dashboard for the current user (v2)
|
||||
*/
|
||||
export const useUnpinDashboardV2 = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof unpinDashboardV2>>,
|
||||
TError,
|
||||
{ pathParams: UnpinDashboardV2PathParameters },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationResult<
|
||||
Awaited<ReturnType<typeof unpinDashboardV2>>,
|
||||
TError,
|
||||
{ pathParams: UnpinDashboardV2PathParameters },
|
||||
TContext
|
||||
> => {
|
||||
return useMutation(getUnpinDashboardV2MutationOptions(options));
|
||||
};
|
||||
/**
|
||||
* Pins the dashboard for the calling user. A user can pin at most 10 dashboards; pinning when at the limit returns 409. Re-pinning an already-pinned dashboard is a no-op success.
|
||||
* @summary Pin a dashboard for the current user (v2)
|
||||
*/
|
||||
export const pinDashboardV2 = (
|
||||
{ id }: PinDashboardV2PathParameters,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<string>({
|
||||
url: `/api/v2/dashboards/${id}/pins/me`,
|
||||
method: 'PUT',
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getPinDashboardV2MutationOptions = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof pinDashboardV2>>,
|
||||
TError,
|
||||
{ pathParams: PinDashboardV2PathParameters },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationOptions<
|
||||
Awaited<ReturnType<typeof pinDashboardV2>>,
|
||||
TError,
|
||||
{ pathParams: PinDashboardV2PathParameters },
|
||||
TContext
|
||||
> => {
|
||||
const mutationKey = ['pinDashboardV2'];
|
||||
const { mutation: mutationOptions } = options
|
||||
? options.mutation &&
|
||||
'mutationKey' in options.mutation &&
|
||||
options.mutation.mutationKey
|
||||
? options
|
||||
: { ...options, mutation: { ...options.mutation, mutationKey } }
|
||||
: { mutation: { mutationKey } };
|
||||
|
||||
const mutationFn: MutationFunction<
|
||||
Awaited<ReturnType<typeof pinDashboardV2>>,
|
||||
{ pathParams: PinDashboardV2PathParameters }
|
||||
> = (props) => {
|
||||
const { pathParams } = props ?? {};
|
||||
|
||||
return pinDashboardV2(pathParams);
|
||||
};
|
||||
|
||||
return { mutationFn, ...mutationOptions };
|
||||
};
|
||||
|
||||
export type PinDashboardV2MutationResult = NonNullable<
|
||||
Awaited<ReturnType<typeof pinDashboardV2>>
|
||||
>;
|
||||
|
||||
export type PinDashboardV2MutationError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @summary Pin a dashboard for the current user (v2)
|
||||
*/
|
||||
export const usePinDashboardV2 = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof pinDashboardV2>>,
|
||||
TError,
|
||||
{ pathParams: PinDashboardV2PathParameters },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationResult<
|
||||
Awaited<ReturnType<typeof pinDashboardV2>>,
|
||||
TError,
|
||||
{ pathParams: PinDashboardV2PathParameters },
|
||||
TContext
|
||||
> => {
|
||||
return useMutation(getPinDashboardV2MutationOptions(options));
|
||||
};
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
* * The file has been auto-generated using Orval for SigNoz
|
||||
* * regenerate with 'pnpm generate:api'
|
||||
* SigNoz
|
||||
* OpenAPI spec version: 0.0.1
|
||||
*/
|
||||
import { useMutation, useQuery } from 'react-query';
|
||||
import type {
|
||||
@@ -19,6 +18,7 @@ import type {
|
||||
} from 'react-query';
|
||||
|
||||
import type {
|
||||
AlertmanagertypesPostablePlannedMaintenanceDTO,
|
||||
CreateDowntimeSchedule201,
|
||||
DeleteDowntimeScheduleByIDPathParameters,
|
||||
GetDowntimeScheduleByID200,
|
||||
@@ -26,7 +26,6 @@ import type {
|
||||
ListDowntimeSchedules200,
|
||||
ListDowntimeSchedulesParams,
|
||||
RenderErrorResponseDTO,
|
||||
RuletypesPostablePlannedMaintenanceDTO,
|
||||
UpdateDowntimeScheduleByIDPathParameters,
|
||||
} from '../sigNoz.schemas';
|
||||
|
||||
@@ -136,14 +135,14 @@ export const invalidateListDowntimeSchedules = async (
|
||||
* @summary Create downtime schedule
|
||||
*/
|
||||
export const createDowntimeSchedule = (
|
||||
ruletypesPostablePlannedMaintenanceDTO?: BodyType<RuletypesPostablePlannedMaintenanceDTO>,
|
||||
alertmanagertypesPostablePlannedMaintenanceDTO?: BodyType<AlertmanagertypesPostablePlannedMaintenanceDTO>,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<CreateDowntimeSchedule201>({
|
||||
url: `/api/v1/downtime_schedules`,
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
data: ruletypesPostablePlannedMaintenanceDTO,
|
||||
data: alertmanagertypesPostablePlannedMaintenanceDTO,
|
||||
signal,
|
||||
});
|
||||
};
|
||||
@@ -155,13 +154,13 @@ export const getCreateDowntimeScheduleMutationOptions = <
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof createDowntimeSchedule>>,
|
||||
TError,
|
||||
{ data?: BodyType<RuletypesPostablePlannedMaintenanceDTO> },
|
||||
{ data?: BodyType<AlertmanagertypesPostablePlannedMaintenanceDTO> },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationOptions<
|
||||
Awaited<ReturnType<typeof createDowntimeSchedule>>,
|
||||
TError,
|
||||
{ data?: BodyType<RuletypesPostablePlannedMaintenanceDTO> },
|
||||
{ data?: BodyType<AlertmanagertypesPostablePlannedMaintenanceDTO> },
|
||||
TContext
|
||||
> => {
|
||||
const mutationKey = ['createDowntimeSchedule'];
|
||||
@@ -175,7 +174,7 @@ export const getCreateDowntimeScheduleMutationOptions = <
|
||||
|
||||
const mutationFn: MutationFunction<
|
||||
Awaited<ReturnType<typeof createDowntimeSchedule>>,
|
||||
{ data?: BodyType<RuletypesPostablePlannedMaintenanceDTO> }
|
||||
{ data?: BodyType<AlertmanagertypesPostablePlannedMaintenanceDTO> }
|
||||
> = (props) => {
|
||||
const { data } = props ?? {};
|
||||
|
||||
@@ -189,7 +188,7 @@ export type CreateDowntimeScheduleMutationResult = NonNullable<
|
||||
Awaited<ReturnType<typeof createDowntimeSchedule>>
|
||||
>;
|
||||
export type CreateDowntimeScheduleMutationBody =
|
||||
| BodyType<RuletypesPostablePlannedMaintenanceDTO>
|
||||
| BodyType<AlertmanagertypesPostablePlannedMaintenanceDTO>
|
||||
| undefined;
|
||||
export type CreateDowntimeScheduleMutationError =
|
||||
ErrorType<RenderErrorResponseDTO>;
|
||||
@@ -204,13 +203,13 @@ export const useCreateDowntimeSchedule = <
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof createDowntimeSchedule>>,
|
||||
TError,
|
||||
{ data?: BodyType<RuletypesPostablePlannedMaintenanceDTO> },
|
||||
{ data?: BodyType<AlertmanagertypesPostablePlannedMaintenanceDTO> },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationResult<
|
||||
Awaited<ReturnType<typeof createDowntimeSchedule>>,
|
||||
TError,
|
||||
{ data?: BodyType<RuletypesPostablePlannedMaintenanceDTO> },
|
||||
{ data?: BodyType<AlertmanagertypesPostablePlannedMaintenanceDTO> },
|
||||
TContext
|
||||
> => {
|
||||
return useMutation(getCreateDowntimeScheduleMutationOptions(options));
|
||||
@@ -404,14 +403,14 @@ export const invalidateGetDowntimeScheduleByID = async (
|
||||
*/
|
||||
export const updateDowntimeScheduleByID = (
|
||||
{ id }: UpdateDowntimeScheduleByIDPathParameters,
|
||||
ruletypesPostablePlannedMaintenanceDTO?: BodyType<RuletypesPostablePlannedMaintenanceDTO>,
|
||||
alertmanagertypesPostablePlannedMaintenanceDTO?: BodyType<AlertmanagertypesPostablePlannedMaintenanceDTO>,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<void>({
|
||||
url: `/api/v1/downtime_schedules/${id}`,
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
data: ruletypesPostablePlannedMaintenanceDTO,
|
||||
data: alertmanagertypesPostablePlannedMaintenanceDTO,
|
||||
signal,
|
||||
});
|
||||
};
|
||||
@@ -425,7 +424,7 @@ export const getUpdateDowntimeScheduleByIDMutationOptions = <
|
||||
TError,
|
||||
{
|
||||
pathParams: UpdateDowntimeScheduleByIDPathParameters;
|
||||
data?: BodyType<RuletypesPostablePlannedMaintenanceDTO>;
|
||||
data?: BodyType<AlertmanagertypesPostablePlannedMaintenanceDTO>;
|
||||
},
|
||||
TContext
|
||||
>;
|
||||
@@ -434,7 +433,7 @@ export const getUpdateDowntimeScheduleByIDMutationOptions = <
|
||||
TError,
|
||||
{
|
||||
pathParams: UpdateDowntimeScheduleByIDPathParameters;
|
||||
data?: BodyType<RuletypesPostablePlannedMaintenanceDTO>;
|
||||
data?: BodyType<AlertmanagertypesPostablePlannedMaintenanceDTO>;
|
||||
},
|
||||
TContext
|
||||
> => {
|
||||
@@ -451,7 +450,7 @@ export const getUpdateDowntimeScheduleByIDMutationOptions = <
|
||||
Awaited<ReturnType<typeof updateDowntimeScheduleByID>>,
|
||||
{
|
||||
pathParams: UpdateDowntimeScheduleByIDPathParameters;
|
||||
data?: BodyType<RuletypesPostablePlannedMaintenanceDTO>;
|
||||
data?: BodyType<AlertmanagertypesPostablePlannedMaintenanceDTO>;
|
||||
}
|
||||
> = (props) => {
|
||||
const { pathParams, data } = props ?? {};
|
||||
@@ -466,7 +465,7 @@ export type UpdateDowntimeScheduleByIDMutationResult = NonNullable<
|
||||
Awaited<ReturnType<typeof updateDowntimeScheduleByID>>
|
||||
>;
|
||||
export type UpdateDowntimeScheduleByIDMutationBody =
|
||||
| BodyType<RuletypesPostablePlannedMaintenanceDTO>
|
||||
| BodyType<AlertmanagertypesPostablePlannedMaintenanceDTO>
|
||||
| undefined;
|
||||
export type UpdateDowntimeScheduleByIDMutationError =
|
||||
ErrorType<RenderErrorResponseDTO>;
|
||||
@@ -483,7 +482,7 @@ export const useUpdateDowntimeScheduleByID = <
|
||||
TError,
|
||||
{
|
||||
pathParams: UpdateDowntimeScheduleByIDPathParameters;
|
||||
data?: BodyType<RuletypesPostablePlannedMaintenanceDTO>;
|
||||
data?: BodyType<AlertmanagertypesPostablePlannedMaintenanceDTO>;
|
||||
},
|
||||
TContext
|
||||
>;
|
||||
@@ -492,7 +491,7 @@ export const useUpdateDowntimeScheduleByID = <
|
||||
TError,
|
||||
{
|
||||
pathParams: UpdateDowntimeScheduleByIDPathParameters;
|
||||
data?: BodyType<RuletypesPostablePlannedMaintenanceDTO>;
|
||||
data?: BodyType<AlertmanagertypesPostablePlannedMaintenanceDTO>;
|
||||
},
|
||||
TContext
|
||||
> => {
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
* * The file has been auto-generated using Orval for SigNoz
|
||||
* * regenerate with 'pnpm generate:api'
|
||||
* SigNoz
|
||||
* OpenAPI spec version: 0.0.1
|
||||
*/
|
||||
import { useQuery } from 'react-query';
|
||||
import type {
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
* * The file has been auto-generated using Orval for SigNoz
|
||||
* * regenerate with 'pnpm generate:api'
|
||||
* SigNoz
|
||||
* OpenAPI spec version: 0.0.1
|
||||
*/
|
||||
import { useQuery } from 'react-query';
|
||||
import type {
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
* * The file has been auto-generated using Orval for SigNoz
|
||||
* * regenerate with 'pnpm generate:api'
|
||||
* SigNoz
|
||||
* OpenAPI spec version: 0.0.1
|
||||
*/
|
||||
import { useMutation, useQuery } from 'react-query';
|
||||
import type {
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
* * The file has been auto-generated using Orval for SigNoz
|
||||
* * regenerate with 'pnpm generate:api'
|
||||
* SigNoz
|
||||
* OpenAPI spec version: 0.0.1
|
||||
*/
|
||||
import { useQuery } from 'react-query';
|
||||
import type {
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
* * The file has been auto-generated using Orval for SigNoz
|
||||
* * regenerate with 'pnpm generate:api'
|
||||
* SigNoz
|
||||
* OpenAPI spec version: 0.0.1
|
||||
*/
|
||||
import { useQuery } from 'react-query';
|
||||
import type {
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
* * The file has been auto-generated using Orval for SigNoz
|
||||
* * regenerate with 'pnpm generate:api'
|
||||
* SigNoz
|
||||
* OpenAPI spec version: 0.0.1
|
||||
*/
|
||||
import { useMutation } from 'react-query';
|
||||
import type {
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
* * The file has been auto-generated using Orval for SigNoz
|
||||
* * regenerate with 'pnpm generate:api'
|
||||
* SigNoz
|
||||
* OpenAPI spec version: 0.0.1
|
||||
*/
|
||||
import { useMutation, useQuery } from 'react-query';
|
||||
import type {
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
* * The file has been auto-generated using Orval for SigNoz
|
||||
* * regenerate with 'pnpm generate:api'
|
||||
* SigNoz
|
||||
* OpenAPI spec version: 0.0.1
|
||||
*/
|
||||
import { useMutation, useQuery } from 'react-query';
|
||||
import type {
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
* * The file has been auto-generated using Orval for SigNoz
|
||||
* * regenerate with 'pnpm generate:api'
|
||||
* SigNoz
|
||||
* OpenAPI spec version: 0.0.1
|
||||
*/
|
||||
import { useMutation, useQuery } from 'react-query';
|
||||
import type {
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
* * The file has been auto-generated using Orval for SigNoz
|
||||
* * regenerate with 'pnpm generate:api'
|
||||
* SigNoz
|
||||
* OpenAPI spec version: 0.0.1
|
||||
*/
|
||||
import { useMutation, useQuery } from 'react-query';
|
||||
import type {
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
* * The file has been auto-generated using Orval for SigNoz
|
||||
* * regenerate with 'pnpm generate:api'
|
||||
* SigNoz
|
||||
* OpenAPI spec version: 0.0.1
|
||||
*/
|
||||
import { useMutation, useQuery } from 'react-query';
|
||||
import type {
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
* * The file has been auto-generated using Orval for SigNoz
|
||||
* * regenerate with 'pnpm generate:api'
|
||||
* SigNoz
|
||||
* OpenAPI spec version: 0.0.1
|
||||
*/
|
||||
import { useMutation } from 'react-query';
|
||||
import type {
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
* * The file has been auto-generated using Orval for SigNoz
|
||||
* * regenerate with 'pnpm generate:api'
|
||||
* SigNoz
|
||||
* OpenAPI spec version: 0.0.1
|
||||
*/
|
||||
import { useMutation, useQuery } from 'react-query';
|
||||
import type {
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
* * The file has been auto-generated using Orval for SigNoz
|
||||
* * regenerate with 'pnpm generate:api'
|
||||
* SigNoz
|
||||
* OpenAPI spec version: 0.0.1
|
||||
*/
|
||||
import { useMutation, useQuery } from 'react-query';
|
||||
import type {
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
* * The file has been auto-generated using Orval for SigNoz
|
||||
* * regenerate with 'pnpm generate:api'
|
||||
* SigNoz
|
||||
* OpenAPI spec version: 0.0.1
|
||||
*/
|
||||
import { useMutation, useQuery } from 'react-query';
|
||||
import type {
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
* * The file has been auto-generated using Orval for SigNoz
|
||||
* * regenerate with 'pnpm generate:api'
|
||||
* SigNoz
|
||||
* OpenAPI spec version: 0.0.1
|
||||
*/
|
||||
import { useMutation, useQuery } from 'react-query';
|
||||
import type {
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
* * The file has been auto-generated using Orval for SigNoz
|
||||
* * regenerate with 'pnpm generate:api'
|
||||
* SigNoz
|
||||
* OpenAPI spec version: 0.0.1
|
||||
*/
|
||||
import { useMutation, useQuery } from 'react-query';
|
||||
import type {
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
* * The file has been auto-generated using Orval for SigNoz
|
||||
* * regenerate with 'pnpm generate:api'
|
||||
* SigNoz
|
||||
* OpenAPI spec version: 0.0.1
|
||||
*/
|
||||
import { useMutation, useQuery } from 'react-query';
|
||||
import type {
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
* * The file has been auto-generated using Orval for SigNoz
|
||||
* * regenerate with 'pnpm generate:api'
|
||||
* SigNoz
|
||||
* OpenAPI spec version: 0.0.1
|
||||
*/
|
||||
import { useMutation } from 'react-query';
|
||||
import type {
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
* * The file has been auto-generated using Orval for SigNoz
|
||||
* * regenerate with 'pnpm generate:api'
|
||||
* SigNoz
|
||||
* OpenAPI spec version: 0.0.1
|
||||
*/
|
||||
import { useMutation, useQuery } from 'react-query';
|
||||
import type {
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
* * The file has been auto-generated using Orval for SigNoz
|
||||
* * regenerate with 'pnpm generate:api'
|
||||
* SigNoz
|
||||
* OpenAPI spec version: 0.0.1
|
||||
*/
|
||||
import { useMutation, useQuery } from 'react-query';
|
||||
import type {
|
||||
|
||||
@@ -13,6 +13,7 @@ import { Events } from 'constants/events';
|
||||
import { LOCALSTORAGE } from 'constants/localStorage';
|
||||
import { getBasePath } from 'utils/basePath';
|
||||
import { eventEmitter } from 'utils/getEventEmitter';
|
||||
import { getIsNoAuthMode } from 'utils/noAuthMode';
|
||||
|
||||
import apiV1, { apiAlertManager, apiV2, apiV3, apiV4, apiV5 } from './apiV1';
|
||||
import { Logout } from './utils';
|
||||
@@ -108,7 +109,10 @@ export const interceptorRejected = async (
|
||||
if (axios.isAxiosError(value) && value.response) {
|
||||
const { response } = value;
|
||||
|
||||
const isNoAuthMode = getIsNoAuthMode();
|
||||
|
||||
if (
|
||||
!isNoAuthMode &&
|
||||
response.status === 401 &&
|
||||
// if the session rotate call or the create session errors out with 401 or the delete sessions call returns 401 then we do not retry!
|
||||
response.config.url !== '/sessions/rotate' &&
|
||||
@@ -140,16 +144,20 @@ export const interceptorRejected = async (
|
||||
return await Promise.resolve(reResponse);
|
||||
} catch (error) {
|
||||
if ((error as AxiosError)?.response?.status === 401) {
|
||||
Logout();
|
||||
void Logout();
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
Logout();
|
||||
void Logout();
|
||||
}
|
||||
}
|
||||
|
||||
if (response.status === 401 && response.config.url === '/sessions/rotate') {
|
||||
Logout();
|
||||
if (
|
||||
!isNoAuthMode &&
|
||||
response.status === 401 &&
|
||||
response.config.url === '/sessions/rotate'
|
||||
) {
|
||||
void Logout();
|
||||
}
|
||||
}
|
||||
return await Promise.reject(value);
|
||||
|
||||
3
frontend/src/assets/Logos/apache-druid.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill="currentColor" d="M8.932 20.806c-.369 0-.738.007-1.109 0-.35-.007-.587-.206-.623-.5a.587.587 0 0 1 .53-.636c.79-.062 1.582-.063 2.372-.003a.548.548 0 0 1 .522.602c-.024.326-.253.526-.616.54zM1.792 8.345c-.392 0-.782.008-1.173.002-.327-.006-.577-.22-.614-.512-.037-.293.146-.544.499-.615.192-.032.388-.045.583-.039a81.515 81.515 0 0 1 1.597 0c.163 0 .325.019.483.056.288.073.445.318.411.617-.034.298-.214.477-.515.487-.424.014-.848.004-1.272.004zm7.588 8.417H4.292a2.464 2.464 0 0 1-.326-.007c-.294-.04-.48-.209-.508-.506-.029-.298.11-.501.391-.606.179-.065.365-.051.549-.051 3.347 0 6.695.005 10.042-.006 1.174-.004 2.187-.439 2.993-1.3.69-.738 1.053-1.63 1.16-2.635.085-.788-.027-1.513-.516-2.156-.544-.718-1.28-1.078-2.163-1.082-3.163-.013-6.328-.005-9.487-.01-.336 0-.673-.027-1.007-.058-.29-.027-.45-.201-.469-.492-.021-.317.141-.545.429-.6a1.55 1.55 0 0 1 .29-.015h10.177c1.71.004 3.187 1.038 3.726 2.654.383 1.147.246 2.304-.182 3.416-.824 2.135-2.762 3.448-5.055 3.454-1.652.005-3.304 0-4.956 0zm2.906-13.568c1.533 0 3.066-.008 4.598 0 2.935.018 5.629 1.892 6.653 4.626.442 1.181.538 2.403.412 3.657-.185 1.842-.735 3.552-1.776 5.084-1.608 2.365-3.873 3.68-6.679 4.118-.95.148-1.905.13-2.86.13-.397 0-.61-.181-.633-.51-.025-.351.196-.621.587-.645.434-.026.87-.004 1.305-.016 2.641-.072 4.928-.982 6.74-2.935 1.269-1.37 1.912-3.039 2.13-4.878.151-1.275.135-2.544-.37-3.752-.773-1.85-2.159-2.983-4.068-3.509-.74-.204-1.5-.243-2.26-.247-2.837-.017-5.675-.007-8.511-.007-.12 0-.24.004-.359-.006a.57.57 0 0 1-.517-.536.557.557 0 0 1 .456-.557c.13-.018.261-.024.392-.019h4.762Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
7
frontend/src/assets/Logos/aspnet.svg
Normal file
@@ -0,0 +1,7 @@
|
||||
<svg width="456" height="456" viewBox="0 0 456 456" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="456" height="456" rx="50" fill="#512BD4"/>
|
||||
<path d="M81.2738 291.333C78.0496 291.333 75.309 290.259 73.052 288.11C70.795 285.906 69.6665 283.289 69.6665 280.259C69.6665 277.173 70.795 274.529 73.052 272.325C75.309 270.121 78.0496 269.019 81.2738 269.019C84.5518 269.019 87.3193 270.121 89.5763 272.325C91.887 274.529 93.0424 277.173 93.0424 280.259C93.0424 283.289 91.887 285.906 89.5763 288.11C87.3193 290.259 84.5518 291.333 81.2738 291.333Z" fill="white"/>
|
||||
<path d="M210.167 289.515H189.209L133.994 202.406C132.597 200.202 131.441 197.915 130.528 195.546H130.044C130.474 198.081 130.689 203.508 130.689 211.827V289.515H112.149V171H134.477L187.839 256.043C190.096 259.57 191.547 261.994 192.192 263.316H192.514C191.977 260.176 191.708 254.859 191.708 247.365V171H210.167V289.515Z" fill="white"/>
|
||||
<path d="M300.449 289.515H235.561V171H297.87V187.695H254.746V221.249H294.485V237.861H254.746V272.903H300.449V289.515Z" fill="white"/>
|
||||
<path d="M392.667 187.695H359.457V289.515H340.272V187.695H307.143V171H392.667V187.695Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
15
frontend/src/assets/Logos/azure-cdn-frontdoor.svg
Normal file
@@ -0,0 +1,15 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 18">
|
||||
<defs>
|
||||
<linearGradient id="a" x1="9" y1="17" x2="9" y2="1" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#0078d4"/>
|
||||
<stop offset="1" stop-color="#5ea0ef"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<circle cx="9" cy="9" r="8" fill="url(#a)"/>
|
||||
<ellipse cx="9" cy="9" rx="3.2" ry="8" fill="none" stroke="#fff" stroke-width=".7"/>
|
||||
<line x1="1" y1="9" x2="17" y2="9" stroke="#fff" stroke-width=".7"/>
|
||||
<line x1="2" y1="5.5" x2="16" y2="5.5" stroke="#fff" stroke-width=".5"/>
|
||||
<line x1="2" y1="12.5" x2="16" y2="12.5" stroke="#fff" stroke-width=".5"/>
|
||||
<circle cx="9" cy="9" r="8" fill="none" stroke="#fff" stroke-width=".7"/>
|
||||
<path d="M13.5 10.5l1.5-1.5-1.5-1.5M4.5 10.5L3 9l1.5-1.5" stroke="#50e6ff" stroke-width="1" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 877 B |
15
frontend/src/assets/Logos/cert-manager.svg
Normal file
@@ -0,0 +1,15 @@
|
||||
<svg width="142" height="142" viewBox="0 0 142 142" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clipPath="url(#clip0_0_812)" transform="matrix(1.002103,0,0,1.0377318,6.9399999e-7,-2.5317276e-4)">
|
||||
<path d="m 141.702,68.418 c 0,7.4632 -4.567,14.1123 -6.748,20.8385 -2.263,6.9789 -2.552,15.0285 -6.776,20.8385 -4.267,5.868 -11.856,8.611 -17.719,12.881 -5.805,4.228 -10.7345,10.628 -17.7061,12.895 -6.7286,2.186 -14.4463,-0.021 -21.9018,-0.021 -7.4555,0 -15.1731,2.207 -21.8998,0.021 C 41.9778,133.604 37.048,127.204 31.2428,122.976 25.3799,118.706 17.7913,115.963 13.5247,110.095 9.30055,104.287 9.01135,96.2374 6.74791,89.2565 4.56351,82.5225 0,75.8735 0,68.418 0,60.9624 4.56737,54.3057 6.74791,47.5795 9.01135,40.6005 9.30055,32.5507 13.5247,26.741 17.7913,20.8753 25.3799,18.1297 31.2428,13.8617 37.048,9.63414 41.9778,3.23209 48.9513,0.966872 55.678,-1.21924 63.3956,0.986167 70.8511,0.986167 c 7.4555,0 15.1732,-2.205407 21.8999,-0.019295 6.9735,2.265218 11.903,8.667268 17.708,12.894828 5.863,4.268 13.452,7.0136 17.719,12.8793 4.224,5.8097 4.513,13.8595 6.776,20.8385 2.181,6.7262 6.748,13.3771 6.748,20.8385 z" fill="#326ce5"/>
|
||||
<path d="m 70.8473,8.18683 c -33.2383,0 -60.1837,26.96657 -60.1837,60.23097 0,33.2642 26.9454,60.2312 60.1837,60.2312 33.2387,0 60.1837,-26.959 60.1837,-60.2312 0,-33.2721 -26.945,-60.23097 -60.1837,-60.23097 z M 70.8319,123.408 C 40.4778,123.408 15.9058,98.8053 15.9,68.4274 15.9,38.0167 40.5357,13.3791 70.9109,13.437 c 30.3751,0.0579 54.9111,24.6589 54.8841,55.0329 -0.027,30.374 -24.609,54.9481 -54.9631,54.9381 z" fill="#ffffff"/>
|
||||
<path d="m 13.5883,60.53 c 8.1804,0 8.1804,3.859 16.3589,3.859 8.1784,0 8.1804,-3.859 16.3607,-3.859 8.1804,0 8.1785,3.859 16.3589,3.859 8.1804,0 8.1785,-3.859 16.3589,-3.859 8.1804,0 8.1803,3.859 16.3588,3.859 8.1785,0 8.1805,-3.859 16.3605,-3.859 8.181,0 8.181,3.859 16.361,3.859" stroke="#ffffff" strokeMiterlimit="10"/>
|
||||
<path d="m 13.5883,68.248 c 8.1804,0 8.1804,3.859 16.3589,3.859 8.1784,0 8.1804,-3.859 16.3607,-3.859 8.1804,0 8.1785,3.859 16.3589,3.859 8.1804,0 8.1785,-3.859 16.3589,-3.859 8.1804,0 8.1803,3.859 16.3588,3.859 8.1785,0 8.1805,-3.859 16.3605,-3.859 8.181,0 8.181,3.859 16.361,3.859" stroke="#ffffff" strokeMiterlimit="10"/>
|
||||
<path d="m 13.5883,77.5095 c 8.1804,0 8.1804,3.859 16.3589,3.859 8.1784,0 8.1804,-3.859 16.3607,-3.859 8.1804,0 8.1785,3.859 16.3589,3.859 8.1804,0 8.1785,-3.859 16.3589,-3.859 8.1804,0 8.1803,3.859 16.3588,3.859 8.1785,0 8.1805,-3.859 16.3605,-3.859 8.181,0 8.181,3.859 16.361,3.859" stroke="#ffffff" strokeMiterlimit="10"/>
|
||||
<path d="m 70.8473,8.18683 c -33.2383,0 -60.1837,26.96657 -60.1837,60.23097 0,33.2642 26.9454,60.2312 60.1837,60.2312 33.2387,0 60.1837,-26.959 60.1837,-60.2312 0,-33.2721 -26.945,-60.23097 -60.1837,-60.23097 z M 70.8319,123.408 C 40.4778,123.408 15.9058,98.8053 15.9,68.4274 15.9,38.0167 40.5357,13.3791 70.9109,13.437 c 30.3751,0.0579 54.9111,24.6589 54.8841,55.0329 -0.027,30.374 -24.609,54.9481 -54.9631,54.9381 z" fill="#ffffff"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_0_812">
|
||||
<rect width="141.702" height="136.837" fill="#ffffff"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.1 KiB |
55
frontend/src/assets/Logos/graphql.svg
Normal file
@@ -0,0 +1,55 @@
|
||||
<svg viewBox="0 0 400 400" xmlns="http://www.w3.org/2000/svg">
|
||||
<g>
|
||||
<g>
|
||||
<g>
|
||||
<rect x="122" y="-0.4" transform="matrix(-0.866 -0.5 0.5 -0.866 163.3196 363.3136)" fill="#E535AB" width="16.6" height="320.3"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<rect x="39.8" y="272.2" fill="#E535AB" width="320.3" height="16.6"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<rect x="37.9" y="312.2" transform="matrix(-0.866 -0.5 0.5 -0.866 83.0693 663.3409)" fill="#E535AB" width="185" height="16.6"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<rect x="177.1" y="71.1" transform="matrix(-0.866 -0.5 0.5 -0.866 463.3409 283.0693)" fill="#E535AB" width="185" height="16.6"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<rect x="122.1" y="-13" transform="matrix(-0.5 -0.866 0.866 -0.5 126.7903 232.1221)" fill="#E535AB" width="16.6" height="185"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<rect x="109.6" y="151.6" transform="matrix(-0.5 -0.866 0.866 -0.5 266.0828 473.3766)" fill="#E535AB" width="320.3" height="16.6"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<rect x="52.5" y="107.5" fill="#E535AB" width="16.6" height="185"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<rect x="330.9" y="107.5" fill="#E535AB" width="16.6" height="185"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<rect x="262.4" y="240.1" transform="matrix(-0.5 -0.866 0.866 -0.5 126.7953 714.2875)" fill="#E535AB" width="14.5" height="160.9"/>
|
||||
</g>
|
||||
</g>
|
||||
<path fill="#E535AB" d="M369.5,297.9c-9.6,16.7-31,22.4-47.7,12.8c-16.7-9.6-22.4-31-12.8-47.7c9.6-16.7,31-22.4,47.7-12.8 C373.5,259.9,379.2,281.2,369.5,297.9"/>
|
||||
<path fill="#E535AB" d="M90.9,137c-9.6,16.7-31,22.4-47.7,12.8c-16.7-9.6-22.4-31-12.8-47.7c9.6-16.7,31-22.4,47.7-12.8 C94.8,99,100.5,120.3,90.9,137"/>
|
||||
<path fill="#E535AB" d="M30.5,297.9c-9.6-16.7-3.9-38,12.8-47.7c16.7-9.6,38-3.9,47.7,12.8c9.6,16.7,3.9,38-12.8,47.7 C61.4,320.3,40.1,314.6,30.5,297.9"/>
|
||||
<path fill="#E535AB" d="M309.1,137c-9.6-16.7-3.9-38,12.8-47.7c16.7-9.6,38-3.9,47.7,12.8c9.6,16.7,3.9,38-12.8,47.7 C340.1,159.4,318.7,153.7,309.1,137"/>
|
||||
<path fill="#E535AB" d="M200,395.8c-19.3,0-34.9-15.6-34.9-34.9c0-19.3,15.6-34.9,34.9-34.9c19.3,0,34.9,15.6,34.9,34.9 C234.9,380.1,219.3,395.8,200,395.8"/>
|
||||
<path fill="#E535AB" d="M200,74c-19.3,0-34.9-15.6-34.9-34.9c0-19.3,15.6-34.9,34.9-34.9c19.3,0,34.9,15.6,34.9,34.9 C234.9,58.4,219.3,74,200,74"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
3
frontend/src/assets/Logos/istio.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 77.62745 102.5">
|
||||
<path fill="#516baa" d="m31.05548,54.44523v24.1773c.00065.04512-.03164.084-.07611.09164l-23.27949,3.99047c-.05091.0076-.09834-.02751-.10594-.07841-.00256-.01712-.0003-.03461.00653-.05051L30.87996,30.58635c.02242-.04633.07815-.06572.12449-.04331.0316.01529.05193.04704.05259.08214l-.00156,23.82005Zm3.92367-13.93321v38.21148c.00046.04691.03573.08617.08232.09164l34.87031,3.89415c.0512.00527.09698-.03196.10226-.08316.00167-.01616-.00092-.03247-.00751-.04732L35.15623,4.70041c-.02237-.04636-.07809-.0658-.12444-.04343-.03117.01504-.05144.04612-.05264.08071v35.77433Zm34.68546,45.76213l-38.57341,11.57218c-.02155.00797-.04524.00797-.06679,0l-23.309-11.57217c-.04636-.0203-.06749-.07435-.04719-.12071.01513-.03455.04988-.0563.08757-.05481h61.88241c.0508.00825.08531.05613.07706.10693-.00482.0297-.02369.05525-.05066.06859Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 901 B |
3
frontend/src/assets/Logos/railway.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill="currentColor" d="M.113 10.27A13.026 13.026 0 000 11.48h18.23c-.064-.125-.15-.237-.235-.347-3.117-4.027-4.793-3.677-7.19-3.78-.8-.034-1.34-.048-4.524-.048-1.704 0-3.555.005-5.358.01-.234.63-.459 1.24-.567 1.737h9.342v1.216H.113v.002zm18.26 2.426H.009c.02.326.05.645.094.961h16.955c.754 0 1.179-.429 1.315-.96zm-17.318 4.28s2.81 6.902 10.93 7.024c4.855 0 9.027-2.883 10.92-7.024H1.056zM11.988 0C7.5 0 3.593 2.466 1.531 6.108l4.75-.005v-.002c3.71 0 3.849.016 4.573.047l.448.016c1.563.052 3.485.22 4.996 1.364.82.621 2.007 1.99 2.712 2.965.654.902.842 1.94.396 2.934-.408.914-1.289 1.458-2.353 1.458H.391s.099.42.249.886h22.748A12.026 12.026 0 0024 12.005C24 5.377 18.621 0 11.988 0z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 776 B |
13
frontend/src/assets/Logos/scala.svg
Normal file
@@ -0,0 +1,13 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 590 270">
|
||||
<path d="M30.36,109.14v.48h0A3.73,3.73,0,0,1,30.36,109.14Z" fill="currentColor" fill-rule="evenodd"/>
|
||||
<path d="M138.66,28.78C107.2,37.87,57.29,43,30.4,43h0V94.35a.8.8,0,0,0,.19.48c18.35,0,75-6,109.18-15.4a129,129,0,0,0,17.49-5.81c4.18-1.88,6.88-3.86,6.88-5.92V15.91C164.1,20.79,151.39,25.11,138.66,28.78Z" fill="#de3423" fill-rule="evenodd"/>
|
||||
<path d="M138.66,95.37c-18.83,5.43-44.24,9.47-67.39,11.83-15.54,1.59-30.06,2.42-40.87,2.42h0v51.31a.8.8,0,0,0,.19.48c18.35,0,75-6,109.18-15.39a130.38,130.38,0,0,0,17.49-5.81c4.18-1.89,6.88-3.86,6.88-5.92V82.5C164.1,87.37,151.39,91.69,138.66,95.37Z" fill="#de3423" fill-rule="evenodd"/>
|
||||
<path d="M138.66,162c-18.83,5.43-44.24,9.46-67.39,11.83-15.56,1.59-30.1,2.42-40.91,2.42V228c18.16,0,75.1-5.95,109.37-15.39,12.63-3.48,24.37-7.44,24.37-11.74V149.08C164.1,154,151.39,158.28,138.66,162Z" fill="#de3423" fill-rule="evenodd"/>
|
||||
<path d="M30.55,94.83C32.4,97.38,48,102.19,71.27,107.2c23.27,4.46,47.47,22.07,66.29,16.64,12.73-3.68,26.54-36.47,26.54-41.34V82c0-3.4-2.55-6.13-6.88-8.4-17.75-9.07-21.11-12.41-27.69-10.6C95.37,72.43,35.06,67.61,30.55,94.83Z" fill="currentColor" fill-rule="evenodd"/>
|
||||
<path d="M30.55,161.41C32.4,164,48,168.77,71.27,173.79c26,4.74,48.61,20.19,67.44,14.75,12.73-3.68,25.39-34.58,25.39-39.46v-.48c0-3.39-2.55-6.13-6.88-8.39-13.54-7.2-31.43-15.13-38-13.32C85,136.3,39.26,138.37,30.55,161.41Z" fill="currentColor" fill-rule="evenodd"/>
|
||||
<path d="M200.7,142.39c6,11.79,15.6,17.6,29.05,17.6,14.44,0,19.59-7.64,19.59-15.11,0-5.15-1.83-8.63-6.64-11.79-4.82-3.32-8.3-4.81-16.93-8-10.63-4-16.77-7-23.41-12.29-6.64-5.48-9.79-13-9.79-22.74a28.28,28.28,0,0,1,10.29-22.58c7-5.81,15.44-8.63,25.56-8.63,15.77,0,27.72,6.31,35.69,18.76L249.34,87.78c-4.48-6.81-11.29-10.3-20.59-10.3-9.13,0-15.77,5.15-15.77,12.29,0,4.81,2,7.14,4.82,10,1.82,1.33,6.47,3.32,8.63,4.48l6,2.32,6.8,2.66c11,4.48,18.76,9.3,23.57,14.44s7.31,12.12,7.31,20.75c0,20.42-14.11,34.2-40.51,34.2-21.41,0-37.18-10-44.48-26.4Z" fill="currentColor"/>
|
||||
<path d="M354.25,104.71,342,117.49a28.14,28.14,0,0,0-21.24-9.13,25,25,0,0,0-18.43,7.47,27.76,27.76,0,0,0,0,37.52,25,25,0,0,0,18.43,7.47A28.14,28.14,0,0,0,342,151.69l12.29,12.78c-9,9.63-20.09,14.44-33.53,14.44-12.79,0-23.58-4.15-32.37-12.62s-13.12-19.09-13.12-31.7,4.32-23.08,13.12-31.54,19.58-12.78,32.37-12.78C334.16,90.27,345.28,95.08,354.25,104.71Z" fill="currentColor"/>
|
||||
<path d="M393.88,125.62C408,124.3,413,122.47,413,116c0-5.15-4.64-9.13-13.94-9.13q-13.44,0-22.41,10.95l-12.28-10.46c8.13-11.45,19.58-17.09,34.36-17.09,20.75,0,33.7,10,33.7,27.05v37c0,5.81,2.15,6.48,7,6.48h.5v15.43c-2,1.17-5.15,1.83-9.3,1.83-4.48,0-8-1.33-10.62-4a14.06,14.06,0,0,1-3-5.48c-5.81,6.8-15.27,10.29-28.39,10.29-18.42,0-30.87-10.13-30.87-25.4C357.7,136.41,369.15,127.78,393.88,125.62ZM391.56,162c13.28,0,21.41-6,21.41-16.6v-9.3a9.75,9.75,0,0,1-4.14,2.49c-3.82,1.33-6.31,1.66-14.28,2.49-11.62,1.33-17.43,5-17.43,10.79C377.12,158.33,382.43,162,391.56,162Z" fill="currentColor"/>
|
||||
<path d="M444.84,60.88h19.92V149.2c0,8.13,2.66,11.62,10,11.62a21.15,21.15,0,0,0,6-.67v17.76a35.56,35.56,0,0,1-9.47,1c-17.59,0-26.39-9-26.39-27.06Z" fill="currentColor"/>
|
||||
<path d="M521.71,125.62c14.11-1.32,19.09-3.15,19.09-9.62,0-5.15-4.64-9.13-13.94-9.13q-13.44,0-22.41,10.95l-12.28-10.46c8.13-11.45,19.58-17.09,34.36-17.09,20.75,0,33.7,10,33.7,27.05v37c0,5.81,2.15,6.48,7,6.48h.5v15.43c-2,1.17-5.15,1.83-9.3,1.83-4.48,0-8-1.33-10.62-4a13.94,13.94,0,0,1-3-5.48c-5.81,6.8-15.27,10.29-28.39,10.29-18.42,0-30.87-10.13-30.87-25.4C485.53,136.41,497,127.78,521.71,125.62ZM519.39,162c13.28,0,21.41-6,21.41-16.6v-9.3a9.73,9.73,0,0,1-4.15,2.49c-3.81,1.33-6.3,1.66-14.27,2.49-11.62,1.33-17.43,5-17.43,10.79C505,158.33,510.26,162,519.39,162Z" fill="currentColor"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.7 KiB |
37
frontend/src/assets/Logos/slog.svg
Normal file
@@ -0,0 +1,37 @@
|
||||
<svg viewBox="0 0 254.5 225" xmlns="http://www.w3.org/2000/svg">
|
||||
<g>
|
||||
<g>
|
||||
<g>
|
||||
<g>
|
||||
<path fill="#00ACD7" d="M40.2,101.1c-0.4,0-0.5-0.2-0.3-0.5l2.1-2.7c0.2-0.3,0.7-0.5,1.1-0.5l35.7,0c0.4,0,0.5,0.3,0.3,0.6 l-1.7,2.6c-0.2,0.3-0.7,0.6-1,0.6L40.2,101.1z"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<g>
|
||||
<path fill="#00ACD7" d="M25.1,110.3c-0.4,0-0.5-0.2-0.3-0.5l2.1-2.7c0.2-0.3,0.7-0.5,1.1-0.5l45.6,0c0.4,0,0.6,0.3,0.5,0.6 l-0.8,2.4c-0.1,0.4-0.5,0.6-0.9,0.6L25.1,110.3z"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<g>
|
||||
<path fill="#00ACD7" d="M49.3,119.5c-0.4,0-0.5-0.3-0.3-0.6l1.4-2.5c0.2-0.3,0.6-0.6,1-0.6l20,0c0.4,0,0.6,0.3,0.6,0.7l-0.2,2.4 c0,0.4-0.4,0.7-0.7,0.7L49.3,119.5z"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g id="CXHf1q_3_">
|
||||
<g>
|
||||
<g>
|
||||
<path fill="#00ACD7" d="M153.1,99.3c-6.3,1.6-10.6,2.8-16.8,4.4c-1.5,0.4-1.6,0.5-2.9-1c-1.5-1.7-2.6-2.8-4.7-3.8 c-6.3-3.1-12.4-2.2-18.1,1.5c-6.8,4.4-10.3,10.9-10.2,19c0.1,8,5.6,14.6,13.5,15.7c6.8,0.9,12.5-1.5,17-6.6 c0.9-1.1,1.7-2.3,2.7-3.7c-3.6,0-8.1,0-19.3,0c-2.1,0-2.6-1.3-1.9-3c1.3-3.1,3.7-8.3,5.1-10.9c0.3-0.6,1-1.6,2.5-1.6 c5.1,0,23.9,0,36.4,0c-0.2,2.7-0.2,5.4-0.6,8.1c-1.1,7.2-3.8,13.8-8.2,19.6c-7.2,9.5-16.6,15.4-28.5,17 c-9.8,1.3-18.9-0.6-26.9-6.6c-7.4-5.6-11.6-13-12.7-22.2c-1.3-10.9,1.9-20.7,8.5-29.3c7.1-9.3,16.5-15.2,28-17.3 c9.4-1.7,18.4-0.6,26.5,4.9c5.3,3.5,9.1,8.3,11.6,14.1C154.7,98.5,154.3,99,153.1,99.3z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path fill="#00ACD7" d="M186.2,154.6c-9.1-0.2-17.4-2.8-24.4-8.8c-5.9-5.1-9.6-11.6-10.8-19.3c-1.8-11.3,1.3-21.3,8.1-30.2 c7.3-9.6,16.1-14.6,28-16.7c10.2-1.8,19.8-0.8,28.5,5.1c7.9,5.4,12.8,12.7,14.1,22.3c1.7,13.5-2.2,24.5-11.5,33.9 c-6.6,6.7-14.7,10.9-24,12.8C191.5,154.2,188.8,154.3,186.2,154.6z M210,114.2c-0.1-1.3-0.1-2.3-0.3-3.3 c-1.8-9.9-10.9-15.5-20.4-13.3c-9.3,2.1-15.3,8-17.5,17.4c-1.8,7.8,2,15.7,9.2,18.9c5.5,2.4,11,2.1,16.3-0.6 C205.2,129.2,209.5,122.8,210,114.2z"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
@@ -0,0 +1,17 @@
|
||||
.breadcrumb {
|
||||
padding-left: 16px;
|
||||
|
||||
ol {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
:global(.ant-breadcrumb-separator) {
|
||||
color: var(--muted-foreground);
|
||||
}
|
||||
}
|
||||
|
||||
.divider {
|
||||
border-color: var(--l1-border);
|
||||
margin: 16px 0;
|
||||
margin-top: 10px;
|
||||
}
|
||||
32
frontend/src/components/AlertBreadcrumb/AlertBreadcrumb.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
import { Breadcrumb, Divider } from 'antd';
|
||||
|
||||
import styles from './AlertBreadcrumb.module.scss';
|
||||
import BreadcrumbItem, { BreadcrumbItemConfig } from './BreadcrumbItem';
|
||||
|
||||
export interface AlertBreadcrumbProps {
|
||||
items: BreadcrumbItemConfig[];
|
||||
className?: string;
|
||||
showDivider?: boolean;
|
||||
}
|
||||
|
||||
function AlertBreadcrumb({
|
||||
items,
|
||||
className,
|
||||
showDivider = true,
|
||||
}: AlertBreadcrumbProps): JSX.Element {
|
||||
const breadcrumbItems = items.map((item) => ({
|
||||
title: <BreadcrumbItem {...item} />,
|
||||
}));
|
||||
|
||||
return (
|
||||
<>
|
||||
<Breadcrumb
|
||||
className={`${styles.breadcrumb} ${className || ''}`}
|
||||
items={breadcrumbItems}
|
||||
/>
|
||||
{showDivider && <Divider className={styles.divider} />}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default AlertBreadcrumb;
|
||||
@@ -0,0 +1,9 @@
|
||||
.item {
|
||||
--button-padding: 0;
|
||||
--button-font-size: var(--periscope-font-size-base);
|
||||
}
|
||||
|
||||
.itemLast {
|
||||
color: var(--muted-foreground);
|
||||
font-size: var(--periscope-font-size-base);
|
||||
}
|
||||
45
frontend/src/components/AlertBreadcrumb/BreadcrumbItem.tsx
Normal file
@@ -0,0 +1,45 @@
|
||||
import { Button } from '@signozhq/ui/button';
|
||||
import { useSafeNavigate } from 'hooks/useSafeNavigate';
|
||||
import { isModifierKeyPressed } from 'utils/app';
|
||||
|
||||
import styles from './BreadcrumbItem.module.scss';
|
||||
|
||||
export type BreadcrumbItemConfig =
|
||||
| {
|
||||
title: string | null;
|
||||
route?: string;
|
||||
}
|
||||
| {
|
||||
title: string | null;
|
||||
isLast?: true;
|
||||
};
|
||||
|
||||
function BreadcrumbItem({
|
||||
title,
|
||||
...props
|
||||
}: BreadcrumbItemConfig): JSX.Element {
|
||||
const { safeNavigate } = useSafeNavigate();
|
||||
|
||||
if ('isLast' in props) {
|
||||
return <div className={styles.itemLast}>{title}</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<Button
|
||||
variant="ghost"
|
||||
color="secondary"
|
||||
className={styles.item}
|
||||
onClick={(e: React.MouseEvent): void => {
|
||||
if (!('route' in props) || !props.route) {
|
||||
return;
|
||||
}
|
||||
|
||||
safeNavigate(props.route, { newTab: isModifierKeyPressed(e) });
|
||||
}}
|
||||
>
|
||||
{title}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
export default BreadcrumbItem;
|
||||
6
frontend/src/components/AlertBreadcrumb/index.tsx
Normal file
@@ -0,0 +1,6 @@
|
||||
export { default } from './AlertBreadcrumb';
|
||||
export {
|
||||
default as BreadcrumbItem,
|
||||
type BreadcrumbItemConfig,
|
||||
} from './BreadcrumbItem';
|
||||
export type { AlertBreadcrumbProps } from './AlertBreadcrumb';
|
||||
@@ -51,13 +51,6 @@
|
||||
background: var(--l1-background);
|
||||
}
|
||||
|
||||
.progress-container {
|
||||
.ant-progress-bg {
|
||||
height: 8px !important;
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-table-tbody > tr:hover > td {
|
||||
background: color-mix(in srgb, var(--l1-foreground) 4%, transparent);
|
||||
}
|
||||
|
||||
@@ -9,13 +9,13 @@ import {
|
||||
Flex,
|
||||
Input,
|
||||
InputRef,
|
||||
Progress,
|
||||
Space,
|
||||
Spin,
|
||||
TableColumnsType,
|
||||
TableColumnType,
|
||||
Tooltip,
|
||||
} from 'antd';
|
||||
import { Progress } from '@signozhq/ui/progress';
|
||||
import { Typography } from '@signozhq/ui/typography';
|
||||
import type { FilterDropdownProps } from 'antd/lib/table/interface';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
@@ -59,7 +59,7 @@ function ProgressRender(item: string | number): JSX.Element {
|
||||
<Progress
|
||||
percent={percent}
|
||||
strokeLinecap="butt"
|
||||
size="small"
|
||||
showInfo
|
||||
strokeColor={((): string => {
|
||||
const cpuPercent = percent;
|
||||
if (cpuPercent >= 90) {
|
||||
|
||||
@@ -137,7 +137,6 @@ function CreateServiceAccountModal(): JSX.Element {
|
||||
<AuthZTooltip checks={[SACreatePermission]}>
|
||||
<Button
|
||||
type="submit"
|
||||
// @ts-expect-error -- form prop not in @signozhq/ui Button type - TODO: Fix this - @SagarRajput
|
||||
form="create-sa-form"
|
||||
variant="solid"
|
||||
color="primary"
|
||||
|
||||
@@ -59,7 +59,7 @@ function getDeleteTooltip(
|
||||
|
||||
function getInviteButtonLabel(
|
||||
isLoading: boolean,
|
||||
existingToken: { expiresAt?: Date } | undefined,
|
||||
existingToken: { expiresAt?: string } | undefined,
|
||||
isExpired: boolean,
|
||||
notFound: boolean,
|
||||
): string {
|
||||
|
||||
@@ -18,21 +18,22 @@ import { Button } from '@signozhq/ui/button';
|
||||
import cx from 'classnames';
|
||||
import OverlayScrollbar from 'components/OverlayScrollbar/OverlayScrollbar';
|
||||
import { GripVertical } from '@signozhq/icons';
|
||||
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||
import { TelemetryFieldKey } from 'types/api/v5/queryRange';
|
||||
import { Typography } from '@signozhq/ui/typography';
|
||||
|
||||
import styles from './FieldsSettings.module.scss';
|
||||
import styles from './FieldsSelector.module.scss';
|
||||
|
||||
function SortableField({
|
||||
field,
|
||||
onRemove,
|
||||
allowDrag,
|
||||
}: {
|
||||
field: BaseAutocompleteData;
|
||||
onRemove: (field: BaseAutocompleteData) => void;
|
||||
field: TelemetryFieldKey;
|
||||
onRemove: (field: TelemetryFieldKey) => void;
|
||||
allowDrag: boolean;
|
||||
}): JSX.Element {
|
||||
const { attributes, listeners, setNodeRef, transform, transition } =
|
||||
useSortable({ id: field.key });
|
||||
useSortable({ id: field.name });
|
||||
|
||||
const style = {
|
||||
transform: CSS.Transform.toString(transform),
|
||||
@@ -50,7 +51,7 @@ function SortableField({
|
||||
>
|
||||
<div {...attributes} {...listeners} className={styles.dragHandle}>
|
||||
{allowDrag && <GripVertical size={14} />}
|
||||
<span className={styles.fieldKey}>{field.key}</span>
|
||||
<span className={styles.fieldKey}>{field.name}</span>
|
||||
</div>
|
||||
<Button
|
||||
className={cx(styles.removeBtn, 'periscope-btn')}
|
||||
@@ -67,41 +68,52 @@ function SortableField({
|
||||
|
||||
interface AddedFieldsProps {
|
||||
inputValue: string;
|
||||
fields: BaseAutocompleteData[];
|
||||
onFieldsChange: (fields: BaseAutocompleteData[]) => void;
|
||||
fields: TelemetryFieldKey[];
|
||||
onFieldsChange: (fields: TelemetryFieldKey[]) => void;
|
||||
maxFields?: number;
|
||||
}
|
||||
|
||||
function AddedFields({
|
||||
inputValue,
|
||||
fields,
|
||||
onFieldsChange,
|
||||
maxFields,
|
||||
}: AddedFieldsProps): JSX.Element {
|
||||
const sensors = useSensors(useSensor(PointerSensor));
|
||||
|
||||
const handleDragEnd = (event: DragEndEvent): void => {
|
||||
const { active, over } = event;
|
||||
if (over && active.id !== over.id) {
|
||||
const oldIndex = fields.findIndex((f) => f.key === active.id);
|
||||
const newIndex = fields.findIndex((f) => f.key === over.id);
|
||||
const oldIndex = fields.findIndex((f) => f.name === active.id);
|
||||
const newIndex = fields.findIndex((f) => f.name === over.id);
|
||||
onFieldsChange(arrayMove(fields, oldIndex, newIndex));
|
||||
}
|
||||
};
|
||||
|
||||
const filteredFields = useMemo(
|
||||
() =>
|
||||
fields.filter((f) => f.key.toLowerCase().includes(inputValue.toLowerCase())),
|
||||
fields.filter((f) =>
|
||||
f.name.toLowerCase().includes(inputValue.toLowerCase()),
|
||||
),
|
||||
[fields, inputValue],
|
||||
);
|
||||
|
||||
const handleRemove = (field: BaseAutocompleteData): void => {
|
||||
onFieldsChange(fields.filter((f) => f.key !== field.key));
|
||||
const handleRemove = (field: TelemetryFieldKey): void => {
|
||||
onFieldsChange(fields.filter((f) => f.name !== field.name));
|
||||
};
|
||||
|
||||
const allowDrag = inputValue.length === 0;
|
||||
|
||||
return (
|
||||
<div className={cx(styles.section, styles.sectionAdded)}>
|
||||
<div className={styles.sectionHeader}>ADDED FIELDS</div>
|
||||
<div className={styles.sectionHeader}>
|
||||
<span>ADDED FIELDS</span>
|
||||
{maxFields !== undefined && (
|
||||
<Typography.Text size="sm" weight="medium" color="muted">
|
||||
Max Allowed: {maxFields}
|
||||
</Typography.Text>
|
||||
)}
|
||||
</div>
|
||||
<div className={styles.addedList}>
|
||||
<OverlayScrollbar>
|
||||
<DndContext
|
||||
@@ -113,13 +125,13 @@ function AddedFields({
|
||||
<div className={styles.noValues}>No values found</div>
|
||||
) : (
|
||||
<SortableContext
|
||||
items={fields.map((f) => f.key)}
|
||||
items={fields.map((f) => f.name)}
|
||||
strategy={verticalListSortingStrategy}
|
||||
disabled={!allowDrag}
|
||||
>
|
||||
{filteredFields.map((field) => (
|
||||
<SortableField
|
||||
key={field.key}
|
||||
key={field.name}
|
||||
field={field}
|
||||
onRemove={handleRemove}
|
||||
allowDrag={allowDrag}
|
||||
@@ -56,12 +56,14 @@
|
||||
}
|
||||
|
||||
.sectionHeader {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
color: var(--muted-foreground);
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
line-height: 18px;
|
||||
letter-spacing: 0.88px;
|
||||
text-transform: uppercase;
|
||||
padding: 8px 12px;
|
||||
}
|
||||
|
||||
@@ -89,13 +91,6 @@
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.limitHint {
|
||||
padding: 8px 12px;
|
||||
text-align: center;
|
||||
color: var(--muted-foreground);
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.fieldItem {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
176
frontend/src/components/FieldsSelector/FieldsSelector.tsx
Normal file
@@ -0,0 +1,176 @@
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { toast } from '@signozhq/ui/sonner';
|
||||
import { Button } from '@signozhq/ui/button';
|
||||
import { Input } from '@signozhq/ui/input';
|
||||
import useDebouncedFn from 'hooks/useDebouncedFunction';
|
||||
import { Check, TableColumnsSplit, X } from '@signozhq/icons';
|
||||
import { FloatingPanel } from 'periscope/components/FloatingPanel';
|
||||
import { TelemetryFieldKey } from 'types/api/v5/queryRange';
|
||||
import { DataSource } from 'types/common/queryBuilder';
|
||||
|
||||
import AddedFields from './AddedFields';
|
||||
import OtherFields from './OtherFields';
|
||||
|
||||
import styles from './FieldsSelector.module.scss';
|
||||
|
||||
const DEFAULT_PANEL_WIDTH = 350;
|
||||
const DEFAULT_PANEL_HEIGHT_OFFSET = 100;
|
||||
const DEFAULT_PANEL_RIGHT_INSET = 100;
|
||||
const DEFAULT_PANEL_TOP_INSET = 50;
|
||||
|
||||
interface FieldsSelectorProps {
|
||||
isOpen: boolean;
|
||||
title: string;
|
||||
fields: TelemetryFieldKey[];
|
||||
onFieldsChange: (fields: TelemetryFieldKey[]) => void;
|
||||
onClose: () => void;
|
||||
signal: DataSource;
|
||||
maxFields?: number;
|
||||
width?: number;
|
||||
height?: number;
|
||||
defaultPosition?: { x: number; y: number };
|
||||
}
|
||||
|
||||
function FieldsSelector({
|
||||
isOpen,
|
||||
title,
|
||||
fields,
|
||||
onFieldsChange,
|
||||
onClose,
|
||||
signal,
|
||||
maxFields,
|
||||
width = DEFAULT_PANEL_WIDTH,
|
||||
height,
|
||||
defaultPosition,
|
||||
}: FieldsSelectorProps): JSX.Element | null {
|
||||
if (!isOpen) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const resolvedHeight =
|
||||
height ?? window.innerHeight - DEFAULT_PANEL_HEIGHT_OFFSET;
|
||||
const resolvedPosition = defaultPosition ?? {
|
||||
x: window.innerWidth - width - DEFAULT_PANEL_RIGHT_INSET,
|
||||
y: DEFAULT_PANEL_TOP_INSET,
|
||||
};
|
||||
const [draftFields, setDraftFields] = useState<TelemetryFieldKey[]>(fields);
|
||||
const [inputValue, setInputValue] = useState('');
|
||||
const [debouncedInputValue, setDebouncedInputValue] = useState('');
|
||||
|
||||
const debouncedUpdate = useDebouncedFn((value) => {
|
||||
setDebouncedInputValue(value as string);
|
||||
}, 400);
|
||||
|
||||
const handleInputChange = useCallback(
|
||||
(e: React.ChangeEvent<HTMLInputElement>): void => {
|
||||
const value = e.target.value.trim().toLowerCase();
|
||||
setInputValue(value);
|
||||
debouncedUpdate(value);
|
||||
},
|
||||
[debouncedUpdate],
|
||||
);
|
||||
|
||||
const handleAdd = useCallback(
|
||||
(field: TelemetryFieldKey): void => {
|
||||
if (maxFields !== undefined && draftFields.length >= maxFields) {
|
||||
return;
|
||||
}
|
||||
if (draftFields.some((f) => f.name === field.name)) {
|
||||
return;
|
||||
}
|
||||
setDraftFields((prev) => [...prev, field]);
|
||||
},
|
||||
[draftFields, maxFields],
|
||||
);
|
||||
|
||||
const handleSave = useCallback((): void => {
|
||||
onFieldsChange(draftFields);
|
||||
toast.success('Saved successfully', {
|
||||
position: 'top-right',
|
||||
});
|
||||
onClose();
|
||||
}, [draftFields, onFieldsChange, onClose]);
|
||||
|
||||
const handleDiscard = useCallback((): void => {
|
||||
setDraftFields(fields);
|
||||
}, [fields]);
|
||||
|
||||
const hasUnsavedChanges = useMemo(
|
||||
() =>
|
||||
!(
|
||||
draftFields.length === fields.length &&
|
||||
draftFields.every((f, i) => f.name === fields[i]?.name)
|
||||
),
|
||||
[draftFields, fields],
|
||||
);
|
||||
|
||||
const isAtLimit = maxFields !== undefined && draftFields.length >= maxFields;
|
||||
|
||||
return (
|
||||
<FloatingPanel
|
||||
isOpen
|
||||
width={width}
|
||||
height={resolvedHeight}
|
||||
defaultPosition={resolvedPosition}
|
||||
enableResizing={false}
|
||||
>
|
||||
<div className={styles.root}>
|
||||
<div className={styles.header}>
|
||||
<div className={styles.title}>
|
||||
<TableColumnsSplit size={16} />
|
||||
{title}
|
||||
</div>
|
||||
<X className={styles.closeIcon} size={16} onClick={onClose} />
|
||||
</div>
|
||||
|
||||
<section>
|
||||
<Input
|
||||
className={styles.searchInput}
|
||||
type="text"
|
||||
value={inputValue}
|
||||
placeholder="Search for a field..."
|
||||
onChange={handleInputChange}
|
||||
/>
|
||||
</section>
|
||||
|
||||
<AddedFields
|
||||
inputValue={inputValue}
|
||||
fields={draftFields}
|
||||
onFieldsChange={setDraftFields}
|
||||
maxFields={maxFields}
|
||||
/>
|
||||
|
||||
<OtherFields
|
||||
signal={signal}
|
||||
debouncedInputValue={debouncedInputValue}
|
||||
addedFields={draftFields}
|
||||
onAdd={handleAdd}
|
||||
isAtLimit={isAtLimit}
|
||||
/>
|
||||
|
||||
{hasUnsavedChanges && (
|
||||
<div className={styles.footer}>
|
||||
<Button
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
onClick={handleDiscard}
|
||||
prefix={<X width={14} height={14} />}
|
||||
>
|
||||
Discard
|
||||
</Button>
|
||||
<Button
|
||||
variant="solid"
|
||||
color="primary"
|
||||
onClick={handleSave}
|
||||
prefix={<Check width={14} height={14} />}
|
||||
>
|
||||
Save changes
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</FloatingPanel>
|
||||
);
|
||||
}
|
||||
|
||||
export default FieldsSelector;
|
||||
@@ -4,51 +4,58 @@ import { Skeleton } from 'antd';
|
||||
import cx from 'classnames';
|
||||
import OverlayScrollbar from 'components/OverlayScrollbar/OverlayScrollbar';
|
||||
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
|
||||
import { useGetAggregateKeys } from 'hooks/queryBuilder/useGetAggregateKeys';
|
||||
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||
import { useGetQueryKeySuggestions } from 'hooks/querySuggestions/useGetQueryKeySuggestions';
|
||||
import {
|
||||
FieldContext,
|
||||
FieldDataType,
|
||||
SignalType,
|
||||
TelemetryFieldKey,
|
||||
} from 'types/api/v5/queryRange';
|
||||
import { DataSource } from 'types/common/queryBuilder';
|
||||
|
||||
import styles from './FieldsSettings.module.scss';
|
||||
import styles from './FieldsSelector.module.scss';
|
||||
|
||||
interface OtherFieldsProps {
|
||||
dataSource: DataSource;
|
||||
signal: DataSource;
|
||||
debouncedInputValue: string;
|
||||
addedFields: BaseAutocompleteData[];
|
||||
onAdd: (field: BaseAutocompleteData) => void;
|
||||
addedFields: TelemetryFieldKey[];
|
||||
onAdd: (field: TelemetryFieldKey) => void;
|
||||
isAtLimit: boolean;
|
||||
}
|
||||
|
||||
function OtherFields({
|
||||
dataSource,
|
||||
signal,
|
||||
debouncedInputValue,
|
||||
addedFields,
|
||||
onAdd,
|
||||
isAtLimit,
|
||||
}: OtherFieldsProps): JSX.Element {
|
||||
// API call to get available attribute keys
|
||||
const { data, isFetching } = useGetAggregateKeys(
|
||||
const { data, isFetching } = useGetQueryKeySuggestions(
|
||||
{
|
||||
signal,
|
||||
searchText: debouncedInputValue,
|
||||
dataSource,
|
||||
aggregateOperator: 'noop',
|
||||
aggregateAttribute: '',
|
||||
tagType: '',
|
||||
},
|
||||
{
|
||||
queryKey: [
|
||||
REACT_QUERY_KEY.GET_OTHER_FILTERS,
|
||||
'preview-fields',
|
||||
REACT_QUERY_KEY.GET_FIELDS_SELECTOR_SUGGESTIONS,
|
||||
signal,
|
||||
debouncedInputValue,
|
||||
],
|
||||
enabled: true,
|
||||
},
|
||||
);
|
||||
|
||||
// Filter out already-added fields, match on .key from API response objects
|
||||
const otherFields = useMemo(() => {
|
||||
const attributes = data?.payload?.attributeKeys || [];
|
||||
const addedKeys = new Set(addedFields.map((f) => f.key));
|
||||
return attributes.filter((attr) => !addedKeys.has(attr.key));
|
||||
const otherFields: TelemetryFieldKey[] = useMemo(() => {
|
||||
const suggestions = Object.values(data?.data.data.keys || {}).flat();
|
||||
const addedNames = new Set(addedFields.map((f) => f.name));
|
||||
return suggestions
|
||||
.filter((attr) => !addedNames.has(attr.name))
|
||||
.map((attr) => ({
|
||||
...attr,
|
||||
signal: attr.signal as SignalType,
|
||||
fieldContext: attr.fieldContext as FieldContext,
|
||||
fieldDataType: attr.fieldDataType as FieldDataType,
|
||||
}));
|
||||
}, [data, addedFields]);
|
||||
|
||||
if (isFetching) {
|
||||
@@ -76,10 +83,10 @@ function OtherFields({
|
||||
) : (
|
||||
otherFields.map((attr) => (
|
||||
<div
|
||||
key={attr.key}
|
||||
key={attr.name}
|
||||
className={cx(styles.fieldItem, styles.otherFieldItem)}
|
||||
>
|
||||
<span className={styles.fieldKey}>{attr.key}</span>
|
||||
<span className={styles.fieldKey}>{attr.name}</span>
|
||||
{!isAtLimit && (
|
||||
<Button
|
||||
className={cx(styles.addBtn, 'periscope-btn')}
|
||||
@@ -94,7 +101,6 @@ function OtherFields({
|
||||
</div>
|
||||
))
|
||||
)}
|
||||
{isAtLimit && <div className={styles.limitHint}>Maximum 10 fields</div>}
|
||||
</>
|
||||
</OverlayScrollbar>
|
||||
</div>
|
||||
1
frontend/src/components/FieldsSelector/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { default } from './FieldsSelector';
|
||||
@@ -11,9 +11,6 @@ import { GuardAuthZ } from './GuardAuthZ';
|
||||
describe('GuardAuthZ', () => {
|
||||
const TestChild = (): ReactElement => <div>Protected Content</div>;
|
||||
const LoadingFallback = (): ReactElement => <div>Loading...</div>;
|
||||
const ErrorFallback = (error: Error): ReactElement => (
|
||||
<div>Error occurred: {error.message}</div>
|
||||
);
|
||||
const NoPermissionFallback = (_response: {
|
||||
requiredPermissionName: BrandedPermission;
|
||||
}): ReactElement => <div>Access denied</div>;
|
||||
@@ -90,40 +87,28 @@ describe('GuardAuthZ', () => {
|
||||
expect(screen.queryByText('Protected Content')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render fallbackOnError when API error occurs', async () => {
|
||||
const errorMessage = 'Internal Server Error';
|
||||
|
||||
it('should render children when API error occurs and no fallbackOnError provided (fail open)', async () => {
|
||||
server.use(
|
||||
rest.post(AUTHZ_CHECK_URL, (_req, res, ctx) => {
|
||||
return res(ctx.status(500), ctx.json({ error: errorMessage }));
|
||||
return res(ctx.status(500), ctx.json({ error: 'Internal Server Error' }));
|
||||
}),
|
||||
);
|
||||
|
||||
render(
|
||||
<GuardAuthZ relation="read" object="role:*" fallbackOnError={ErrorFallback}>
|
||||
<GuardAuthZ relation="read" object="role:*">
|
||||
<TestChild />
|
||||
</GuardAuthZ>,
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText(/Error occurred:/)).toBeInTheDocument();
|
||||
expect(screen.getByText('Protected Content')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
expect(screen.queryByText('Protected Content')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should pass error object to fallbackOnError function', async () => {
|
||||
const errorMessage = 'Network request failed';
|
||||
let receivedError: Error | null = null;
|
||||
|
||||
const errorFallbackWithCapture = (error: Error): ReactElement => {
|
||||
receivedError = error;
|
||||
return <div>Captured error: {error.message}</div>;
|
||||
};
|
||||
|
||||
it('should render fallbackOnError when API error occurs and fallbackOnError is provided', async () => {
|
||||
server.use(
|
||||
rest.post(AUTHZ_CHECK_URL, (_req, res, ctx) => {
|
||||
return res(ctx.status(500), ctx.json({ error: errorMessage }));
|
||||
return res(ctx.status(500), ctx.json({ error: 'Internal Server Error' }));
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -131,35 +116,14 @@ describe('GuardAuthZ', () => {
|
||||
<GuardAuthZ
|
||||
relation="read"
|
||||
object="role:*"
|
||||
fallbackOnError={errorFallbackWithCapture}
|
||||
fallbackOnError={<div>Custom error fallback</div>}
|
||||
>
|
||||
<TestChild />
|
||||
</GuardAuthZ>,
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(receivedError).not.toBeNull();
|
||||
});
|
||||
|
||||
expect(receivedError).toBeInstanceOf(Error);
|
||||
expect(screen.getByText(/Captured error:/)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render null when error occurs and no fallbackOnError provided', async () => {
|
||||
server.use(
|
||||
rest.post(AUTHZ_CHECK_URL, (_req, res, ctx) => {
|
||||
return res(ctx.status(500), ctx.json({ error: 'Internal Server Error' }));
|
||||
}),
|
||||
);
|
||||
|
||||
const { container } = render(
|
||||
<GuardAuthZ relation="read" object="role:*">
|
||||
<TestChild />
|
||||
</GuardAuthZ>,
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(container.firstChild).toBeNull();
|
||||
expect(screen.getByText('Custom error fallback')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
expect(screen.queryByText('Protected Content')).not.toBeInTheDocument();
|
||||
@@ -12,7 +12,7 @@ export type GuardAuthZProps<R extends AuthZRelation> = {
|
||||
relation: R;
|
||||
object: AuthZObject<R>;
|
||||
fallbackOnLoading?: JSX.Element;
|
||||
fallbackOnError?: (error: Error) => JSX.Element;
|
||||
fallbackOnError?: JSX.Element;
|
||||
fallbackOnNoPermissions?: (response: {
|
||||
requiredPermissionName: BrandedPermission;
|
||||
}) => JSX.Element;
|
||||
@@ -35,7 +35,7 @@ export function GuardAuthZ<R extends AuthZRelation>({
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return fallbackOnError?.(error) ?? null;
|
||||
return fallbackOnError ?? children;
|
||||
}
|
||||
|
||||
if (!permissions?.[permission]?.isGranted) {
|
||||
|
||||
@@ -5,6 +5,8 @@ import { Button } from '@signozhq/ui/button';
|
||||
import { TooltipSimple } from '@signozhq/ui/tooltip';
|
||||
import { Popover } from 'antd';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import { AIAssistantEvents } from 'container/AIAssistant/events';
|
||||
import { normalizePage } from 'container/AIAssistant/hooks/useAIAssistantAnalyticsContext';
|
||||
import {
|
||||
openAIAssistant,
|
||||
useAIAssistantStore,
|
||||
@@ -50,6 +52,14 @@ function HeaderRightSection({
|
||||
setOpenAnnouncementsModal(false);
|
||||
}, [location.pathname]);
|
||||
|
||||
const handleOpenAIAssistant = useCallback((): void => {
|
||||
void logEvent(AIAssistantEvents.Opened, {
|
||||
source: 'header',
|
||||
currentPage: normalizePage(location.pathname),
|
||||
});
|
||||
openAIAssistant();
|
||||
}, [location.pathname]);
|
||||
|
||||
const handleOpenShareURLModal = useCallback((): void => {
|
||||
logEvent('Share: Clicked', {
|
||||
page: location.pathname,
|
||||
@@ -101,7 +111,7 @@ function HeaderRightSection({
|
||||
<Button
|
||||
variant="solid"
|
||||
color="secondary"
|
||||
onClick={openAIAssistant}
|
||||
onClick={handleOpenAIAssistant}
|
||||
aria-label={
|
||||
showHeaderPendingBadge
|
||||
? pendingUserInputCount === 1
|
||||
|
||||
@@ -4,7 +4,8 @@ import { useSelector } from 'react-redux';
|
||||
import { matchPath, useLocation } from 'react-router-dom';
|
||||
import { useCopyToClipboard } from 'react-use';
|
||||
import { Color } from '@signozhq/design-tokens';
|
||||
import { Button, Switch } from 'antd';
|
||||
import { Button } from 'antd';
|
||||
import { Switch } from '@signozhq/ui/switch';
|
||||
import { Typography } from '@signozhq/ui/typography';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import { QueryParams } from 'constants/query';
|
||||
@@ -125,9 +126,8 @@ function ShareURLModal(): JSX.Element {
|
||||
<Info size={14} color={Color.BG_AMBER_600} />
|
||||
)}
|
||||
<Switch
|
||||
checked={enableAbsoluteTime}
|
||||
value={enableAbsoluteTime}
|
||||
disabled={!isValidateRelativeTime}
|
||||
size="small"
|
||||
onChange={(): void => {
|
||||
setEnableAbsoluteTime((prev) => !prev);
|
||||
}}
|
||||
|
||||
@@ -50,6 +50,7 @@ import {
|
||||
import { JsonView } from 'periscope/components/JsonView';
|
||||
import { useAppContext } from 'providers/App/App';
|
||||
import { AppState } from 'store/reducers';
|
||||
import { ILogBody } from 'types/api/logs/log';
|
||||
import { Query, TagFilter } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { DataSource, StringOperators } from 'types/common/queryBuilder';
|
||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
@@ -217,20 +218,17 @@ function LogDetailInner({
|
||||
|
||||
const logBody = useMemo(() => {
|
||||
if (!isBodyJsonQueryEnabled) {
|
||||
return log?.body || '';
|
||||
return (log?.body as string) ?? '';
|
||||
}
|
||||
|
||||
try {
|
||||
const json = JSON.parse(log?.body || '');
|
||||
|
||||
if (typeof json?.message === 'string' && json.message !== '') {
|
||||
return json.message;
|
||||
}
|
||||
|
||||
return log?.body || '';
|
||||
} catch {
|
||||
return log?.body || '';
|
||||
// Feature enabled: body is always a map; message is always a string
|
||||
const bodyObj = log?.body as ILogBody;
|
||||
if (!bodyObj) {
|
||||
return '';
|
||||
}
|
||||
if (bodyObj.message) {
|
||||
return bodyObj.message;
|
||||
}
|
||||
return JSON.stringify(bodyObj);
|
||||
}, [isBodyJsonQueryEnabled, log?.body]);
|
||||
|
||||
const htmlBody = useMemo(
|
||||
|
||||
@@ -9,7 +9,10 @@ import { Color } from '@signozhq/design-tokens';
|
||||
import { Tooltip } from 'antd';
|
||||
import { VIEW_TYPES } from 'components/LogDetail/constants';
|
||||
import { DATE_TIME_FORMATS } from 'constants/dateTimeFormats';
|
||||
import { getSanitizedLogBody } from 'container/LogDetailedView/utils';
|
||||
import {
|
||||
getBodyDisplayString,
|
||||
getSanitizedLogBody,
|
||||
} from 'container/LogDetailedView/utils';
|
||||
import { useCopyLogLink } from 'hooks/logs/useCopyLogLink';
|
||||
// hooks
|
||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||
@@ -99,7 +102,7 @@ function RawLogView({
|
||||
// Check if body is selected
|
||||
const showBody = selectedFields.some((field) => field.name === 'body');
|
||||
if (showBody) {
|
||||
parts.push(`${attributesText} ${data.body}`);
|
||||
parts.push(`${attributesText} ${getBodyDisplayString(data.body)}`);
|
||||
} else {
|
||||
parts.push(attributesText);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,10 @@ import type { ReactElement } from 'react';
|
||||
import { useMemo } from 'react';
|
||||
import TanStackTable from 'components/TanStackTableView';
|
||||
import { DATE_TIME_FORMATS } from 'constants/dateTimeFormats';
|
||||
import { getSanitizedLogBody } from 'container/LogDetailedView/utils';
|
||||
import {
|
||||
getBodyDisplayString,
|
||||
getSanitizedLogBody,
|
||||
} from 'container/LogDetailedView/utils';
|
||||
import { FontSize } from 'container/OptionsMenu/types';
|
||||
import { FlatLogData } from 'lib/logs/flatLogData';
|
||||
import { useTimezone } from 'providers/Timezone';
|
||||
@@ -87,7 +90,7 @@ export function useLogsTableColumns({
|
||||
? {
|
||||
id: 'body',
|
||||
header: 'Body',
|
||||
accessorFn: (log): string => log.body,
|
||||
accessorFn: (log): string => getBodyDisplayString(log.body),
|
||||
canBeHidden: false,
|
||||
width: { default: '100%', min: 300 },
|
||||
cell: ({ value, isActive }): ReactElement => (
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
.banner {
|
||||
height: var(--spacing-20);
|
||||
|
||||
a {
|
||||
color: var(--callout-warning-title);
|
||||
text-decoration: underline;
|
||||
|
||||
&:hover {
|
||||
color: var(--callout-warning-title);
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
}
|
||||
26
frontend/src/components/NoAuthBanner/NoAuthBanner.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import { PersistedAnnouncementBanner } from '@signozhq/ui/announcement-banner';
|
||||
|
||||
import styles from './NoAuthBanner.module.scss';
|
||||
|
||||
export function NoAuthBanner(): JSX.Element {
|
||||
return (
|
||||
<PersistedAnnouncementBanner
|
||||
type="warning"
|
||||
storageKey="no-auth-banner-v1"
|
||||
testId="no-auth-banner"
|
||||
className={styles.banner}
|
||||
>
|
||||
Impersonation mode: authentication is disabled. Anyone with access to this
|
||||
instance has admin privileges.{' '}
|
||||
<a
|
||||
href="https://signoz.io/docs/manage/administrator-guide/configuration/impersonation-mode/"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
Learn more
|
||||
</a>
|
||||
</PersistedAnnouncementBanner>
|
||||
);
|
||||
}
|
||||
|
||||
export default NoAuthBanner;
|
||||
@@ -0,0 +1,24 @@
|
||||
import { render, screen } from 'tests/test-utils';
|
||||
|
||||
import { NoAuthBanner } from '../NoAuthBanner';
|
||||
|
||||
describe('NoAuthBanner', () => {
|
||||
it('renders the no-auth message', () => {
|
||||
render(<NoAuthBanner />);
|
||||
expect(
|
||||
screen.getByText(/Impersonation mode: authentication is disabled/i),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders with the warning test id', () => {
|
||||
render(<NoAuthBanner />);
|
||||
expect(screen.getByTestId('no-auth-banner')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders a docs link that opens in a new tab', () => {
|
||||
render(<NoAuthBanner />);
|
||||
const link = screen.getByRole('link', { name: /learn more/i });
|
||||
expect(link).toHaveAttribute('target', '_blank');
|
||||
expect(link).toHaveAttribute('rel', 'noreferrer');
|
||||
});
|
||||
});
|
||||
@@ -14,7 +14,8 @@ import {
|
||||
ComboboxList,
|
||||
ComboboxTrigger,
|
||||
} from '@signozhq/ui/combobox';
|
||||
import { Skeleton, Switch, Tooltip } from 'antd';
|
||||
import { Skeleton, Tooltip } from 'antd';
|
||||
import { Switch } from '@signozhq/ui/switch';
|
||||
import { Typography } from '@signozhq/ui/typography';
|
||||
import getLocalStorageKey from 'api/browser/localstorage/get';
|
||||
import setLocalStorageKey from 'api/browser/localstorage/set';
|
||||
@@ -281,9 +282,8 @@ export default function QuickFilters(props: IQuickFiltersProps): JSX.Element {
|
||||
<div className="api-quick-filters-header">
|
||||
<Typography.Text>Show IP addresses</Typography.Text>
|
||||
<Switch
|
||||
size="small"
|
||||
style={{ marginLeft: 'auto' }}
|
||||
checked={showIP ?? true}
|
||||
value={showIP ?? true}
|
||||
onChange={(checked): void => {
|
||||
logEvent('API Monitoring: Show IP addresses clicked', {
|
||||
showIP: checked,
|
||||
|
||||
@@ -4,7 +4,8 @@ import type {
|
||||
TableColumnsType as ColumnsType,
|
||||
TableColumnType as ColumnType,
|
||||
} from 'antd';
|
||||
import { Button, Dropdown, Flex, MenuProps, Switch } from 'antd';
|
||||
import { Button, Dropdown, Flex, MenuProps } from 'antd';
|
||||
import { Switch } from '@signozhq/ui/switch';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import LaunchChatSupport from 'components/LaunchChatSupport/LaunchChatSupport';
|
||||
import { useSafeNavigate } from 'hooks/useSafeNavigate';
|
||||
@@ -60,9 +61,7 @@ function DynamicColumnTable({
|
||||
|
||||
const onToggleHandler =
|
||||
(index: number, column: ColumnGroupType<any> | ColumnType<any>) =>
|
||||
(checked: boolean, event: React.MouseEvent<HTMLButtonElement>): void => {
|
||||
event.stopPropagation();
|
||||
|
||||
(checked: boolean): void => {
|
||||
if (shouldSendAlertsLogEvent) {
|
||||
logEvent('Alert: Column toggled', {
|
||||
column: column?.title,
|
||||
@@ -88,10 +87,14 @@ function DynamicColumnTable({
|
||||
const items: MenuProps['items'] =
|
||||
dynamicColumns?.map((column, index) => ({
|
||||
label: (
|
||||
<div className="dynamicColumnsTable-items">
|
||||
<div
|
||||
className="dynamicColumnsTable-items"
|
||||
onClick={(e): void => e.stopPropagation()}
|
||||
role="presentation"
|
||||
>
|
||||
<div>{column.title?.toString()}</div>
|
||||
<Switch
|
||||
checked={columnsData?.findIndex((c) => c.key === column.key) !== -1}
|
||||
value={columnsData?.findIndex((c) => c.key === column.key) !== -1}
|
||||
onChange={onToggleHandler(index, column)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -144,6 +144,7 @@ function RolesSelect(props: RolesSelectProps): JSX.Element {
|
||||
loading={loading}
|
||||
notFoundContent={notFoundContent}
|
||||
options={options}
|
||||
optionFilterProp="label"
|
||||
optionRender={(option): JSX.Element => (
|
||||
<Checkbox
|
||||
checked={value.includes(option.value as string)}
|
||||
@@ -162,6 +163,7 @@ function RolesSelect(props: RolesSelectProps): JSX.Element {
|
||||
return (
|
||||
<Select
|
||||
id={id}
|
||||
showSearch
|
||||
value={value || undefined}
|
||||
onChange={onChange}
|
||||
placeholder={placeholder}
|
||||
@@ -170,6 +172,7 @@ function RolesSelect(props: RolesSelectProps): JSX.Element {
|
||||
loading={loading}
|
||||
notFoundContent={notFoundContent}
|
||||
options={options}
|
||||
optionFilterProp="label"
|
||||
getPopupContainer={getPopupContainer}
|
||||
disabled={disabled}
|
||||
/>
|
||||
|
||||
@@ -127,7 +127,6 @@ function KeyFormPhase({
|
||||
>
|
||||
<Button
|
||||
type="submit"
|
||||
// @ts-expect-error -- form prop not in @signozhq/ui Button type - TODO: Fix this - @SagarRajput
|
||||
form={FORM_ID}
|
||||
variant="solid"
|
||||
color="primary"
|
||||
|
||||
@@ -190,7 +190,6 @@ function EditKeyForm({
|
||||
>
|
||||
<Button
|
||||
type="submit"
|
||||
// @ts-expect-error -- form prop not in @signozhq/ui Button type - TODO: Fix this - @SagarRajput
|
||||
form={FORM_ID}
|
||||
variant="solid"
|
||||
color="primary"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import { KeyRound, X } from '@signozhq/icons';
|
||||
import { Button } from '@signozhq/ui/button';
|
||||
import { Skeleton, Table } from 'antd';
|
||||
import { Skeleton, Table, Tooltip } from 'antd';
|
||||
import type { ColumnsType } from 'antd/es/table/interface';
|
||||
import type { ServiceaccounttypesGettableFactorAPIKeyDTO } from 'api/generated/services/sigNoz.schemas';
|
||||
import AuthZTooltip from 'components/AuthZTooltip/AuthZTooltip';
|
||||
@@ -110,28 +110,34 @@ function buildColumns({
|
||||
onClick: (e): void => e.stopPropagation(),
|
||||
style: { cursor: 'default' },
|
||||
}),
|
||||
render: (_, record): JSX.Element => (
|
||||
<AuthZTooltip
|
||||
checks={[
|
||||
buildAPIKeyDeletePermission(record.id),
|
||||
buildSADetachPermission(accountId),
|
||||
]}
|
||||
enabled={!isDisabled && !!accountId}
|
||||
>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
color="destructive"
|
||||
disabled={isDisabled}
|
||||
onClick={(): void => {
|
||||
onRevokeClick(record.id);
|
||||
}}
|
||||
className="keys-tab__revoke-btn"
|
||||
render: (_, record): JSX.Element => {
|
||||
const tooltipTitle = isDisabled ? 'Service account disabled' : 'Revoke Key';
|
||||
return (
|
||||
<AuthZTooltip
|
||||
checks={[
|
||||
buildAPIKeyDeletePermission(record.id),
|
||||
buildSADetachPermission(accountId),
|
||||
]}
|
||||
enabled={!isDisabled && !!accountId}
|
||||
>
|
||||
<X size={12} />
|
||||
</Button>
|
||||
</AuthZTooltip>
|
||||
),
|
||||
<Tooltip title={tooltipTitle}>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
color="destructive"
|
||||
disabled={isDisabled}
|
||||
onClick={(e): void => {
|
||||
e.stopPropagation();
|
||||
onRevokeClick(record.id);
|
||||
}}
|
||||
className="keys-tab__revoke-btn"
|
||||
>
|
||||
<X size={12} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</AuthZTooltip>
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ const mockKey: ServiceaccounttypesGettableFactorAPIKeyDTO = {
|
||||
id: 'key-1',
|
||||
name: 'Original Key Name',
|
||||
expiresAt: 0,
|
||||
lastObservedAt: null as unknown as Date,
|
||||
lastObservedAt: null as unknown as string,
|
||||
serviceAccountId: 'sa-1',
|
||||
};
|
||||
|
||||
|
||||
@@ -29,14 +29,14 @@ const keys: ServiceaccounttypesGettableFactorAPIKeyDTO[] = [
|
||||
id: 'key-1',
|
||||
name: 'Production Key',
|
||||
expiresAt: 0,
|
||||
lastObservedAt: null as unknown as Date,
|
||||
lastObservedAt: null as unknown as string,
|
||||
serviceAccountId: 'sa-1',
|
||||
},
|
||||
{
|
||||
id: 'key-2',
|
||||
name: 'Staging Key',
|
||||
expiresAt: 1924905600, // 2030-12-31
|
||||
lastObservedAt: new Date('2026-03-10T10:00:00Z'),
|
||||
lastObservedAt: '2026-03-10T10:00:00Z',
|
||||
serviceAccountId: 'sa-1',
|
||||
},
|
||||
];
|
||||
|
||||
@@ -626,6 +626,10 @@ function TanStackTableInner<TData>(
|
||||
onChange={(value): void => {
|
||||
setLimit(+value);
|
||||
pagination.onLimitChange?.(+value);
|
||||
if (page !== 1) {
|
||||
setPage(1);
|
||||
pagination.onPageChange?.(1);
|
||||
}
|
||||
}}
|
||||
items={paginationPageSizeItems}
|
||||
/>
|
||||
|
||||
@@ -401,6 +401,62 @@ describe('TanStackTableView Integration', () => {
|
||||
expect(onLimitChange).toHaveBeenCalledWith(20);
|
||||
});
|
||||
});
|
||||
|
||||
it('resets page to 1 when limit changes', async () => {
|
||||
const user = userEvent.setup();
|
||||
const onUrlUpdate = jest.fn<void, [UrlUpdateEvent]>();
|
||||
const onPageChange = jest.fn();
|
||||
|
||||
renderTanStackTable({
|
||||
props: {
|
||||
pagination: { total: 100, defaultPage: 1, defaultLimit: 10, onPageChange },
|
||||
enableQueryParams: true,
|
||||
},
|
||||
onUrlUpdate,
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByRole('navigation')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// Navigate to page 2
|
||||
const nav = screen.getByRole('navigation');
|
||||
const page2 = Array.from(nav.querySelectorAll('button')).find(
|
||||
(btn) => btn.textContent?.trim() === '2',
|
||||
);
|
||||
if (!page2) {
|
||||
throw new Error('Page 2 button not found in pagination');
|
||||
}
|
||||
await user.click(page2);
|
||||
|
||||
await waitFor(() => {
|
||||
const lastPage = onUrlUpdate.mock.calls
|
||||
.map((call) => call[0].searchParams.get('page'))
|
||||
.filter(Boolean)
|
||||
.pop();
|
||||
expect(lastPage).toBe('2');
|
||||
});
|
||||
|
||||
// Change page size
|
||||
const comboboxTrigger = document.querySelector(
|
||||
'button[aria-haspopup="dialog"]',
|
||||
) as HTMLElement;
|
||||
await user.click(comboboxTrigger);
|
||||
|
||||
const option20 = await screen.findByRole('option', { name: '20' });
|
||||
await user.click(option20);
|
||||
|
||||
// Verify page reset to 1 (nuqs removes default values from URL)
|
||||
await waitFor(() => {
|
||||
const lastCall = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1];
|
||||
const lastPage = lastCall[0].searchParams.get('page');
|
||||
expect(lastPage === '1' || lastPage === null).toBe(true);
|
||||
expect(lastCall[0].searchParams.get('limit')).toBe('20');
|
||||
});
|
||||
|
||||
// Verify onPageChange callback was called with 1
|
||||
expect(onPageChange).toHaveBeenCalledWith(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('sorting', () => {
|
||||
|
||||
@@ -204,7 +204,7 @@ describe('createGuardedRoute', () => {
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render error fallback when API error occurs', async () => {
|
||||
it('should render the component when API error occurs (fail open)', async () => {
|
||||
server.use(
|
||||
rest.post(AUTHZ_CHECK_URL, (_req, res, ctx) => {
|
||||
return res(ctx.status(500), ctx.json({ error: 'Internal Server Error' }));
|
||||
@@ -230,12 +230,8 @@ describe('createGuardedRoute', () => {
|
||||
render(<GuardedComponent {...props} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText(/Something went wrong/i)).toBeInTheDocument();
|
||||
expect(screen.getByText('Test Component: test-value')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
expect(
|
||||
screen.queryByText('Test Component: test-value'),
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render no permissions fallback when permission is denied', async () => {
|
||||
@@ -9,14 +9,11 @@ import { parsePermission } from 'hooks/useAuthZ/utils';
|
||||
|
||||
import noDataUrl from '@/assets/Icons/no-data.svg';
|
||||
|
||||
import ErrorBoundaryFallback from '../../pages/ErrorBoundaryFallback/ErrorBoundaryFallback';
|
||||
import AppLoading from '../AppLoading/AppLoading';
|
||||
import { GuardAuthZ } from '../GuardAuthZ/GuardAuthZ';
|
||||
|
||||
import './createGuardedRoute.styles.scss';
|
||||
|
||||
const onErrorFallback = (): JSX.Element => <ErrorBoundaryFallback />;
|
||||
|
||||
function OnNoPermissionsFallback(response: {
|
||||
requiredPermissionName: BrandedPermission;
|
||||
}): ReactElement {
|
||||
@@ -63,7 +60,6 @@ export function createGuardedRoute<P extends object, R extends AuthZRelation>(
|
||||
relation={relation}
|
||||
object={resolvedObject}
|
||||
fallbackOnLoading={<AppLoading />}
|
||||
fallbackOnError={onErrorFallback}
|
||||
fallbackOnNoPermissions={(response): ReactElement => (
|
||||
<OnNoPermissionsFallback {...response} />
|
||||
)}
|
||||
|
||||
@@ -10,4 +10,6 @@ export enum FeatureKeys {
|
||||
ONBOARDING_V3 = 'onboarding_v3',
|
||||
DOT_METRICS_ENABLED = 'dot_metrics_enabled',
|
||||
USE_JSON_BODY = 'use_json_body',
|
||||
USE_FINE_GRAINED_AUTHZ = 'use_fine_grained_authz',
|
||||
DASHBOARD_V2 = 'dashboard_v2',
|
||||
}
|
||||
|
||||
@@ -42,4 +42,5 @@ export enum LOCALSTORAGE {
|
||||
LICENSE_KEY_CALLOUT_DISMISSED = 'LICENSE_KEY_CALLOUT_DISMISSED',
|
||||
DASHBOARD_PREFERENCES = 'DASHBOARD_PREFERENCES',
|
||||
ACTIVE_SIGNOZ_INSTANCE_URL = 'ACTIVE_SIGNOZ_INSTANCE_URL',
|
||||
DASHBOARDS_LIST_VISIBLE_COLUMNS = 'DASHBOARDS_LIST_VISIBLE_COLUMNS',
|
||||
}
|
||||
|
||||
@@ -108,4 +108,7 @@ export const REACT_QUERY_KEY = {
|
||||
|
||||
// Dashboard Grid Card Query Keys
|
||||
DASHBOARD_GRID_CARD_QUERY_RANGE: 'DASHBOARD_GRID_CARD_QUERY_RANGE',
|
||||
|
||||
// Fields Selector Query Keys
|
||||
GET_FIELDS_SELECTOR_SUGGESTIONS: 'GET_FIELDS_SELECTOR_SUGGESTIONS',
|
||||
} as const;
|
||||
|
||||
@@ -29,7 +29,6 @@ const ROUTES = {
|
||||
ALERTS_NEW: '/alerts/new',
|
||||
ALERT_HISTORY: '/alerts/history',
|
||||
ALERT_OVERVIEW: '/alerts/overview',
|
||||
ALERT_TYPE_SELECTION: '/alerts/type-selection',
|
||||
ALL_CHANNELS: '/settings/channels',
|
||||
CHANNELS_NEW: '/settings/channels/new',
|
||||
CHANNELS_EDIT: '/settings/channels/edit/:channelId',
|
||||
|
||||
@@ -1,13 +1,20 @@
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { useHistory, useLocation } from 'react-router-dom';
|
||||
import { Button } from '@signozhq/ui/button';
|
||||
import { TooltipSimple } from '@signozhq/ui/tooltip';
|
||||
import ROUTES from 'constants/routes';
|
||||
import { History, Maximize2, Minus, Plus, Sparkles, X } from '@signozhq/icons';
|
||||
|
||||
import logEvent from 'api/common/logEvent';
|
||||
|
||||
import HistorySidebar from '../components/ConversationsList';
|
||||
import ConversationView from '../ConversationView';
|
||||
import { AIAssistantEvents } from '../events';
|
||||
import {
|
||||
normalizePage,
|
||||
useAIAssistantAnalyticsContext,
|
||||
} from '../hooks/useAIAssistantAnalyticsContext';
|
||||
import { useAIAssistantStore } from '../store/useAIAssistantStore';
|
||||
import { VariantContext } from '../VariantContext';
|
||||
|
||||
@@ -24,6 +31,7 @@ import styles from './AIAssistantModal.module.scss';
|
||||
// eslint-disable-next-line sonarjs/cognitive-complexity
|
||||
export default function AIAssistantModal(): JSX.Element | null {
|
||||
const history = useHistory();
|
||||
const { pathname } = useLocation();
|
||||
const [showHistory, setShowHistory] = useState(false);
|
||||
|
||||
const isOpen = useAIAssistantStore((s) => s.isModalOpen);
|
||||
@@ -36,6 +44,7 @@ export default function AIAssistantModal(): JSX.Element | null {
|
||||
const startNewConversation = useAIAssistantStore(
|
||||
(s) => s.startNewConversation,
|
||||
);
|
||||
const analyticsCtx = useAIAssistantAnalyticsContext();
|
||||
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (e: KeyboardEvent): void => {
|
||||
@@ -55,6 +64,10 @@ export default function AIAssistantModal(): JSX.Element | null {
|
||||
} else {
|
||||
startNewConversation();
|
||||
setShowHistory(false);
|
||||
void logEvent(AIAssistantEvents.Opened, {
|
||||
source: 'shortcut',
|
||||
currentPage: normalizePage(pathname),
|
||||
});
|
||||
openModal();
|
||||
}
|
||||
return;
|
||||
@@ -68,7 +81,7 @@ export default function AIAssistantModal(): JSX.Element | null {
|
||||
|
||||
window.addEventListener('keydown', handleKeyDown);
|
||||
return (): void => window.removeEventListener('keydown', handleKeyDown);
|
||||
}, [isOpen, openModal, closeModal, startNewConversation]);
|
||||
}, [isOpen, openModal, closeModal, startNewConversation, pathname]);
|
||||
|
||||
// ── Handlers ────────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -77,15 +90,28 @@ export default function AIAssistantModal(): JSX.Element | null {
|
||||
return;
|
||||
}
|
||||
closeModal();
|
||||
// Router state tells AIAssistantPage to skip its mount-time Opened fire:
|
||||
// the assistant was already open in the modal, so this is a surface
|
||||
// switch, not a new open.
|
||||
history.push(
|
||||
ROUTES.AI_ASSISTANT.replace(':conversationId', activeConversationId),
|
||||
{ fromInApp: true },
|
||||
);
|
||||
}, [activeConversationId, closeModal, history]);
|
||||
|
||||
const handleNew = useCallback(() => {
|
||||
void logEvent(AIAssistantEvents.NewChatClicked, {
|
||||
...analyticsCtx,
|
||||
// useAIAssistantAnalyticsContext() runs above this component's
|
||||
// VariantContext.Provider, so the hook reports the default 'page'
|
||||
// mode. Override here: the modal collapses to 'sidepane' in our
|
||||
// taxonomy alongside the drawer.
|
||||
mode: 'sidepane',
|
||||
source: 'header',
|
||||
});
|
||||
startNewConversation();
|
||||
setShowHistory(false);
|
||||
}, [startNewConversation]);
|
||||
}, [startNewConversation, analyticsCtx]);
|
||||
|
||||
const handleHistorySelect = useCallback(() => {
|
||||
setShowHistory(false);
|
||||
|
||||