@qlover/create-app 0.10.1 → 0.10.2
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/CHANGELOG.md +141 -0
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/templates/next-app/config/IOCIdentifier.ts +2 -2
- package/dist/templates/next-app/config/Identifier/common/common.ts +14 -0
- package/dist/templates/next-app/config/Identifier/pages/index.ts +1 -0
- package/dist/templates/next-app/config/Identifier/pages/page.about.ts +20 -0
- package/dist/templates/next-app/config/common.ts +1 -1
- package/dist/templates/next-app/config/cookies.ts +23 -0
- package/dist/templates/next-app/config/i18n/AboutI18n.ts +14 -0
- package/dist/templates/next-app/config/i18n/i18nConfig.ts +3 -1
- package/dist/templates/next-app/config/i18n/index.ts +1 -0
- package/dist/templates/next-app/config/i18n/loginI18n.ts +8 -0
- package/dist/templates/next-app/config/theme.ts +4 -0
- package/dist/templates/next-app/eslint.config.mjs +4 -1
- package/dist/templates/next-app/next.config.ts +1 -0
- package/dist/templates/next-app/package.json +13 -4
- package/dist/templates/next-app/public/locales/en.json +5 -0
- package/dist/templates/next-app/public/locales/zh.json +5 -0
- package/dist/templates/next-app/src/app/[locale]/admin/AdminI18nProvider.tsx +37 -0
- package/dist/templates/next-app/src/app/[locale]/admin/layout.tsx +30 -6
- package/dist/templates/next-app/src/app/[locale]/admin/locales/page.tsx +1 -1
- package/dist/templates/next-app/src/app/[locale]/layout.tsx +47 -10
- package/dist/templates/next-app/src/app/[locale]/login/LoginForm.tsx +1 -1
- package/dist/templates/next-app/src/app/[locale]/login/page.tsx +22 -10
- package/dist/templates/next-app/src/app/[locale]/page.tsx +23 -8
- package/dist/templates/next-app/src/app/[locale]/register/page.tsx +21 -9
- package/dist/templates/next-app/src/app/api/admin/locales/create/route.ts +7 -28
- package/dist/templates/next-app/src/app/api/admin/locales/import/route.ts +7 -34
- package/dist/templates/next-app/src/app/api/admin/locales/route.ts +12 -34
- package/dist/templates/next-app/src/app/api/admin/locales/update/route.ts +7 -26
- package/dist/templates/next-app/src/app/api/admin/users/route.ts +14 -33
- package/dist/templates/next-app/src/app/api/locales/json/route.ts +13 -25
- package/dist/templates/next-app/src/app/api/user/login/route.ts +6 -46
- package/dist/templates/next-app/src/app/api/user/logout/route.ts +5 -24
- package/dist/templates/next-app/src/app/api/user/register/route.ts +6 -45
- package/dist/templates/next-app/src/app/manifest.ts +16 -0
- package/dist/templates/next-app/src/app/robots.txt +2 -0
- package/dist/templates/next-app/src/base/cases/NavigateBridge.ts +12 -2
- package/dist/templates/next-app/src/base/cases/RouterService.ts +5 -5
- package/dist/templates/next-app/src/base/port/AppApiInterface.ts +22 -0
- package/dist/templates/next-app/src/base/port/IOCInterface.ts +9 -0
- package/dist/templates/next-app/src/base/types/{PageProps.ts → AppPageRouter.ts} +4 -1
- package/dist/templates/next-app/src/base/types/PagesRouter.ts +9 -0
- package/dist/templates/next-app/src/core/bootstraps/BootstrapClient.ts +19 -5
- package/dist/templates/next-app/src/core/bootstraps/BootstrapServer.ts +2 -2
- package/dist/templates/next-app/src/core/bootstraps/BootstrapsRegistry.ts +0 -1
- package/dist/templates/next-app/src/core/clientIoc/ClientIOC.ts +29 -8
- package/dist/templates/next-app/src/core/clientIoc/ClientIOCRegister.ts +4 -2
- package/dist/templates/next-app/src/core/serverIoc/ServerIOC.ts +25 -7
- package/dist/templates/next-app/src/core/serverIoc/ServerIOCRegister.ts +1 -1
- package/dist/templates/next-app/src/i18n/loadMessages.ts +103 -0
- package/dist/templates/next-app/src/i18n/request.ts +3 -22
- package/dist/templates/next-app/src/pages/[locale]/about.tsx +61 -0
- package/dist/templates/next-app/src/pages/_app.tsx +50 -0
- package/dist/templates/next-app/src/pages/_document.tsx +13 -0
- package/dist/templates/next-app/src/{middleware.ts → proxy.ts} +2 -1
- package/dist/templates/next-app/src/server/AppPageRouteParams.ts +94 -0
- package/dist/templates/next-app/src/server/NextApiServer.ts +53 -0
- package/dist/templates/next-app/src/server/PagesRouteParams.ts +136 -0
- package/dist/templates/next-app/src/server/{sqlBridges/SupabaseBridge.ts → SupabaseBridge.ts} +2 -0
- package/dist/templates/next-app/src/server/controllers/AdminLocalesController.ts +74 -0
- package/dist/templates/next-app/src/server/controllers/AdminUserController.ts +39 -0
- package/dist/templates/next-app/src/server/controllers/LocalesController.ts +33 -0
- package/dist/templates/next-app/src/server/controllers/UserController.ts +77 -0
- package/dist/templates/next-app/src/server/port/AIControllerInterface.ts +8 -0
- package/dist/templates/next-app/src/server/port/AdminLocalesControllerInterface.ts +21 -0
- package/dist/templates/next-app/src/server/port/AdminUserControllerInterface.ts +11 -0
- package/dist/templates/next-app/src/server/port/LocalesControllerInterface.ts +10 -0
- package/dist/templates/next-app/src/server/port/{ParamsHandlerInterface.ts → RouteParamsnHandlerInterface.ts} +9 -2
- package/dist/templates/next-app/src/server/port/ServerInterface.ts +2 -2
- package/dist/templates/next-app/src/server/port/UserControllerInerface.ts +8 -0
- package/dist/templates/next-app/src/server/port/UserServiceInterface.ts +1 -1
- package/dist/templates/next-app/src/server/port/ValidatorInterface.ts +2 -2
- package/dist/templates/next-app/src/server/repositorys/LocalesRepository.ts +8 -2
- package/dist/templates/next-app/src/{base → server}/services/AdminLocalesService.ts +2 -2
- package/dist/templates/next-app/src/server/services/ApiLocaleService.ts +25 -10
- package/dist/templates/next-app/src/server/services/ApiUserService.ts +5 -2
- package/dist/templates/next-app/src/server/validators/LocalesValidator.ts +4 -2
- package/dist/templates/next-app/src/server/validators/LoginValidator.ts +1 -1
- package/dist/templates/next-app/src/server/validators/PaginationValidator.ts +10 -10
- package/dist/templates/next-app/src/styles/css/antd-themes/_common/_default.css +0 -44
- package/dist/templates/next-app/src/styles/css/antd-themes/_common/dark.css +0 -44
- package/dist/templates/next-app/src/styles/css/antd-themes/_common/pink.css +0 -44
- package/dist/templates/next-app/src/styles/css/index.css +1 -1
- package/dist/templates/next-app/src/styles/css/scrollbar.css +34 -0
- package/dist/templates/next-app/src/uikit/components/AdminLayout.tsx +34 -11
- package/dist/templates/next-app/src/uikit/components/BootstrapsProvider.tsx +69 -39
- package/dist/templates/next-app/src/uikit/components/ClientRootProvider.tsx +64 -0
- package/dist/templates/next-app/src/uikit/components/ClinetRenderProvider.tsx +42 -0
- package/dist/templates/next-app/src/uikit/components/IOCProvider.tsx +34 -0
- package/dist/templates/next-app/src/uikit/components-app/AppBridge.tsx +17 -0
- package/dist/templates/next-app/src/uikit/components-app/AppRoutePage.tsx +112 -0
- package/dist/templates/next-app/src/uikit/{components → components-app}/LanguageSwitcher.tsx +15 -19
- package/dist/templates/next-app/src/uikit/{components → components-app}/ThemeSwitcher.tsx +53 -52
- package/dist/templates/next-app/src/uikit/components-pages/LanguageSwitcher.tsx +98 -0
- package/dist/templates/next-app/src/uikit/components-pages/PagesRoutePage.tsx +93 -0
- package/dist/templates/next-app/src/uikit/context/IOCContext.ts +16 -4
- package/dist/templates/next-app/src/uikit/hook/useStrictEffect.ts +32 -0
- package/dist/templates/next-app/tsconfig.json +3 -2
- package/package.json +1 -1
- package/dist/templates/next-app/src/server/PageParams.ts +0 -66
- package/dist/templates/next-app/src/uikit/components/BaseHeader.tsx +0 -80
- package/dist/templates/next-app/src/uikit/components/BaseLayout.tsx +0 -65
- package/dist/templates/next-app/src/uikit/components/ComboProvider.tsx +0 -58
- package/dist/templates/next-app/src/uikit/components/NextIntlProvider.tsx +0 -21
- /package/dist/templates/next-app/{src/app/[locale] → public}/favicon.ico +0 -0
- /package/dist/templates/next-app/src/uikit/{components → components-app}/LogoutButton.tsx +0 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { TranslationOutlined } from '@ant-design/icons';
|
|
4
|
+
import { Dropdown } from 'antd';
|
|
5
|
+
import { useRouter } from 'next/router';
|
|
6
|
+
import { useLocale } from 'next-intl';
|
|
7
|
+
import { useCallback, useMemo, useState } from 'react';
|
|
8
|
+
import { useLocaleRoutes } from '@config/common';
|
|
9
|
+
import { i18nConfig } from '@config/i18n';
|
|
10
|
+
import type { LocaleType } from '@config/i18n';
|
|
11
|
+
import type { ItemType } from 'antd/es/menu/interface';
|
|
12
|
+
|
|
13
|
+
export function LanguageSwitcher() {
|
|
14
|
+
const router = useRouter();
|
|
15
|
+
const currentLocale = useLocale() as LocaleType;
|
|
16
|
+
const [isPending, setIsPending] = useState(false);
|
|
17
|
+
|
|
18
|
+
const options: ItemType[] = useMemo(() => {
|
|
19
|
+
return i18nConfig.supportedLngs.map(
|
|
20
|
+
(lang) =>
|
|
21
|
+
({
|
|
22
|
+
type: 'item',
|
|
23
|
+
key: lang,
|
|
24
|
+
value: lang,
|
|
25
|
+
label:
|
|
26
|
+
i18nConfig.localeNames[lang as keyof typeof i18nConfig.localeNames]
|
|
27
|
+
}) as ItemType
|
|
28
|
+
);
|
|
29
|
+
}, []);
|
|
30
|
+
|
|
31
|
+
const handleLanguageChange = useCallback(
|
|
32
|
+
async (value: string) => {
|
|
33
|
+
if (isPending || value === currentLocale) return;
|
|
34
|
+
|
|
35
|
+
setIsPending(true);
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
// Get current path
|
|
39
|
+
let newPath = router.asPath;
|
|
40
|
+
|
|
41
|
+
if (useLocaleRoutes) {
|
|
42
|
+
// Replace locale in path (e.g., /en/about -> /zh/about)
|
|
43
|
+
const pathWithoutLocale = newPath.replace(
|
|
44
|
+
new RegExp(`^/${currentLocale}(/|$)`),
|
|
45
|
+
'/'
|
|
46
|
+
);
|
|
47
|
+
// Remove leading slash if path is root
|
|
48
|
+
const cleanPath = pathWithoutLocale === '/' ? '' : pathWithoutLocale;
|
|
49
|
+
newPath = `/${value}${cleanPath}`;
|
|
50
|
+
} else {
|
|
51
|
+
// If not using locale routes, just update query param
|
|
52
|
+
newPath = router.pathname;
|
|
53
|
+
router.replace({
|
|
54
|
+
pathname: router.pathname,
|
|
55
|
+
query: { ...router.query, locale: value }
|
|
56
|
+
});
|
|
57
|
+
setIsPending(false);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Replace the route
|
|
62
|
+
router.replace(newPath);
|
|
63
|
+
} finally {
|
|
64
|
+
setIsPending(false);
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
[router, currentLocale, isPending]
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
const nextLocale = useMemo(() => {
|
|
71
|
+
const targetIndex = i18nConfig.supportedLngs.indexOf(currentLocale) + 1;
|
|
72
|
+
return i18nConfig.supportedLngs[
|
|
73
|
+
targetIndex % i18nConfig.supportedLngs.length
|
|
74
|
+
];
|
|
75
|
+
}, [currentLocale]);
|
|
76
|
+
|
|
77
|
+
return (
|
|
78
|
+
<Dropdown
|
|
79
|
+
data-testid="LanguageSwitcherDropdown"
|
|
80
|
+
trigger={['hover']}
|
|
81
|
+
menu={{
|
|
82
|
+
selectedKeys: [currentLocale],
|
|
83
|
+
items: options,
|
|
84
|
+
onClick: ({ key }) => {
|
|
85
|
+
handleLanguageChange(key);
|
|
86
|
+
}
|
|
87
|
+
}}
|
|
88
|
+
>
|
|
89
|
+
<span
|
|
90
|
+
data-testid="LanguageSwitcher"
|
|
91
|
+
className="text-text hover:text-text-hover cursor-pointer text-lg transition-colors"
|
|
92
|
+
onClick={() => handleLanguageChange(nextLocale)}
|
|
93
|
+
>
|
|
94
|
+
<TranslationOutlined />
|
|
95
|
+
</span>
|
|
96
|
+
</Dropdown>
|
|
97
|
+
);
|
|
98
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { TeamOutlined } from '@ant-design/icons';
|
|
2
|
+
import { clsx } from 'clsx';
|
|
3
|
+
import { useLocale } from 'next-intl';
|
|
4
|
+
import { useMemo } from 'react';
|
|
5
|
+
import { LanguageSwitcher } from './LanguageSwitcher';
|
|
6
|
+
import { LocaleLink } from '../components/LocaleLink';
|
|
7
|
+
import { ThemeSwitcher } from '../components-app/ThemeSwitcher';
|
|
8
|
+
import type { AppRoutePageProps } from '../components-app/AppRoutePage';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* App Route Page
|
|
12
|
+
*
|
|
13
|
+
* 主要用于 /src/app 目录下页面的基础布局,包含头部、主体内容等
|
|
14
|
+
*
|
|
15
|
+
* @description
|
|
16
|
+
* - /src/app/[locale]/page.tsx
|
|
17
|
+
* - /src/app/[locale]/login/page.tsx
|
|
18
|
+
*
|
|
19
|
+
*/
|
|
20
|
+
export function PagesRoutePage({
|
|
21
|
+
children,
|
|
22
|
+
showAdminButton,
|
|
23
|
+
mainProps,
|
|
24
|
+
headerClassName,
|
|
25
|
+
tt,
|
|
26
|
+
headerHref = '/',
|
|
27
|
+
...props
|
|
28
|
+
}: AppRoutePageProps) {
|
|
29
|
+
const locale = useLocale();
|
|
30
|
+
const adminTitle = tt.adminTitle;
|
|
31
|
+
|
|
32
|
+
const actions = useMemo(() => {
|
|
33
|
+
return [
|
|
34
|
+
showAdminButton && (
|
|
35
|
+
<LocaleLink
|
|
36
|
+
key="admin-button"
|
|
37
|
+
href="/admin"
|
|
38
|
+
title={adminTitle}
|
|
39
|
+
locale={locale}
|
|
40
|
+
className="text-text hover:text-text-hover cursor-pointer text-lg transition-colors"
|
|
41
|
+
>
|
|
42
|
+
<TeamOutlined className="text-lg text-text" />
|
|
43
|
+
</LocaleLink>
|
|
44
|
+
),
|
|
45
|
+
|
|
46
|
+
<LanguageSwitcher key="language-switcher" />,
|
|
47
|
+
|
|
48
|
+
<ThemeSwitcher key="theme-switcher" />
|
|
49
|
+
].filter(Boolean);
|
|
50
|
+
}, [adminTitle, showAdminButton, locale]);
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<div
|
|
54
|
+
data-testid="AppRoutePage"
|
|
55
|
+
className="flex flex-col min-h-screen"
|
|
56
|
+
{...props}
|
|
57
|
+
>
|
|
58
|
+
<header
|
|
59
|
+
data-testid="BaseHeader"
|
|
60
|
+
className="h-14 bg-secondary border-b border-c-border sticky top-0 z-50"
|
|
61
|
+
>
|
|
62
|
+
<div
|
|
63
|
+
className={clsx(
|
|
64
|
+
'flex items-center justify-between h-full px-4 mx-auto max-w-7xl',
|
|
65
|
+
headerClassName
|
|
66
|
+
)}
|
|
67
|
+
>
|
|
68
|
+
<div className="flex items-center">
|
|
69
|
+
<LocaleLink
|
|
70
|
+
data-testid="BaseHeaderLogo"
|
|
71
|
+
title={tt.title}
|
|
72
|
+
href={headerHref}
|
|
73
|
+
locale={locale}
|
|
74
|
+
className="flex items-center hover:opacity-80 transition-opacity"
|
|
75
|
+
>
|
|
76
|
+
<span
|
|
77
|
+
data-testid="base-header-app-name"
|
|
78
|
+
className="text-lg font-semibold text-text"
|
|
79
|
+
>
|
|
80
|
+
{tt.title}
|
|
81
|
+
</span>
|
|
82
|
+
</LocaleLink>
|
|
83
|
+
</div>
|
|
84
|
+
<div className="flex items-center gap-2 md:gap-4">{actions}</div>
|
|
85
|
+
</div>
|
|
86
|
+
</header>
|
|
87
|
+
|
|
88
|
+
<main className="flex flex-1 flex-col bg-primary" {...mainProps}>
|
|
89
|
+
{children}
|
|
90
|
+
</main>
|
|
91
|
+
</div>
|
|
92
|
+
);
|
|
93
|
+
}
|
|
@@ -1,11 +1,23 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
+
import {
|
|
4
|
+
type IOCContainerInterface,
|
|
5
|
+
type IOCFunctionInterface
|
|
6
|
+
} from '@qlover/corekit-bridge';
|
|
3
7
|
import { createContext } from 'react';
|
|
8
|
+
import { ClientIOC } from '@/core/clientIoc/ClientIOC';
|
|
4
9
|
import type { IOCIdentifierMap } from '@config/IOCIdentifier';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
10
|
+
|
|
11
|
+
// export const IOCInstance = new ClientIOC();
|
|
12
|
+
|
|
13
|
+
// export const IOCContext =
|
|
14
|
+
// createContext<IOCInterface<IOCIdentifierMap, IOCContainerInterface>>(
|
|
15
|
+
// IOCInstance
|
|
16
|
+
// );
|
|
17
|
+
|
|
18
|
+
export const clientIOC = new ClientIOC();
|
|
19
|
+
|
|
20
|
+
export const IOCInstance = clientIOC.create();
|
|
9
21
|
|
|
10
22
|
export const IOCContext = createContext<IOCFunctionInterface<
|
|
11
23
|
IOCIdentifierMap,
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { useEffect, useRef } from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* In React Strict Mode, ensure that the effect is executed only once when each dependency changes
|
|
5
|
+
* @param effect The effect function to execute
|
|
6
|
+
* @param deps The dependency array
|
|
7
|
+
*/
|
|
8
|
+
export const useStrictEffect = (
|
|
9
|
+
effect: () => void | (() => void),
|
|
10
|
+
deps?: React.DependencyList
|
|
11
|
+
) => {
|
|
12
|
+
const mountedRef = useRef(false);
|
|
13
|
+
const depsRef = useRef(deps);
|
|
14
|
+
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
// Check if the dependencies have changed
|
|
17
|
+
const depsChanged =
|
|
18
|
+
!deps ||
|
|
19
|
+
!depsRef.current ||
|
|
20
|
+
deps.some((dep, i) => dep !== depsRef.current![i]);
|
|
21
|
+
|
|
22
|
+
// Update the dependency reference
|
|
23
|
+
depsRef.current = deps;
|
|
24
|
+
|
|
25
|
+
// If it's the first mount or the dependencies have changed, execute the effect
|
|
26
|
+
if (!mountedRef.current || depsChanged) {
|
|
27
|
+
mountedRef.current = true;
|
|
28
|
+
return effect();
|
|
29
|
+
}
|
|
30
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
31
|
+
}, deps);
|
|
32
|
+
};
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"moduleResolution": "bundler",
|
|
12
12
|
"resolveJsonModule": true,
|
|
13
13
|
"isolatedModules": true,
|
|
14
|
-
"jsx": "
|
|
14
|
+
"jsx": "react-jsx",
|
|
15
15
|
"incremental": true,
|
|
16
16
|
"experimentalDecorators": true,
|
|
17
17
|
"emitDecoratorMetadata": true,
|
|
@@ -32,7 +32,8 @@
|
|
|
32
32
|
"src/**/*.tsx",
|
|
33
33
|
"migrations/**/*.ts",
|
|
34
34
|
".next/types/**/*.ts",
|
|
35
|
-
"config/**/*.ts"
|
|
35
|
+
"config/**/*.ts",
|
|
36
|
+
".next/dev/types/**/*.ts"
|
|
36
37
|
],
|
|
37
38
|
"exclude": ["node_modules"]
|
|
38
39
|
}
|
package/package.json
CHANGED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import { notFound } from 'next/navigation';
|
|
2
|
-
import { getMessages, getTranslations } from 'next-intl/server';
|
|
3
|
-
import { TranslateI18nInterface } from '@/base/cases/TranslateI18nInterface';
|
|
4
|
-
import { i18nConfig } from '@config/i18n';
|
|
5
|
-
import type { LocaleType, PageI18nInterface } from '@config/i18n';
|
|
6
|
-
import type { ParamsHandlerInterface as ParamsHandlerInterface } from './port/ParamsHandlerInterface';
|
|
7
|
-
|
|
8
|
-
export interface PageWithParams {
|
|
9
|
-
params?: Promise<PageParamsType>;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export interface PageParamsType {
|
|
13
|
-
locale?: string;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Handler Page Params
|
|
18
|
-
*/
|
|
19
|
-
export class PageParams implements ParamsHandlerInterface {
|
|
20
|
-
private locale: string | null;
|
|
21
|
-
|
|
22
|
-
constructor(protected readonly params: PageParamsType) {
|
|
23
|
-
this.locale = this.params.locale || i18nConfig.fallbackLng;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
public getLocale(defaultLocale?: string): string {
|
|
27
|
-
if (this.locale) {
|
|
28
|
-
return this.locale;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
this.locale = this.params.locale || defaultLocale || i18nConfig.fallbackLng;
|
|
32
|
-
|
|
33
|
-
return this.locale;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
public getI18nWithNotFound(): string {
|
|
37
|
-
const locale = this.getLocale();
|
|
38
|
-
|
|
39
|
-
if (!i18nConfig.supportedLngs.includes(locale as LocaleType)) {
|
|
40
|
-
notFound();
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
return locale;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
public async getI18nMessages(): Promise<Record<string, string>> {
|
|
47
|
-
const locale = this.getLocale();
|
|
48
|
-
|
|
49
|
-
const messages = await getMessages({ locale });
|
|
50
|
-
|
|
51
|
-
return messages;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
public async getI18nInterface<T extends PageI18nInterface>(
|
|
55
|
-
i18nInterface: T,
|
|
56
|
-
namespace?: string
|
|
57
|
-
): Promise<T> {
|
|
58
|
-
// Load translation messages from the HomePage namespace
|
|
59
|
-
const t = await getTranslations({
|
|
60
|
-
locale: this.getLocale(),
|
|
61
|
-
namespace: namespace
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
return TranslateI18nInterface.translate<T>(i18nInterface, t);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { clsx } from 'clsx';
|
|
4
|
-
import { useLocale } from 'next-intl';
|
|
5
|
-
import { useMemo } from 'react';
|
|
6
|
-
import { useIOC } from '@/uikit/hook/useIOC';
|
|
7
|
-
import { COMMON_ADMIN_TITLE } from '@config/Identifier';
|
|
8
|
-
import { IOCIdentifier } from '@config/IOCIdentifier';
|
|
9
|
-
import { LocaleLink } from './LocaleLink';
|
|
10
|
-
import { useWarnTranslations } from '../hook/useWarnTranslations';
|
|
11
|
-
|
|
12
|
-
export type RenderLeftFunction = (props: {
|
|
13
|
-
locale: string;
|
|
14
|
-
defaultElement: React.ReactNode;
|
|
15
|
-
}) => React.ReactNode;
|
|
16
|
-
|
|
17
|
-
export function BaseHeader(props: {
|
|
18
|
-
href?: string;
|
|
19
|
-
renderLeft?: React.ReactNode | RenderLeftFunction;
|
|
20
|
-
rightActions?: React.ReactNode;
|
|
21
|
-
className?: string;
|
|
22
|
-
}) {
|
|
23
|
-
const { href = '/', className, renderLeft, rightActions } = props;
|
|
24
|
-
const appConfig = useIOC(IOCIdentifier.AppConfig);
|
|
25
|
-
const locale = useLocale();
|
|
26
|
-
const t = useWarnTranslations();
|
|
27
|
-
|
|
28
|
-
const tt = {
|
|
29
|
-
title: appConfig.appName,
|
|
30
|
-
admin: t(COMMON_ADMIN_TITLE)
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
const leftDefault = useMemo(
|
|
34
|
-
() => (
|
|
35
|
-
<LocaleLink
|
|
36
|
-
data-testid="BaseHeaderLogo"
|
|
37
|
-
title={tt.title}
|
|
38
|
-
href={href}
|
|
39
|
-
locale={locale}
|
|
40
|
-
className="flex items-center hover:opacity-80 transition-opacity"
|
|
41
|
-
>
|
|
42
|
-
<span
|
|
43
|
-
data-testid="base-header-app-name"
|
|
44
|
-
className="text-lg font-semibold text-text"
|
|
45
|
-
>
|
|
46
|
-
{tt.title}
|
|
47
|
-
</span>
|
|
48
|
-
</LocaleLink>
|
|
49
|
-
),
|
|
50
|
-
[href, locale, tt.title]
|
|
51
|
-
);
|
|
52
|
-
|
|
53
|
-
const RenderLeft = useMemo(() => {
|
|
54
|
-
if (!renderLeft) {
|
|
55
|
-
return leftDefault;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
if (typeof renderLeft === 'function') {
|
|
59
|
-
return renderLeft({ locale, defaultElement: leftDefault });
|
|
60
|
-
}
|
|
61
|
-
return renderLeft;
|
|
62
|
-
}, [renderLeft, locale, leftDefault]);
|
|
63
|
-
|
|
64
|
-
return (
|
|
65
|
-
<header
|
|
66
|
-
data-testid="BaseHeader"
|
|
67
|
-
className="h-14 bg-secondary border-b border-c-border sticky top-0 z-50"
|
|
68
|
-
>
|
|
69
|
-
<div
|
|
70
|
-
className={clsx(
|
|
71
|
-
'flex items-center justify-between h-full px-4 mx-auto max-w-7xl',
|
|
72
|
-
className
|
|
73
|
-
)}
|
|
74
|
-
>
|
|
75
|
-
<div className="flex items-center">{RenderLeft}</div>
|
|
76
|
-
<div className="flex items-center gap-2 md:gap-4">{rightActions}</div>
|
|
77
|
-
</div>
|
|
78
|
-
</header>
|
|
79
|
-
);
|
|
80
|
-
}
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import { TeamOutlined } from '@ant-design/icons';
|
|
2
|
-
import { useLocale } from 'next-intl';
|
|
3
|
-
import { useMemo, type HTMLAttributes } from 'react';
|
|
4
|
-
import { COMMON_ADMIN_TITLE } from '@config/Identifier';
|
|
5
|
-
import { BaseHeader } from './BaseHeader';
|
|
6
|
-
import { LanguageSwitcher } from './LanguageSwitcher';
|
|
7
|
-
import { LocaleLink } from './LocaleLink';
|
|
8
|
-
import { LogoutButton } from './LogoutButton';
|
|
9
|
-
import { ThemeSwitcher } from './ThemeSwitcher';
|
|
10
|
-
import { useWarnTranslations } from '../hook/useWarnTranslations';
|
|
11
|
-
|
|
12
|
-
export interface BaseLayoutProps extends HTMLAttributes<HTMLDivElement> {
|
|
13
|
-
showLogoutButton?: boolean;
|
|
14
|
-
showAdminButton?: boolean;
|
|
15
|
-
mainProps?: HTMLAttributes<HTMLElement>;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export function BaseLayout({
|
|
19
|
-
children,
|
|
20
|
-
showLogoutButton,
|
|
21
|
-
showAdminButton,
|
|
22
|
-
mainProps,
|
|
23
|
-
...props
|
|
24
|
-
}: BaseLayoutProps) {
|
|
25
|
-
const locale = useLocale();
|
|
26
|
-
const t = useWarnTranslations();
|
|
27
|
-
|
|
28
|
-
const tt = {
|
|
29
|
-
admin: t(COMMON_ADMIN_TITLE)
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
const actions = useMemo(
|
|
33
|
-
() =>
|
|
34
|
-
[
|
|
35
|
-
showAdminButton && (
|
|
36
|
-
<LocaleLink
|
|
37
|
-
key="admin-button"
|
|
38
|
-
href="/admin"
|
|
39
|
-
title={tt.admin}
|
|
40
|
-
locale={locale}
|
|
41
|
-
className="text-text hover:text-text-hover cursor-pointer text-lg transition-colors"
|
|
42
|
-
>
|
|
43
|
-
<TeamOutlined className="text-lg text-text" />
|
|
44
|
-
</LocaleLink>
|
|
45
|
-
),
|
|
46
|
-
<LanguageSwitcher key="language-switcher" />,
|
|
47
|
-
<ThemeSwitcher key="theme-switcher" />,
|
|
48
|
-
showLogoutButton && <LogoutButton key="logout-button" />
|
|
49
|
-
].filter(Boolean),
|
|
50
|
-
[showAdminButton, tt.admin, locale, showLogoutButton]
|
|
51
|
-
);
|
|
52
|
-
|
|
53
|
-
return (
|
|
54
|
-
<div
|
|
55
|
-
data-testid="BaseLayout"
|
|
56
|
-
className="flex flex-col min-h-screen"
|
|
57
|
-
{...props}
|
|
58
|
-
>
|
|
59
|
-
<BaseHeader rightActions={actions} />
|
|
60
|
-
<main className="flex flex-1 flex-col bg-primary" {...mainProps}>
|
|
61
|
-
{children}
|
|
62
|
-
</main>
|
|
63
|
-
</div>
|
|
64
|
-
);
|
|
65
|
-
}
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
import '@ant-design/v5-patch-for-react-19';
|
|
3
|
-
import { AntdRegistry } from '@ant-design/nextjs-registry';
|
|
4
|
-
import { AntdThemeProvider } from '@brain-toolkit/antd-theme-override/react';
|
|
5
|
-
import { useMountedClient } from '@brain-toolkit/react-kit';
|
|
6
|
-
import { ThemeProvider } from 'next-themes';
|
|
7
|
-
import { clientIOC } from '@/core/clientIoc/ClientIOC';
|
|
8
|
-
import { IOCIdentifier } from '@config/IOCIdentifier';
|
|
9
|
-
import type { CommonThemeConfig } from '@config/theme';
|
|
10
|
-
import { BootstrapsProvider } from './BootstrapsProvider';
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* CommonProvider is a provider for the common components
|
|
14
|
-
*
|
|
15
|
-
* - IOCProvider
|
|
16
|
-
* - BootstrapsProvider
|
|
17
|
-
* - ThemeProvider
|
|
18
|
-
* - AntdProvider
|
|
19
|
-
*
|
|
20
|
-
* @param param0
|
|
21
|
-
* @returns
|
|
22
|
-
*/
|
|
23
|
-
export function ComboProvider(props: {
|
|
24
|
-
themeConfig: CommonThemeConfig;
|
|
25
|
-
children: React.ReactNode;
|
|
26
|
-
}) {
|
|
27
|
-
/**
|
|
28
|
-
* useMountedClient 会等待客户端完全初始化
|
|
29
|
-
* 只有在客户端准备就绪后才渲染组件内容
|
|
30
|
-
* 这样可以确保所有的客户端代码(包括 API 配置、插件等)都已经正确初始化
|
|
31
|
-
*/
|
|
32
|
-
const mounted = useMountedClient();
|
|
33
|
-
|
|
34
|
-
const { themeConfig, children } = props;
|
|
35
|
-
|
|
36
|
-
const IOC = clientIOC.create();
|
|
37
|
-
|
|
38
|
-
return (
|
|
39
|
-
<AntdThemeProvider
|
|
40
|
-
data-testid="ComboProvider"
|
|
41
|
-
theme={themeConfig.antdTheme}
|
|
42
|
-
staticApi={IOC(IOCIdentifier.DialogHandler)}
|
|
43
|
-
>
|
|
44
|
-
<ThemeProvider
|
|
45
|
-
themes={themeConfig.supportedThemes as unknown as string[]}
|
|
46
|
-
attribute={themeConfig.domAttribute}
|
|
47
|
-
defaultTheme={themeConfig.defaultTheme}
|
|
48
|
-
enableSystem
|
|
49
|
-
enableColorScheme={false}
|
|
50
|
-
storageKey={themeConfig.storageKey}
|
|
51
|
-
>
|
|
52
|
-
<BootstrapsProvider>
|
|
53
|
-
<AntdRegistry>{mounted ? children : null}</AntdRegistry>
|
|
54
|
-
</BootstrapsProvider>
|
|
55
|
-
</ThemeProvider>
|
|
56
|
-
</AntdThemeProvider>
|
|
57
|
-
);
|
|
58
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { NextIntlClientProvider, useMessages } from 'next-intl';
|
|
2
|
-
import type { ReactNode } from 'react';
|
|
3
|
-
|
|
4
|
-
type Props = {
|
|
5
|
-
children: ReactNode;
|
|
6
|
-
locale: string;
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
export function NextIntlProvider({ children, locale }: Props) {
|
|
10
|
-
const messages = useMessages();
|
|
11
|
-
|
|
12
|
-
return (
|
|
13
|
-
<NextIntlClientProvider
|
|
14
|
-
data-testid="NextIntlProvider"
|
|
15
|
-
locale={locale}
|
|
16
|
-
messages={messages}
|
|
17
|
-
>
|
|
18
|
-
{children}
|
|
19
|
-
</NextIntlClientProvider>
|
|
20
|
-
);
|
|
21
|
-
}
|
|
File without changes
|
|
File without changes
|