@qlover/create-app 0.7.5 → 0.7.7

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 (166) hide show
  1. package/CHANGELOG.md +257 -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 +45 -0
  9. package/dist/templates/next-app/config/Identifier/common.error.ts +34 -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/plugins/eslint-plugin-testid.mjs +94 -0
  31. package/dist/templates/next-app/plugins/generateLocalesPlugin.ts +33 -0
  32. package/dist/templates/next-app/postcss.config.mjs +5 -0
  33. package/dist/templates/next-app/public/file.svg +1 -0
  34. package/dist/templates/next-app/public/globe.svg +1 -0
  35. package/dist/templates/next-app/public/locales/en/common.json +183 -0
  36. package/dist/templates/next-app/public/locales/zh/common.json +183 -0
  37. package/dist/templates/next-app/public/next.svg +1 -0
  38. package/dist/templates/next-app/public/vercel.svg +1 -0
  39. package/dist/templates/next-app/public/window.svg +1 -0
  40. package/dist/templates/next-app/src/app/[locale]/favicon.ico +0 -0
  41. package/dist/templates/next-app/src/app/[locale]/layout.tsx +44 -0
  42. package/dist/templates/next-app/src/app/[locale]/login/FeatureItem.tsx +13 -0
  43. package/dist/templates/next-app/src/app/[locale]/login/LoginForm.tsx +115 -0
  44. package/dist/templates/next-app/src/app/[locale]/login/page.tsx +73 -0
  45. package/dist/templates/next-app/src/app/[locale]/not-found.tsx +24 -0
  46. package/dist/templates/next-app/src/app/[locale]/page.tsx +106 -0
  47. package/dist/templates/next-app/src/base/cases/AppConfig.ts +15 -0
  48. package/dist/templates/next-app/src/base/cases/InversifyContainer.ts +33 -0
  49. package/dist/templates/next-app/src/base/port/I18nServiceInterface.ts +25 -0
  50. package/dist/templates/next-app/src/base/port/UserServiceInterface.ts +11 -0
  51. package/dist/templates/next-app/src/base/services/I18nService.ts +115 -0
  52. package/dist/templates/next-app/src/base/services/UserService.ts +23 -0
  53. package/dist/templates/next-app/src/core/IOC.ts +58 -0
  54. package/dist/templates/next-app/src/core/IocRegisterImpl.ts +100 -0
  55. package/dist/templates/next-app/src/core/bootstraps/BootstrapClient.ts +98 -0
  56. package/dist/templates/next-app/src/core/bootstraps/BootstrapsRegistry.ts +47 -0
  57. package/dist/templates/next-app/src/core/bootstraps/IocIdentifierTest.ts +26 -0
  58. package/dist/templates/next-app/src/core/bootstraps/PrintBootstrap.ts +18 -0
  59. package/dist/templates/next-app/src/core/globals.ts +21 -0
  60. package/dist/templates/next-app/src/i18n/request.ts +22 -0
  61. package/dist/templates/next-app/src/i18n/routing.ts +30 -0
  62. package/dist/templates/next-app/src/middleware.ts +22 -0
  63. package/dist/templates/next-app/src/server/getServerI18n.ts +26 -0
  64. package/dist/templates/next-app/src/styles/css/antd-themes/_default.css +239 -0
  65. package/dist/templates/next-app/src/styles/css/antd-themes/dark.css +178 -0
  66. package/dist/templates/next-app/src/styles/css/antd-themes/index.css +3 -0
  67. package/dist/templates/next-app/src/styles/css/antd-themes/no-context.css +34 -0
  68. package/dist/templates/next-app/src/styles/css/antd-themes/pink.css +204 -0
  69. package/dist/templates/next-app/src/styles/css/index.css +6 -0
  70. package/dist/templates/next-app/src/styles/css/page.css +19 -0
  71. package/dist/templates/next-app/src/styles/css/tailwind.css +5 -0
  72. package/dist/templates/next-app/src/styles/css/themes/_default.css +29 -0
  73. package/dist/templates/next-app/src/styles/css/themes/dark.css +29 -0
  74. package/dist/templates/next-app/src/styles/css/themes/index.css +3 -0
  75. package/dist/templates/next-app/src/styles/css/themes/pink.css +29 -0
  76. package/dist/templates/next-app/src/styles/css/zIndex.css +9 -0
  77. package/dist/templates/next-app/src/uikit/components/BaseHeader.tsx +42 -0
  78. package/dist/templates/next-app/src/uikit/components/BootstrapsProvider.tsx +25 -0
  79. package/dist/templates/next-app/src/uikit/components/ComboProvider.tsx +45 -0
  80. package/dist/templates/next-app/src/uikit/components/LanguageSwitcher.tsx +52 -0
  81. package/dist/templates/next-app/src/uikit/components/LocaleLink.tsx +51 -0
  82. package/dist/templates/next-app/src/uikit/components/NextIntlProvider.tsx +21 -0
  83. package/dist/templates/next-app/src/uikit/components/ThemeSwitcher.tsx +86 -0
  84. package/dist/templates/next-app/src/uikit/context/IOCContext.ts +6 -0
  85. package/dist/templates/next-app/src/uikit/hook/useI18nInterface.ts +28 -0
  86. package/dist/templates/next-app/src/uikit/hook/useIOC.ts +37 -0
  87. package/dist/templates/next-app/src/uikit/hook/useMountedClient.ts +11 -0
  88. package/dist/templates/next-app/src/uikit/hook/useStore.ts +15 -0
  89. package/dist/templates/next-app/tailwind.config.ts +8 -0
  90. package/dist/templates/next-app/tsconfig.json +36 -0
  91. package/dist/templates/react-app/.env.template +0 -2
  92. package/dist/templates/react-app/__tests__/src/base/services/I18nService.test.ts +1 -1
  93. package/dist/templates/react-app/__tests__/src/core/IOC.test.ts +6 -31
  94. package/dist/templates/react-app/__tests__/src/core/bootstraps/BootstrapsApp.test.ts +1 -1
  95. package/dist/templates/react-app/config/IOCIdentifier.ts +77 -5
  96. package/dist/templates/react-app/config/app.router.ts +2 -2
  97. package/dist/templates/react-app/package.json +4 -7
  98. package/dist/templates/react-app/public/locales/en/common.json +1 -1
  99. package/dist/templates/react-app/public/locales/zh/common.json +1 -1
  100. package/dist/templates/react-app/src/App.tsx +9 -4
  101. package/dist/templates/react-app/src/base/apis/userApi/UserApi.ts +1 -1
  102. package/dist/templates/react-app/src/base/apis/userApi/UserApiBootstarp.ts +4 -0
  103. package/dist/templates/react-app/src/base/cases/DialogHandler.ts +16 -13
  104. package/dist/templates/react-app/src/base/cases/I18nKeyErrorPlugin.ts +4 -3
  105. package/dist/templates/react-app/src/base/cases/InversifyContainer.ts +2 -2
  106. package/dist/templates/react-app/src/base/cases/RequestLanguages.ts +39 -0
  107. package/dist/templates/react-app/src/base/cases/RequestState.ts +20 -0
  108. package/dist/templates/react-app/src/base/cases/RequestStatusCatcher.ts +2 -2
  109. package/dist/templates/react-app/src/base/cases/RouterLoader.ts +8 -2
  110. package/dist/templates/react-app/src/base/port/AsyncStateInterface.ts +7 -0
  111. package/dist/templates/react-app/src/base/port/ExecutorPageBridgeInterface.ts +24 -0
  112. package/dist/templates/react-app/src/base/port/I18nServiceInterface.ts +10 -0
  113. package/dist/templates/react-app/src/base/port/JSONStoragePageBridgeInterface.ts +20 -0
  114. package/dist/templates/react-app/src/base/port/ProcesserExecutorInterface.ts +20 -0
  115. package/dist/templates/react-app/src/base/port/RequestPageBridgeInterface.ts +23 -0
  116. package/dist/templates/react-app/src/base/port/RequestStatusInterface.ts +5 -0
  117. package/dist/templates/react-app/src/base/port/RouteServiceInterface.ts +27 -0
  118. package/dist/templates/react-app/src/base/port/UserServiceInterface.ts +12 -0
  119. package/dist/templates/react-app/src/base/services/I18nService.ts +10 -6
  120. package/dist/templates/react-app/src/base/services/ProcesserExecutor.ts +23 -5
  121. package/dist/templates/react-app/src/base/services/RouteService.ts +25 -54
  122. package/dist/templates/react-app/src/base/services/UserService.ts +10 -20
  123. package/dist/templates/react-app/src/core/IOC.ts +1 -26
  124. package/dist/templates/react-app/src/core/IocRegisterImpl.ts +125 -0
  125. package/dist/templates/react-app/src/core/bootstraps/BootstrapApp.ts +4 -6
  126. package/dist/templates/react-app/src/core/bootstraps/BootstrapsRegistry.ts +8 -6
  127. package/dist/templates/react-app/src/core/bootstraps/IocIdentifierTest.ts +26 -0
  128. package/dist/templates/react-app/src/pages/auth/Layout.tsx +2 -2
  129. package/dist/templates/react-app/src/pages/auth/LoginPage.tsx +5 -6
  130. package/dist/templates/react-app/src/pages/auth/RegisterPage.tsx +8 -7
  131. package/dist/templates/react-app/src/pages/base/ExecutorPage.tsx +8 -19
  132. package/dist/templates/react-app/src/pages/base/{ErrorIdentifierPage.tsx → IdentifierPage.tsx} +1 -1
  133. package/dist/templates/react-app/src/pages/base/JSONStoragePage.tsx +11 -15
  134. package/dist/templates/react-app/src/pages/base/Layout.tsx +1 -1
  135. package/dist/templates/react-app/src/pages/base/RedirectPathname.tsx +2 -2
  136. package/dist/templates/react-app/src/pages/base/RequestPage.tsx +34 -46
  137. package/dist/templates/react-app/src/styles/css/antd-themes/_default.css +2 -2
  138. package/dist/templates/react-app/src/styles/css/antd-themes/dark.css +2 -2
  139. package/dist/templates/react-app/src/styles/css/antd-themes/pink.css +2 -2
  140. package/dist/templates/react-app/src/styles/css/index.css +1 -0
  141. package/dist/templates/react-app/src/styles/css/page.css +8 -0
  142. package/dist/templates/react-app/src/styles/css/zIndex.css +9 -0
  143. package/dist/templates/react-app/src/uikit/{controllers/ExecutorController.ts → bridges/ExecutorPageBridge.ts} +13 -36
  144. package/dist/templates/react-app/src/uikit/bridges/JSONStoragePageBridge.ts +41 -0
  145. package/dist/templates/react-app/src/uikit/bridges/NavigateBridge.ts +16 -0
  146. package/dist/templates/react-app/src/uikit/bridges/RequestPageBridge.ts +136 -0
  147. package/dist/templates/react-app/src/uikit/components/LanguageSwitcher.tsx +3 -2
  148. package/dist/templates/react-app/src/uikit/components/LogoutButton.tsx +2 -2
  149. package/dist/templates/react-app/src/uikit/{providers → components}/ProcessExecutorProvider.tsx +4 -4
  150. package/dist/templates/react-app/src/uikit/components/RouterRenderComponent.tsx +1 -1
  151. package/dist/templates/react-app/src/uikit/components/ThemeSwitcher.tsx +3 -5
  152. package/dist/templates/react-app/src/uikit/{providers → components}/UserAuthProvider.tsx +3 -3
  153. package/dist/templates/react-app/src/uikit/hooks/useI18nGuard.ts +5 -2
  154. package/dist/templates/react-app/src/uikit/hooks/{userRouterService.ts → useNavigateBridge.ts} +3 -3
  155. package/dist/templates/react-app/src/uikit/hooks/useStore.ts +5 -2
  156. package/dist/templates/react-app/tsconfig.json +1 -4
  157. package/package.json +1 -1
  158. package/dist/templates/react-app/src/base/port/InteractionHubInterface.ts +0 -94
  159. package/dist/templates/react-app/src/base/port/UIDependenciesInterface.ts +0 -37
  160. package/dist/templates/react-app/src/core/registers/IocRegisterImpl.ts +0 -25
  161. package/dist/templates/react-app/src/core/registers/RegisterCommon.ts +0 -74
  162. package/dist/templates/react-app/src/core/registers/RegisterControllers.ts +0 -26
  163. package/dist/templates/react-app/src/core/registers/RegisterGlobals.ts +0 -30
  164. package/dist/templates/react-app/src/uikit/controllers/JSONStorageController.ts +0 -49
  165. package/dist/templates/react-app/src/uikit/controllers/RequestController.ts +0 -158
  166. /package/dist/templates/react-app/src/uikit/{providers → components}/BaseRouteProvider.tsx +0 -0
