@qlover/create-app 0.7.6 → 0.7.8

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.
Files changed (104) hide show
  1. package/CHANGELOG.md +328 -0
  2. package/dist/index.cjs +1 -1
  3. package/dist/index.js +1 -1
  4. package/dist/templates/next-app/.env.template +22 -0
  5. package/dist/templates/next-app/.prettierignore +58 -0
  6. package/dist/templates/next-app/README.md +36 -0
  7. package/dist/templates/next-app/build/generateLocales.ts +25 -0
  8. package/dist/templates/next-app/config/IOCIdentifier.ts +58 -0
  9. package/dist/templates/next-app/config/Identifier/common.error.ts +41 -0
  10. package/dist/templates/next-app/config/Identifier/common.ts +62 -0
  11. package/dist/templates/next-app/config/Identifier/index.ts +10 -0
  12. package/dist/templates/next-app/config/Identifier/page.about.ts +181 -0
  13. package/dist/templates/next-app/config/Identifier/page.executor.ts +272 -0
  14. package/dist/templates/next-app/config/Identifier/page.home.ts +63 -0
  15. package/dist/templates/next-app/config/Identifier/page.identifiter.ts +39 -0
  16. package/dist/templates/next-app/config/Identifier/page.jsonStorage.ts +72 -0
  17. package/dist/templates/next-app/config/Identifier/page.login.ts +165 -0
  18. package/dist/templates/next-app/config/Identifier/page.register.ts +147 -0
  19. package/dist/templates/next-app/config/Identifier/page.request.ts +182 -0
  20. package/dist/templates/next-app/config/common.ts +34 -0
  21. package/dist/templates/next-app/config/i18n/PageI18nInterface.ts +51 -0
  22. package/dist/templates/next-app/config/i18n/i18nConfig.ts +12 -0
  23. package/dist/templates/next-app/config/i18n/index.ts +3 -0
  24. package/dist/templates/next-app/config/i18n/loginI18n.ts +42 -0
  25. package/dist/templates/next-app/config/theme.ts +23 -0
  26. package/dist/templates/next-app/docs/env.md +94 -0
  27. package/dist/templates/next-app/eslint.config.mjs +181 -0
  28. package/dist/templates/next-app/next.config.ts +21 -0
  29. package/dist/templates/next-app/package.json +58 -0
  30. package/dist/templates/next-app/postcss.config.mjs +5 -0
  31. package/dist/templates/next-app/public/file.svg +1 -0
  32. package/dist/templates/next-app/public/globe.svg +1 -0
  33. package/dist/templates/next-app/public/locales/en.json +184 -0
  34. package/dist/templates/next-app/public/locales/zh.json +184 -0
  35. package/dist/templates/next-app/public/next.svg +1 -0
  36. package/dist/templates/next-app/public/vercel.svg +1 -0
  37. package/dist/templates/next-app/public/window.svg +1 -0
  38. package/dist/templates/next-app/src/app/[locale]/favicon.ico +0 -0
  39. package/dist/templates/next-app/src/app/[locale]/layout.tsx +32 -0
  40. package/dist/templates/next-app/src/app/[locale]/login/FeatureItem.tsx +13 -0
  41. package/dist/templates/next-app/src/app/[locale]/login/LoginForm.tsx +114 -0
  42. package/dist/templates/next-app/src/app/[locale]/login/page.tsx +86 -0
  43. package/dist/templates/next-app/src/app/[locale]/not-found.tsx +24 -0
  44. package/dist/templates/next-app/src/app/[locale]/page.tsx +119 -0
  45. package/dist/templates/next-app/src/base/cases/AppConfig.ts +15 -0
  46. package/dist/templates/next-app/src/base/cases/DialogHandler.ts +92 -0
  47. package/dist/templates/next-app/src/base/cases/InversifyContainer.ts +33 -0
  48. package/dist/templates/next-app/src/base/cases/NavigateBridge.ts +16 -0
  49. package/dist/templates/next-app/src/base/cases/PageParams.ts +74 -0
  50. package/dist/templates/next-app/src/base/cases/RouterService.ts +35 -0
  51. package/dist/templates/next-app/src/base/cases/ServerAuth.ts +17 -0
  52. package/dist/templates/next-app/src/base/cases/ServerErrorHandler.ts +27 -0
  53. package/dist/templates/next-app/src/base/port/I18nServiceInterface.ts +25 -0
  54. package/dist/templates/next-app/src/base/port/IOCInterface.ts +24 -0
  55. package/dist/templates/next-app/src/base/port/ParamsHandlerInterface.ts +11 -0
  56. package/dist/templates/next-app/src/base/port/RouterInterface.ts +11 -0
  57. package/dist/templates/next-app/src/base/port/ServerAuthInterface.ts +3 -0
  58. package/dist/templates/next-app/src/base/port/ServerInterface.ts +12 -0
  59. package/dist/templates/next-app/src/base/port/UserServiceInterface.ts +11 -0
  60. package/dist/templates/next-app/src/base/services/I18nService.ts +115 -0
  61. package/dist/templates/next-app/src/base/services/UserService.ts +23 -0
  62. package/dist/templates/next-app/src/base/types/PageProps.ts +9 -0
  63. package/dist/templates/next-app/src/core/bootstraps/BootstrapClient.ts +61 -0
  64. package/dist/templates/next-app/src/core/bootstraps/BootstrapServer.ts +78 -0
  65. package/dist/templates/next-app/src/core/bootstraps/BootstrapsRegistry.ts +47 -0
  66. package/dist/templates/next-app/src/core/bootstraps/IocIdentifierTest.ts +26 -0
  67. package/dist/templates/next-app/src/core/bootstraps/PrintBootstrap.ts +18 -0
  68. package/dist/templates/next-app/src/core/clientIoc/ClientIOC.ts +37 -0
  69. package/dist/templates/next-app/src/core/clientIoc/ClientIOCRegister.ts +97 -0
  70. package/dist/templates/next-app/src/core/globals.ts +24 -0
  71. package/dist/templates/next-app/src/core/serverIoc/ServerIOC.ts +52 -0
  72. package/dist/templates/next-app/src/core/serverIoc/ServerIOCRegister.ts +63 -0
  73. package/dist/templates/next-app/src/i18n/request.ts +21 -0
  74. package/dist/templates/next-app/src/i18n/routing.ts +30 -0
  75. package/dist/templates/next-app/src/middleware.ts +25 -0
  76. package/dist/templates/next-app/src/styles/css/antd-themes/_default.css +239 -0
  77. package/dist/templates/next-app/src/styles/css/antd-themes/dark.css +178 -0
  78. package/dist/templates/next-app/src/styles/css/antd-themes/index.css +3 -0
  79. package/dist/templates/next-app/src/styles/css/antd-themes/no-context.css +34 -0
  80. package/dist/templates/next-app/src/styles/css/antd-themes/pink.css +204 -0
  81. package/dist/templates/next-app/src/styles/css/index.css +6 -0
  82. package/dist/templates/next-app/src/styles/css/page.css +19 -0
  83. package/dist/templates/next-app/src/styles/css/tailwind.css +5 -0
  84. package/dist/templates/next-app/src/styles/css/themes/_default.css +29 -0
  85. package/dist/templates/next-app/src/styles/css/themes/dark.css +29 -0
  86. package/dist/templates/next-app/src/styles/css/themes/index.css +3 -0
  87. package/dist/templates/next-app/src/styles/css/themes/pink.css +29 -0
  88. package/dist/templates/next-app/src/styles/css/zIndex.css +9 -0
  89. package/dist/templates/next-app/src/uikit/components/BaseHeader.tsx +47 -0
  90. package/dist/templates/next-app/src/uikit/components/BootstrapsProvider.tsx +37 -0
  91. package/dist/templates/next-app/src/uikit/components/ComboProvider.tsx +50 -0
  92. package/dist/templates/next-app/src/uikit/components/LanguageSwitcher.tsx +52 -0
  93. package/dist/templates/next-app/src/uikit/components/LocaleLink.tsx +51 -0
  94. package/dist/templates/next-app/src/uikit/components/LogoutButton.tsx +34 -0
  95. package/dist/templates/next-app/src/uikit/components/NextIntlProvider.tsx +21 -0
  96. package/dist/templates/next-app/src/uikit/components/ThemeSwitcher.tsx +99 -0
  97. package/dist/templates/next-app/src/uikit/context/IOCContext.ts +13 -0
  98. package/dist/templates/next-app/src/uikit/hook/useI18nInterface.ts +28 -0
  99. package/dist/templates/next-app/src/uikit/hook/useIOC.ts +37 -0
  100. package/dist/templates/next-app/src/uikit/hook/useMountedClient.ts +11 -0
  101. package/dist/templates/next-app/src/uikit/hook/useStore.ts +15 -0
  102. package/dist/templates/next-app/tailwind.config.ts +8 -0
  103. package/dist/templates/next-app/tsconfig.json +36 -0
  104. package/package.json +1 -1
