mirror of
https://github.com/logto-io/logto.git
synced 2024-12-30 20:33:54 -05:00
refactor(console): use consts and i18n
This commit is contained in:
parent
f505d87638
commit
02e9b8fde4
13 changed files with 185 additions and 13 deletions
|
@ -17,8 +17,11 @@
|
|||
"dependencies": {
|
||||
"@logto/phrases": "^0.1.0",
|
||||
"@logto/schemas": "^0.1.0",
|
||||
"i18next": "^21.6.12",
|
||||
"i18next-browser-languagedetector": "^6.1.3",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2"
|
||||
"react-dom": "^17.0.2",
|
||||
"react-i18next": "^11.15.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@parcel/core": "^2.3.1",
|
||||
|
@ -46,7 +49,11 @@
|
|||
"rules": {
|
||||
"react/jsx-curly-brace-presence": [
|
||||
"error",
|
||||
{ "props": "never", "children": "never", "propElementValues": "always" }
|
||||
{
|
||||
"props": "never",
|
||||
"children": "never",
|
||||
"propElementValues": "always"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
|
|
|
@ -5,6 +5,9 @@ import * as styles from './App.module.scss';
|
|||
import Content from './components/Content';
|
||||
import Sidebar from './components/Sidebar';
|
||||
import Topbar from './components/Topbar';
|
||||
import initI18n from './i18n/init';
|
||||
|
||||
void initI18n();
|
||||
|
||||
export const App = () => {
|
||||
return (
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
.row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: _.unit(3) _.unit(6);
|
||||
margin: 0 _.unit(5) 0 _.unit(2);
|
||||
padding: _.unit(3) _.unit(4);
|
||||
color: var(--color-on-surface-variant);
|
||||
|
||||
> div + div {
|
||||
|
|
46
packages/console/src/components/Sidebar/consts.tsx
Normal file
46
packages/console/src/components/Sidebar/consts.tsx
Normal file
|
@ -0,0 +1,46 @@
|
|||
import { FC } from 'react';
|
||||
import { TFuncKey } from 'react-i18next';
|
||||
|
||||
import BarGraph from './icons/BarGraph';
|
||||
import Bolt from './icons/Bolt';
|
||||
import Box from './icons/Box';
|
||||
import Cloud from './icons/Cloud';
|
||||
|
||||
type SidebarItem = {
|
||||
Icon: FC;
|
||||
title: TFuncKey<'translation', 'admin_console.tabs'>;
|
||||
};
|
||||
|
||||
type SidebarSection = {
|
||||
title: TFuncKey<'translation', 'admin_console.tab_sections'>;
|
||||
items: SidebarItem[];
|
||||
};
|
||||
|
||||
export const sections: SidebarSection[] = [
|
||||
{
|
||||
title: 'overview',
|
||||
items: [
|
||||
{
|
||||
Icon: Bolt,
|
||||
title: 'get_started',
|
||||
},
|
||||
{
|
||||
Icon: BarGraph,
|
||||
title: 'dashboard',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'resource_management',
|
||||
items: [
|
||||
{
|
||||
Icon: Box,
|
||||
title: 'applications',
|
||||
},
|
||||
{
|
||||
Icon: Cloud,
|
||||
title: 'api_resources',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
11
packages/console/src/components/Sidebar/icons/Box.tsx
Normal file
11
packages/console/src/components/Sidebar/icons/Box.tsx
Normal file
|
@ -0,0 +1,11 @@
|
|||
import React from 'react';
|
||||
|
||||
const Box = () => {
|
||||
return (
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M20.47 7.37005C20.47 7.37005 20.47 7.37005 20.47 7.29005L20.41 7.14005C20.3891 7.10822 20.3657 7.07812 20.34 7.05005C20.3133 7.00772 20.2832 6.96759 20.25 6.93005L20.16 6.86005L20 6.78005L12.5 2.15005C12.3411 2.05072 12.1574 1.99805 11.97 1.99805C11.7826 1.99805 11.599 2.05072 11.44 2.15005L4.00002 6.78005L3.91002 6.86005L3.82002 6.93005C3.78687 6.96759 3.75678 7.00772 3.73002 7.05005C3.70431 7.07812 3.68091 7.10822 3.66002 7.14005L3.60002 7.29005C3.60002 7.29005 3.60002 7.29005 3.60002 7.37005C3.59019 7.45644 3.59019 7.54366 3.60002 7.63005V16.3701C3.59968 16.54 3.64266 16.7072 3.72489 16.8559C3.80713 17.0047 3.92591 17.13 4.07002 17.22L11.57 21.85C11.6162 21.8786 11.6669 21.8989 11.72 21.91C11.72 21.91 11.77 21.91 11.8 21.91C11.9692 21.9637 12.1508 21.9637 12.32 21.91C12.32 21.91 12.37 21.91 12.4 21.91C12.4531 21.8989 12.5039 21.8786 12.55 21.85L20 17.22C20.1441 17.13 20.2629 17.0047 20.3452 16.8559C20.4274 16.7072 20.4704 16.54 20.47 16.3701V7.63005C20.4799 7.54366 20.4799 7.45644 20.47 7.37005ZM11 19.21L5.50002 15.8101V9.43005L11 12.82V19.21ZM12 11.09L6.40002 7.63005L12 4.18005L17.6 7.63005L12 11.09ZM18.5 15.8101L13 19.21V12.82L18.5 9.43005V15.8101Z" />
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export default Box;
|
11
packages/console/src/components/Sidebar/icons/Cloud.tsx
Normal file
11
packages/console/src/components/Sidebar/icons/Cloud.tsx
Normal file
|
@ -0,0 +1,11 @@
|
|||
import React from 'react';
|
||||
|
||||
const Cloud = () => {
|
||||
return (
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M22.5 14.5C22.5 13.9696 22.2893 13.4608 21.9142 13.0858C21.5391 12.7107 21.0304 12.5 20.5 12.5H14.5C13.9696 12.5 13.4609 12.7107 13.0858 13.0858C12.7107 13.4608 12.5 13.9696 12.5 14.5H9.49999V11.5H11.83C12.699 11.5014 13.5402 11.1936 14.203 10.6317C14.8658 10.0697 15.3071 9.29019 15.4478 8.43265C15.5885 7.57512 15.4195 6.69549 14.971 5.95115C14.5226 5.20682 13.8239 4.64633 13 4.36998C12.5427 3.41642 11.7949 2.63241 10.8639 2.13064C9.93303 1.62887 8.86699 1.43518 7.81907 1.57742C6.77114 1.71966 5.7953 2.19051 5.03182 2.92227C4.26834 3.65404 3.75654 4.60903 3.56999 5.64998C2.89278 5.87257 2.31695 6.32903 1.94569 6.93758C1.57444 7.54612 1.43201 8.26699 1.54393 8.971C1.65585 9.67502 2.0148 10.3162 2.55648 10.7796C3.09815 11.243 3.78714 11.4984 4.49999 11.5H7.49999V19.5C7.49999 19.7652 7.60535 20.0196 7.79289 20.2071C7.98042 20.3946 8.23478 20.5 8.49999 20.5H12.5C12.5 21.0304 12.7107 21.5391 13.0858 21.9142C13.4609 22.2893 13.9696 22.5 14.5 22.5H20.5C21.0304 22.5 21.5391 22.2893 21.9142 21.9142C22.2893 21.5391 22.5 21.0304 22.5 20.5V18.5C22.4963 18.1478 22.3997 17.8029 22.22 17.5C22.3997 17.1971 22.4963 16.8522 22.5 16.5V14.5ZM4.49999 9.49998C4.23478 9.49998 3.98042 9.39463 3.79289 9.20709C3.60535 9.01956 3.49999 8.7652 3.49999 8.49998C3.49999 8.23477 3.60535 7.98041 3.79289 7.79288C3.98042 7.60534 4.23478 7.49998 4.49999 7.49998C4.76521 7.49998 5.01956 7.39463 5.2071 7.20709C5.39464 7.01956 5.49999 6.7652 5.49999 6.49998C5.49192 5.78634 5.73854 5.0932 6.19555 4.54502C6.65256 3.99685 7.29002 3.62956 7.99347 3.5091C8.69692 3.38865 9.42027 3.52292 10.0336 3.88782C10.647 4.25271 11.1102 4.82431 11.34 5.49998C11.3987 5.66996 11.5021 5.82098 11.6393 5.93713C11.7766 6.05328 11.9427 6.13026 12.12 6.15998C12.5128 6.22501 12.8688 6.42987 13.1225 6.73678C13.3761 7.0437 13.5102 7.43197 13.5 7.82998C13.5 8.2729 13.324 8.69767 13.0109 9.01085C12.6977 9.32404 12.2729 9.49998 11.83 9.49998H4.49999ZM12.5 18.5H9.49999V16.5H12.5C12.5037 16.8522 12.6002 17.1971 12.78 17.5C12.6002 17.8029 12.5037 18.1478 12.5 18.5ZM14.5 20.5V18.5H20.5V20.5H14.5ZM14.5 16.5V14.5H20.5V16.5H14.5Z" />
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export default Cloud;
|
|
@ -1,22 +1,28 @@
|
|||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import Item from './components/Item';
|
||||
import Section from './components/Section';
|
||||
import BarGraph from './icons/BarGraph';
|
||||
import Bolt from './icons/Bolt';
|
||||
import { sections } from './consts';
|
||||
import * as styles from './index.module.scss';
|
||||
|
||||
const Sidebar = () => {
|
||||
const { t: tSection } = useTranslation(undefined, {
|
||||
keyPrefix: 'admin_console.tab_sections',
|
||||
});
|
||||
const { t: tItem } = useTranslation(undefined, {
|
||||
keyPrefix: 'admin_console.tabs',
|
||||
});
|
||||
|
||||
return (
|
||||
<div className={styles.sidebar}>
|
||||
<Section title="Overview">
|
||||
<Item icon={<Bolt />} title="Get Started" />
|
||||
<Item icon={<BarGraph />} title="Dashboard" />
|
||||
</Section>
|
||||
<Section title="Resource Management">
|
||||
<Item icon={<Bolt />} title="Get Started" />
|
||||
<Item icon={<BarGraph />} title="Dashboard" />
|
||||
</Section>
|
||||
{sections.map(({ title, items }) => (
|
||||
<Section key={title} title={tSection(title)}>
|
||||
{items.map(({ title, Icon }) => (
|
||||
<Item key={title} title={tItem(title)} icon={<Icon />} />
|
||||
))}
|
||||
</Section>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
18
packages/console/src/i18n/init.ts
Normal file
18
packages/console/src/i18n/init.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
import resources from '@logto/phrases';
|
||||
import i18next from 'i18next';
|
||||
import LanguageDetector from 'i18next-browser-languagedetector';
|
||||
import { initReactI18next } from 'react-i18next';
|
||||
|
||||
const initI18n = async () =>
|
||||
i18next
|
||||
.use(initReactI18next)
|
||||
.use(LanguageDetector)
|
||||
.init({
|
||||
resources,
|
||||
fallbackLng: 'en',
|
||||
interpolation: {
|
||||
escapeValue: false,
|
||||
},
|
||||
});
|
||||
|
||||
export default initI18n;
|
11
packages/console/src/include.d/react-i18next.d.ts
vendored
Normal file
11
packages/console/src/include.d/react-i18next.d.ts
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
// https://react.i18next.com/latest/typescript#create-a-declaration-file
|
||||
|
||||
// eslint-disable-next-line import/no-unassigned-import
|
||||
import 'react-i18next';
|
||||
import en from '@logto/phrases/lib/locales/en.js';
|
||||
|
||||
declare module 'react-i18next' {
|
||||
interface CustomTypeOptions {
|
||||
resources: typeof en;
|
||||
}
|
||||
}
|
|
@ -7,6 +7,7 @@ import { Resource } from './types';
|
|||
export type LogtoErrorCode = NormalizeKeyPaths<typeof en.errors>;
|
||||
export type LogtoErrorI18nKey = `errors:${LogtoErrorCode}`;
|
||||
export type Languages = keyof Resource;
|
||||
export type I18nKey = NormalizeKeyPaths<typeof en.translation>;
|
||||
|
||||
const resource: Resource = {
|
||||
en,
|
||||
|
|
|
@ -12,6 +12,18 @@ const translation = {
|
|||
loading: 'Creating Account...',
|
||||
have_account: 'Already have an account?',
|
||||
},
|
||||
admin_console: {
|
||||
tab_sections: {
|
||||
overview: 'Overview',
|
||||
resource_management: 'Resource Management',
|
||||
},
|
||||
tabs: {
|
||||
get_started: 'Get Started',
|
||||
dashboard: 'Dashboard',
|
||||
applications: 'Applications',
|
||||
api_resources: 'API Resources',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const errors = {
|
||||
|
|
|
@ -14,6 +14,18 @@ const translation = {
|
|||
loading: '创建中...',
|
||||
have_account: '已经有账户?',
|
||||
},
|
||||
admin_console: {
|
||||
tab_sections: {
|
||||
overview: '概览',
|
||||
resource_management: '资源管理',
|
||||
},
|
||||
tabs: {
|
||||
get_started: '开始使用',
|
||||
dashboard: '仪表盘',
|
||||
applications: '应用',
|
||||
api_resources: 'API 资源',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const errors = {
|
||||
|
|
|
@ -31,6 +31,8 @@ importers:
|
|||
'@types/react': ^17.0.14
|
||||
'@types/react-dom': ^17.0.9
|
||||
eslint: ^8.10.0
|
||||
i18next: ^21.6.12
|
||||
i18next-browser-languagedetector: ^6.1.3
|
||||
lint-staged: ^11.1.1
|
||||
parcel: ^2.3.1
|
||||
postcss: ^8.4.6
|
||||
|
@ -38,13 +40,17 @@ importers:
|
|||
prettier: ^2.3.2
|
||||
react: ^17.0.2
|
||||
react-dom: ^17.0.2
|
||||
react-i18next: ^11.15.4
|
||||
stylelint: ^13.13.1
|
||||
typescript: ^4.5.5
|
||||
dependencies:
|
||||
'@logto/phrases': link:../phrases
|
||||
'@logto/schemas': link:../schemas
|
||||
i18next: 21.6.12
|
||||
i18next-browser-languagedetector: 6.1.3
|
||||
react: 17.0.2
|
||||
react-dom: 17.0.2_react@17.0.2
|
||||
react-i18next: 11.15.4_2c37a602a29bb6bd53f3de707a8cfcc5
|
||||
devDependencies:
|
||||
'@parcel/core': 2.3.1
|
||||
'@parcel/transformer-sass': 2.3.1_@parcel+core@2.3.1
|
||||
|
@ -6904,6 +6910,12 @@ packages:
|
|||
'@babel/runtime': 7.16.3
|
||||
dev: false
|
||||
|
||||
/i18next/21.6.12:
|
||||
resolution: {integrity: sha512-xlGTPdu2g5PZEUIE6TA1mQ9EIAAv9nMFONzgwAIrKL/KTmYYWufQNGgOmp5Og1PvgUji+6i1whz0rMdsz1qaKw==}
|
||||
dependencies:
|
||||
'@babel/runtime': 7.16.3
|
||||
dev: false
|
||||
|
||||
/iconv-lite/0.4.24:
|
||||
resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
@ -11209,6 +11221,27 @@ packages:
|
|||
scheduler: 0.20.2
|
||||
dev: false
|
||||
|
||||
/react-i18next/11.15.4_2c37a602a29bb6bd53f3de707a8cfcc5:
|
||||
resolution: {integrity: sha512-jKJNAcVcbPGK+yrTcXhLblgPY16n6NbpZZL3Mk8nswj1v3ayIiUBVDU09SgqnT+DluyQBS97hwSvPU5yVFG0yg==}
|
||||
peerDependencies:
|
||||
i18next: '>= 19.0.0'
|
||||
react: '>= 16.8.0'
|
||||
react-dom: '*'
|
||||
react-native: '*'
|
||||
peerDependenciesMeta:
|
||||
react-dom:
|
||||
optional: true
|
||||
react-native:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@babel/runtime': 7.16.3
|
||||
html-escaper: 2.0.2
|
||||
html-parse-stringify: 3.0.1
|
||||
i18next: 21.6.12
|
||||
react: 17.0.2
|
||||
react-dom: 17.0.2_react@17.0.2
|
||||
dev: false
|
||||
|
||||
/react-i18next/11.15.4_3fb644aa30122a07f960d67fa51d6dc1:
|
||||
resolution: {integrity: sha512-jKJNAcVcbPGK+yrTcXhLblgPY16n6NbpZZL3Mk8nswj1v3ayIiUBVDU09SgqnT+DluyQBS97hwSvPU5yVFG0yg==}
|
||||
peerDependencies:
|
||||
|
|
Loading…
Reference in a new issue