@@ -0,0 +1,28 @@
1
+ import { useTranslations } from 'next-intl';
2
+ import { useMemo } from 'react';
3
+ import type { PageI18nInterface } from '@config/i18n/PageI18nInterface';
4
+
5
+ /**
6
+ * Hook to get the i18n interface
7
+ *
8
+ * @param i18nInterface - The i18n interface to get
9
+ * @returns The i18n interface
10
+ */
11
+ export function useI18nInterface<T extends PageI18nInterface>(
12
+ i18nInterface: T
13
+ ): T {
14
+ const t = useTranslations();
15
+
16
+ const i18n = useMemo(() => {
17
+ return Object.fromEntries(
18
+ Object.entries(i18nInterface).map(([key, value]) => {
19
+ if (typeof value === 'string') {
20
+ return [key, t(value)];
21
+ }
22
+ return [key, value];
23
+ })
24
+ ) as T;
25
+ }, [i18nInterface, t]);
26
+
27
+ return i18n;
28
+ }
@@ -0,0 +1,37 @@
1
+ import { useContext } from 'react';
2
+ import { IOCContext } from '../context/IOCContext';
3
+ import type { IOCIdentifierMap } from '@config/IOCIdentifier';
4
+ import type {
5
+ IOCContainerInterface,
6
+ IOCFunctionInterface
7
+ } from '@qlover/corekit-bridge';
8
+
9
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
10
+ type IOCIdentifier = keyof IOCIdentifierMap | (new (...args: any[]) => any);
11
+
12
+ export function useIOC(): IOCFunctionInterface<
13
+ IOCIdentifierMap,
14
+ IOCContainerInterface
15
+ >;
16
+ export function useIOC<T extends IOCIdentifier>(
17
+ identifier: T
18
+ ): T extends keyof IOCIdentifierMap
19
+ ? IOCIdentifierMap[T]
20
+ : // eslint-disable-next-line @typescript-eslint/no-explicit-any
21
+ T extends new (...args: any[]) => any
22
+ ? InstanceType<T>
23
+ : never;
24
+
25
+ export function useIOC<T extends IOCIdentifier>(identifier?: T) {
26
+ const IOC = useContext(IOCContext);
27
+
28
+ if (!IOC) {
29
+ throw new Error('IOC is not found');
30
+ }
31
+
32
+ if (identifier === undefined) {
33
+ return IOC;
34
+ }
35
+
36
+ return IOC(identifier);
37
+ }
@@ -0,0 +1,11 @@
1
+ import { useEffect, useState } from 'react';
2
+
3
+ export const useMountedClient = () => {
4
+ const [mounted, setMounted] = useState(false);
5
+
6
+ useEffect(() => {
7
+ setMounted(true);
8
+ }, []);
9
+
10
+ return mounted;
11
+ };
@@ -0,0 +1,15 @@
1
+ import { useSliceStore, type SliceStore } from '@qlover/slice-store-react';
2
+ import type {
3
+ StoreInterface,
4
+ StoreStateInterface
5
+ } from '@qlover/corekit-bridge';
6
+
7
+ export function useStore<
8
+ C extends StoreInterface<StoreStateInterface>,
9
+ State = C['state']
10
+ >(store: C, selector?: (state: C['state']) => State): State {
11
+ return useSliceStore(
12
+ store as unknown as SliceStore<StoreStateInterface>,
13
+ selector
14
+ );
15
+ }
@@ -0,0 +1,8 @@
1
+ import type { Config } from 'tailwindcss';
2
+
3
+ const config: Config = {
4
+ darkMode: ['class', '[data-theme="dark"]'],
5
+ content: ['./src/**/*.{js,ts,jsx,tsx}']
6
+ };
7
+
8
+ export default config;
@@ -0,0 +1,36 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2017",
4
+ "lib": ["dom", "dom.iterable", "esnext"],
5
+ "allowJs": true,
6
+ "skipLibCheck": true,
7
+ "strict": true,
8
+ "noEmit": true,
9
+ "esModuleInterop": true,
10
+ "module": "esnext",
11
+ "moduleResolution": "bundler",
12
+ "resolveJsonModule": true,
13
+ "isolatedModules": true,
14
+ "jsx": "preserve",
15
+ "incremental": true,
16
+ "experimentalDecorators": true,
17
+ "emitDecoratorMetadata": true,
18
+ "plugins": [
19
+ {
20
+ "name": "next"
21
+ }
22
+ ],
23
+ "paths": {
24
+ "@/*": ["./src/*"],
25
+ "@config/*": ["./config/*"]
26
+ }
27
+ },
28
+ "include": [
29
+ "next-env.d.ts",
30
+ "src/**/*.ts",
31
+ "src/**/*.tsx",
32
+ ".next/types/**/*.ts",
33
+ "config/**/*.ts"
34
+ ],
35
+ "exclude": ["node_modules"]
36
+ }
@@ -1,5 +1,3 @@
1
- NODE_ENV=production
2
-
3
1
  # ci