@@ -0,0 +1,92 @@
1
+ import type {
2
+ AntdStaticApiInterface,
3
+ MessageApi,
4
+ ModalApi,
5
+ NotificationApi
6
+ } from '@brain-toolkit/antd-theme-override/react';
7
+ import type {
8
+ UIDialogInterface,
9
+ NotificationOptions
10
+ } from '@qlover/corekit-bridge';
11
+ import type { ModalFuncProps } from 'antd';
12
+
13
+ export interface DialogHandlerOptions
14
+ extends NotificationOptions,
15
+ ModalFuncProps {
16
+ content: string;
17
+ }
18
+
19
+ /**
20
+ * Dialog Handler Implementation
21
+ *
22
+ * Implements the InteractionHubInterface using Ant Design components.
23
+ * Provides concrete implementations for displaying notifications and confirmation dialogs.
24
+ *
25
+ * Features:
26
+ * - Uses Ant Design's message component for notifications
27
+ * - Uses Ant Design's Modal component for confirmations
28
+ * - Supports customizable display durations
29
+ * - Handles error objects appropriately
30
+ *
31
+ * @example
32
+ * const dialog = new DialogHandler();
33
+ * dialog.success('Data saved successfully');
34
+ * dialog.confirm({
35
+ * title: 'Confirm Delete',
36
+ * content: 'Are you sure you want to delete this item?',
37
+ * onOk: () => handleDelete(),
38
+ * });
39
+ */
40
+ export class DialogHandler
41
+ implements UIDialogInterface<DialogHandlerOptions>, AntdStaticApiInterface
42
+ {
43
+ protected antds: {
44
+ message?: MessageApi;
45
+ modal?: ModalApi;
46
+ notification?: NotificationApi;
47
+ } = {};
48
+
49
+ setMessage(message: MessageApi): void {
50
+ this.antds.message = message;
51
+ }
52
+
53
+ setModal(modal: ModalApi): void {
54
+ this.antds.modal = modal;
55
+ }
56
+
57
+ setNotification(notification: NotificationApi): void {
58
+ this.antds.notification = notification;
59
+ }
60
+
61
+ /**
62
+ * Formats error message from various error types
63
+ */
64
+ protected formatErrorMessage(error: unknown): string {
65
+ if (error instanceof Error) return error.message;
66
+ if (typeof error === 'string') return error;
67
+ return 'An unknown error occurred';
68
+ }
69
+
70
+ public success(msg: string, options?: NotificationOptions): void {
71
+ this.antds.message?.success({ content: msg, ...options });
72
+ }
73
+
74
+ public error(msg: string, options?: NotificationOptions): void {
75
+ this.antds.message?.error({
76
+ content: options?.error ? this.formatErrorMessage(options.error) : msg,
77
+ ...options
78
+ });
79
+ }
80
+
81
+ public info(msg: string, options?: NotificationOptions): void {
82
+ this.antds.message?.info({ content: msg, ...options });
83
+ }
84
+
85
+ public warn(msg: string, options?: NotificationOptions): void {
86
+ this.antds.message?.warning({ content: msg, ...options });
87
+ }
88
+
89
+ public confirm(options: DialogHandlerOptions): void {
90
+ this.antds.modal?.confirm(options);
91
+ }
92
+ }
@@ -0,0 +1,33 @@
1
+ import { Container } from 'inversify';
2
+ import type { IOCIdentifierMap } from '@config/IOCIdentifier';
3
+ import type {
4
+ IOCContainerInterface,
5
+ ServiceIdentifier
6
+ } from '@qlover/corekit-bridge';
7
+
8
+ export class InversifyContainer implements IOCContainerInterface {
9
+ protected container: Container;
10
+
11
+ constructor() {
12
+ this.container = new Container({
13
+ // allow `@injectable` decorator, auto bind injectable classes
14
+ autobind: true,
15
+ // use singleton scope
16
+ defaultScope: 'Singleton'
17
+ });
18
+ }
19
+
20
+ bind<T>(key: ServiceIdentifier<T>, value: T): void {
21
+ this.container.bind<T>(key).toConstantValue(value);
22
+ }
23
+
24
+ get<K extends keyof IOCIdentifierMap>(
25
+ serviceIdentifier: K
26
+ ): IOCIdentifierMap[K];
27
+ get<T>(serviceIdentifier: ServiceIdentifier<T>): T;
28
+ get<T, K extends keyof IOCIdentifierMap>(
29
+ serviceIdentifier: ServiceIdentifier<T> | K
30
+ ): T | IOCIdentifierMap[K] {
31
+ return this.container.get<T>(serviceIdentifier);
32
+ }
33
+ }
@@ -0,0 +1,16 @@
1
+ import { injectable } from 'inversify';
2
+ import type { UIBridgeInterface } from '@qlover/corekit-bridge';
3
+ import type { AppRouterInstance } from 'next/dist/shared/lib/app-router-context.shared-runtime';
4
+
5
+ @injectable()
6
+ export class NavigateBridge implements UIBridgeInterface<AppRouterInstance> {
7
+ protected navigate: AppRouterInstance | null = null;
8
+
9
+ setUIBridge(ui: AppRouterInstance): void {
10
+ this.navigate = ui;
11
+ }
12
+
13
+ getUIBridge(): AppRouterInstance | null {
14
+ return this.navigate;
15
+ }
16
+ }
@@ -0,0 +1,74 @@
1
+ import { notFound } from 'next/navigation';
2
+ import { getMessages, getTranslations } from 'next-intl/server';
3
+ import { i18nConfig } from '@config/i18n';
4
+ import type { ParamsHandlerInterface as ParamsHandlerInterface } from '../port/ParamsHandlerInterface';
5
+ import type { LocaleType, PageI18nInterface } from '@config/i18n';
6
+
7
+ export interface PageWithParams {
8
+ params?: Promise<PageParamsType>;
9
+ }
10
+
11
+ export interface PageParamsType {
12
+ locale?: string;
13
+ }
14
+
15
+ /**
16
+ * Handler Page Params
17
+ */
18
+ export class PageParams implements ParamsHandlerInterface {
19
+ private locale: string | null;
20
+
21
+ constructor(protected readonly params: PageParamsType) {
22
+ this.locale = this.params.locale || i18nConfig.fallbackLng;
23
+ }
24
+
25
+ public getLocale(defaultLocale?: string): string {
26
+ if (this.locale) {
27
+ return this.locale;
28
+ }
29
+
30
+ this.locale = this.params.locale || defaultLocale || i18nConfig.fallbackLng;
31
+
32
+ return this.locale;
33
+ }
34
+
35
+ public getI18nWithNotFound(): string {
36
+ const locale = this.getLocale();
37
+
38
+ if (!i18nConfig.supportedLngs.includes(locale as LocaleType)) {
39
+ notFound();
40
+ }
41
+
42
+ return locale;
43
+ }
44
+
45
+ public async getI18nMessages(): Promise<Record<string, string>> {
46
+ const locale = this.getLocale();
47
+
48
+ const messages = await getMessages({ locale });
49
+
50
+ return messages;
51
+ }
52
+
53
+ public async getI18nInterface<T extends PageI18nInterface>(
54
+ i18nInterface: T,
55
+ namespace?: string
56
+ ): Promise<T> {
57
+ // Load translation messages from the HomePage namespace
58
+ const t = await getTranslations({
59
+ locale: this.getLocale(),
60
+ namespace: namespace
61
+ });
62
+
63
+ const result = Object.fromEntries(
64
+ Object.entries(i18nInterface).map(([key, value]) => {
65
+ if (typeof value === 'string') {
66
+ return [key, t(value)];
67
+ }
68
+ return [key, value];
69
+ })
70
+ ) as T;
71
+
72
+ return result;
73
+ }
74
+ }
@@ -0,0 +1,35 @@
1
+ import { inject, injectable } from 'inversify';
2
+ import { NavigateBridge } from './NavigateBridge';
3
+ import type { RouterInterface, RouterPathname } from '../port/RouterInterface';
4
+ import type { UIBridgeInterface } from '@qlover/corekit-bridge';
5
+ import type { AppRouterInstance } from 'next/dist/shared/lib/app-router-context.shared-runtime';
6
+
7
+ @injectable()
8
+ export class RouterService implements RouterInterface {
9
+ protected locale: string = '';
10
+
11
+ constructor(
12
+ @inject(NavigateBridge)
13
+ protected uiBridge: UIBridgeInterface<AppRouterInstance>
14
+ ) {}
15
+
16
+ protected padLocaleHref(href: RouterPathname): string {
17
+ return `/${this.locale}${href === '/' ? '' : href}`;
18
+ }
19
+
20
+ goto(href: RouterPathname): void {
21
+ this.uiBridge.getUIBridge()?.push(this.padLocaleHref(href));
22
+ }
23
+
24
+ gotoHome(): void {
25
+ this.goto('/');
26
+ }
27
+
28
+ setLocale(locale: string): void {
29
+ this.locale = locale;
30
+ }
31
+
32
+ getLocale(): string {
33
+ return this.locale;
34
+ }
35
+ }
@@ -0,0 +1,17 @@
1
+ import { cookies } from 'next/headers';
2
+ import { I } from '@config/IOCIdentifier';
3
+ import type { ServerAuthInterface } from '../port/ServerAuthInterface';
4
+ import type { ServerInterface } from '../port/ServerInterface';
5
+
6
+ export class ServerAuth implements ServerAuthInterface {
7
+ constructor(protected server: ServerInterface) {}
8
+
9
+ async hasAuth(): Promise<boolean> {
10
+ const cookieStore = await cookies();
11
+ const appConfig = this.server.getIOC(I.AppConfig);
12
+
13
+ const token = cookieStore.get(appConfig.userTokenKey);
14
+
15
+ return !!token;
16
+ }
17
+ }
@@ -0,0 +1,27 @@
1
+ import { ExecutorError, type ExecutorContext } from '@qlover/fe-corekit';
2
+ import { SERVER_AUTH_ERROR } from '@config/Identifier';
3
+ import type { BootstrapServerContextValue } from '@/core/bootstraps/BootstrapServer';
4
+ import type { BootstrapExecutorPlugin } from '@qlover/corekit-bridge';
5
+
6
+ export class ServerErrorHandler implements BootstrapExecutorPlugin {
7
+ pluginName = 'ServerErrorHandler';
8
+
9
+ onError(
10
+ context: ExecutorContext<BootstrapServerContextValue>
11
+ ): ExecutorError | void {
12
+ const { parameters, error } = context;
13
+ const { messages } = parameters;
14
+
15
+ if (error instanceof Error) {
16
+ const messageId = error.message;
17
+
18
+ if (messageId === SERVER_AUTH_ERROR) {
19
+ const message = messages[messageId];
20
+
21
+ if (message) {
22
+ return new ExecutorError(messageId, message);
23
+ }
24
+ }
25
+ }
26
+ }
27
+ }
@@ -0,0 +1,25 @@
1
+ import {
2
+ StoreInterface,
3
+ type StoreStateInterface
4
+ } from '@qlover/corekit-bridge';
5
+ import type { i18nConfig } from '@config/i18n';
6
+
7
+ export type SupportedLocale = (typeof i18nConfig.supportedLngs)[number];
8
+ export type SupportedNamespace = typeof i18nConfig.fallbackLng;
9
+ export type I18nServiceLocale = SupportedLocale;
10
+
11
+ export class I18nServiceState implements StoreStateInterface {
12
+ loading: boolean = false;
13
+ constructor(public language: I18nServiceLocale) {}
14
+ }
15
+ export abstract class I18nServiceInterface
16
+ extends StoreInterface<I18nServiceState>
17
+ implements I18nServiceInterface
18
+ {
19
+ abstract t(key: string, params?: Record<string, unknown>): Promise<string>;
20
+ abstract changeLanguage(language: I18nServiceLocale): Promise<void>;
21
+ abstract changeLoading(loading: boolean): void;
22
+ abstract getCurrentLanguage(): Promise<I18nServiceLocale>;
23
+ abstract isValidLanguage(language: string): language is I18nServiceLocale;
24
+ abstract getSupportedLanguages(): I18nServiceLocale[];
25
+ }
@@ -0,0 +1,24 @@
1
+ import type {
2
+ EnvConfigInterface,
3
+ IOCContainerInterface,
4
+ IOCFunctionInterface
5
+ } from '@qlover/corekit-bridge';
6
+
7
+ /**
8
+ * IOC register options
9
+ */
10
+ export type IocRegisterOptions = {
11
+ /**
12
+ * The app config
13
+ */
14
+ appConfig: EnvConfigInterface;
15
+ };
16
+
17
+ export interface IOCInterface<
18
+ IdentifierMap,
19
+ IOCContainer extends IOCContainerInterface
20
+ > {
21
+ create(
22
+ options: IocRegisterOptions
23
+ ): IOCFunctionInterface<IdentifierMap, IOCContainer>;
24
+ }
@@ -0,0 +1,11 @@
1
+ import type { PageI18nInterface } from '@config/i18n';
2
+
3
+ export interface ParamsHandlerInterface {
4
+ getLocale(defaultLocale?: string): string;
5
+ getI18nWithNotFound(): string;
6
+ getI18nMessages(): Promise<Record<string, string>>;
7
+ getI18nInterface<T extends PageI18nInterface>(
8
+ i18nInterface: T,
9
+ namespace?: string
10
+ ): Promise<T>;
11
+ }
@@ -0,0 +1,11 @@
1
+ import type { redirect } from '@/i18n/routing';
2
+ export type RouterPathname = Parameters<typeof redirect>[0]['href'];
3
+
4
+ export interface RouterInterface {
5
+ gotoHome(): void;
6
+ goto(href: RouterPathname): void;
7
+
8
+ getLocale(): string;
9
+
10
+ setLocale(locale: string): void;
11
+ }
@@ -0,0 +1,3 @@
1
+ export interface ServerAuthInterface {
2
+ hasAuth(): Promise<boolean>;
3
+ }
@@ -0,0 +1,12 @@
1
+ import {
2
+ type IOCContainerInterface,
3
+ type IOCFunctionInterface
4
+ } from '@qlover/corekit-bridge';
5
+ import { type IOCIdentifierMapServer } from '@config/IOCIdentifier';
6
+
7
+ export interface ServerInterface {
8
+ getIOC(): IOCFunctionInterface<IOCIdentifierMapServer, IOCContainerInterface>;
9
+ getIOC<T extends keyof IOCIdentifierMapServer>(
10
+ identifier: T
11
+ ): IOCIdentifierMapServer[T];
12
+ }
@@ -0,0 +1,11 @@
1
+ import { ImagicaAuthService } from '@brain-toolkit/bridge';
2
+ import type { ExecutorPlugin } from '@qlover/fe-corekit';
3
+
4
+ export abstract class UserServiceInterface
5
+ extends ImagicaAuthService
6
+ implements ExecutorPlugin
7
+ {
8
+ readonly pluginName = 'UserService';
9
+
10
+ abstract getToken(): string | null;
11
+ }
@@ -0,0 +1,115 @@
1
+ import { useLocaleRoutes } from '@config/common';
2
+ import { i18nConfig } from '@config/i18n';
3
+ import {
4
+ I18nServiceInterface,
5
+ I18nServiceState
6
+ } from '../port/I18nServiceInterface';
7
+ import type { I18nServiceLocale } from '../port/I18nServiceInterface';
8
+ import type { useTranslations } from 'next-intl';
9
+
10
+ type TranslationFunction = ReturnType<typeof useTranslations>;
11
+
12
+ export class I18nService extends I18nServiceInterface {
13
+ readonly pluginName = 'I18nService';
14
+ private initialized: boolean = false;
15
+ private pathname: string = '';
16
+ private translator: TranslationFunction | null = null;
17
+
18
+ constructor() {
19
+ super(() => new I18nServiceState(i18nConfig.fallbackLng));
20
+ }
21
+
22
+ private async ensureInitialized(): Promise<void> {
23
+ if (!this.initialized) {
24
+ throw new Error('I18nService not initialized');
25
+ }
26
+ }
27
+
28
+ setPathname(pathname: string): void {
29
+ this.pathname = pathname;
30
+ }
31
+
32
+ setTranslator(translator: TranslationFunction): void {
33
+ this.translator = translator;
34
+ }
35
+
36
+ /**
37
+ * @override
38
+ */
39
+ async onBefore(): Promise<void> {
40
+ if (this.initialized) {
41
+ return;
42
+ }
43
+
44
+ this.initialized = true;
45
+
46
+ // 初始化语言状态
47
+ const currentLang = this.getCurrentLanguageFromPath();
48
+ if (this.isValidLanguage(currentLang)) {
49
+ this.emit({ ...this.state, language: currentLang });
50
+ }
51
+ }
52
+
53
+ private getCurrentLanguageFromPath(): string {
54
+ const paths = this.pathname.split('/');
55
+
56
+ for (const path of paths) {
57
+ if (this.isValidLanguage(path)) {
58
+ return path;
59
+ }
60
+ }
61
+
62
+ return i18nConfig.fallbackLng;
63
+ }
64
+
65
+ override async changeLanguage(language: I18nServiceLocale): Promise<void> {
66
+ try {
67
+ this.changeLoading(true);
68
+ await this.ensureInitialized();
69
+
70
+ // 在这里我们只需要更新状态,因为实际的语言切换会通过路由处理
71
+ this.emit({ ...this.state, language });
72
+
73
+ // 如果不使用本地化路由,则保存语言设置到本地存储
74
+ if (!useLocaleRoutes && typeof window !== 'undefined') {
75
+ window.localStorage.setItem('i18nextLng', language);
76
+ }
77
+ } finally {
78
+ this.changeLoading(false);
79
+ }
80
+ }
81
+
82
+ override changeLoading(loading: boolean): void {
83
+ this.emit({ ...this.state, loading });
84
+ }
85
+
86
+ override async getCurrentLanguage(): Promise<I18nServiceLocale> {
87
+ await this.ensureInitialized();
88
+ return this.getCurrentLanguageFromPath() as I18nServiceLocale;
89
+ }
90
+
91
+ override isValidLanguage(language: string): language is I18nServiceLocale {
92
+ return i18nConfig.supportedLngs.includes(language as I18nServiceLocale);
93
+ }
94
+
95
+ override getSupportedLanguages(): I18nServiceLocale[] {
96
+ return [...i18nConfig.supportedLngs];
97
+ }
98
+
99
+ override async t(
100
+ key: string,
101
+ params?: Record<string, string | number | Date>
102
+ ): Promise<string> {
103
+ await this.ensureInitialized();
104
+
105
+ if (!this.translator) {
106
+ return key;
107
+ }
108
+
109
+ try {
110
+ return this.translator(key, params);
111
+ } catch {
112
+ return key;
113
+ }
114
+ }
115
+ }
@@ -0,0 +1,23 @@
1
+ import { CookieStorage } from '@qlover/corekit-bridge';
2
+ import { injectable, inject } from 'inversify';
3
+ import { AppConfig } from '../cases/AppConfig';
4
+ import { UserServiceInterface } from '../port/UserServiceInterface';
5
+
6
+ @injectable()
7
+ export class UserService extends UserServiceInterface {
8
+ constructor(@inject(AppConfig) protected appConfig: AppConfig) {
9
+ super({
10
+ credentialStorage: {
11
+ key: appConfig.userTokenKey,
12
+ storage: new CookieStorage()
13
+ },
14
+ requestConfig: {
15
+ env: appConfig.env !== 'production' ? 'development' : 'production'
16
+ }
17
+ });
18
+ }
19
+
20
+ getToken(): string | null {
21
+ return this.store.getCredential();
22
+ }
23
+ }
@@ -0,0 +1,9 @@
1
+ import type { PageWithParams } from '@/base/cases/PageParams';
2
+
3
+ export interface PageParamsProps extends PageWithParams {
4
+ searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
5
+ }
6
+
7
+ export interface PageLayoutProps extends PageWithParams {
8
+ children: React.ReactNode;
9
+ }
@@ -0,0 +1,61 @@
1
+ import { Bootstrap } from '@qlover/corekit-bridge';
2
+ import { isObject } from 'lodash';
3
+ import { browserGlobalsName } from '@config/common';
4
+ import { BootstrapsRegistry } from './BootstrapsRegistry';
5
+ import * as globals from '../globals';
6
+ import type { IOCIdentifierMap } from '@config/IOCIdentifier';
7
+ import type {
8
+ IOCContainerInterface,
9
+ IOCFunctionInterface
10
+ } from '@qlover/corekit-bridge';
11
+
12
+ export type BootstrapAppArgs = {
13
+ /**
14
+ * 启动的根节点,通常是window
15
+ */
16
+ root: unknown;
17
+ /**
18
+ * 当前路径
19
+ */
20
+ pathname: string;
21
+ /**
22
+ * IOC容器
23
+ */
24
+ IOC: IOCFunctionInterface<IOCIdentifierMap, IOCContainerInterface>;
25
+ };
26
+
27
+ export class BootstrapClient {
28
+ static async main(args: BootstrapAppArgs): Promise<BootstrapAppArgs> {
29
+ const { root, IOC } = args;
30
+
31
+ if (!isObject(root)) {
32
+ throw new Error('root is not an object');
33
+ }
34
+
35
+ const { logger, appConfig } = globals;
36
+
37
+ const bootstrap = new Bootstrap({
38
+ root,
39
+ logger,
40
+ ioc: {
41
+ manager: IOC
42
+ },
43
+ globalOptions: {
44
+ sources: globals,
45
+ target: browserGlobalsName
46
+ }
47
+ });
48
+
49
+ try {
50
+ await bootstrap.initialize();
51
+
52
+ const bootstrapsRegistry = new BootstrapsRegistry(args);
53
+
54
+ await bootstrap.use(bootstrapsRegistry.register()).start();
55
+ } catch (error) {
56
+ logger.error(`${appConfig.appName} starup error:`, error);
57
+ }
58
+
59
+ return args;
60
+ }
61
+ }