@shellui/core 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -0
- package/dist/ContentView-CZG-ro_B.js +146 -0
- package/dist/ContentView-CZG-ro_B.js.map +1 -0
- package/dist/CookiePreferencesView-MhO9FO-4.js +213 -0
- package/dist/CookiePreferencesView-MhO9FO-4.js.map +1 -0
- package/dist/DefaultLayout-Dbb3uJED.js +394 -0
- package/dist/DefaultLayout-Dbb3uJED.js.map +1 -0
- package/dist/FullscreenLayout-1SgPHWw-.js +30 -0
- package/dist/FullscreenLayout-1SgPHWw-.js.map +1 -0
- package/dist/HomeView-DYU-O_Il.js +21 -0
- package/dist/HomeView-DYU-O_Il.js.map +1 -0
- package/dist/NotFoundView-CeYjJNg0.js +52 -0
- package/dist/NotFoundView-CeYjJNg0.js.map +1 -0
- package/dist/OverlayShell-pzbqQW25.js +642 -0
- package/dist/OverlayShell-pzbqQW25.js.map +1 -0
- package/dist/SettingsView-Bndrta44.js +2207 -0
- package/dist/SettingsView-Bndrta44.js.map +1 -0
- package/dist/ViewRoute-ChSPabOy.js +32 -0
- package/dist/ViewRoute-ChSPabOy.js.map +1 -0
- package/dist/WindowsLayout-CXGNPKoY.js +633 -0
- package/dist/WindowsLayout-CXGNPKoY.js.map +1 -0
- package/dist/app.d.ts +3 -0
- package/dist/app.d.ts.map +1 -0
- package/dist/components/ContentView.d.ts +10 -0
- package/dist/components/ContentView.d.ts.map +1 -0
- package/dist/components/HomeView.d.ts +2 -0
- package/dist/components/HomeView.d.ts.map +1 -0
- package/dist/components/LoadingOverlay.d.ts +2 -0
- package/dist/components/LoadingOverlay.d.ts.map +1 -0
- package/dist/components/NotFoundView.d.ts +2 -0
- package/dist/components/NotFoundView.d.ts.map +1 -0
- package/dist/components/RouteErrorBoundary.d.ts +2 -0
- package/dist/components/RouteErrorBoundary.d.ts.map +1 -0
- package/dist/components/ViewRoute.d.ts +7 -0
- package/dist/components/ViewRoute.d.ts.map +1 -0
- package/dist/components/ui/alert-dialog.d.ts +32 -0
- package/dist/components/ui/alert-dialog.d.ts.map +1 -0
- package/dist/components/ui/breadcrumb.d.ts +20 -0
- package/dist/components/ui/breadcrumb.d.ts.map +1 -0
- package/dist/components/ui/button-group.d.ts +7 -0
- package/dist/components/ui/button-group.d.ts.map +1 -0
- package/dist/components/ui/button.d.ts +12 -0
- package/dist/components/ui/button.d.ts.map +1 -0
- package/dist/components/ui/dialog.d.ts +24 -0
- package/dist/components/ui/dialog.d.ts.map +1 -0
- package/dist/components/ui/drawer.d.ts +38 -0
- package/dist/components/ui/drawer.d.ts.map +1 -0
- package/dist/components/ui/select.d.ts +5 -0
- package/dist/components/ui/select.d.ts.map +1 -0
- package/dist/components/ui/sidebar.d.ts +46 -0
- package/dist/components/ui/sidebar.d.ts.map +1 -0
- package/dist/components/ui/sonner.d.ts +6 -0
- package/dist/components/ui/sonner.d.ts.map +1 -0
- package/dist/components/ui/switch.d.ts +8 -0
- package/dist/components/ui/switch.d.ts.map +1 -0
- package/dist/constants/urls.d.ts +6 -0
- package/dist/constants/urls.d.ts.map +1 -0
- package/dist/constants/urls.js +8 -0
- package/dist/constants/urls.js.map +1 -0
- package/dist/features/alertDialog/DialogContext.d.ts +12 -0
- package/dist/features/alertDialog/DialogContext.d.ts.map +1 -0
- package/dist/features/config/ConfigProvider.d.ts +15 -0
- package/dist/features/config/ConfigProvider.d.ts.map +1 -0
- package/dist/features/config/types.d.ts +177 -0
- package/dist/features/config/types.d.ts.map +1 -0
- package/dist/features/config/useConfig.d.ts +8 -0
- package/dist/features/config/useConfig.d.ts.map +1 -0
- package/dist/features/cookieConsent/CookieConsentModal.d.ts +6 -0
- package/dist/features/cookieConsent/CookieConsentModal.d.ts.map +1 -0
- package/dist/features/cookieConsent/CookiePreferencesView.d.ts +2 -0
- package/dist/features/cookieConsent/CookiePreferencesView.d.ts.map +1 -0
- package/dist/features/cookieConsent/cookieConsent.d.ts +22 -0
- package/dist/features/cookieConsent/cookieConsent.d.ts.map +1 -0
- package/dist/features/cookieConsent/useCookieConsent.d.ts +15 -0
- package/dist/features/cookieConsent/useCookieConsent.d.ts.map +1 -0
- package/dist/features/drawer/DrawerContext.d.ts +24 -0
- package/dist/features/drawer/DrawerContext.d.ts.map +1 -0
- package/dist/features/layouts/AppLayout.d.ts +12 -0
- package/dist/features/layouts/AppLayout.d.ts.map +1 -0
- package/dist/features/layouts/DefaultLayout.d.ts +10 -0
- package/dist/features/layouts/DefaultLayout.d.ts.map +1 -0
- package/dist/features/layouts/FullscreenLayout.d.ts +9 -0
- package/dist/features/layouts/FullscreenLayout.d.ts.map +1 -0
- package/dist/features/layouts/LayoutProviders.d.ts +9 -0
- package/dist/features/layouts/LayoutProviders.d.ts.map +1 -0
- package/dist/features/layouts/OverlayShell.d.ts +10 -0
- package/dist/features/layouts/OverlayShell.d.ts.map +1 -0
- package/dist/features/layouts/WindowsLayout.d.ts +24 -0
- package/dist/features/layouts/WindowsLayout.d.ts.map +1 -0
- package/dist/features/layouts/utils.d.ts +16 -0
- package/dist/features/layouts/utils.d.ts.map +1 -0
- package/dist/features/modal/ModalContext.d.ts +20 -0
- package/dist/features/modal/ModalContext.d.ts.map +1 -0
- package/dist/features/sentry/initSentry.d.ts +14 -0
- package/dist/features/sentry/initSentry.d.ts.map +1 -0
- package/dist/features/settings/SettingsContext.d.ts +10 -0
- package/dist/features/settings/SettingsContext.d.ts.map +1 -0
- package/dist/features/settings/SettingsIcons.d.ts +22 -0
- package/dist/features/settings/SettingsIcons.d.ts.map +1 -0
- package/dist/features/settings/SettingsProvider.d.ts +5 -0
- package/dist/features/settings/SettingsProvider.d.ts.map +1 -0
- package/dist/features/settings/SettingsRoutes.d.ts +7 -0
- package/dist/features/settings/SettingsRoutes.d.ts.map +1 -0
- package/dist/features/settings/SettingsView.d.ts +2 -0
- package/dist/features/settings/SettingsView.d.ts.map +1 -0
- package/dist/features/settings/components/Advanced.d.ts +2 -0
- package/dist/features/settings/components/Advanced.d.ts.map +1 -0
- package/dist/features/settings/components/Appearance.d.ts +2 -0
- package/dist/features/settings/components/Appearance.d.ts.map +1 -0
- package/dist/features/settings/components/DataPrivacy.d.ts +2 -0
- package/dist/features/settings/components/DataPrivacy.d.ts.map +1 -0
- package/dist/features/settings/components/Develop.d.ts +2 -0
- package/dist/features/settings/components/Develop.d.ts.map +1 -0
- package/dist/features/settings/components/LanguageAndRegion.d.ts +2 -0
- package/dist/features/settings/components/LanguageAndRegion.d.ts.map +1 -0
- package/dist/features/settings/components/ServiceWorker.d.ts +2 -0
- package/dist/features/settings/components/ServiceWorker.d.ts.map +1 -0
- package/dist/features/settings/components/UpdateApp.d.ts +2 -0
- package/dist/features/settings/components/UpdateApp.d.ts.map +1 -0
- package/dist/features/settings/components/develop/DialogTestButtons.d.ts +2 -0
- package/dist/features/settings/components/develop/DialogTestButtons.d.ts.map +1 -0
- package/dist/features/settings/components/develop/DrawerTestButtons.d.ts +2 -0
- package/dist/features/settings/components/develop/DrawerTestButtons.d.ts.map +1 -0
- package/dist/features/settings/components/develop/ModalTestButtons.d.ts +2 -0
- package/dist/features/settings/components/develop/ModalTestButtons.d.ts.map +1 -0
- package/dist/features/settings/components/develop/ToastTestButtons.d.ts +2 -0
- package/dist/features/settings/components/develop/ToastTestButtons.d.ts.map +1 -0
- package/dist/features/settings/hooks/useSettings.d.ts +2 -0
- package/dist/features/settings/hooks/useSettings.d.ts.map +1 -0
- package/dist/features/sonner/SonnerContext.d.ts +29 -0
- package/dist/features/sonner/SonnerContext.d.ts.map +1 -0
- package/dist/features/theme/ThemeProvider.d.ts +11 -0
- package/dist/features/theme/ThemeProvider.d.ts.map +1 -0
- package/dist/features/theme/themes.d.ts +114 -0
- package/dist/features/theme/themes.d.ts.map +1 -0
- package/dist/features/theme/useTheme.d.ts +10 -0
- package/dist/features/theme/useTheme.d.ts.map +1 -0
- package/dist/i18n/I18nProvider.d.ts +9 -0
- package/dist/i18n/I18nProvider.d.ts.map +1 -0
- package/dist/i18n/config.d.ts +23 -0
- package/dist/i18n/config.d.ts.map +1 -0
- package/dist/i18n/translations/en/common.json.d.ts +19 -0
- package/dist/i18n/translations/en/cookieConsent.json.d.ts +53 -0
- package/dist/i18n/translations/en/settings.json.d.ts +358 -0
- package/dist/i18n/translations/fr/common.json.d.ts +19 -0
- package/dist/i18n/translations/fr/cookieConsent.json.d.ts +53 -0
- package/dist/i18n/translations/fr/settings.json.d.ts +358 -0
- package/dist/index-lmRk5L6z.js +2160 -0
- package/dist/index-lmRk5L6z.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/utils.d.ts +3 -0
- package/dist/lib/utils.d.ts.map +1 -0
- package/dist/lib/z-index.d.ts +29 -0
- package/dist/lib/z-index.d.ts.map +1 -0
- package/dist/router/router.d.ts +3 -0
- package/dist/router/router.d.ts.map +1 -0
- package/dist/router/routes.d.ts +4 -0
- package/dist/router/routes.d.ts.map +1 -0
- package/dist/sidebar-ClIeZ2zb.js +303 -0
- package/dist/sidebar-ClIeZ2zb.js.map +1 -0
- package/dist/style.css +1 -0
- package/dist/switch-8SzUJz7Q.js +44 -0
- package/dist/switch-8SzUJz7Q.js.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +93 -0
- package/postcss.config.js +6 -0
- package/src/app.tsx +119 -0
- package/src/components/ContentView.tsx +258 -0
- package/src/components/HomeView.tsx +19 -0
- package/src/components/LoadingOverlay.tsx +12 -0
- package/src/components/NotFoundView.tsx +84 -0
- package/src/components/RouteErrorBoundary.tsx +95 -0
- package/src/components/ViewRoute.tsx +47 -0
- package/src/components/ui/alert-dialog.tsx +181 -0
- package/src/components/ui/breadcrumb.tsx +155 -0
- package/src/components/ui/button-group.tsx +52 -0
- package/src/components/ui/button.tsx +51 -0
- package/src/components/ui/dialog.tsx +160 -0
- package/src/components/ui/drawer.tsx +200 -0
- package/src/components/ui/select.tsx +24 -0
- package/src/components/ui/sidebar.tsx +406 -0
- package/src/components/ui/sonner.tsx +36 -0
- package/src/components/ui/switch.tsx +45 -0
- package/src/constants/urls.ts +4 -0
- package/src/features/alertDialog/DialogContext.tsx +468 -0
- package/src/features/config/ConfigProvider.ts +96 -0
- package/src/features/config/types.ts +195 -0
- package/src/features/config/useConfig.ts +15 -0
- package/src/features/cookieConsent/CookieConsentModal.tsx +122 -0
- package/src/features/cookieConsent/CookiePreferencesView.tsx +328 -0
- package/src/features/cookieConsent/cookieConsent.ts +84 -0
- package/src/features/cookieConsent/useCookieConsent.ts +39 -0
- package/src/features/drawer/DrawerContext.tsx +116 -0
- package/src/features/layouts/AppLayout.tsx +63 -0
- package/src/features/layouts/DefaultLayout.tsx +625 -0
- package/src/features/layouts/FullscreenLayout.tsx +55 -0
- package/src/features/layouts/LayoutProviders.tsx +20 -0
- package/src/features/layouts/OverlayShell.tsx +171 -0
- package/src/features/layouts/WindowsLayout.tsx +860 -0
- package/src/features/layouts/utils.ts +99 -0
- package/src/features/modal/ModalContext.tsx +112 -0
- package/src/features/sentry/initSentry.ts +72 -0
- package/src/features/settings/SettingsContext.tsx +19 -0
- package/src/features/settings/SettingsIcons.tsx +452 -0
- package/src/features/settings/SettingsProvider.tsx +341 -0
- package/src/features/settings/SettingsRoutes.tsx +66 -0
- package/src/features/settings/SettingsView.tsx +327 -0
- package/src/features/settings/components/Advanced.tsx +128 -0
- package/src/features/settings/components/Appearance.tsx +306 -0
- package/src/features/settings/components/DataPrivacy.tsx +142 -0
- package/src/features/settings/components/Develop.tsx +174 -0
- package/src/features/settings/components/LanguageAndRegion.tsx +329 -0
- package/src/features/settings/components/ServiceWorker.tsx +363 -0
- package/src/features/settings/components/UpdateApp.tsx +206 -0
- package/src/features/settings/components/develop/DialogTestButtons.tsx +137 -0
- package/src/features/settings/components/develop/DrawerTestButtons.tsx +67 -0
- package/src/features/settings/components/develop/ModalTestButtons.tsx +30 -0
- package/src/features/settings/components/develop/ToastTestButtons.tsx +179 -0
- package/src/features/settings/hooks/useSettings.tsx +10 -0
- package/src/features/sonner/SonnerContext.tsx +286 -0
- package/src/features/theme/ThemeProvider.tsx +16 -0
- package/src/features/theme/themes.ts +561 -0
- package/src/features/theme/useTheme.tsx +71 -0
- package/src/i18n/I18nProvider.tsx +32 -0
- package/src/i18n/config.ts +107 -0
- package/src/i18n/translations/en/common.json +16 -0
- package/src/i18n/translations/en/cookieConsent.json +50 -0
- package/src/i18n/translations/en/settings.json +355 -0
- package/src/i18n/translations/fr/common.json +16 -0
- package/src/i18n/translations/fr/cookieConsent.json +50 -0
- package/src/i18n/translations/fr/settings.json +355 -0
- package/src/index.css +412 -0
- package/src/index.html +100 -0
- package/src/index.ts +31 -0
- package/src/lib/utils.ts +6 -0
- package/src/lib/z-index.ts +29 -0
- package/src/main.tsx +26 -0
- package/src/router/router.tsx +8 -0
- package/src/router/routes.tsx +115 -0
- package/src/service-worker/register.ts +1199 -0
- package/src/service-worker/sw-dev.ts +87 -0
- package/src/service-worker/sw.ts +105 -0
- package/tailwind.config.js +60 -0
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
import { useState, useEffect, useMemo, useCallback } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
Breadcrumb,
|
|
4
|
+
BreadcrumbItem,
|
|
5
|
+
BreadcrumbList,
|
|
6
|
+
BreadcrumbPage,
|
|
7
|
+
BreadcrumbSeparator,
|
|
8
|
+
} from '@/components/ui/breadcrumb';
|
|
9
|
+
import {
|
|
10
|
+
Sidebar,
|
|
11
|
+
SidebarContent,
|
|
12
|
+
SidebarGroup,
|
|
13
|
+
SidebarGroupContent,
|
|
14
|
+
SidebarGroupLabel,
|
|
15
|
+
SidebarMenu,
|
|
16
|
+
SidebarMenuButton,
|
|
17
|
+
SidebarMenuItem,
|
|
18
|
+
SidebarProvider,
|
|
19
|
+
} from '@/components/ui/sidebar';
|
|
20
|
+
import { Route, Routes, useLocation, useNavigate, Navigate } from 'react-router';
|
|
21
|
+
import { useTranslation } from 'react-i18next';
|
|
22
|
+
import urls from '@/constants/urls';
|
|
23
|
+
import { createSettingsRoutes } from './SettingsRoutes';
|
|
24
|
+
import { useSettings } from './hooks/useSettings';
|
|
25
|
+
import { useConfig } from '../config/useConfig';
|
|
26
|
+
import { isTauri } from '@/service-worker/register';
|
|
27
|
+
import { Button } from '@/components/ui/button';
|
|
28
|
+
import { ChevronRightIcon, ChevronLeftIcon } from './SettingsIcons';
|
|
29
|
+
|
|
30
|
+
export const SettingsView = () => {
|
|
31
|
+
const location = useLocation();
|
|
32
|
+
const navigate = useNavigate();
|
|
33
|
+
const { settings } = useSettings();
|
|
34
|
+
const { config } = useConfig();
|
|
35
|
+
const { t } = useTranslation('settings');
|
|
36
|
+
// Re-check isTauri after mount and after a short delay so we catch late-injected __TAURI__ in dev
|
|
37
|
+
const [isTauriEnv, setIsTauriEnv] = useState(() => isTauri());
|
|
38
|
+
|
|
39
|
+
useEffect(() => {
|
|
40
|
+
setIsTauriEnv(isTauri());
|
|
41
|
+
const tid = window.setTimeout(() => setIsTauriEnv(isTauri()), 200);
|
|
42
|
+
return () => window.clearTimeout(tid);
|
|
43
|
+
}, []);
|
|
44
|
+
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
if (config?.title) {
|
|
47
|
+
const settingsLabel = t('settings', { ns: 'common' });
|
|
48
|
+
document.title = `${settingsLabel} | ${config.title}`;
|
|
49
|
+
}
|
|
50
|
+
}, [config?.title, t]);
|
|
51
|
+
|
|
52
|
+
// Create routes with translations (service-worker route is already omitted in Tauri by createSettingsRoutes)
|
|
53
|
+
const settingsRoutes = useMemo(() => createSettingsRoutes(t), [t]);
|
|
54
|
+
|
|
55
|
+
// In Tauri, hide service worker route; use reactive isTauriEnv so we catch late injection (e.g. dev)
|
|
56
|
+
const routesWithoutTauriSw = useMemo(() => {
|
|
57
|
+
if (isTauriEnv) {
|
|
58
|
+
return settingsRoutes.filter((route) => route.path !== 'service-worker');
|
|
59
|
+
}
|
|
60
|
+
return settingsRoutes;
|
|
61
|
+
}, [settingsRoutes, isTauriEnv]);
|
|
62
|
+
|
|
63
|
+
// Filter routes based on developer features setting
|
|
64
|
+
const filteredRoutes = useMemo(() => {
|
|
65
|
+
if (settings.developerFeatures.enabled) {
|
|
66
|
+
return routesWithoutTauriSw;
|
|
67
|
+
}
|
|
68
|
+
return routesWithoutTauriSw.filter(
|
|
69
|
+
(route) => route.path !== 'developpers' && route.path !== 'service-worker',
|
|
70
|
+
);
|
|
71
|
+
}, [settings.developerFeatures.enabled, routesWithoutTauriSw]);
|
|
72
|
+
|
|
73
|
+
// Group routes by category
|
|
74
|
+
const groupedRoutes = useMemo(() => {
|
|
75
|
+
const developerOnlyPaths = ['developpers', 'service-worker'];
|
|
76
|
+
const groups = [
|
|
77
|
+
{
|
|
78
|
+
title: t('categories.preferences'),
|
|
79
|
+
routes: filteredRoutes.filter((route) =>
|
|
80
|
+
['appearance', 'language-and-region', 'data-privacy'].includes(route.path),
|
|
81
|
+
),
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
title: t('categories.system'),
|
|
85
|
+
routes: filteredRoutes.filter((route) => ['update-app', 'advanced'].includes(route.path)),
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
title: t('categories.developer'),
|
|
89
|
+
routes: filteredRoutes.filter((route) => developerOnlyPaths.includes(route.path)),
|
|
90
|
+
},
|
|
91
|
+
];
|
|
92
|
+
return groups.filter((group) => group.routes.length > 0);
|
|
93
|
+
}, [filteredRoutes, t]);
|
|
94
|
+
|
|
95
|
+
// Find matching nav item by checking if URL contains or ends with the item path
|
|
96
|
+
const getSelectedItemFromUrl = useCallback(() => {
|
|
97
|
+
const pathname = location.pathname;
|
|
98
|
+
|
|
99
|
+
// Find matching nav item by checking if pathname contains the item path
|
|
100
|
+
// This works regardless of the URL structure/prefix
|
|
101
|
+
const matchedItem = filteredRoutes.find((item) => {
|
|
102
|
+
// Normalize paths for comparison (remove leading/trailing slashes)
|
|
103
|
+
const normalizedPathname = pathname.replace(/^\/+|\/+$/g, '');
|
|
104
|
+
const normalizedItemPath = item.path.replace(/^\/+|\/+$/g, '');
|
|
105
|
+
|
|
106
|
+
// Check if pathname ends with the item path, or contains it as a path segment
|
|
107
|
+
return (
|
|
108
|
+
normalizedPathname === normalizedItemPath ||
|
|
109
|
+
normalizedPathname.endsWith(`/${normalizedItemPath}`) ||
|
|
110
|
+
normalizedPathname.includes(`/${normalizedItemPath}/`)
|
|
111
|
+
);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
return matchedItem;
|
|
115
|
+
}, [location.pathname, filteredRoutes]);
|
|
116
|
+
|
|
117
|
+
const selectedItem = useMemo(() => getSelectedItemFromUrl(), [getSelectedItemFromUrl]);
|
|
118
|
+
|
|
119
|
+
// Check if we're at the settings root (no specific route selected)
|
|
120
|
+
const isSettingsRoot = useMemo(() => {
|
|
121
|
+
const pathname = location.pathname;
|
|
122
|
+
// Normalize the settings URL (remove leading/trailing slashes)
|
|
123
|
+
const normalizedSettingsPath = urls.settings.replace(/^\/+|\/+$/g, '');
|
|
124
|
+
// Normalize the current pathname (remove leading/trailing slashes)
|
|
125
|
+
const normalizedPathname = pathname.replace(/^\/+|\/+$/g, '');
|
|
126
|
+
|
|
127
|
+
// Check if we're exactly at the settings root
|
|
128
|
+
// This handles both "/__settings" and "/__settings/" cases
|
|
129
|
+
if (normalizedPathname === normalizedSettingsPath) {
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// If pathname starts with settings path followed by a slash, we're at a subpage
|
|
134
|
+
// e.g., "__settings/appearance" means we're NOT at root
|
|
135
|
+
const settingsPathWithSlash = `${normalizedSettingsPath}/`;
|
|
136
|
+
if (normalizedPathname.startsWith(settingsPathWithSlash)) {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Pathname doesn't match settings path structure - not in settings
|
|
141
|
+
return false;
|
|
142
|
+
}, [location.pathname, filteredRoutes]);
|
|
143
|
+
|
|
144
|
+
// Navigate back to settings root
|
|
145
|
+
const handleBackToSettings = useCallback(() => {
|
|
146
|
+
// Navigate to settings root, replacing current history entry
|
|
147
|
+
navigate(urls.settings, { replace: true });
|
|
148
|
+
}, [navigate]);
|
|
149
|
+
|
|
150
|
+
return (
|
|
151
|
+
<SidebarProvider>
|
|
152
|
+
<div className="flex h-full w-full overflow-hidden items-start">
|
|
153
|
+
{/* Desktop Sidebar */}
|
|
154
|
+
<Sidebar className="hidden md:flex">
|
|
155
|
+
<SidebarContent>
|
|
156
|
+
{groupedRoutes.map((group) => (
|
|
157
|
+
<SidebarGroup key={group.title}>
|
|
158
|
+
<SidebarGroupLabel>{group.title}</SidebarGroupLabel>
|
|
159
|
+
<SidebarGroupContent>
|
|
160
|
+
<SidebarMenu>
|
|
161
|
+
{group.routes.map((item) => (
|
|
162
|
+
<SidebarMenuItem key={item.name}>
|
|
163
|
+
<SidebarMenuButton
|
|
164
|
+
asChild
|
|
165
|
+
isActive={item.name === selectedItem?.name}
|
|
166
|
+
>
|
|
167
|
+
<button
|
|
168
|
+
onClick={() => navigate(`${urls.settings}/${item.path}`)}
|
|
169
|
+
className="cursor-pointer"
|
|
170
|
+
>
|
|
171
|
+
<item.icon />
|
|
172
|
+
<span>{item.name}</span>
|
|
173
|
+
</button>
|
|
174
|
+
</SidebarMenuButton>
|
|
175
|
+
</SidebarMenuItem>
|
|
176
|
+
))}
|
|
177
|
+
</SidebarMenu>
|
|
178
|
+
</SidebarGroupContent>
|
|
179
|
+
</SidebarGroup>
|
|
180
|
+
))}
|
|
181
|
+
</SidebarContent>
|
|
182
|
+
</Sidebar>
|
|
183
|
+
|
|
184
|
+
{/* Mobile List View */}
|
|
185
|
+
<div className="md:hidden flex h-full w-full flex-col overflow-hidden">
|
|
186
|
+
{isSettingsRoot ? (
|
|
187
|
+
// Show list of settings pages
|
|
188
|
+
<div className="flex flex-1 flex-col overflow-y-auto bg-background">
|
|
189
|
+
<header className="flex h-16 shrink-0 items-center justify-center px-4 border-b">
|
|
190
|
+
<h1 className="text-lg font-semibold">{t('title')}</h1>
|
|
191
|
+
</header>
|
|
192
|
+
<div className="flex flex-1 flex-col p-4 gap-6">
|
|
193
|
+
{groupedRoutes.map((group) => (
|
|
194
|
+
<div
|
|
195
|
+
key={group.title}
|
|
196
|
+
className="flex flex-col gap-2"
|
|
197
|
+
>
|
|
198
|
+
<h2
|
|
199
|
+
className="text-xs font-semibold text-foreground/60 uppercase tracking-wider px-2"
|
|
200
|
+
style={{ fontFamily: 'var(--heading-font-family, inherit)' }}
|
|
201
|
+
>
|
|
202
|
+
{group.title}
|
|
203
|
+
</h2>
|
|
204
|
+
<div className="flex flex-col bg-card rounded-lg overflow-hidden border border-border">
|
|
205
|
+
{group.routes.map((item, itemIndex) => {
|
|
206
|
+
const Icon = item.icon;
|
|
207
|
+
const isLast = itemIndex === group.routes.length - 1;
|
|
208
|
+
return (
|
|
209
|
+
<div
|
|
210
|
+
key={item.name}
|
|
211
|
+
className="relative"
|
|
212
|
+
>
|
|
213
|
+
{!isLast && (
|
|
214
|
+
<div className="absolute left-0 right-0 bottom-0 h-[1px] bg-border" />
|
|
215
|
+
)}
|
|
216
|
+
<button
|
|
217
|
+
onClick={() => navigate(`${urls.settings}/${item.path}`)}
|
|
218
|
+
className="w-full flex items-center justify-between px-4 py-3 bg-transparent hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:bg-sidebar-accent active:text-sidebar-accent-foreground transition-colors cursor-pointer rounded-none"
|
|
219
|
+
>
|
|
220
|
+
<div className="flex items-center gap-2 flex-1 min-w-0">
|
|
221
|
+
<div className="flex-shrink-0 text-foreground/70">
|
|
222
|
+
<Icon />
|
|
223
|
+
</div>
|
|
224
|
+
<span className="text-sm font-normal text-foreground">
|
|
225
|
+
{item.name}
|
|
226
|
+
</span>
|
|
227
|
+
</div>
|
|
228
|
+
<div className="flex-shrink-0 ml-2 text-foreground/40">
|
|
229
|
+
<ChevronRightIcon />
|
|
230
|
+
</div>
|
|
231
|
+
</button>
|
|
232
|
+
</div>
|
|
233
|
+
);
|
|
234
|
+
})}
|
|
235
|
+
</div>
|
|
236
|
+
</div>
|
|
237
|
+
))}
|
|
238
|
+
</div>
|
|
239
|
+
</div>
|
|
240
|
+
) : (
|
|
241
|
+
// Show selected settings page with back button
|
|
242
|
+
<div className="flex h-full flex-1 flex-col overflow-hidden">
|
|
243
|
+
<header className="flex h-16 shrink-0 items-center gap-2 px-4 border-b">
|
|
244
|
+
<Button
|
|
245
|
+
variant="ghost"
|
|
246
|
+
size="icon"
|
|
247
|
+
onClick={handleBackToSettings}
|
|
248
|
+
className="mr-2"
|
|
249
|
+
>
|
|
250
|
+
<ChevronLeftIcon />
|
|
251
|
+
</Button>
|
|
252
|
+
<h1 className="text-lg font-semibold">{selectedItem?.name}</h1>
|
|
253
|
+
</header>
|
|
254
|
+
<div className="flex flex-1 flex-col gap-4 overflow-y-auto p-4 pt-4">
|
|
255
|
+
<Routes>
|
|
256
|
+
<Route
|
|
257
|
+
index
|
|
258
|
+
element={
|
|
259
|
+
filteredRoutes.length > 0 ? (
|
|
260
|
+
<Navigate
|
|
261
|
+
to={`${urls.settings}/${filteredRoutes[0].path}`}
|
|
262
|
+
replace
|
|
263
|
+
/>
|
|
264
|
+
) : null
|
|
265
|
+
}
|
|
266
|
+
/>
|
|
267
|
+
{filteredRoutes.map((item) => (
|
|
268
|
+
<Route
|
|
269
|
+
key={item.path}
|
|
270
|
+
path={item.path}
|
|
271
|
+
element={item.element}
|
|
272
|
+
/>
|
|
273
|
+
))}
|
|
274
|
+
</Routes>
|
|
275
|
+
</div>
|
|
276
|
+
</div>
|
|
277
|
+
)}
|
|
278
|
+
</div>
|
|
279
|
+
|
|
280
|
+
{/* Desktop Main Content */}
|
|
281
|
+
<main className="hidden md:flex h-full flex-1 flex-col overflow-hidden">
|
|
282
|
+
{selectedItem && (
|
|
283
|
+
<header className="flex h-16 shrink-0 items-center gap-2 transition-[width,height] ease-linear">
|
|
284
|
+
<div className="flex items-center gap-2 px-4">
|
|
285
|
+
<Breadcrumb>
|
|
286
|
+
<BreadcrumbList>
|
|
287
|
+
<BreadcrumbItem>{t('title')}</BreadcrumbItem>
|
|
288
|
+
<BreadcrumbSeparator />
|
|
289
|
+
<BreadcrumbItem>
|
|
290
|
+
<BreadcrumbPage>{selectedItem.name}</BreadcrumbPage>
|
|
291
|
+
</BreadcrumbItem>
|
|
292
|
+
</BreadcrumbList>
|
|
293
|
+
</Breadcrumb>
|
|
294
|
+
</div>
|
|
295
|
+
</header>
|
|
296
|
+
)}
|
|
297
|
+
<div className="flex flex-1 flex-col gap-4 overflow-y-auto p-4 pt-0">
|
|
298
|
+
<Routes>
|
|
299
|
+
<Route
|
|
300
|
+
index
|
|
301
|
+
element={
|
|
302
|
+
<div className="flex flex-1 flex-col items-center justify-center p-8 text-center">
|
|
303
|
+
<div className="max-w-md">
|
|
304
|
+
<h2 className="text-lg font-semibold mb-2">{t('title')}</h2>
|
|
305
|
+
<p className="text-sm text-muted-foreground">
|
|
306
|
+
{t('selectCategory', {
|
|
307
|
+
defaultValue: 'Select a category from the sidebar to get started.',
|
|
308
|
+
})}
|
|
309
|
+
</p>
|
|
310
|
+
</div>
|
|
311
|
+
</div>
|
|
312
|
+
}
|
|
313
|
+
/>
|
|
314
|
+
{filteredRoutes.map((item) => (
|
|
315
|
+
<Route
|
|
316
|
+
key={item.path}
|
|
317
|
+
path={item.path}
|
|
318
|
+
element={item.element}
|
|
319
|
+
/>
|
|
320
|
+
))}
|
|
321
|
+
</Routes>
|
|
322
|
+
</div>
|
|
323
|
+
</main>
|
|
324
|
+
</div>
|
|
325
|
+
</SidebarProvider>
|
|
326
|
+
);
|
|
327
|
+
};
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { useTranslation } from 'react-i18next';
|
|
2
|
+
import { Switch } from '@/components/ui/switch';
|
|
3
|
+
import { useConfig } from '@/features/config/useConfig';
|
|
4
|
+
import { closeSentry, initSentry } from '@/features/sentry/initSentry';
|
|
5
|
+
import { useSettings } from '../hooks/useSettings';
|
|
6
|
+
import { Button } from '@/components/ui/button';
|
|
7
|
+
import { shellui } from '@shellui/sdk';
|
|
8
|
+
|
|
9
|
+
export const Advanced = () => {
|
|
10
|
+
const { t } = useTranslation('settings');
|
|
11
|
+
const { config } = useConfig();
|
|
12
|
+
const { settings, updateSetting, resetAllData } = useSettings();
|
|
13
|
+
const errorReportingConfigured = Boolean(config?.sentry?.dsn);
|
|
14
|
+
|
|
15
|
+
const handleErrorReportingChange = (checked: boolean) => {
|
|
16
|
+
updateSetting('errorReporting', { enabled: checked });
|
|
17
|
+
if (checked) {
|
|
18
|
+
initSentry();
|
|
19
|
+
} else {
|
|
20
|
+
closeSentry();
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const handleResetClick = () => {
|
|
25
|
+
shellui.dialog({
|
|
26
|
+
title: t('advanced.dangerZone.reset.toast.title'),
|
|
27
|
+
description: t('advanced.dangerZone.reset.toast.description'),
|
|
28
|
+
mode: 'delete',
|
|
29
|
+
size: 'sm',
|
|
30
|
+
okLabel: t('advanced.dangerZone.reset.toast.confirm'),
|
|
31
|
+
cancelLabel: t('advanced.dangerZone.reset.toast.cancel'),
|
|
32
|
+
onOk: () => {
|
|
33
|
+
resetAllData();
|
|
34
|
+
shellui.toast({
|
|
35
|
+
title: t('advanced.dangerZone.reset.toast.success.title'),
|
|
36
|
+
description: t('advanced.dangerZone.reset.toast.success.description'),
|
|
37
|
+
type: 'success',
|
|
38
|
+
});
|
|
39
|
+
shellui.navigate('/');
|
|
40
|
+
},
|
|
41
|
+
onCancel: () => {
|
|
42
|
+
// User cancelled, no action needed
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<div className="space-y-4">
|
|
49
|
+
<div className="flex items-center justify-between">
|
|
50
|
+
<div className="space-y-0.5">
|
|
51
|
+
<span
|
|
52
|
+
className="text-sm font-medium leading-none"
|
|
53
|
+
style={{ fontFamily: 'var(--heading-font-family, inherit)' }}
|
|
54
|
+
>
|
|
55
|
+
{t('advanced.errorReporting.label')}
|
|
56
|
+
</span>
|
|
57
|
+
<p className="text-sm text-muted-foreground">
|
|
58
|
+
{errorReportingConfigured
|
|
59
|
+
? t('advanced.errorReporting.statusConfigured')
|
|
60
|
+
: t('advanced.errorReporting.statusNotConfigured')}
|
|
61
|
+
</p>
|
|
62
|
+
</div>
|
|
63
|
+
{errorReportingConfigured && (
|
|
64
|
+
<Switch
|
|
65
|
+
id="error-reporting"
|
|
66
|
+
checked={settings.errorReporting.enabled}
|
|
67
|
+
onCheckedChange={handleErrorReportingChange}
|
|
68
|
+
/>
|
|
69
|
+
)}
|
|
70
|
+
</div>
|
|
71
|
+
|
|
72
|
+
<div className="flex items-center justify-between">
|
|
73
|
+
<div className="space-y-0.5">
|
|
74
|
+
<label
|
|
75
|
+
htmlFor="developer-features"
|
|
76
|
+
className="text-sm font-medium leading-none"
|
|
77
|
+
style={{ fontFamily: 'var(--heading-font-family, inherit)' }}
|
|
78
|
+
>
|
|
79
|
+
{t('advanced.developerFeatures.label')}
|
|
80
|
+
</label>
|
|
81
|
+
<p className="text-sm text-muted-foreground">
|
|
82
|
+
{t('advanced.developerFeatures.description')}
|
|
83
|
+
</p>
|
|
84
|
+
</div>
|
|
85
|
+
<Switch
|
|
86
|
+
id="developer-features"
|
|
87
|
+
checked={settings.developerFeatures.enabled}
|
|
88
|
+
onCheckedChange={(checked) => updateSetting('developerFeatures', { enabled: checked })}
|
|
89
|
+
/>
|
|
90
|
+
</div>
|
|
91
|
+
|
|
92
|
+
<div className="border-t pt-6 mt-6">
|
|
93
|
+
<div className="space-y-4">
|
|
94
|
+
<div className="space-y-0.5">
|
|
95
|
+
<h3
|
|
96
|
+
className="text-sm font-semibold leading-none text-destructive"
|
|
97
|
+
style={{ fontFamily: 'var(--heading-font-family, inherit)' }}
|
|
98
|
+
>
|
|
99
|
+
{t('advanced.dangerZone.title')}
|
|
100
|
+
</h3>
|
|
101
|
+
<p className="text-sm text-muted-foreground">{t('advanced.dangerZone.description')}</p>
|
|
102
|
+
</div>
|
|
103
|
+
|
|
104
|
+
<div className="space-y-2">
|
|
105
|
+
<div className="space-y-0.5">
|
|
106
|
+
<label
|
|
107
|
+
className="text-sm font-medium leading-none"
|
|
108
|
+
style={{ fontFamily: 'var(--heading-font-family, inherit)' }}
|
|
109
|
+
>
|
|
110
|
+
{t('advanced.dangerZone.reset.title')}
|
|
111
|
+
</label>
|
|
112
|
+
<p className="text-sm text-muted-foreground">
|
|
113
|
+
{t('advanced.dangerZone.reset.warning')}
|
|
114
|
+
</p>
|
|
115
|
+
</div>
|
|
116
|
+
<Button
|
|
117
|
+
variant="destructive"
|
|
118
|
+
onClick={handleResetClick}
|
|
119
|
+
className="w-full sm:w-auto"
|
|
120
|
+
>
|
|
121
|
+
{t('advanced.dangerZone.reset.button')}
|
|
122
|
+
</Button>
|
|
123
|
+
</div>
|
|
124
|
+
</div>
|
|
125
|
+
</div>
|
|
126
|
+
</div>
|
|
127
|
+
);
|
|
128
|
+
};
|