mirror of
https://github.com/logto-io/logto.git
synced 2025-01-06 20:40:08 -05:00
refactor(console): add AppBoundary in admin console
This commit is contained in:
parent
7781d49667
commit
ba13ea06e5
5 changed files with 100 additions and 61 deletions
|
@ -8,6 +8,7 @@ import './scss/normalized.scss';
|
||||||
// eslint-disable-next-line import/no-unassigned-import
|
// eslint-disable-next-line import/no-unassigned-import
|
||||||
import '@fontsource/roboto-mono';
|
import '@fontsource/roboto-mono';
|
||||||
|
|
||||||
|
import AppBoundary from './components/AppBoundary';
|
||||||
import AppContent from './components/AppContent';
|
import AppContent from './components/AppContent';
|
||||||
import ErrorBoundary from './components/ErrorBoundary';
|
import ErrorBoundary from './components/ErrorBoundary';
|
||||||
import Toast from './components/Toast';
|
import Toast from './components/Toast';
|
||||||
|
@ -36,19 +37,39 @@ const Main = () => {
|
||||||
return (
|
return (
|
||||||
<ErrorBoundary>
|
<ErrorBoundary>
|
||||||
<SWRConfig value={{ fetcher }}>
|
<SWRConfig value={{ fetcher }}>
|
||||||
<Toast />
|
<AppBoundary>
|
||||||
<Routes>
|
<Toast />
|
||||||
<Route path="callback" element={<Callback />} />
|
<Routes>
|
||||||
<Route element={<AppContent />}>
|
<Route path="callback" element={<Callback />} />
|
||||||
<Route path="*" element={<NotFound />} />
|
<Route element={<AppContent />}>
|
||||||
<Route path="get-started" element={<GetStarted />} />
|
<Route path="*" element={<NotFound />} />
|
||||||
<Route path="applications">
|
<Route path="get-started" element={<GetStarted />} />
|
||||||
<Route index element={<Applications />} />
|
<Route path="applications">
|
||||||
<Route path=":id">
|
<Route index element={<Applications />} />
|
||||||
<Route index element={<Navigate to="settings" />} />
|
<Route path=":id">
|
||||||
<Route path="settings" element={<ApplicationDetails />} />
|
<Route index element={<Navigate to="settings" />} />
|
||||||
<Route path="advanced-settings" element={<ApplicationDetails />} />
|
<Route path="settings" element={<ApplicationDetails />} />
|
||||||
|
<Route path="advanced-settings" element={<ApplicationDetails />} />
|
||||||
|
</Route>
|
||||||
</Route>
|
</Route>
|
||||||
|
<Route path="api-resources">
|
||||||
|
<Route index element={<ApiResources />} />
|
||||||
|
<Route path=":id" element={<ApiResourceDetails />} />
|
||||||
|
</Route>
|
||||||
|
<Route path="connectors">
|
||||||
|
<Route index element={<Connectors />} />
|
||||||
|
<Route path="social" element={<Connectors />} />
|
||||||
|
<Route path=":connectorId" element={<ConnectorDetails />} />
|
||||||
|
</Route>
|
||||||
|
<Route path="users">
|
||||||
|
<Route index element={<Users />} />
|
||||||
|
<Route path=":id" element={<UserDetails />} />
|
||||||
|
</Route>
|
||||||
|
<Route path="sign-in-experience">
|
||||||
|
<Route index element={<Navigate to="experience" />} />
|
||||||
|
<Route path=":tab" element={<SignInExperience />} />
|
||||||
|
</Route>
|
||||||
|
<Route path="settings" element={<Settings />} />
|
||||||
</Route>
|
</Route>
|
||||||
<Route path="api-resources">
|
<Route path="api-resources">
|
||||||
<Route index element={<ApiResources />} />
|
<Route index element={<ApiResources />} />
|
||||||
|
@ -69,8 +90,8 @@ const Main = () => {
|
||||||
</Route>
|
</Route>
|
||||||
<Route path="settings" element={<Settings />} />
|
<Route path="settings" element={<Settings />} />
|
||||||
<Route path="dashboard" element={<Dashboard />} />
|
<Route path="dashboard" element={<Dashboard />} />
|
||||||
</Route>
|
</Routes>
|
||||||
</Routes>
|
</AppBoundary>
|
||||||
</SWRConfig>
|
</SWRConfig>
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
@use '@logto/shared/scss/console-themes' as themes;
|
||||||
|
|
||||||
|
.light {
|
||||||
|
@include themes.light;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark {
|
||||||
|
@include themes.dark;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: light) {
|
||||||
|
body {
|
||||||
|
@include themes.light;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
body {
|
||||||
|
@include themes.dark;
|
||||||
|
}
|
||||||
|
}
|
44
packages/console/src/components/AppBoundary/index.tsx
Normal file
44
packages/console/src/components/AppBoundary/index.tsx
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
import { AppearanceMode } from '@logto/schemas';
|
||||||
|
import React, { ReactNode, useEffect } from 'react';
|
||||||
|
|
||||||
|
import { themeStorageKey } from '@/consts';
|
||||||
|
import useAdminConsoleConfigs from '@/hooks/use-configs';
|
||||||
|
import initI18n from '@/i18n/init';
|
||||||
|
|
||||||
|
import * as styles from './index.module.scss';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
children?: ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
const AppBoundary = ({ children }: Props) => {
|
||||||
|
const defaultTheme = localStorage.getItem(themeStorageKey) ?? AppearanceMode.SyncWithSystem;
|
||||||
|
const { configs } = useAdminConsoleConfigs();
|
||||||
|
const theme = configs?.appearanceMode ?? defaultTheme;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const isFollowSystem = theme === AppearanceMode.SyncWithSystem;
|
||||||
|
const className = styles[theme] ?? '';
|
||||||
|
|
||||||
|
if (!isFollowSystem) {
|
||||||
|
document.body.classList.add(className);
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (!isFollowSystem) {
|
||||||
|
document.body.classList.remove(className);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, [theme]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
(async () => {
|
||||||
|
void initI18n(configs?.language);
|
||||||
|
})();
|
||||||
|
}, [configs?.language]);
|
||||||
|
|
||||||
|
// eslint-disable-next-line react/jsx-no-useless-fragment
|
||||||
|
return <>{children}</>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AppBoundary;
|
|
@ -26,23 +26,3 @@
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.light {
|
|
||||||
@include themes.light;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dark {
|
|
||||||
@include themes.dark;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (prefers-color-scheme: light) {
|
|
||||||
body {
|
|
||||||
@include themes.light;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
body {
|
|
||||||
@include themes.dark;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,22 +1,17 @@
|
||||||
import { LogtoClientError, useLogto } from '@logto/react';
|
import { LogtoClientError, useLogto } from '@logto/react';
|
||||||
import { AppearanceMode } from '@logto/schemas';
|
|
||||||
import React, { useEffect } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import { Outlet, useHref, useLocation, useNavigate } from 'react-router-dom';
|
import { Outlet, useHref, useLocation, useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
import AppError from '@/components/AppError';
|
import AppError from '@/components/AppError';
|
||||||
import LogtoLoading from '@/components/LogtoLoading';
|
import LogtoLoading from '@/components/LogtoLoading';
|
||||||
import SessionExpired from '@/components/SessionExpired';
|
import SessionExpired from '@/components/SessionExpired';
|
||||||
import { themeStorageKey } from '@/consts';
|
|
||||||
import useAdminConsoleConfigs from '@/hooks/use-configs';
|
import useAdminConsoleConfigs from '@/hooks/use-configs';
|
||||||
import initI18n from '@/i18n/init';
|
|
||||||
|
|
||||||
import Sidebar, { getPath } from './components/Sidebar';
|
import Sidebar, { getPath } from './components/Sidebar';
|
||||||
import { useSidebarMenuItems } from './components/Sidebar/hook';
|
import { useSidebarMenuItems } from './components/Sidebar/hook';
|
||||||
import Topbar from './components/Topbar';
|
import Topbar from './components/Topbar';
|
||||||
import * as styles from './index.module.scss';
|
import * as styles from './index.module.scss';
|
||||||
|
|
||||||
const defaultTheme = localStorage.getItem(themeStorageKey) ?? AppearanceMode.SyncWithSystem;
|
|
||||||
|
|
||||||
const AppContent = () => {
|
const AppContent = () => {
|
||||||
const { isAuthenticated, error, signIn } = useLogto();
|
const { isAuthenticated, error, signIn } = useLogto();
|
||||||
const href = useHref('/callback');
|
const href = useHref('/callback');
|
||||||
|
@ -33,28 +28,6 @@ const AppContent = () => {
|
||||||
}
|
}
|
||||||
}, [href, isAuthenticated, signIn]);
|
}, [href, isAuthenticated, signIn]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const theme = configs?.appearanceMode ?? defaultTheme;
|
|
||||||
const isFollowSystem = theme === AppearanceMode.SyncWithSystem;
|
|
||||||
const className = styles[theme] ?? '';
|
|
||||||
|
|
||||||
if (!isFollowSystem) {
|
|
||||||
document.body.classList.add(className);
|
|
||||||
}
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
if (!isFollowSystem) {
|
|
||||||
document.body.classList.remove(className);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}, [configs?.appearanceMode]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
(async () => {
|
|
||||||
void initI18n(configs?.language);
|
|
||||||
})();
|
|
||||||
}, [configs?.language]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Navigate to the first menu item after configs are loaded.
|
// Navigate to the first menu item after configs are loaded.
|
||||||
if (configs && location.pathname === '/') {
|
if (configs && location.pathname === '/') {
|
||||||
|
|
Loading…
Reference in a new issue