Compare commits

...

5 Commits

Author SHA1 Message Date
ahrefabhi
8f0afe5448 chore: disabled default props rule in eslint 2025-07-24 15:01:43 +05:30
ahrefabhi
76837b9ac8 chore: minor linter fix 2025-07-24 14:41:10 +05:30
ahrefabhi
d097fc7c4e chore: linter fixes 2025-07-24 14:26:26 +05:30
ahrefabhi
649ff2f0f7 chore: moved switch to routes in router api migeration 2025-07-24 11:15:33 +05:30
ahrefabhi
288fa9a1d7 refactor: updated routing components 2025-07-23 12:04:12 +05:30
17 changed files with 357 additions and 268 deletions

View File

@@ -54,6 +54,7 @@ module.exports = {
}, },
], ],
'react/prop-types': 'off', 'react/prop-types': 'off',
'react/require-default-props': 'off',
'@typescript-eslint/explicit-function-return-type': 'error', '@typescript-eslint/explicit-function-return-type': 'error',
'@typescript-eslint/no-var-requires': 'error', '@typescript-eslint/no-var-requires': 'error',
'react/no-array-index-key': 'error', 'react/no-array-index-key': 'error',

View File

@@ -12,6 +12,7 @@ import { LOCALSTORAGE } from 'constants/localStorage';
import ROUTES from 'constants/routes'; import ROUTES from 'constants/routes';
import AppLayout from 'container/AppLayout'; import AppLayout from 'container/AppLayout';
import { KeyboardHotkeysProvider } from 'hooks/hotkeys/useKeyboardHotkeys'; import { KeyboardHotkeysProvider } from 'hooks/hotkeys/useKeyboardHotkeys';
import { useAppRoutes } from 'hooks/useAppRoutes';
import { useThemeConfig } from 'hooks/useDarkMode'; import { useThemeConfig } from 'hooks/useDarkMode';
import { useGetTenantLicense } from 'hooks/useGetTenantLicense'; import { useGetTenantLicense } from 'hooks/useGetTenantLicense';
import { NotificationProvider } from 'hooks/useNotifications'; import { NotificationProvider } from 'hooks/useNotifications';
@@ -27,19 +28,15 @@ import { DashboardProvider } from 'providers/Dashboard/Dashboard';
import { ErrorModalProvider } from 'providers/ErrorModalProvider'; import { ErrorModalProvider } from 'providers/ErrorModalProvider';
import { QueryBuilderProvider } from 'providers/QueryBuilder'; import { QueryBuilderProvider } from 'providers/QueryBuilder';
import { Suspense, useCallback, useEffect, useState } from 'react'; import { Suspense, useCallback, useEffect, useState } from 'react';
import { Route, Router, Switch } from 'react-router-dom'; import { Router } from 'react-router-dom';
import { CompatRouter } from 'react-router-dom-v5-compat'; import { CompatRouter, Route, Routes } from 'react-router-dom-v5-compat';
import { LicenseStatus } from 'types/api/licensesV3/getActive'; import { LicenseStatus } from 'types/api/licensesV3/getActive';
import { Userpilot } from 'userpilot'; import { Userpilot } from 'userpilot';
import { extractDomain } from 'utils/app'; import { extractDomain } from 'utils/app';
import { Home } from './pageComponents'; import { Home } from './pageComponents';
import PrivateRoute from './Private'; import PrivateRoute from './Private';
import defaultRoutes, { import { AppRoutes, LIST_LICENSES, SUPPORT_ROUTE } from './routes';
AppRoutes,
LIST_LICENSES,
SUPPORT_ROUTE,
} from './routes';
function App(): JSX.Element { function App(): JSX.Element {
const themeConfig = useThemeConfig(); const themeConfig = useThemeConfig();
@@ -57,6 +54,7 @@ function App(): JSX.Element {
featureFlags, featureFlags,
org, org,
} = useAppContext(); } = useAppContext();
const { routes: defaultRoutes } = useAppRoutes();
const [routes, setRoutes] = useState<AppRoutes[]>(defaultRoutes); const [routes, setRoutes] = useState<AppRoutes[]>(defaultRoutes);
const { hostname, pathname } = window.location; const { hostname, pathname } = window.location;
@@ -213,6 +211,7 @@ function App(): JSX.Element {
} }
setRoutes(updatedRoutes); setRoutes(updatedRoutes);
} }
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [ }, [
isLoggedInState, isLoggedInState,
user, user,
@@ -363,6 +362,17 @@ function App(): JSX.Element {
} }
} }
const renderRoutes = (routes: AppRoutes[]): JSX.Element[] | null => {
if (!routes || routes.length === 0) {
return null;
}
return routes.map(({ path, element: Component, children }: AppRoutes) => (
<Route key={`${path}`} path={path as string} element={<Component />}>
{children && renderRoutes(children)}
</Route>
));
};
return ( return (
<Sentry.ErrorBoundary fallback={<ErrorBoundaryFallback />}> <Sentry.ErrorBoundary fallback={<ErrorBoundaryFallback />}>
<ConfigProvider theme={themeConfig}> <ConfigProvider theme={themeConfig}>
@@ -379,18 +389,11 @@ function App(): JSX.Element {
<AlertRuleProvider> <AlertRuleProvider>
<AppLayout> <AppLayout>
<Suspense fallback={<Spinner size="large" tip="Loading..." />}> <Suspense fallback={<Spinner size="large" tip="Loading..." />}>
<Switch> <Routes>
{routes.map(({ path, component, exact }) => ( {routes && renderRoutes(routes)}
<Route <Route path="/" element={<Home />} />
key={`${path}`} <Route path="*" element={<NotFound />} />
exact={exact} </Routes>
path={path}
component={component}
/>
))}
<Route exact path="/" component={Home} />
<Route path="*" component={NotFound} />
</Switch>
</Suspense> </Suspense>
</AppLayout> </AppLayout>
</AlertRuleProvider> </AlertRuleProvider>

View File

@@ -128,11 +128,23 @@ export const AlertOverview = Loadable(
); );
export const CreateAlertChannelAlerts = Loadable( export const CreateAlertChannelAlerts = Loadable(
() => import(/* webpackChunkName: "Create Channels" */ 'pages/Settings'), () =>
import(/* webpackChunkName: "Create Channels" */ 'pages/CreateAlertChannels'),
); );
export const AllAlertChannels = Loadable( export const AllAlertChannelsPage = Loadable(
() => import(/* webpackChunkName: "All Channels" */ 'pages/Settings'), () =>
import(
/* webpackChunkName: "All Alert Channels" */ 'pages/AllAlertChannelsSettings'
),
);
export const ChannelsEditPage = Loadable(
() =>
import(
/* webpackChunkName: "Channels Edit Page" */
'pages/ChannelsEdit'
),
); );
export const AllErrors = Loadable( export const AllErrors = Loadable(
@@ -147,25 +159,40 @@ export const StatusPage = Loadable(
() => import(/* webpackChunkName: "All Status" */ 'pages/Status'), () => import(/* webpackChunkName: "All Status" */ 'pages/Status'),
); );
export const OrganizationSettings = Loadable( export const OrganizationSettingsPage = Loadable(
() => import(/* webpackChunkName: "All Settings" */ 'pages/Settings'), () =>
import(
/* webpackChunkName: "Organization Settings" */ 'pages/OrganizationSettings'
),
); );
export const IngestionSettings = Loadable( export const IngestionSettings = Loadable(
() => import(/* webpackChunkName: "Ingestion Settings" */ 'pages/Settings'), () =>
import(
/* webpackChunkName: "Ingestion Settings" */ 'pages/IngestionSettings'
),
); );
export const APIKeys = Loadable( export const APIKeys = Loadable(
() => import(/* webpackChunkName: "All Settings" */ 'pages/Settings'), () => import(/* webpackChunkName: "API Keys Settings" */ 'pages/APIKeys'),
); );
export const MySettings = Loadable( export const MySettings = Loadable(
() => import(/* webpackChunkName: "All MySettings" */ 'pages/Settings'), () => import(/* webpackChunkName: "All MySettings" */ 'pages/MySettings'),
);
export const GeneralSettings = Loadable(
() =>
import(
/* webpackChunkName: "General MySettings" */ 'pages/Settings/GeneralSettings'
),
); );
export const CustomDomainSettings = Loadable( export const CustomDomainSettings = Loadable(
() => () =>
import(/* webpackChunkName: "Custom Domain Settings" */ 'pages/Settings'), import(
/* webpackChunkName: "Custom Domain Settings" */ 'pages/CustomDomainSettings'
),
); );
export const Logs = Loadable( export const Logs = Loadable(
@@ -217,7 +244,7 @@ export const LogsIndexToFields = Loadable(
); );
export const BillingPage = Loadable( export const BillingPage = Loadable(
() => import(/* webpackChunkName: "BillingPage" */ 'pages/Settings'), () => import(/* webpackChunkName: "BillingPage" */ 'pages/Billing'),
); );
export const SupportPage = Loadable( export const SupportPage = Loadable(
@@ -244,7 +271,7 @@ export const WorkspaceAccessRestricted = Loadable(
); );
export const ShortcutsPage = Loadable( export const ShortcutsPage = Loadable(
() => import(/* webpackChunkName: "ShortcutsPage" */ 'pages/Settings'), () => import(/* webpackChunkName: "ShortcutsPage" */ 'pages/Shortcuts'),
); );
export const InstalledIntegrations = Loadable( export const InstalledIntegrations = Loadable(

View File

@@ -1,21 +1,28 @@
import ROUTES from 'constants/routes'; import ROUTES from 'constants/routes';
import MessagingQueues from 'pages/MessagingQueues'; import MessagingQueues from 'pages/MessagingQueues';
import { RouteProps } from 'react-router-dom'; import React from 'react';
import { RouteProps } from 'react-router-dom-v5-compat';
import { import {
AlertHistory, AlertHistory,
AlertOverview, AlertOverview,
AllAlertChannels, AllAlertChannelsPage,
AllErrors, AllErrors,
APIKeys,
ApiMonitoring, ApiMonitoring,
BillingPage,
ChannelsEditPage,
CreateAlertChannelAlerts, CreateAlertChannelAlerts,
CreateNewAlerts, CreateNewAlerts,
CustomDomainSettings,
DashboardPage, DashboardPage,
DashboardWidget, DashboardWidget,
EditRulesPage, EditRulesPage,
ErrorDetails, ErrorDetails,
GeneralSettings,
Home, Home,
InfrastructureMonitoring, InfrastructureMonitoring,
IngestionSettings,
InstalledIntegrations, InstalledIntegrations,
LicensePage, LicensePage,
ListAllALertsPage, ListAllALertsPage,
@@ -26,10 +33,12 @@ import {
LogsIndexToFields, LogsIndexToFields,
LogsSaveViews, LogsSaveViews,
MetricsExplorer, MetricsExplorer,
MySettings,
NewDashboardPage, NewDashboardPage,
OldLogsExplorer, OldLogsExplorer,
Onboarding, Onboarding,
OnboardingV2, OnboardingV2,
OrganizationSettingsPage,
OrgOnboarding, OrgOnboarding,
PasswordReset, PasswordReset,
PipelinePage, PipelinePage,
@@ -38,6 +47,7 @@ import {
ServicesTablePage, ServicesTablePage,
ServiceTopLevelOperationsPage, ServiceTopLevelOperationsPage,
SettingsPage, SettingsPage,
ShortcutsPage,
SignupPage, SignupPage,
SomethingWentWrong, SomethingWentWrong,
StatusPage, StatusPage,
@@ -57,387 +67,388 @@ import {
const routes: AppRoutes[] = [ const routes: AppRoutes[] = [
{ {
component: SignupPage, element: SignupPage,
path: ROUTES.SIGN_UP, path: ROUTES.SIGN_UP,
exact: true,
isPrivate: false, isPrivate: false,
key: 'SIGN_UP', key: 'SIGN_UP',
}, },
{ {
path: ROUTES.GET_STARTED, path: ROUTES.GET_STARTED,
exact: false, element: Onboarding,
component: Onboarding,
isPrivate: true, isPrivate: true,
key: 'GET_STARTED', key: 'GET_STARTED',
}, },
{ {
path: ROUTES.GET_STARTED_WITH_CLOUD, path: ROUTES.GET_STARTED_WITH_CLOUD,
exact: false, element: OnboardingV2,
component: OnboardingV2,
isPrivate: true, isPrivate: true,
key: 'GET_STARTED_WITH_CLOUD', key: 'GET_STARTED_WITH_CLOUD',
}, },
{ {
path: ROUTES.HOME, path: ROUTES.HOME,
exact: true, element: Home,
component: Home,
isPrivate: true, isPrivate: true,
key: 'HOME', key: 'HOME',
}, },
{ {
path: ROUTES.ONBOARDING, path: ROUTES.ONBOARDING,
exact: false, element: OrgOnboarding,
component: OrgOnboarding,
isPrivate: true, isPrivate: true,
key: 'ONBOARDING', key: 'ONBOARDING',
}, },
{ {
component: LogsIndexToFields, element: LogsIndexToFields,
path: ROUTES.LOGS_INDEX_FIELDS, path: ROUTES.LOGS_INDEX_FIELDS,
exact: true,
isPrivate: true, isPrivate: true,
key: 'LOGS_INDEX_FIELDS', key: 'LOGS_INDEX_FIELDS',
}, },
{ {
component: ServicesTablePage, element: ServicesTablePage,
path: ROUTES.APPLICATION, path: ROUTES.APPLICATION,
exact: true,
isPrivate: true, isPrivate: true,
key: 'APPLICATION', key: 'APPLICATION',
}, },
{ {
path: ROUTES.SERVICE_METRICS, path: ROUTES.SERVICE_METRICS,
exact: true, element: ServiceMetricsPage,
component: ServiceMetricsPage,
isPrivate: true, isPrivate: true,
key: 'SERVICE_METRICS', key: 'SERVICE_METRICS',
}, },
{ {
path: ROUTES.SERVICE_TOP_LEVEL_OPERATIONS, path: ROUTES.SERVICE_TOP_LEVEL_OPERATIONS,
exact: true, element: ServiceTopLevelOperationsPage,
component: ServiceTopLevelOperationsPage,
isPrivate: true, isPrivate: true,
key: 'SERVICE_TOP_LEVEL_OPERATIONS', key: 'SERVICE_TOP_LEVEL_OPERATIONS',
}, },
{ {
path: ROUTES.SERVICE_MAP, path: ROUTES.SERVICE_MAP,
component: ServiceMapPage, element: ServiceMapPage,
isPrivate: true, isPrivate: true,
exact: true,
key: 'SERVICE_MAP', key: 'SERVICE_MAP',
}, },
{ {
path: ROUTES.LOGS_SAVE_VIEWS, path: ROUTES.LOGS_SAVE_VIEWS,
component: LogsSaveViews, element: LogsSaveViews,
isPrivate: true, isPrivate: true,
exact: true,
key: 'LOGS_SAVE_VIEWS', key: 'LOGS_SAVE_VIEWS',
}, },
{ {
path: ROUTES.TRACE_DETAIL, path: ROUTES.TRACE_DETAIL,
exact: true, element: TraceDetail,
component: TraceDetail,
isPrivate: true, isPrivate: true,
key: 'TRACE_DETAIL', key: 'TRACE_DETAIL',
}, },
{ {
path: ROUTES.SETTINGS, path: ROUTES.SETTINGS,
exact: false, element: SettingsPage,
component: SettingsPage,
isPrivate: true, isPrivate: true,
key: 'SETTINGS', key: 'SETTINGS',
children: [
{
path: '', // Route for /settings
element: GeneralSettings,
isPrivate: true,
key: 'SETTINGS',
},
{
path: 'my-settings',
element: MySettings,
isPrivate: true,
key: 'MY_SETTINGS',
},
{
path: 'custom-domain-settings',
element: CustomDomainSettings,
isPrivate: true,
key: 'CUSTOM_DOMAIN_SETTINGS',
},
{
path: 'org-settings',
element: OrganizationSettingsPage,
isPrivate: true,
key: 'ORG_SETTINGS',
},
{
path: 'channels',
element: AllAlertChannelsPage,
isPrivate: true,
key: 'ALL_CHANNELS',
},
{
path: 'ingestion-settings',
element: IngestionSettings,
isPrivate: true,
key: 'INGESTION_SETTINGS',
},
{
path: 'api-keys',
element: APIKeys,
isPrivate: true,
key: 'API_KEYS',
},
{
path: 'billing',
element: BillingPage,
isPrivate: true,
key: 'BILLING',
},
{
path: 'shortcuts',
element: ShortcutsPage,
isPrivate: true,
key: 'SHORTCUTS',
},
{
path: 'channels/new',
element: CreateAlertChannelAlerts,
isPrivate: true,
key: 'CHANNELS_NEW',
},
{
path: 'channels/edit/:channelId',
element: ChannelsEditPage,
isPrivate: true,
key: 'CHANNELS_EDIT',
},
],
}, },
{ {
path: ROUTES.USAGE_EXPLORER, path: ROUTES.USAGE_EXPLORER,
exact: true, element: UsageExplorerPage,
component: UsageExplorerPage,
isPrivate: true, isPrivate: true,
key: 'USAGE_EXPLORER', key: 'USAGE_EXPLORER',
}, },
{ {
path: ROUTES.ALL_DASHBOARD, path: ROUTES.ALL_DASHBOARD,
exact: true, element: DashboardPage,
component: DashboardPage,
isPrivate: true, isPrivate: true,
key: 'ALL_DASHBOARD', key: 'ALL_DASHBOARD',
}, },
{ {
path: ROUTES.DASHBOARD, path: ROUTES.DASHBOARD,
exact: true, element: NewDashboardPage,
component: NewDashboardPage,
isPrivate: true, isPrivate: true,
key: 'DASHBOARD', key: 'DASHBOARD',
}, },
{ {
path: ROUTES.DASHBOARD_WIDGET, path: ROUTES.DASHBOARD_WIDGET,
exact: true, element: DashboardWidget,
component: DashboardWidget,
isPrivate: true, isPrivate: true,
key: 'DASHBOARD_WIDGET', key: 'DASHBOARD_WIDGET',
}, },
{ {
path: ROUTES.EDIT_ALERTS, path: ROUTES.EDIT_ALERTS,
exact: true, element: EditRulesPage,
component: EditRulesPage,
isPrivate: true, isPrivate: true,
key: 'EDIT_ALERTS', key: 'EDIT_ALERTS',
}, },
{ {
path: ROUTES.LIST_ALL_ALERT, path: ROUTES.LIST_ALL_ALERT,
exact: true, element: ListAllALertsPage,
component: ListAllALertsPage,
isPrivate: true, isPrivate: true,
key: 'LIST_ALL_ALERT', key: 'LIST_ALL_ALERT',
}, },
{ {
path: ROUTES.ALERTS_NEW, path: ROUTES.ALERTS_NEW,
exact: true, element: CreateNewAlerts,
component: CreateNewAlerts,
isPrivate: true, isPrivate: true,
key: 'ALERTS_NEW', key: 'ALERTS_NEW',
}, },
{ {
path: ROUTES.ALERT_HISTORY, path: ROUTES.ALERT_HISTORY,
exact: true, element: AlertHistory,
component: AlertHistory,
isPrivate: true, isPrivate: true,
key: 'ALERT_HISTORY', key: 'ALERT_HISTORY',
}, },
{ {
path: ROUTES.ALERT_OVERVIEW, path: ROUTES.ALERT_OVERVIEW,
exact: true, element: AlertOverview,
component: AlertOverview,
isPrivate: true, isPrivate: true,
key: 'ALERT_OVERVIEW', key: 'ALERT_OVERVIEW',
}, },
{ {
path: ROUTES.TRACE, path: ROUTES.TRACE,
exact: true, element: TraceFilter,
component: TraceFilter,
isPrivate: true, isPrivate: true,
key: 'TRACE', key: 'TRACE',
}, },
{ {
path: ROUTES.TRACES_EXPLORER, path: ROUTES.TRACES_EXPLORER,
exact: true, element: TracesExplorer,
component: TracesExplorer,
isPrivate: true, isPrivate: true,
key: 'TRACES_EXPLORER', key: 'TRACES_EXPLORER',
}, },
{ {
path: ROUTES.TRACES_SAVE_VIEWS, path: ROUTES.TRACES_SAVE_VIEWS,
exact: true, element: TracesSaveViews,
component: TracesSaveViews,
isPrivate: true, isPrivate: true,
key: 'TRACES_SAVE_VIEWS', key: 'TRACES_SAVE_VIEWS',
}, },
{ {
path: ROUTES.TRACES_FUNNELS, path: ROUTES.TRACES_FUNNELS,
exact: true, element: TracesFunnels,
component: TracesFunnels,
isPrivate: true, isPrivate: true,
key: 'TRACES_FUNNELS', key: 'TRACES_FUNNELS',
}, },
{ {
path: ROUTES.TRACES_FUNNELS_DETAIL, path: ROUTES.TRACES_FUNNELS_DETAIL,
exact: true, element: TracesFunnelDetails,
component: TracesFunnelDetails,
isPrivate: true, isPrivate: true,
key: 'TRACES_FUNNELS_DETAIL', key: 'TRACES_FUNNELS_DETAIL',
}, },
{
path: ROUTES.CHANNELS_NEW,
exact: true,
component: CreateAlertChannelAlerts,
isPrivate: true,
key: 'CHANNELS_NEW',
},
{
path: ROUTES.ALL_CHANNELS,
exact: true,
component: AllAlertChannels,
isPrivate: true,
key: 'ALL_CHANNELS',
},
{ {
path: ROUTES.ALL_ERROR, path: ROUTES.ALL_ERROR,
exact: true,
isPrivate: true, isPrivate: true,
component: AllErrors, element: AllErrors,
key: 'ALL_ERROR', key: 'ALL_ERROR',
}, },
{ {
path: ROUTES.ERROR_DETAIL, path: ROUTES.ERROR_DETAIL,
exact: true, element: ErrorDetails,
component: ErrorDetails,
isPrivate: true, isPrivate: true,
key: 'ERROR_DETAIL', key: 'ERROR_DETAIL',
}, },
{ {
path: ROUTES.VERSION, path: ROUTES.VERSION,
exact: true, element: StatusPage,
component: StatusPage,
isPrivate: true, isPrivate: true,
key: 'VERSION', key: 'VERSION',
}, },
{ {
path: ROUTES.LOGS, path: ROUTES.LOGS,
exact: true, element: Logs,
component: Logs,
key: 'LOGS', key: 'LOGS',
isPrivate: true, isPrivate: true,
}, },
{ {
path: ROUTES.LOGS_EXPLORER, path: ROUTES.LOGS_EXPLORER,
exact: true, element: LogsExplorer,
component: LogsExplorer,
key: 'LOGS_EXPLORER', key: 'LOGS_EXPLORER',
isPrivate: true, isPrivate: true,
}, },
{ {
path: ROUTES.OLD_LOGS_EXPLORER, path: ROUTES.OLD_LOGS_EXPLORER,
exact: true, element: OldLogsExplorer,
component: OldLogsExplorer,
key: 'OLD_LOGS_EXPLORER', key: 'OLD_LOGS_EXPLORER',
isPrivate: true, isPrivate: true,
}, },
{ {
path: ROUTES.LIVE_LOGS, path: ROUTES.LIVE_LOGS,
exact: true, element: LiveLogs,
component: LiveLogs,
key: 'LIVE_LOGS', key: 'LIVE_LOGS',
isPrivate: true, isPrivate: true,
}, },
{ {
path: ROUTES.LOGS_PIPELINES, path: ROUTES.LOGS_PIPELINES,
exact: true, element: PipelinePage,
component: PipelinePage,
key: 'LOGS_PIPELINES', key: 'LOGS_PIPELINES',
isPrivate: true, isPrivate: true,
}, },
{ {
path: ROUTES.LOGIN, path: ROUTES.LOGIN,
exact: true, element: Login,
component: Login,
isPrivate: false, isPrivate: false,
key: 'LOGIN', key: 'LOGIN',
}, },
{ {
path: ROUTES.UN_AUTHORIZED, path: ROUTES.UN_AUTHORIZED,
exact: true, element: UnAuthorized,
component: UnAuthorized,
key: 'UN_AUTHORIZED', key: 'UN_AUTHORIZED',
isPrivate: true, isPrivate: true,
}, },
{ {
path: ROUTES.PASSWORD_RESET, path: ROUTES.PASSWORD_RESET,
exact: true, element: PasswordReset,
component: PasswordReset,
key: 'PASSWORD_RESET', key: 'PASSWORD_RESET',
isPrivate: false, isPrivate: false,
}, },
{ {
path: ROUTES.SOMETHING_WENT_WRONG, path: ROUTES.SOMETHING_WENT_WRONG,
exact: true, element: SomethingWentWrong,
component: SomethingWentWrong,
key: 'SOMETHING_WENT_WRONG', key: 'SOMETHING_WENT_WRONG',
isPrivate: false, isPrivate: false,
}, },
{ {
path: ROUTES.WORKSPACE_LOCKED, path: ROUTES.WORKSPACE_LOCKED,
exact: true, element: WorkspaceBlocked,
component: WorkspaceBlocked,
isPrivate: true, isPrivate: true,
key: 'WORKSPACE_LOCKED', key: 'WORKSPACE_LOCKED',
}, },
{ {
path: ROUTES.WORKSPACE_SUSPENDED, path: ROUTES.WORKSPACE_SUSPENDED,
exact: true, element: WorkspaceSuspended,
component: WorkspaceSuspended,
isPrivate: true, isPrivate: true,
key: 'WORKSPACE_SUSPENDED', key: 'WORKSPACE_SUSPENDED',
}, },
{ {
path: ROUTES.WORKSPACE_ACCESS_RESTRICTED, path: ROUTES.WORKSPACE_ACCESS_RESTRICTED,
exact: true, element: WorkspaceAccessRestricted,
component: WorkspaceAccessRestricted,
isPrivate: true, isPrivate: true,
key: 'WORKSPACE_ACCESS_RESTRICTED', key: 'WORKSPACE_ACCESS_RESTRICTED',
}, },
{ {
path: ROUTES.INTEGRATIONS, path: ROUTES.INTEGRATIONS,
exact: true, element: InstalledIntegrations,
component: InstalledIntegrations,
isPrivate: true, isPrivate: true,
key: 'INTEGRATIONS', key: 'INTEGRATIONS',
}, },
{ {
path: ROUTES.MESSAGING_QUEUES_KAFKA, path: ROUTES.MESSAGING_QUEUES_KAFKA,
exact: true, element: MessagingQueues,
component: MessagingQueues,
key: 'MESSAGING_QUEUES_KAFKA', key: 'MESSAGING_QUEUES_KAFKA',
isPrivate: true, isPrivate: true,
}, },
{ {
path: ROUTES.MESSAGING_QUEUES_CELERY_TASK, path: ROUTES.MESSAGING_QUEUES_CELERY_TASK,
exact: true, element: MessagingQueues,
component: MessagingQueues,
key: 'MESSAGING_QUEUES_CELERY_TASK', key: 'MESSAGING_QUEUES_CELERY_TASK',
isPrivate: true, isPrivate: true,
}, },
{ {
path: ROUTES.MESSAGING_QUEUES_OVERVIEW, path: ROUTES.MESSAGING_QUEUES_OVERVIEW,
exact: true, element: MessagingQueues,
component: MessagingQueues,
key: 'MESSAGING_QUEUES_OVERVIEW', key: 'MESSAGING_QUEUES_OVERVIEW',
isPrivate: true, isPrivate: true,
}, },
{ {
path: ROUTES.MESSAGING_QUEUES_KAFKA_DETAIL, path: ROUTES.MESSAGING_QUEUES_KAFKA_DETAIL,
exact: true, element: MessagingQueues,
component: MessagingQueues,
key: 'MESSAGING_QUEUES_KAFKA_DETAIL', key: 'MESSAGING_QUEUES_KAFKA_DETAIL',
isPrivate: true, isPrivate: true,
}, },
{ {
path: ROUTES.INFRASTRUCTURE_MONITORING_HOSTS, path: ROUTES.INFRASTRUCTURE_MONITORING_HOSTS,
exact: true, element: InfrastructureMonitoring,
component: InfrastructureMonitoring,
key: 'INFRASTRUCTURE_MONITORING_HOSTS', key: 'INFRASTRUCTURE_MONITORING_HOSTS',
isPrivate: true, isPrivate: true,
}, },
{ {
path: ROUTES.INFRASTRUCTURE_MONITORING_KUBERNETES, path: ROUTES.INFRASTRUCTURE_MONITORING_KUBERNETES,
exact: true, element: InfrastructureMonitoring,
component: InfrastructureMonitoring,
key: 'INFRASTRUCTURE_MONITORING_KUBERNETES', key: 'INFRASTRUCTURE_MONITORING_KUBERNETES',
isPrivate: true, isPrivate: true,
}, },
{ {
path: ROUTES.METRICS_EXPLORER, path: ROUTES.METRICS_EXPLORER,
exact: true, element: MetricsExplorer,
component: MetricsExplorer,
key: 'METRICS_EXPLORER', key: 'METRICS_EXPLORER',
isPrivate: true, isPrivate: true,
}, },
{ {
path: ROUTES.METRICS_EXPLORER_EXPLORER, path: ROUTES.METRICS_EXPLORER_EXPLORER,
exact: true, element: MetricsExplorer,
component: MetricsExplorer,
key: 'METRICS_EXPLORER_EXPLORER', key: 'METRICS_EXPLORER_EXPLORER',
isPrivate: true, isPrivate: true,
}, },
{ {
path: ROUTES.METRICS_EXPLORER_VIEWS, path: ROUTES.METRICS_EXPLORER_VIEWS,
exact: true, element: MetricsExplorer,
component: MetricsExplorer,
key: 'METRICS_EXPLORER_VIEWS', key: 'METRICS_EXPLORER_VIEWS',
isPrivate: true, isPrivate: true,
}, },
{ {
path: ROUTES.API_MONITORING, path: ROUTES.API_MONITORING,
exact: true, element: ApiMonitoring,
component: ApiMonitoring,
key: 'API_MONITORING', key: 'API_MONITORING',
isPrivate: true, isPrivate: true,
}, },
@@ -445,16 +456,14 @@ const routes: AppRoutes[] = [
export const SUPPORT_ROUTE: AppRoutes = { export const SUPPORT_ROUTE: AppRoutes = {
path: ROUTES.SUPPORT, path: ROUTES.SUPPORT,
exact: true, element: SupportPage,
component: SupportPage,
key: 'SUPPORT', key: 'SUPPORT',
isPrivate: true, isPrivate: true,
}; };
export const LIST_LICENSES: AppRoutes = { export const LIST_LICENSES: AppRoutes = {
path: ROUTES.LIST_LICENSES, path: ROUTES.LIST_LICENSES,
exact: true, element: LicensePage,
component: LicensePage,
isPrivate: true, isPrivate: true,
key: 'LIST_LICENSES', key: 'LIST_LICENSES',
}; };
@@ -487,11 +496,13 @@ export const ROUTES_NOT_TO_BE_OVERRIDEN: string[] = [
]; ];
export interface AppRoutes { export interface AppRoutes {
component: RouteProps['component']; element:
| React.ComponentType<any>
| React.LazyExoticComponent<React.ComponentType<any>>;
path: RouteProps['path']; path: RouteProps['path'];
exact: RouteProps['exact'];
isPrivate: boolean; isPrivate: boolean;
key: keyof typeof ROUTES; key: keyof typeof ROUTES;
children?: AppRoutes[];
} }
export default routes; export default routes;

View File

@@ -27,18 +27,23 @@ const ROUTES = {
ALERTS_NEW: '/alerts/new', ALERTS_NEW: '/alerts/new',
ALERT_HISTORY: '/alerts/history', ALERT_HISTORY: '/alerts/history',
ALERT_OVERVIEW: '/alerts/overview', ALERT_OVERVIEW: '/alerts/overview',
ALL_CHANNELS: '/settings/channels',
CHANNELS_NEW: '/settings/channels/new', // Setting routes
CHANNELS_EDIT: '/settings/channels/edit/:channelId',
ALL_ERROR: '/exceptions',
ERROR_DETAIL: '/error-detail',
VERSION: '/status',
SETTINGS: '/settings', SETTINGS: '/settings',
MY_SETTINGS: '/settings/my-settings', MY_SETTINGS: '/settings/my-settings',
ORG_SETTINGS: '/settings/org-settings', ORG_SETTINGS: '/settings/org-settings',
CUSTOM_DOMAIN_SETTINGS: '/settings/custom-domain-settings', CUSTOM_DOMAIN_SETTINGS: '/settings/custom-domain-settings',
API_KEYS: '/settings/api-keys', API_KEYS: '/settings/api-keys',
INGESTION_SETTINGS: '/settings/ingestion-settings', INGESTION_SETTINGS: '/settings/ingestion-settings',
ALL_CHANNELS: '/settings/channels',
CHANNELS_NEW: '/settings/channels/new',
CHANNELS_EDIT: '/settings/channels/edit/:channelId',
BILLING: '/settings/billing',
SHORTCUTS: '/settings/shortcuts',
ALL_ERROR: '/exceptions',
ERROR_DETAIL: '/error-detail',
VERSION: '/status',
SOMETHING_WENT_WRONG: '/something-went-wrong', SOMETHING_WENT_WRONG: '/something-went-wrong',
UN_AUTHORIZED: '/un-authorized', UN_AUTHORIZED: '/un-authorized',
NOT_FOUND: '/not-found', NOT_FOUND: '/not-found',
@@ -52,7 +57,6 @@ const ROUTES = {
LIST_LICENSES: '/licenses', LIST_LICENSES: '/licenses',
LOGS_INDEX_FIELDS: '/logs-explorer/index-fields', LOGS_INDEX_FIELDS: '/logs-explorer/index-fields',
TRACE_EXPLORER: '/trace-explorer', TRACE_EXPLORER: '/trace-explorer',
BILLING: '/settings/billing',
SUPPORT: '/support', SUPPORT: '/support',
LOGS_SAVE_VIEWS: '/logs/saved-views', LOGS_SAVE_VIEWS: '/logs/saved-views',
TRACES_SAVE_VIEWS: '/traces/saved-views', TRACES_SAVE_VIEWS: '/traces/saved-views',
@@ -60,7 +64,6 @@ const ROUTES = {
TRACES_FUNNELS_DETAIL: '/traces/funnels/:funnelId', TRACES_FUNNELS_DETAIL: '/traces/funnels/:funnelId',
WORKSPACE_LOCKED: '/workspace-locked', WORKSPACE_LOCKED: '/workspace-locked',
WORKSPACE_SUSPENDED: '/workspace-suspended', WORKSPACE_SUSPENDED: '/workspace-suspended',
SHORTCUTS: '/settings/shortcuts',
INTEGRATIONS: '/integrations', INTEGRATIONS: '/integrations',
MESSAGING_QUEUES_BASE: '/messaging-queues', MESSAGING_QUEUES_BASE: '/messaging-queues',
MESSAGING_QUEUES_KAFKA: '/messaging-queues/kafka', MESSAGING_QUEUES_KAFKA: '/messaging-queues/kafka',
@@ -80,4 +83,17 @@ const ROUTES = {
HOME_PAGE: '/', HOME_PAGE: '/',
} as const; } as const;
export const SETTINGS_NESTED_ROUTES = {
MY_SETTINGS: 'my-settings',
ORG_SETTINGS: 'org-settings',
CUSTOM_DOMAIN_SETTINGS: 'custom-domain-settings',
API_KEYS: 'api-keys',
INGESTION_SETTINGS: 'ingestion-settings',
ALL_CHANNELS: 'channels',
CHANNELS_NEW: 'channels/new',
CHANNELS_EDIT: 'channels/edit/:channelId',
BILLING: 'billing',
SHORTCUTS: 'shortcuts',
};
export default ROUTES; export default ROUTES;

View File

@@ -571,7 +571,7 @@ function CreateAlertChannels({
} }
interface CreateAlertChannelsProps { interface CreateAlertChannelsProps {
preType: ChannelType; preType?: ChannelType;
} }
export default CreateAlertChannels; export default CreateAlertChannels;

View File

@@ -3,40 +3,37 @@ import './TopNav.styles.scss';
import { Col, Row, Space } from 'antd'; import { Col, Row, Space } from 'antd';
import ROUTES from 'constants/routes'; import ROUTES from 'constants/routes';
import { useMemo } from 'react'; import { useMemo } from 'react';
import { matchPath, useHistory } from 'react-router-dom'; import { matchPath, useLocation } from 'react-router-dom-v5-compat';
import NewExplorerCTA from '../NewExplorerCTA'; import NewExplorerCTA from '../NewExplorerCTA';
import DateTimeSelector from './DateTimeSelectionV2'; import DateTimeSelector from './DateTimeSelectionV2';
import { routesToDisable, routesToSkip } from './DateTimeSelectionV2/config'; import { routesToDisable, routesToSkip } from './DateTimeSelectionV2/config';
function TopNav(): JSX.Element | null { function TopNav(): JSX.Element | null {
const { location } = useHistory(); const location = useLocation();
console.log({
location,
match: matchPath(location.pathname, '/settings/channels/edit/:channelId'),
});
const isRouteToSkip = useMemo( const isRouteToSkip = useMemo(
() => () => routesToSkip.some((route) => matchPath(route, location.pathname)),
routesToSkip.some((route) =>
matchPath(location.pathname, { path: route, exact: true }),
),
[location.pathname], [location.pathname],
); );
const isDisabled = useMemo( const isDisabled = useMemo(
() => () => routesToDisable.some((route) => matchPath(route, location.pathname)),
routesToDisable.some((route) =>
matchPath(location.pathname, { path: route, exact: true }),
),
[location.pathname], [location.pathname],
); );
const isSignUpPage = useMemo( const isSignUpPage = useMemo(
() => matchPath(location.pathname, { path: ROUTES.SIGN_UP, exact: true }), () => matchPath(location.pathname, ROUTES.SIGN_UP),
[location.pathname], [location.pathname],
); );
const isNewAlertsLandingPage = useMemo( const isNewAlertsLandingPage = useMemo(
() => () => matchPath(location.pathname, ROUTES.ALERTS_NEW) && !location.search,
matchPath(location.pathname, { path: ROUTES.ALERTS_NEW, exact: true }) &&
!location.search,
[location.pathname, location.search], [location.pathname, location.search],
); );

View File

@@ -0,0 +1,49 @@
import routes, { AppRoutes } from 'AppRoutes/routes';
import { FeatureKeys } from 'constants/features';
import { getRoutes } from 'pages/Settings/utils';
import { useAppContext } from 'providers/App/App';
import { useMemo } from 'react';
import useComponentPermission from './useComponentPermission';
import { useGetTenantLicense } from './useGetTenantLicense';
export const useAppRoutes = (): { routes: AppRoutes[] } => {
const { user, featureFlags, trialInfo } = useAppContext();
const { isCloudUser, isEnterpriseSelfHostedUser } = useGetTenantLicense();
const [isCurrentOrgSettings] = useComponentPermission(
['current_org_settings'],
user.role,
);
const isGatewayEnabled =
featureFlags?.find((feature) => feature.name === FeatureKeys.GATEWAY)
?.active || false;
const isWorkspaceBlocked = trialInfo?.workSpaceBlock || false;
const tabRoutes = useMemo(
() =>
getRoutes(
user.role,
isCurrentOrgSettings,
isGatewayEnabled,
isWorkspaceBlocked,
isCloudUser,
isEnterpriseSelfHostedUser,
),
[
user.role,
isCurrentOrgSettings,
isGatewayEnabled,
isWorkspaceBlocked,
isCloudUser,
isEnterpriseSelfHostedUser,
],
);
console.log({ tabRoutes });
return { routes };
};

View File

@@ -0,0 +1,7 @@
import APIKeysSettingsContainer from 'container/APIKeys/APIKeys';
function APIKeysSettings(): JSX.Element {
return <APIKeysSettingsContainer />;
}
export default APIKeysSettings;

View File

@@ -0,0 +1,7 @@
import AllAlertChannelsSettingsContainer from 'container/AllAlertChannels';
function AllAlertChannelsSettings(): JSX.Element {
return <AllAlertChannelsSettingsContainer />;
}
export default AllAlertChannelsSettings;

View File

@@ -0,0 +1,7 @@
import CreateAlertChannelsContainer from 'container/CreateAlertChannels';
function CreateAlertChannelsSettings(): JSX.Element {
return <CreateAlertChannelsContainer />;
}
export default CreateAlertChannelsSettings;

View File

@@ -0,0 +1,6 @@
import CustomDomainSettingsContainer from 'container/CustomDomainSettings';
function CustomDomainSettings(): JSX.Element {
return <CustomDomainSettingsContainer />;
}
export default CustomDomainSettings;

View File

@@ -0,0 +1,7 @@
import IngestionSettingsContainer from 'container/IngestionSettings/MultiIngestionSettings';
function IngestionSettings(): JSX.Element {
return <IngestionSettingsContainer />;
}
export default IngestionSettings;

View File

@@ -0,0 +1,7 @@
import OrganizationSettingsContainer from 'container/OrganizationSettings';
function OrganizationSettings(): JSX.Element {
return <OrganizationSettingsContainer />;
}
export default OrganizationSettings;

View File

@@ -0,0 +1,7 @@
import GeneralSettingsContainer from 'container/GeneralSettings';
function GeneralSettings(): JSX.Element {
return <GeneralSettingsContainer />;
}
export default GeneralSettings;

View File

@@ -1,35 +1,25 @@
import './Settings.styles.scss'; import './Settings.styles.scss';
import logEvent from 'api/common/logEvent'; import logEvent from 'api/common/logEvent';
import RouteTab from 'components/RouteTab';
import { FeatureKeys } from 'constants/features';
import ROUTES from 'constants/routes'; import ROUTES from 'constants/routes';
import { routeConfig } from 'container/SideNav/config'; import { routeConfig } from 'container/SideNav/config';
import { getQueryString } from 'container/SideNav/helper'; import { getQueryString } from 'container/SideNav/helper';
import { settingsMenuItems as defaultSettingsMenuItems } from 'container/SideNav/menuItems'; import { settingsMenuItems as defaultSettingsMenuItems } from 'container/SideNav/menuItems';
import NavItem from 'container/SideNav/NavItem/NavItem'; import NavItem from 'container/SideNav/NavItem/NavItem';
import { SidebarItem } from 'container/SideNav/sideNav.types'; import { SidebarItem } from 'container/SideNav/sideNav.types';
import useComponentPermission from 'hooks/useComponentPermission';
import { useGetTenantLicense } from 'hooks/useGetTenantLicense'; import { useGetTenantLicense } from 'hooks/useGetTenantLicense';
import history from 'lib/history'; import history from 'lib/history';
import { Wrench } from 'lucide-react'; import { Wrench } from 'lucide-react';
import { useAppContext } from 'providers/App/App'; import { useAppContext } from 'providers/App/App';
import { useCallback, useEffect, useMemo, useState } from 'react'; import { useCallback, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useLocation } from 'react-router-dom'; import { useLocation } from 'react-router-dom';
import { Outlet } from 'react-router-dom-v5-compat';
import { USER_ROLES } from 'types/roles'; import { USER_ROLES } from 'types/roles';
import { getRoutes } from './utils';
function SettingsPage(): JSX.Element { function SettingsPage(): JSX.Element {
const { pathname, search } = useLocation(); const { pathname, search } = useLocation();
const { const { user, trialInfo, isFetchingActiveLicense } = useAppContext();
user,
featureFlags,
trialInfo,
isFetchingActiveLicense,
} = useAppContext();
const { isCloudUser, isEnterpriseSelfHostedUser } = useGetTenantLicense(); const { isCloudUser, isEnterpriseSelfHostedUser } = useGetTenantLicense();
const [settingsMenuItems, setSettingsMenuItems] = useState<SidebarItem[]>( const [settingsMenuItems, setSettingsMenuItems] = useState<SidebarItem[]>(
@@ -39,18 +29,6 @@ function SettingsPage(): JSX.Element {
const isAdmin = user.role === USER_ROLES.ADMIN; const isAdmin = user.role === USER_ROLES.ADMIN;
const isEditor = user.role === USER_ROLES.EDITOR; const isEditor = user.role === USER_ROLES.EDITOR;
const isWorkspaceBlocked = trialInfo?.workSpaceBlock || false;
const [isCurrentOrgSettings] = useComponentPermission(
['current_org_settings'],
user.role,
);
const { t } = useTranslation(['routes']);
const isGatewayEnabled =
featureFlags?.find((feature) => feature.name === FeatureKeys.GATEWAY)
?.active || false;
// eslint-disable-next-line sonarjs/cognitive-complexity // eslint-disable-next-line sonarjs/cognitive-complexity
useEffect(() => { useEffect(() => {
setSettingsMenuItems((prevItems) => { setSettingsMenuItems((prevItems) => {
@@ -162,28 +140,6 @@ function SettingsPage(): JSX.Element {
pathname, pathname,
]); ]);
const routes = useMemo(
() =>
getRoutes(
user.role,
isCurrentOrgSettings,
isGatewayEnabled,
isWorkspaceBlocked,
isCloudUser,
isEnterpriseSelfHostedUser,
t,
),
[
user.role,
isCurrentOrgSettings,
isGatewayEnabled,
isWorkspaceBlocked,
isCloudUser,
isEnterpriseSelfHostedUser,
t,
],
);
const isCtrlMetaKey = (e: MouseEvent): boolean => e.ctrlKey || e.metaKey; const isCtrlMetaKey = (e: MouseEvent): boolean => e.ctrlKey || e.metaKey;
const openInNewTab = (path: string): void => { const openInNewTab = (path: string): void => {
@@ -261,12 +217,7 @@ function SettingsPage(): JSX.Element {
</div> </div>
<div className="settings-page-content"> <div className="settings-page-content">
<RouteTab <Outlet />
routes={routes}
activeKey={pathname}
history={history}
tabBarStyle={{ display: 'none' }}
/>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -1,22 +1,6 @@
import { RouteTabProps } from 'components/RouteTab/types'; import ROUTES, { SETTINGS_NESTED_ROUTES } from 'constants/routes';
import { TFunction } from 'i18next';
import { ROLES, USER_ROLES } from 'types/roles'; import { ROLES, USER_ROLES } from 'types/roles';
import {
alertChannels,
apiKeys,
billingSettings,
createAlertChannels,
customDomainSettings,
editAlertChannels,
generalSettings,
ingestionSettings,
keyboardShortcuts,
multiIngestionSettings,
mySettings,
organizationSettings,
} from './config';
export const getRoutes = ( export const getRoutes = (
userRole: ROLES | null, userRole: ROLES | null,
isCurrentOrgSettings: boolean, isCurrentOrgSettings: boolean,
@@ -24,8 +8,7 @@ export const getRoutes = (
isWorkspaceBlocked: boolean, isWorkspaceBlocked: boolean,
isCloudUser: boolean, isCloudUser: boolean,
isEnterpriseSelfHostedUser: boolean, isEnterpriseSelfHostedUser: boolean,
t: TFunction, ): string[] => {
): RouteTabProps['routes'] => {
const settings = []; const settings = [];
const isAdmin = userRole === USER_ROLES.ADMIN; const isAdmin = userRole === USER_ROLES.ADMIN;
@@ -33,44 +16,47 @@ export const getRoutes = (
if (isWorkspaceBlocked && isAdmin) { if (isWorkspaceBlocked && isAdmin) {
settings.push( settings.push(
...organizationSettings(t), SETTINGS_NESTED_ROUTES.ORG_SETTINGS,
...mySettings(t), SETTINGS_NESTED_ROUTES.MY_SETTINGS,
...billingSettings(t), SETTINGS_NESTED_ROUTES.BILLING,
...keyboardShortcuts(t), SETTINGS_NESTED_ROUTES.SHORTCUTS,
); );
return settings; return settings;
} }
settings.push(...generalSettings(t)); settings.push(ROUTES.SETTINGS);
if (isCurrentOrgSettings) { if (isCurrentOrgSettings) {
settings.push(...organizationSettings(t)); settings.push(SETTINGS_NESTED_ROUTES.ORG_SETTINGS);
} }
if (isGatewayEnabled && (isAdmin || isEditor)) { if (isGatewayEnabled && (isAdmin || isEditor)) {
settings.push(...multiIngestionSettings(t)); settings.push(SETTINGS_NESTED_ROUTES.INGESTION_SETTINGS);
} }
if (isCloudUser && !isGatewayEnabled) { if (isCloudUser && !isGatewayEnabled) {
settings.push(...ingestionSettings(t)); settings.push(SETTINGS_NESTED_ROUTES.INGESTION_SETTINGS);
} }
settings.push(...alertChannels(t)); settings.push(SETTINGS_NESTED_ROUTES.ALL_CHANNELS);
if (isAdmin) { if (isAdmin) {
settings.push(...apiKeys(t)); settings.push(SETTINGS_NESTED_ROUTES.API_KEYS);
} }
if ((isCloudUser || isEnterpriseSelfHostedUser) && isAdmin) { if ((isCloudUser || isEnterpriseSelfHostedUser) && isAdmin) {
settings.push(...customDomainSettings(t), ...billingSettings(t)); settings.push(
SETTINGS_NESTED_ROUTES.CUSTOM_DOMAIN_SETTINGS,
SETTINGS_NESTED_ROUTES.BILLING,
);
} }
settings.push( settings.push(
...mySettings(t), SETTINGS_NESTED_ROUTES.MY_SETTINGS,
...createAlertChannels(t), SETTINGS_NESTED_ROUTES.CHANNELS_NEW,
...editAlertChannels(t), SETTINGS_NESTED_ROUTES.CHANNELS_EDIT,
...keyboardShortcuts(t), SETTINGS_NESTED_ROUTES.SHORTCUTS,
); );
return settings; return settings;