0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-03-10 22:22:45 -05:00

feat(console): integrate React SDK

This commit is contained in:
Gao Sun 2022-03-11 19:04:27 +08:00
parent 56c8b6069a
commit 38d8196794
No known key found for this signature in database
GPG key ID: 13EBE123E4773688
32 changed files with 168 additions and 51 deletions

1
.npmrc
View file

@ -1,4 +1,5 @@
# Hoist for Parcel # Hoist for Parcel
public-hoist-pattern[]=@parcel/* public-hoist-pattern[]=@parcel/*
public-hoist-pattern[]=postcss public-hoist-pattern[]=postcss
public-hoist-pattern[]=process
public-hoist-pattern[]=*eslint* public-hoist-pattern[]=*eslint*

View file

@ -32,6 +32,7 @@
}, },
"alias": { "alias": {
"html-parse-stringify": "html-parse-stringify/dist/html-parse-stringify.module.js", "html-parse-stringify": "html-parse-stringify/dist/html-parse-stringify.module.js",
"react-hook-form": "react-hook-form/dist/index.esm.mjs" "react-hook-form": "react-hook-form/dist/index.esm.mjs",
"superstruct": "superstruct/lib/index.es.js"
} }
} }

View file

@ -17,6 +17,7 @@
}, },
"dependencies": { "dependencies": {
"@logto/phrases": "^0.1.0", "@logto/phrases": "^0.1.0",
"@logto/react": "^0.1.2",
"@logto/schemas": "^0.1.0", "@logto/schemas": "^0.1.0",
"@monaco-editor/react": "^4.3.1", "@monaco-editor/react": "^4.3.1",
"@silverhand/essentials": "^1.1.6", "@silverhand/essentials": "^1.1.6",
@ -55,6 +56,7 @@
"postcss": "^8.4.6", "postcss": "^8.4.6",
"postcss-modules": "^4.3.0", "postcss-modules": "^4.3.0",
"prettier": "^2.3.2", "prettier": "^2.3.2",
"process": "^0.11.10",
"stylelint": "^13.13.1", "stylelint": "^13.13.1",
"typescript": "^4.6.2" "typescript": "^4.6.2"
}, },

View file

@ -1,9 +0,0 @@
@use '@/scss/underscore' as _;
.content {
flex-grow: 1;
display: flex;
padding-right: _.unit(5);
margin-bottom: _.unit(6);
overflow: hidden;
}

View file

@ -1,14 +1,13 @@
import { LogtoProvider } from '@logto/react';
import React, { useEffect } from 'react'; import React, { useEffect } from 'react';
import { BrowserRouter, Navigate, Route, Routes, useLocation, useNavigate } from 'react-router-dom'; import { BrowserRouter, Navigate, Route, Routes, useLocation, useNavigate } from 'react-router-dom';
import { SWRConfig } from 'swr'; import { SWRConfig } from 'swr';
import './scss/normalized.scss'; import './scss/normalized.scss';
import * as styles from './App.module.scss';
import AppContent from './components/AppContent'; import AppContent from './components/AppContent';
import Content from './components/Content'; import { getPath, sections } from './components/AppContent/components/Sidebar';
import Sidebar, { getPath, sections } from './components/Sidebar'; import Callback from './components/Callback';
import Toast from './components/Toast'; import Toast from './components/Toast';
import Topbar from './components/Topbar';
import initI18n from './i18n/init'; import initI18n from './i18n/init';
import ApiResourceDetails from './pages/ApiResourceDetails'; import ApiResourceDetails from './pages/ApiResourceDetails';
import ApiResources from './pages/ApiResources'; import ApiResources from './pages/ApiResources';
@ -34,39 +33,36 @@ const Main = () => {
}, [location.pathname, navigate]); }, [location.pathname, navigate]);
return ( return (
<SWRConfig value={{ fetcher }}> <LogtoProvider logtoConfig={{ endpoint: 'https://logto.dev', clientId: 'foo' }}>
<Toast /> <SWRConfig value={{ fetcher }}>
<AppContent theme="light"> <Toast />
<Topbar /> <Routes>
<div className={styles.content}> <Route path="callback" element={<Callback />} />
<Sidebar /> <Route element={<AppContent theme="light" />}>
<Content> <Route path="applications">
<Routes> <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>
<Route index element={<ApiResources />} /> <Route path="api-resources">
<Route path=":id" element={<ApiResourceDetails />} /> <Route index element={<ApiResources />} />
</Route> <Route path=":id" element={<ApiResourceDetails />} />
<Route path="connectors"> </Route>
<Route index element={<Connectors />} /> <Route path="connectors">
<Route path="social" element={<Connectors />} /> <Route index element={<Connectors />} />
<Route path=":connectorId" element={<ConnectorDetails />} /> <Route path="social" element={<Connectors />} />
</Route> <Route path=":connectorId" element={<ConnectorDetails />} />
<Route path="users"> </Route>
<Route index element={<Users />} /> <Route path="users">
</Route> <Route index element={<Users />} />
</Routes> </Route>
</Content> </Route>
</div> </Routes>
</AppContent> </SWRConfig>
</SWRConfig> </LogtoProvider>
); );
}; };

View file

@ -1,4 +1,5 @@
@use '@/scss/colors' as colors; @use '@/scss/colors' as colors;
@use '@/scss/underscore' as _;
.app { .app {
position: absolute; position: absolute;
@ -7,6 +8,14 @@
flex-direction: column; flex-direction: column;
} }
.content {
flex-grow: 1;
display: flex;
padding-right: _.unit(5);
margin-bottom: _.unit(6);
overflow: hidden;
}
.light { .light {
@include colors.light-theme; @include colors.light-theme;
} }

View file

@ -1,15 +1,22 @@
import React, { ReactNode, useEffect } from 'react'; import { useLogto } from '@logto/react';
import React, { useEffect } from 'react';
import { Outlet, useHref } from 'react-router-dom';
import Content from './components/Content';
import Sidebar from './components/Sidebar';
import Topbar from './components/Topbar';
import * as styles from './index.module.scss'; import * as styles from './index.module.scss';
type Theme = 'light'; type Theme = 'light';
type Props = { type Props = {
children: ReactNode;
theme: Theme; theme: Theme;
}; };
const AppContent = ({ children, theme }: Props) => { const AppContent = ({ theme }: Props) => {
const { isAuthenticated, signIn } = useLogto();
const href = useHref('/callback');
useEffect(() => { useEffect(() => {
const classes = [styles.web, styles[theme]].filter((value): value is string => Boolean(value)); const classes = [styles.web, styles[theme]].filter((value): value is string => Boolean(value));
document.body.classList.add(...classes); document.body.classList.add(...classes);
@ -19,7 +26,27 @@ const AppContent = ({ children, theme }: Props) => {
}; };
}, [theme]); }, [theme]);
return <div className={styles.app}>{children}</div>; useEffect(() => {
if (!isAuthenticated) {
void signIn(new URL(href, window.location.origin).toString());
}
}, [href, isAuthenticated, signIn]);
if (!isAuthenticated) {
return <>loading</>;
}
return (
<div className={styles.app}>
<Topbar />
<div className={styles.content}>
<Sidebar />
<Content>
<Outlet />
</Content>
</div>
</div>
);
}; };
export default AppContent; export default AppContent;

View file

@ -0,0 +1,18 @@
import { useLogto } from '@logto/react';
import React, { useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
const Callback = () => {
const { isAuthenticated, isLoading } = useLogto();
const navigate = useNavigate();
useEffect(() => {
if (isAuthenticated && !isLoading) {
navigate('/', { replace: true });
}
}, [isAuthenticated, isLoading, navigate]);
return <p>Redirecting...</p>;
};
export default Callback;

74
pnpm-lock.yaml generated
View file

@ -21,6 +21,7 @@ importers:
packages/console: packages/console:
specifiers: specifiers:
'@logto/phrases': ^0.1.0 '@logto/phrases': ^0.1.0
'@logto/react': ^0.1.2
'@logto/schemas': ^0.1.0 '@logto/schemas': ^0.1.0
'@monaco-editor/react': ^4.3.1 '@monaco-editor/react': ^4.3.1
'@parcel/core': ^2.3.2 '@parcel/core': ^2.3.2
@ -47,6 +48,7 @@ importers:
postcss: ^8.4.6 postcss: ^8.4.6
postcss-modules: ^4.3.0 postcss-modules: ^4.3.0
prettier: ^2.3.2 prettier: ^2.3.2
process: ^0.11.10
react: ^17.0.2 react: ^17.0.2
react-dom: ^17.0.2 react-dom: ^17.0.2
react-hook-form: ^7.27.1 react-hook-form: ^7.27.1
@ -61,6 +63,7 @@ importers:
typescript: ^4.6.2 typescript: ^4.6.2
dependencies: dependencies:
'@logto/phrases': link:../phrases '@logto/phrases': link:../phrases
'@logto/react': 0.1.2_react@17.0.2
'@logto/schemas': link:../schemas '@logto/schemas': link:../schemas
'@monaco-editor/react': 4.3.1_e62f1489d5efe674a41c3f8d6971effe '@monaco-editor/react': 4.3.1_e62f1489d5efe674a41c3f8d6971effe
'@silverhand/essentials': 1.1.6 '@silverhand/essentials': 1.1.6
@ -98,6 +101,7 @@ importers:
postcss: 8.4.6 postcss: 8.4.6
postcss-modules: 4.3.0_postcss@8.4.6 postcss-modules: 4.3.0_postcss@8.4.6
prettier: 2.5.1 prettier: 2.5.1
process: 0.11.10
stylelint: 13.13.1 stylelint: 13.13.1
typescript: 4.6.2 typescript: 4.6.2
@ -2233,6 +2237,38 @@ packages:
prop-types: 15.8.1 prop-types: 15.8.1
react: 17.0.2 react: 17.0.2
react-dom: 17.0.2_react@17.0.2 react-dom: 17.0.2_react@17.0.2
/@logto/browser/0.1.2:
resolution: {integrity: sha512-sTJjnx00BXYEChCbbO/LPs0x0wE1bDSHniFi+u93cynyEHgoT5yjMnH4N39NhrpmRdkXxOxaIkXmyAT1nSmYzQ==}
requiresBuild: true
dependencies:
'@logto/js': 0.1.2
'@silverhand/essentials': 1.1.6
jose: 4.6.0
lodash.get: 4.4.2
superstruct: 0.15.4
dev: false
/@logto/js/0.1.2:
resolution: {integrity: sha512-kz7l++gfpXa1OaUaB5WvnHNXQKEJDFnBzVYAofPADnPftHEF0zYRuQDCa8PJMd6/ECKfL1XPLMlJMNP/5U2fqQ==}
requiresBuild: true
dependencies:
'@silverhand/essentials': 1.1.6
camelcase-keys: 7.0.2
jose: 4.6.0
js-base64: 3.7.2
lodash.get: 4.4.2
superstruct: 0.15.4
dev: false
/@logto/react/0.1.2_react@17.0.2:
resolution: {integrity: sha512-kxLOvxOv3IB8BjpbcoRFsBFX249wAfbOL0tfJlSWmOXrkPouai1bRItEFBHFKDI1zLJATVQRoJNQfaczJy/c0A==}
requiresBuild: true
peerDependencies:
react: '>=16.8.0'
dependencies:
'@logto/browser': 0.1.2
'@silverhand/essentials': 1.1.6
react: 17.0.2
dev: false dev: false
/@nodelib/fs.scandir/2.1.5: /@nodelib/fs.scandir/2.1.5:
@ -4506,6 +4542,16 @@ packages:
quick-lru: 4.0.1 quick-lru: 4.0.1
dev: true dev: true
/camelcase-keys/7.0.2:
resolution: {integrity: sha512-Rjs1H+A9R+Ig+4E/9oyB66UC5Mj9Xq3N//vcLf2WzgdTi/3gUu3Z9KoqmlrEG4VuuLK8wJHofxzdQXz/knhiYg==}
engines: {node: '>=12'}
dependencies:
camelcase: 6.3.0
map-obj: 4.3.0
quick-lru: 5.1.1
type-fest: 1.4.0
dev: false
/camelcase/5.3.1: /camelcase/5.3.1:
resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==}
engines: {node: '>=6'} engines: {node: '>=6'}
@ -4515,6 +4561,11 @@ packages:
resolution: {integrity: sha512-tVI4q5jjFV5CavAU8DXfza/TJcZutVKo/5Foskmsqcm0MsL91moHvwiGNnqaa2o6PF/7yT5ikDRcVcl8Rj6LCA==} resolution: {integrity: sha512-tVI4q5jjFV5CavAU8DXfza/TJcZutVKo/5Foskmsqcm0MsL91moHvwiGNnqaa2o6PF/7yT5ikDRcVcl8Rj6LCA==}
engines: {node: '>=10'} engines: {node: '>=10'}
/camelcase/6.3.0:
resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==}
engines: {node: '>=10'}
dev: false
/caniuse-api/3.0.0: /caniuse-api/3.0.0:
resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==} resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==}
dependencies: dependencies:
@ -8568,6 +8619,14 @@ packages:
resolution: {integrity: sha512-S7Xfsy8nN9Iw/AZxk+ZxEbd5ImIwJPM0TfAo8zI8FF+3lidQ2yiK4dqzsaPKSbZD0woNVSY0KCql6rlKc5V7ug==} resolution: {integrity: sha512-S7Xfsy8nN9Iw/AZxk+ZxEbd5ImIwJPM0TfAo8zI8FF+3lidQ2yiK4dqzsaPKSbZD0woNVSY0KCql6rlKc5V7ug==}
dev: false dev: false
/jose/4.6.0:
resolution: {integrity: sha512-0hNAkhMBNi4soKSAX4zYOFV+aqJlEz/4j4fregvasJzEVtjDChvWqRjPvHwLqr5hx28Ayr6bsOs1Kuj87V0O8w==}
dev: false
/js-base64/3.7.2:
resolution: {integrity: sha512-NnRs6dsyqUXejqk/yv2aiXlAvOs56sLkX6nUdeaNezI5LFFLlsZjOThmwnrcwh5ZZRwZlCMnVAY3CvhIhoVEKQ==}
dev: false
/js-tokens/4.0.0: /js-tokens/4.0.0:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
@ -9073,7 +9132,6 @@ packages:
/lodash.get/4.4.2: /lodash.get/4.4.2:
resolution: {integrity: sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=} resolution: {integrity: sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=}
dev: true
/lodash.ismatch/4.4.0: /lodash.ismatch/4.4.0:
resolution: {integrity: sha1-dWy1FQyjum8RCFp4hJZF8Yj4Xzc=} resolution: {integrity: sha1-dWy1FQyjum8RCFp4hJZF8Yj4Xzc=}
@ -11565,6 +11623,11 @@ packages:
resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==}
dev: true dev: true
/process/0.11.10:
resolution: {integrity: sha1-czIwDoQBYb2j5podHZGn1LwW8YI=}
engines: {node: '>= 0.6.0'}
dev: true
/promise-deferred/2.0.3: /promise-deferred/2.0.3:
resolution: {integrity: sha512-n10XaoznCzLfyPFOlEE8iurezHpxrYzyjgq/1eW9Wk1gJwur/N7BdBmjJYJpqMeMcXK4wEbzo2EvZQcqjYcKUQ==} resolution: {integrity: sha512-n10XaoznCzLfyPFOlEE8iurezHpxrYzyjgq/1eW9Wk1gJwur/N7BdBmjJYJpqMeMcXK4wEbzo2EvZQcqjYcKUQ==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
@ -13156,6 +13219,10 @@ packages:
- supports-color - supports-color
dev: true dev: true
/superstruct/0.15.4:
resolution: {integrity: sha512-eOoMeSbP9ZJChNOm/9RYjE+F36rYR966AAqeG3xhQB02j2sfAUXDp4EQ/7bAOqnlJnuFDB8yvOu50SocvKpUEw==}
dev: false
/supertest/6.2.2: /supertest/6.2.2:
resolution: {integrity: sha512-wCw9WhAtKJsBvh07RaS+/By91NNE0Wh0DN19/hWPlBOU8tAfOtbZoVSV4xXeoKoxgPx0rx2y+y+8660XtE7jzg==} resolution: {integrity: sha512-wCw9WhAtKJsBvh07RaS+/By91NNE0Wh0DN19/hWPlBOU8tAfOtbZoVSV4xXeoKoxgPx0rx2y+y+8660XtE7jzg==}
engines: {node: '>=6.0.0'} engines: {node: '>=6.0.0'}
@ -13666,6 +13733,11 @@ packages:
engines: {node: '>=8'} engines: {node: '>=8'}
dev: true dev: true
/type-fest/1.4.0:
resolution: {integrity: sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==}
engines: {node: '>=10'}
dev: false
/type-fest/2.8.0: /type-fest/2.8.0:
resolution: {integrity: sha512-O+V9pAshf9C6loGaH0idwsmugI2LxVNR7DtS40gVo2EXZVYFgz9OuNtOhgHLdHdapOEWNdvz9Ob/eeuaWwwlxA==} resolution: {integrity: sha512-O+V9pAshf9C6loGaH0idwsmugI2LxVNR7DtS40gVo2EXZVYFgz9OuNtOhgHLdHdapOEWNdvz9Ob/eeuaWwwlxA==}
engines: {node: '>=12.20'} engines: {node: '>=12.20'}