4
2
  NPM_TOKEN=
5
3
  GITHUB_TOKEN=
@@ -87,7 +87,7 @@ describe('I18nService', () => {
87
87
  expect.objectContaining({
88
88
  debug: false,
89
89
  detection: {
90
- order: ['pathLanguageDetector', 'navigator'],
90
+ order: ['pathLanguageDetector', 'navigator', 'localStorage'],
91
91
  caches: []
92
92
  }
93
93
  })
@@ -1,6 +1,6 @@
1
- import { IOC, type IOCContainer, type IOCIdentifierMap } from '@/core/IOC';
1
+ import { IOC, type IOCContainer } from '@/core/IOC';
2
2
  import { InversifyContainer } from '@/base/cases/InversifyContainer';
3
- import { IocRegisterImpl } from '@/core/registers/IocRegisterImpl';
3
+ import { IocRegisterImpl } from '@/core/IocRegisterImpl';
4
4
  import { IOCIdentifier } from '@config/IOCIdentifier';
5
5
  import type { AppConfig } from '@/base/cases/AppConfig';
6
6
 
@@ -35,7 +35,8 @@ describe('IOC Container Tests', () => {
35
35
  aiApiToken: '',
36
36
  aiApiTokenPrefix: 'Bearer',
37
37
  aiApiRequireToken: true,
38
- bootHref: ''
38
+ bootHref: '',
39
+ isProduction: false
39
40
  };
40
41
  });
41
42
 
@@ -102,12 +103,6 @@ describe('IOC Container Tests', () => {
102
103
  expect(registerImpl).toBeInstanceOf(IocRegisterImpl);
103
104
  });
104
105
 
105
- it('should return register list', () => {
106
- const registerList = registerImpl.getRegisterList();
107
- expect(Array.isArray(registerList)).toBe(true);
108
- expect(registerList.length).toBeGreaterThan(0);
109
- });
110
-
111
106
  it('should register services without throwing', () => {
112
107
  const mockManager = {
113
108
  get: vi.fn(),
@@ -125,34 +120,14 @@ describe('IOC Container Tests', () => {
125
120
  describe('IOCIdentifierMap Type Safety', () => {
126
121
  it('should have correct type mapping for JSON', () => {
127
122
  // This test verifies that the type mapping is correct
128
- const identifierMap: IOCIdentifierMap = {} as IOCIdentifierMap;
123
+ const identifierMap = {};
129
124
 
130
125
  // TypeScript should enforce that these keys exist
131
- expect(IOCIdentifier.JSON in identifierMap).toBeDefined();
126
+ expect(IOCIdentifier.JSONSerializer in identifierMap).toBeDefined();
132
127
  expect(IOCIdentifier.LocalStorage in identifierMap).toBeDefined();
133
128
  expect(IOCIdentifier.Logger in identifierMap).toBeDefined();
134
129
  expect(IOCIdentifier.AppConfig in identifierMap).toBeDefined();
135
130
  });
136
-
137
- it('should have all required identifiers', () => {
138
- const requiredIdentifiers = [
139
- 'JSON',
140
- 'LocalStorage',
141
- 'LocalStorageEncrypt',
142
- 'CookieStorage',
143
- 'Logger',
144
- 'FeApiToken',
145
- 'FeApiCommonPlugin',
146
- 'AppConfig',
147
- 'ApiMockPlugin',
148
- 'ApiCatchPlugin',
149
- 'DialogHandler'
150
- ];
151
-
152
- requiredIdentifiers.forEach((identifier) => {
153
- expect(IOCIdentifier).toHaveProperty(identifier);
154
- });
155
- });
156
131
  });
157
132
 
158
133
  describe('Service Registration Integration', () => {
@@ -2,7 +2,7 @@ import { InversifyContainer } from '@/base/cases/InversifyContainer';
2
2
  import { BootstrapApp } from '@/core/bootstraps/BootstrapApp';
3
3
  import type { BootstrapAppArgs } from '@/core/bootstraps/BootstrapApp';
4
4
  import { createIOCFunction } from '@qlover/corekit-bridge';
5
- import type { IOCIdentifierMap } from '@/core/IOC';
5
+ import type { IOCIdentifierMap } from '@config/IOCIdentifier';
6
6
  import { name, version } from '../../../../package.json';
7
7
  import { browserGlobalsName } from '@config/common';
8
8
 
@@ -1,13 +1,85 @@
1
+ import type * as CorekitBridge from '@qlover/corekit-bridge';
2
+ import type * as FeCorekit from '@qlover/fe-corekit';
3
+ import type * as Logger from '@qlover/logger';
4
+ import type { DialogHandler } from '@/base/cases/DialogHandler';
5
+ import type { AppConfig } from '@/base/cases/AppConfig';
6
+ import type { RequestStatusCatcher } from '@/base/cases/RequestStatusCatcher';
7
+ import type { I18nService } from '@/base/services/I18nService';
8
+ import type { ProcesserExecutor } from '@/base/services/ProcesserExecutor';
9
+ import type { RouteService } from '@/base/services/RouteService';
10
+ import type { UserService } from '@/base/services/UserService';
11
+ import type { I18nKeyErrorPlugin } from '@/base/cases/I18nKeyErrorPlugin';
12
+ import type { ExecutorPageBridgeInterface } from '@/base/port/ExecutorPageBridgeInterface';
13
+ import type { JSONStoragePageBridgeInterface } from '@/base/port/JSONStoragePageBridgeInterface';
14
+ import type { RequestPageBridgeInterface } from '@/base/port/RequestPageBridgeInterface';
15
+
16
+ /**
17
+ * IOC identifier
18
+ */
1
19
  export const IOCIdentifier = Object.freeze({
2
- JSON: 'JSON',
20
+ JSONSerializer: 'JSONSerializer',
21
+ Logger: 'Logger',
22
+ AppConfig: 'AppConfig',
23
+ DialogHandler: 'DialogHandler',
3
24
  LocalStorage: 'LocalStorage',
4
25
  LocalStorageEncrypt: 'LocalStorageEncrypt',
5
26
  CookieStorage: 'CookieStorage',
6
- Logger: 'Logger',
7
- FeApiToken: 'FeApiToken',
27
+ EnvConfigInterface: 'EnvConfigInterface',
28
+ UIDialogInterface: 'UIDialogInterface',
29
+ AntdStaticApiInterface: 'AntdStaticApiInterface',
30
+ RequestCatcherInterface: 'RequestCatcherInterface',
31
+ I18nServiceInterface: 'I18nServiceInterface',
32
+ ProcesserExecutorInterface: 'ProcesserExecutorInterface',
33
+ RouteServiceInterface: 'RouteServiceInterface',
34
+ UserServiceInterface: 'UserServiceInterface',
35
+ I18nKeyErrorPlugin: 'I18nKeyErrorPlugin',
8
36
  FeApiCommonPlugin: 'FeApiCommonPlugin',
9
- AppConfig: 'AppConfig',
10
37
  ApiMockPlugin: 'ApiMockPlugin',
11
38
  ApiCatchPlugin: 'ApiCatchPlugin',
12
- DialogHandler: 'DialogHandler'
39
+ ThemeService: 'ThemeService',
40
+ ExecutorPageBridgeInterface: 'ExecutorPageBridgeInterface',
41
+ JSONStoragePageInterface: 'JSONStoragePageInterface',
42
+ RequestPageBridgeInterface: 'RequestPageBridgeInterface'
13
43
  });
44
+
45
+ export const I = IOCIdentifier;
46
+
47
+ /**
48
+ * IOC identifier map
49
+ *
50
+ * Define the implementation class corresponding to the string identifier
51
+ *
52
+ * - key: interface alias or name
53
+ * - value: implementation class
54
+ */
55
+ export interface IOCIdentifierMap {
56
+ [IOCIdentifier.JSONSerializer]: FeCorekit.JSONSerializer;
57
+ [IOCIdentifier.Logger]: Logger.Logger;
58
+ [IOCIdentifier.AppConfig]: AppConfig;
59
+ [IOCIdentifier.DialogHandler]: DialogHandler;
60
+ [IOCIdentifier.LocalStorage]: FeCorekit.SyncStorage<
61
+ unknown,
62
+ FeCorekit.ObjectStorageOptions
63
+ >;
64
+ [IOCIdentifier.LocalStorageEncrypt]: FeCorekit.SyncStorage<
65
+ unknown,
66
+ FeCorekit.ObjectStorageOptions
67
+ >;
68
+ [IOCIdentifier.CookieStorage]: CorekitBridge.CookieStorage;
69
+ [IOCIdentifier.EnvConfigInterface]: AppConfig;
70
+ [IOCIdentifier.UIDialogInterface]: DialogHandler;
71
+ [IOCIdentifier.AntdStaticApiInterface]: DialogHandler;
72
+ [IOCIdentifier.RequestCatcherInterface]: RequestStatusCatcher;
73
+ [IOCIdentifier.I18nServiceInterface]: I18nService;
74
+ [IOCIdentifier.ProcesserExecutorInterface]: ProcesserExecutor;
75
+ [IOCIdentifier.RouteServiceInterface]: RouteService;
76
+ [IOCIdentifier.UserServiceInterface]: UserService;
77
+ [IOCIdentifier.I18nKeyErrorPlugin]: I18nKeyErrorPlugin;
78
+ [IOCIdentifier.FeApiCommonPlugin]: CorekitBridge.RequestCommonPlugin;
79
+ [IOCIdentifier.ApiMockPlugin]: CorekitBridge.ApiMockPlugin;
80
+ [IOCIdentifier.ApiCatchPlugin]: CorekitBridge.ApiCatchPlugin;
81
+ [IOCIdentifier.ThemeService]: CorekitBridge.ThemeService;
82
+ [IOCIdentifier.ExecutorPageBridgeInterface]: ExecutorPageBridgeInterface;
83
+ [IOCIdentifier.JSONStoragePageInterface]: JSONStoragePageBridgeInterface;
84
+ [IOCIdentifier.RequestPageBridgeInterface]: RequestPageBridgeInterface;
85
+ }
@@ -64,8 +64,8 @@ export const baseRoutes: RouteConfigValue[] = [
64
64
  }
65
65
  },
66
66
  {
67
- path: 'errorIdentifier',
68
- element: 'base/ErrorIdentifierPage',
67
+ path: 'identifier',
68
+ element: 'base/IdentifierPage',
69
69
  meta: {
70
70
  title: identifier.PAGE_ERROR_IDENTIFIER_TITLE,
71
71
  description: identifier.PAGE_ERROR_IDENTIFIER_DESCRIPTION,
@@ -4,7 +4,7 @@
4
4
  "version": "0.0.0",
5
5
  "type": "module",
6
6
  "private": true,
7
- "homepage": "",
7
+ "homepage": "https://github.com/qlover/fe-base/tree/master/packages/create-app/templates/react-app#readme",
8
8
  "author": "qlover",
9
9
  "license": "ISC",
10
10
  "repository": {
@@ -26,9 +26,6 @@
26
26
  "publishConfig": {
27
27
  "access": "public"
28
28
  },
29
- "bin": {
30
- "fe-release": "./bin/release.js"
31
- },
32
29
  "scripts": {
33
30
  "dev": "vite --mode localhost",
34
31
  "dev:staging": "vite --mode staging",
@@ -44,7 +41,7 @@
44
41
  "@qlover/corekit-bridge": "latest",
45
42
  "@qlover/fe-corekit": "latest",
46
43
  "@qlover/logger": "^0.1.1",
47
- "@qlover/slice-store-react": "^1.2.6",
44
+ "@qlover/slice-store-react": "^1.4.1",
48
45
  "@tailwindcss/postcss": "^4.1.8",
49
46
  "@tailwindcss/vite": "^4.1.8",
50
47
  "antd": "^5.25.3",
@@ -64,9 +61,9 @@
64
61
  "devDependencies": {
65
62
  "@brain-toolkit/ts2locales": "^0.2.3",
66
63
  "@qlover/env-loader": "latest",
67
- "@qlover/eslint-plugin": "^0.2.0",
64
+ "@qlover/eslint-plugin": "latest",
68
65
  "@qlover/fe-scripts": "latest",
69
- "@qlover/fe-standard": "^0.0.4",
66
+ "@qlover/fe-standard": "latest",
70
67
  "@testing-library/react": "^16.3.0",
71
68
  "@types/lodash": "^4.17.13",
72
69
  "@types/react": "^18.3.11",
@@ -178,4 +178,4 @@
178
178
  "page.request.stop_abort": "Stop Abort Request",
179
179
  "page.request.trigger_api_catch": "Trigger API Catch Result",
180
180
  "page.request.stop_api_catch": "Stop API Catch Result"
181
- }
181
+ }
@@ -178,4 +178,4 @@
178
178
  "page.request.stop_abort": "停止中止请求",
179
179
  "page.request.trigger_api_catch": "触发 API 捕获结果",
180
180
  "page.request.stop_api_catch": "停止 API 捕获结果"
181
- }
181
+ }
@@ -3,11 +3,12 @@ import { createBrowserRouter, RouterProvider } from 'react-router-dom';
3
3
  import { lazy, useMemo } from 'react';
4
4
  import { RouterRenderComponent } from './uikit/components/RouterRenderComponent';
5
5
  import { IOC } from './core/IOC';
6
- import { RouteService } from './base/services/RouteService';
7
6
  import { RouterLoader, type ComponentValue } from '@/base/cases/RouterLoader';
8
7
  import { AntdThemeProvider } from '@brain-toolkit/antd-theme-override/react';
9
8
  import { routerPrefix } from '@config/common';
10
9
  import { useStore } from './uikit/hooks/useStore';
10
+ import { IOCIdentifier } from '@config/IOCIdentifier';
11
+ import { logger } from './core/globals';
11
12
 
12
13
  function getAllPages() {
13
14
  const modules = import.meta.glob('./pages/**/*.tsx');
@@ -25,11 +26,15 @@ function getAllPages() {
25
26
 
26
27
  const routerLoader = new RouterLoader({
27
28
  componentMaps: getAllPages(),
28
- render: RouterRenderComponent
29
+ render: RouterRenderComponent,
30
+ logger: logger
29
31
  });
30
32
 
31
33
  function App() {
32
- const routes = useStore(IOC(RouteService), (state) => state.routes);
34
+ const routes = useStore(
35
+ IOC(IOCIdentifier.RouteServiceInterface),
36
+ (state) => state.routes
37
+ );
33
38
 
34
39
  const routerBase = useMemo(() => {
35
40
  const routeList = routes.map((route) => routerLoader.toRoute(route));
@@ -41,7 +46,7 @@ function App() {
41
46
 
42
47
  return (
43
48
  <AntdThemeProvider
44
- staticApi={IOC('DialogHandler')}
49
+ staticApi={IOC('AntdStaticApiInterface')}
45
50
  theme={{
46
51
  cssVar: {
47
52
  key: 'fe-theme',
@@ -37,7 +37,7 @@ export class UserApi
37
37
  protected store: UserAuthStoreInterface<UserInfo> | null = null;
38
38
 
39
39
  constructor(
40
- @inject(FetchAbortPlugin) private abortPlugin: FetchAbortPlugin,
40
+ @inject(FetchAbortPlugin) protected abortPlugin: FetchAbortPlugin,
41
41
  @inject(UserApiAdapter) adapter: RequestAdapterFetch
42
42
  ) {
43
43
  super(adapter);
@@ -16,6 +16,7 @@ import {
16
16
  type ApiCatchPluginResponse
17
17
  } from '@qlover/corekit-bridge';
18
18
  import { UserApi } from './UserApi';
19
+ import { RequestLanguages } from '../../cases/RequestLanguages';
19
20
 
20
21
  /**
21
22
  * UserApiConfig
@@ -68,6 +69,9 @@ export class UserApiBootstarp implements BootstrapExecutorPlugin {
68
69
  .get<UserApi>(UserApi)
69
70
  .usePlugin(new FetchURLPlugin())
70
71
  .usePlugin(IOC.get(IOCIdentifier.FeApiCommonPlugin))
72
+ .usePlugin(
73
+ new RequestLanguages(ioc.get(IOCIdentifier.I18nServiceInterface))
74
+ )
71
75
  .usePlugin(IOC.get(IOCIdentifier.ApiMockPlugin))
72
76
  .usePlugin(IOC.get(RequestLogger))
73
77
  .usePlugin(IOC.get(FetchAbortPlugin))
@@ -4,11 +4,14 @@ import type {
4
4
  ModalApi,
5
5
  NotificationApi
6
6
  } from '@brain-toolkit/antd-theme-override/react';
7
- import type {
8
- InteractionHubInterface,
9
- InteractionOptions,
10
- ConfirmOptions
11
- } from '../port/InteractionHubInterface';
7
+ import { UIDialogInterface, NotificationOptions } from '@qlover/corekit-bridge';
8
+ import { ModalFuncProps } from 'antd';
9
+
10
+ export interface DialogHandlerOptions
11
+ extends NotificationOptions,
12
+ ModalFuncProps {
13
+ content: string;
14
+ }
12
15
 
13
16
  /**
14
17
  * Dialog Handler Implementation
@@ -32,9 +35,9 @@ import type {
32
35
  * });
33
36
  */
34
37
  export class DialogHandler
35
- implements InteractionHubInterface, AntdStaticApiInterface
38
+ implements UIDialogInterface<DialogHandlerOptions>, AntdStaticApiInterface
36
39
  {
37
- private antds: {
40
+ protected antds: {
38
41
  message?: MessageApi;
39
42
  modal?: ModalApi;
40
43
  notification?: NotificationApi;
@@ -55,32 +58,32 @@ export class DialogHandler
55
58
  /**
56
59
  * Formats error message from various error types
57
60
  */
58
- private formatErrorMessage(error: unknown): string {
61
+ protected formatErrorMessage(error: unknown): string {
59
62
  if (error instanceof Error) return error.message;
60
63
  if (typeof error === 'string') return error;
61
64
  return 'An unknown error occurred';
62
65
  }
63
66
 
64
- public success(msg: string, options?: InteractionOptions): void {
67
+ public success(msg: string, options?: NotificationOptions): void {
65
68
  this.antds.message?.success({ content: msg, ...options });
66
69
  }
67
70
 
68
- public error(msg: string, options?: InteractionOptions): void {
71
+ public error(msg: string, options?: NotificationOptions): void {
69
72
  this.antds.message?.error({
70
73
  content: options?.error ? this.formatErrorMessage(options.error) : msg,
71
74
  ...options
72
75
  });
73
76
  }
74
77
 
75
- public info(msg: string, options?: InteractionOptions): void {
78
+ public info(msg: string, options?: NotificationOptions): void {
76
79
  this.antds.message?.info({ content: msg, ...options });
77
80
  }
78
81
 
79
- public warn(msg: string, options?: InteractionOptions): void {
82
+ public warn(msg: string, options?: NotificationOptions): void {
80
83
  this.antds.message?.warning({ content: msg, ...options });
81
84
  }
82
85
 
83
- public confirm(options: ConfirmOptions): void {
86
+ public confirm(options: DialogHandlerOptions): void {
84
87
  this.antds.modal?.confirm(options);
85
88
  }
86
89
  }
@@ -1,8 +1,8 @@
1
1
  import type { ExecutorContext, ExecutorPlugin } from '@qlover/fe-corekit';
2
2
  import type { LoggerInterface } from '@qlover/logger';
3
3
  import { inject, injectable } from 'inversify';
4
- import { I18nService } from '../services/I18nService';
5
4
  import { IOCIdentifier } from '@config/IOCIdentifier';
5
+ import type { I18nServiceInterface } from '../port/I18nServiceInterface';
6
6
 
7
7
  /**
8
8
  * When throw error, it will be converted to i18n key
@@ -14,8 +14,9 @@ export class I18nKeyErrorPlugin implements ExecutorPlugin {
14
14
  readonly pluginName = 'I18nKeyErrorPlugin';
15
15
 
16
16
  constructor(
17
- @inject(IOCIdentifier.Logger) private logger: LoggerInterface,
18
- @inject(I18nService) private i18nService: I18nService
17
+ @inject(IOCIdentifier.Logger) protected logger: LoggerInterface,
18
+ @inject(IOCIdentifier.I18nServiceInterface)
19
+ protected i18nService: I18nServiceInterface
19
20
  ) {}
20
21
 
21
22
  onError(context: ExecutorContext<unknown>): Error | void {
@@ -1,4 +1,4 @@
1
- import { IOCIdentifierMap } from '@/core/IOC';
1
+ import { IOCIdentifierMap } from '@config/IOCIdentifier';
2
2
  import {
3
3
  IOCContainerInterface,
4
4
  ServiceIdentifier
@@ -6,7 +6,7 @@ import {
6
6
  import { Container } from 'inversify';
7
7
 
8
8
  export class InversifyContainer implements IOCContainerInterface {
9
- private container: Container;
9
+ protected container: Container;
10
10
 
11
11
  constructor() {
12
12
  this.container = new Container({
@@ -0,0 +1,39 @@
1
+ import { I18nServiceInterface } from '@/base/port/I18nServiceInterface';
2
+ import {
3
+ ExecutorContext,
4
+ ExecutorPlugin,
5
+ RequestAdapterConfig
6
+ } from '@qlover/fe-corekit';
7
+
8
+ export class RequestLanguages implements ExecutorPlugin {
9
+ readonly pluginName = 'RequestLanguages';
10
+
11
+ constructor(
12
+ protected i18nService: I18nServiceInterface,
13
+ protected headerName = 'accept-language'
14
+ ) {}
15
+
16
+ buildAcceptLanguage(langs: string[]): string {
17
+ return langs
18
+ .map((lang, index) => {
19
+ const q = Math.max(1 - index * 0.1, 0.1).toFixed(1);
20
+ return index === 0 ? lang : `${lang};q=${q}`;
21
+ })
22
+ .join(',');
23
+ }
24
+
25
+ onBefore(context: ExecutorContext<RequestAdapterConfig>): void {
26
+ const currentLanguage = this.i18nService.getCurrentLanguage();
27
+ const languages = this.i18nService.getSupportedLanguages();
28
+
29
+ if (!context.parameters.headers) {
30
+ context.parameters.headers = {};
31
+ }
32
+
33
+ const languageValue = this.buildAcceptLanguage(
34
+ Array.from(new Set([currentLanguage, ...languages]))
35
+ );
36
+
37
+ context.parameters.headers[this.headerName] = languageValue;
38
+ }
39
+ }
@@ -0,0 +1,20 @@
1
+ import { AsyncStateInterface } from '@/base/port/AsyncStateInterface';
2
+
3
+ export class RequestState<T> implements AsyncStateInterface<T> {
4
+ startTime: number;
5
+ endTime: number;
6
+
7
+ constructor(
8
+ public loading: boolean = false,
9
+ public result: T | null = null,
10
+ public error: unknown | null = null
11
+ ) {
12
+ this.startTime = Date.now();
13
+ this.endTime = 0;
14
+ }
15
+
16
+ end(): this {
17
+ this.endTime = Date.now();
18
+ return this;
19
+ }
20
+ }
@@ -9,9 +9,9 @@ export class RequestStatusCatcher
9
9
  implements RequestCatcherInterface<RequestAdapterResponse>
10
10
  {
11
11
  constructor(
12
- @inject(IOCIdentifier.Logger)
13
- private readonly logger: LoggerInterface
12
+ @inject(IOCIdentifier.Logger) protected logger: LoggerInterface
14
13
  ) {}
14
+
15
15
  /**
16
16
  * default handler
17
17
  * @override