@qlover/create-app 0.1.15 → 0.1.16

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 (112) hide show
  1. package/CHANGELOG.md +33 -27
  2. package/bin/create-app.js +0 -0
  3. package/configs/_common/package.json.template +10 -0
  4. package/configs/react-app/eslint.config.js +70 -42
  5. package/dist/index.cjs +1 -3631
  6. package/dist/index.js +1 -3625
  7. package/package.json +7 -7
  8. package/templates/react-app/.env +1 -0
  9. package/templates/react-app/config/ErrorIdentifier.ts +27 -0
  10. package/templates/react-app/config/app.router.json +9 -0
  11. package/templates/react-app/config/common.ts +12 -0
  12. package/templates/react-app/config/feapi.mock.json +15 -2
  13. package/templates/react-app/config/i18n.ts +3 -11
  14. package/templates/react-app/package.json +7 -4
  15. package/templates/react-app/pnpm-lock.yaml +176 -25
  16. package/templates/react-app/public/locales/en/common.json +6 -2
  17. package/templates/react-app/public/locales/zh/common.json +6 -3
  18. package/templates/react-app/src/App.tsx +8 -8
  19. package/templates/react-app/src/base/apis/AiApi.ts +55 -0
  20. package/templates/react-app/src/base/apis/feApi/FeApi.ts +13 -44
  21. package/templates/react-app/src/base/apis/feApi/FeApiAdapter.ts +14 -0
  22. package/templates/react-app/src/base/apis/feApi/FeApiBootstarp.ts +67 -0
  23. package/templates/react-app/src/base/apis/feApi/FeApiType.ts +2 -35
  24. package/templates/react-app/src/base/apis/userApi/UserApi.ts +64 -0
  25. package/templates/react-app/src/base/apis/userApi/UserApiAdapter.ts +14 -0
  26. package/templates/react-app/src/base/apis/userApi/UserApiBootstarp.ts +75 -0
  27. package/templates/react-app/src/base/apis/userApi/UserApiType.ts +52 -0
  28. package/templates/react-app/src/base/cases/RequestLogger.ts +71 -0
  29. package/templates/react-app/src/base/cases/RequestStatusCatcher.ts +41 -0
  30. package/templates/react-app/src/base/cases/appError/AppError.ts +13 -0
  31. package/templates/react-app/{lib/router-loader/RouterLoader.ts → src/base/cases/router-loader/index.ts} +1 -1
  32. package/templates/react-app/src/base/port/ApiTransactionInterface.ts +7 -0
  33. package/templates/react-app/src/base/port/InversifyIocInterface.ts +1 -1
  34. package/templates/react-app/src/base/port/LoginInterface.ts +4 -0
  35. package/templates/react-app/src/base/port/RequestCatcherInterface.ts +12 -0
  36. package/templates/react-app/src/{services → base/services}/I18nService.ts +7 -2
  37. package/templates/react-app/src/{services/processer → base/services}/ProcesserService.ts +10 -11
  38. package/templates/react-app/src/base/types/Page.ts +1 -13
  39. package/templates/react-app/src/core/AppConfig.ts +14 -5
  40. package/templates/react-app/src/core/IOC.ts +77 -37
  41. package/templates/react-app/src/core/bootstrap.ts +38 -25
  42. package/templates/react-app/src/core/bootstraps/BootstrapApp.ts +7 -0
  43. package/templates/react-app/src/core/bootstraps/index.ts +12 -0
  44. package/templates/react-app/src/core/globals.ts +7 -10
  45. package/templates/react-app/src/core/registers/RegisterApi.ts +1 -52
  46. package/templates/react-app/src/core/registers/RegisterCommon.ts +44 -6
  47. package/templates/react-app/src/core/registers/RegisterControllers.ts +5 -34
  48. package/templates/react-app/src/core/registers/RegisterGlobals.ts +8 -2
  49. package/templates/react-app/src/core/registers/index.ts +2 -1
  50. package/templates/react-app/src/main.tsx +4 -1
  51. package/templates/react-app/src/pages/auth/Layout.tsx +4 -3
  52. package/templates/react-app/src/pages/auth/Login.tsx +5 -3
  53. package/templates/react-app/src/pages/base/ErrorIdentifier.tsx +39 -0
  54. package/templates/react-app/src/pages/base/Executor.tsx +31 -10
  55. package/templates/react-app/src/pages/base/Home.tsx +58 -30
  56. package/templates/react-app/src/pages/base/JSONStorage.tsx +2 -4
  57. package/templates/react-app/src/pages/base/Request.tsx +318 -73
  58. package/templates/react-app/src/pages/base/components/BaseHeader.tsx +2 -2
  59. package/templates/react-app/src/{components → uikit/components}/RouterRenderComponent.tsx +1 -1
  60. package/templates/react-app/src/{components → uikit/components}/ThemeSwitcher.tsx +6 -6
  61. package/templates/react-app/src/uikit/contexts/BaseRouteContext.ts +6 -3
  62. package/templates/react-app/src/uikit/controllers/ExecutorController.ts +52 -22
  63. package/templates/react-app/src/uikit/controllers/JSONStorageController.ts +7 -3
  64. package/templates/react-app/src/uikit/controllers/RequestController.ts +65 -11
  65. package/templates/react-app/src/uikit/controllers/RouterController.ts +8 -7
  66. package/templates/react-app/src/uikit/controllers/UserController.ts +48 -54
  67. package/templates/react-app/src/uikit/hooks/useLanguageGuard.ts +1 -2
  68. package/templates/react-app/src/uikit/providers/ProcessProvider.tsx +4 -4
  69. package/templates/react-app/src/uikit/styles/css/index.css +1 -1
  70. package/templates/react-app/src/uikit/styles/css/page.css +1 -1
  71. package/templates/react-app/tailwind.config.js +2 -2
  72. package/templates/react-app/tsconfig.json +0 -1
  73. package/templates/react-app/tsconfig.node.json +1 -2
  74. package/templates/react-app/vite.config.ts +21 -26
  75. package/templates/react-app/.env.local +0 -24
  76. package/templates/react-app/lib/bootstrap/Bootstrap.ts +0 -36
  77. package/templates/react-app/lib/bootstrap/BootstrapExecutorPlugin.ts +0 -20
  78. package/templates/react-app/lib/bootstrap/IOCContainerInterface.ts +0 -33
  79. package/templates/react-app/lib/bootstrap/IOCManagerInterface.ts +0 -12
  80. package/templates/react-app/lib/bootstrap/index.ts +0 -7
  81. package/templates/react-app/lib/bootstrap/plugins/InjectEnv.ts +0 -61
  82. package/templates/react-app/lib/bootstrap/plugins/InjectGlobal.ts +0 -36
  83. package/templates/react-app/lib/bootstrap/plugins/InjectIOC.ts +0 -24
  84. package/templates/react-app/lib/env-config/injectPkgConfig.ts +0 -11
  85. package/templates/react-app/lib/fe-react-controller/FeController.ts +0 -19
  86. package/templates/react-app/lib/fe-react-controller/index.ts +0 -2
  87. package/templates/react-app/lib/fe-react-controller/useController.ts +0 -71
  88. package/templates/react-app/lib/fe-react-theme/ThemeController.ts +0 -40
  89. package/templates/react-app/lib/fe-react-theme/ThemeStateGetter.ts +0 -53
  90. package/templates/react-app/lib/fe-react-theme/index.ts +0 -3
  91. package/templates/react-app/lib/fe-react-theme/type.ts +0 -21
  92. package/templates/react-app/lib/openAiApi/OpenAIAuthPlugin.ts +0 -29
  93. package/templates/react-app/lib/openAiApi/OpenAIClient.ts +0 -51
  94. package/templates/react-app/lib/openAiApi/StreamProcessor.ts +0 -81
  95. package/templates/react-app/lib/openAiApi/index.ts +0 -3
  96. package/templates/react-app/lib/request-common-plugin/index.ts +0 -169
  97. package/templates/react-app/lib/router-loader/Page.ts +0 -68
  98. package/templates/react-app/lib/tw-root10px/index.css +0 -4
  99. package/templates/react-app/src/base/apis/feApi/FeApiMockPlugin.ts +0 -44
  100. package/templates/react-app/src/base/apis/feApi/index.ts +0 -2
  101. package/templates/react-app/src/base/cases/UserToken.ts +0 -47
  102. package/templates/react-app/src/base/consts/IOCIdentifier.ts +0 -16
  103. package/templates/react-app/src/base/port/IOCFunctionInterface.ts +0 -39
  104. package/templates/react-app/src/base/port/StorageTokenInterface.ts +0 -27
  105. package/templates/react-app/src/core/AppIOCContainer.ts +0 -30
  106. package/templates/react-app/src/uikit/utils/RequestLogger.ts +0 -37
  107. package/templates/react-app/src/uikit/utils/datetime.ts +0 -30
  108. package/templates/react-app/src/uikit/utils/thread.ts +0 -3
  109. /package/templates/react-app/lib/{tw-root10px/index.js → tailwind/root10px.js} +0 -0
  110. /package/templates/react-app/lib/{fe-react-theme/tw-generator.js → tailwind/theme-generator.js} +0 -0
  111. /package/templates/react-app/src/{components → uikit/components}/Loading.tsx +0 -0
  112. /package/templates/react-app/src/{components → uikit/components}/LocaleLink.tsx +0 -0
@@ -1,94 +1,339 @@
1
1
  import { IOC } from '@/core/IOC';
2
- import { useControllerState } from '@lib/fe-react-controller';
3
2
  import { useBaseRoutePage } from '@/uikit/contexts/BaseRouteContext';
4
3
  import { JSONStorageController } from '@/uikit/controllers/JSONStorageController';
5
4
  import { RequestController } from '@/uikit/controllers/RequestController';
5
+ import { useMemo } from 'react';
6
+ import { useSliceStore } from '@qlover/slice-store-react';
7
+
8
+ function JSONValue({ value }: { value: unknown }) {
9
+ const output = useMemo(() => {
10
+ try {
11
+ return JSON.stringify(value, null, 2);
12
+ } catch {
13
+ return 'Invalid JSON';
14
+ }
15
+ }, [value]);
16
+ return (
17
+ <pre className="mt-1 text-sm text-gray-600 font-mono bg-gray-50 p-2 rounded overflow-x-auto">
18
+ {output}
19
+ </pre>
20
+ );
21
+ }
6
22
 
7
23
  export default function Request() {
8
24
  const requestController = IOC(RequestController);
9
- const requestControllerState = useControllerState(requestController);
10
- const jsonStorageControllerState = useControllerState(
11
- IOC(JSONStorageController)
12
- );
25
+ const requestControllerState = useSliceStore(requestController);
26
+ const jsonStorageControllerState = useSliceStore(IOC(JSONStorageController));
13
27
  const { t } = useBaseRoutePage();
14
28
 
15
29
  return (
16
- <div className="min-h-screen bg-gray-100 py-6 flex flex-col sm:py-12">
17
- <div className="bg-white shadow-lg rounded-lg px-8 py-6">
18
- {t('requestTimeout')}: {jsonStorageControllerState.requestTimeout}
19
- </div>
20
-
21
- <div className="bg-white shadow-lg rounded-lg px-8 py-6">
22
- <button
23
- className="bg-blue-500 text-white px-4 py-2 rounded-md"
24
- onClick={requestController.onHello}
25
- >
26
- {requestControllerState.helloState.loading ? 'Loading...' : 'Hello'}
27
- </button>
28
- <div className="mt-4">
29
- {t('helloResult')}:{' '}
30
- {JSON.stringify(requestControllerState.helloState.result)}
31
- </div>
32
- <div className="mt-4">
33
- {t('helloError')}:{' '}
34
- {JSON.stringify(requestControllerState.helloState.error)}
30
+ <div className="min-h-screen bg-gray-50 py-8 px-4 sm:px-6 lg:px-8">
31
+ <div className="max-w-4xl mx-auto space-y-6">
32
+ {/* Request Timeout Information */}
33
+ <div className="bg-white shadow sm:rounded-lg p-6 border border-gray-200">
34
+ <h2 className="text-lg font-medium text-gray-900 mb-2">
35
+ {t('requestTimeout')}
36
+ </h2>
37
+ <div className="text-sm text-gray-600 font-mono bg-gray-50 p-2 rounded">
38
+ {jsonStorageControllerState.requestTimeout}
39
+ </div>
35
40
  </div>
36
- </div>
37
41
 
38
- <div className="bg-white shadow-lg rounded-lg px-8 py-6">
39
- <button
40
- className="bg-blue-500 text-white px-4 py-2 rounded-md"
41
- onClick={requestController.onIpInfo}
42
- >
43
- {requestControllerState.ipInfoState.loading
44
- ? t('loading')
45
- : t('ipInfo')}
46
- </button>
47
- <div className="mt-4">
48
- {t('ipInfoResult')}:{' '}
49
- {JSON.stringify(requestControllerState.ipInfoState.result)}
42
+ {/* Hello Request Card */}
43
+ <div className="bg-white shadow sm:rounded-lg p-6 border border-gray-200">
44
+ <h2 className="text-lg font-medium text-gray-900 mb-4">
45
+ AI API: Hello
46
+ </h2>
47
+
48
+ <p className="text-sm text-gray-600 mb-4">
49
+ 函数式 api, 使用了 FetchURLPlugin, RequestCommonPlugin,
50
+ ApiMockPlugin, RequestLogger 插件
51
+ </p>
52
+ <button
53
+ className="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition-colors"
54
+ onClick={requestController.onHello}
55
+ >
56
+ {requestControllerState.helloState.loading ? (
57
+ <>
58
+ <svg
59
+ className="animate-spin -ml-1 mr-2 h-4 w-4 text-white"
60
+ xmlns="http://www.w3.org/2000/svg"
61
+ fill="none"
62
+ viewBox="0 0 24 24"
63
+ >
64
+ <circle
65
+ className="opacity-25"
66
+ cx="12"
67
+ cy="12"
68
+ r="10"
69
+ stroke="currentColor"
70
+ strokeWidth="4"
71
+ ></circle>
72
+ <path
73
+ className="opacity-75"
74
+ fill="currentColor"
75
+ d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
76
+ ></path>
77
+ </svg>
78
+ {t('loading')}
79
+ </>
80
+ ) : (
81
+ 'Hello'
82
+ )}
83
+ </button>
84
+
85
+ <div className="mt-4 space-y-2">
86
+ <div>
87
+ <p className="text-sm font-medium text-gray-700">
88
+ {t('helloResult')}:
89
+ </p>
90
+ <JSONValue value={requestControllerState.helloState.result} />
91
+ </div>
92
+
93
+ <div>
94
+ <p className="text-sm font-medium text-gray-700">
95
+ {t('helloError')}:
96
+ </p>
97
+ <JSONValue value={requestControllerState.helloState.error} />
98
+ </div>
99
+ </div>
50
100
  </div>
51
- </div>
52
101
 
53
- <div className="bg-white shadow-lg rounded-lg px-8 py-6">
54
- <button
55
- className="bg-blue-500 text-white px-4 py-2 rounded-md"
56
- onClick={requestController.onRandomUser}
57
- >
58
- {requestControllerState.randomUserState.loading
59
- ? t('loading')
60
- : t('randomUser')}
61
- </button>
62
- <div className="mt-4">
63
- {t('randomUserResult')}:{' '}
64
- {JSON.stringify(requestControllerState.randomUserState.result)}
102
+ {/* IP Information Card */}
103
+ <div className="bg-white shadow sm:rounded-lg p-6 border border-gray-200">
104
+ <h2 className="text-lg font-medium text-gray-900 mb-4">
105
+ FeApi: IP Information
106
+ </h2>
107
+
108
+ <p className="text-sm text-gray-600 mb-4">
109
+ RequestScheduler 类式 api, 使用了 FetchURLPlugin,
110
+ RequestCommonPlugin, RequestLogger, ApiPickDataPlugin 插件, 其中
111
+ ApiPickDataPlugin 插件可以将返回类型统一扁平到 data 字段
112
+ </p>
113
+ <button
114
+ className="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition-colors"
115
+ onClick={requestController.onIpInfo}
116
+ >
117
+ {requestControllerState.ipInfoState.loading ? (
118
+ <>
119
+ <svg
120
+ className="animate-spin -ml-1 mr-2 h-4 w-4 text-white"
121
+ xmlns="http://www.w3.org/2000/svg"
122
+ fill="none"
123
+ viewBox="0 0 24 24"
124
+ >
125
+ <circle
126
+ className="opacity-25"
127
+ cx="12"
128
+ cy="12"
129
+ r="10"
130
+ stroke="currentColor"
131
+ strokeWidth="4"
132
+ ></circle>
133
+ <path
134
+ className="opacity-75"
135
+ fill="currentColor"
136
+ d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
137
+ ></path>
138
+ </svg>
139
+ {t('loading')}
140
+ </>
141
+ ) : (
142
+ t('ipInfo')
143
+ )}
144
+ </button>
145
+
146
+ <div className="mt-4">
147
+ <p className="text-sm font-medium text-gray-700">
148
+ {t('ipInfoResult')}:
149
+ </p>
150
+ <JSONValue value={requestControllerState.ipInfoState.result} />
151
+ </div>
65
152
  </div>
66
- <div className="mt-4">
67
- {t('randomUserError')}:{' '}
68
- {JSON.stringify(requestControllerState.randomUserState.error)}
153
+
154
+ {/* Random User Card */}
155
+ <div className="bg-white shadow sm:rounded-lg p-6 border border-gray-200">
156
+ <h2 className="text-lg font-medium text-gray-900 mb-4">
157
+ UserApi:Random User
158
+ </h2>
159
+ <p className="text-sm text-gray-600 mb-4">
160
+ RequestTransaction 类式 api, 使用了 FetchURLPlugin,
161
+ RequestCommonPlugin, ApiMockPlugin, FetchAbortPlugin,
162
+ RequestLogger,ApiCatchPlugin 插件, 其中 FetchAbortPlugin 可以
163
+ 中止请求, ApiCatchPlugin 可以将捕获的错误统一到 apiCatchResult 字段
164
+ </p>
165
+ <button
166
+ className="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition-colors"
167
+ onClick={requestController.onRandomUser}
168
+ >
169
+ {requestControllerState.randomUserState.loading ? (
170
+ <>
171
+ <svg
172
+ className="animate-spin -ml-1 mr-2 h-4 w-4 text-white"
173
+ xmlns="http://www.w3.org/2000/svg"
174
+ fill="none"
175
+ viewBox="0 0 24 24"
176
+ >
177
+ <circle
178
+ className="opacity-25"
179
+ cx="12"
180
+ cy="12"
181
+ r="10"
182
+ stroke="currentColor"
183
+ strokeWidth="4"
184
+ ></circle>
185
+ <path
186
+ className="opacity-75"
187
+ fill="currentColor"
188
+ d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
189
+ ></path>
190
+ </svg>
191
+ {t('loading')}
192
+ </>
193
+ ) : (
194
+ t('randomUser')
195
+ )}
196
+ </button>
197
+
198
+ <div className="mt-4 space-y-2">
199
+ <div>
200
+ <p className="text-sm font-medium text-gray-700">
201
+ {t('randomUserResult')}:
202
+ </p>
203
+ <JSONValue
204
+ value={requestControllerState.randomUserState.result}
205
+ />
206
+ </div>
207
+
208
+ <div>
209
+ <p className="text-sm font-medium text-gray-700">
210
+ {t('randomUserError')}:
211
+ </p>
212
+ <JSONValue value={requestControllerState.randomUserState.error} />
213
+ </div>
214
+ </div>
69
215
  </div>
70
- </div>
71
216
 
72
- <div className="bg-white shadow-lg rounded-lg px-8 py-6">
73
- <button
74
- className="bg-blue-500 text-white px-4 py-2 rounded-md"
75
- onClick={requestController.onTriggerAbortRequest}
76
- >
77
- {requestControllerState.abortState.loading
78
- ? t('stopAbortRequest')
79
- : t('triggerAbortRequest')}
80
- <span className="text-xs">
81
- {requestControllerState.abortState.loading ? t('loading') : ''}
82
- </span>
83
- </button>
84
-
85
- <div className="mt-4">
86
- {t('abortRequestResult')}:{' '}
87
- {JSON.stringify(requestControllerState.abortState.result)}
217
+ {/* Api catch result */}
218
+ <div className="bg-white shadow sm:rounded-lg p-6 border border-gray-200">
219
+ <h2 className="text-lg font-medium text-gray-900 mb-4">
220
+ UserApi: Api Catch Result
221
+ </h2>
222
+ <button
223
+ className={`inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition-colors ${
224
+ requestControllerState.apiCatchResultState.loading
225
+ ? 'bg-red-600 hover:bg-red-700'
226
+ : 'bg-blue-600 hover:bg-blue-700'
227
+ }`}
228
+ onClick={requestController.onTriggerApiCatchResult}
229
+ >
230
+ {requestControllerState.apiCatchResultState.loading ? (
231
+ <>
232
+ <svg
233
+ className="animate-spin -ml-1 mr-2 h-4 w-4 text-white"
234
+ xmlns="http://www.w3.org/2000/svg"
235
+ fill="none"
236
+ viewBox="0 0 24 24"
237
+ >
238
+ <circle
239
+ className="opacity-25"
240
+ cx="12"
241
+ cy="12"
242
+ r="10"
243
+ stroke="currentColor"
244
+ strokeWidth="4"
245
+ ></circle>
246
+ <path
247
+ className="opacity-75"
248
+ fill="currentColor"
249
+ d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
250
+ ></path>
251
+ </svg>
252
+ {t('stopApiCatchResult')}
253
+ </>
254
+ ) : (
255
+ t('triggerApiCatchResult')
256
+ )}
257
+ </button>
258
+
259
+ <div className="mt-4 space-y-2">
260
+ <div>
261
+ <p className="text-sm font-medium text-gray-700">
262
+ {t('abortRequestResult')}:
263
+ </p>
264
+ <JSONValue
265
+ value={requestControllerState.apiCatchResultState.result}
266
+ />
267
+ </div>
268
+
269
+ <div>
270
+ <p className="text-sm font-medium text-gray-700">
271
+ {t('abortRequestError')}:
272
+ </p>
273
+ <JSONValue
274
+ value={requestControllerState.apiCatchResultState.error}
275
+ />
276
+ </div>
277
+ </div>
88
278
  </div>
89
- <div className="mt-4">
90
- {t('abortRequestError')}:{' '}
91
- {JSON.stringify(requestControllerState.abortState.error)}
279
+
280
+ {/* Abort Request Card */}
281
+ <div className="bg-white shadow sm:rounded-lg p-6 border border-gray-200">
282
+ <h2 className="text-lg font-medium text-gray-900 mb-4">
283
+ UserApi: Abort Request
284
+ </h2>
285
+ <button
286
+ className={`inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition-colors ${
287
+ requestControllerState.abortState.loading
288
+ ? 'bg-red-600 hover:bg-red-700'
289
+ : 'bg-blue-600 hover:bg-blue-700'
290
+ }`}
291
+ onClick={requestController.onTriggerAbortRequest}
292
+ >
293
+ {requestControllerState.abortState.loading ? (
294
+ <>
295
+ <svg
296
+ className="animate-spin -ml-1 mr-2 h-4 w-4 text-white"
297
+ xmlns="http://www.w3.org/2000/svg"
298
+ fill="none"
299
+ viewBox="0 0 24 24"
300
+ >
301
+ <circle
302
+ className="opacity-25"
303
+ cx="12"
304
+ cy="12"
305
+ r="10"
306
+ stroke="currentColor"
307
+ strokeWidth="4"
308
+ ></circle>
309
+ <path
310
+ className="opacity-75"
311
+ fill="currentColor"
312
+ d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
313
+ ></path>
314
+ </svg>
315
+ {t('stopAbortRequest')}
316
+ </>
317
+ ) : (
318
+ t('triggerAbortRequest')
319
+ )}
320
+ </button>
321
+
322
+ <div className="mt-4 space-y-2">
323
+ <div>
324
+ <p className="text-sm font-medium text-gray-700">
325
+ {t('abortRequestResult')}:
326
+ </p>
327
+ <JSONValue value={requestControllerState.abortState.result} />
328
+ </div>
329
+
330
+ <div>
331
+ <p className="text-sm font-medium text-gray-700">
332
+ {t('abortRequestError')}:
333
+ </p>
334
+ <JSONValue value={requestControllerState.abortState.error} />
335
+ </div>
336
+ </div>
92
337
  </div>
93
338
  </div>
94
339
  </div>
@@ -1,5 +1,5 @@
1
- import ThemeSwitcher from '@/components/ThemeSwitcher';
2
- import LocaleLink from '@/components/LocaleLink';
1
+ import ThemeSwitcher from '@/uikit/components/ThemeSwitcher';
2
+ import LocaleLink from '@/uikit/components/LocaleLink';
3
3
 
4
4
  export default function BaseHeader() {
5
5
  return (
@@ -1,7 +1,7 @@
1
1
  import { Suspense } from 'react';
2
2
  import { Loading } from './Loading';
3
- import { RouterLoaderRender } from '@lib/router-loader/RouterLoader';
4
3
  import BaseRouteProvider from '@/uikit/providers/BaseRouteProvider';
4
+ import { RouterLoaderRender } from '@/base/cases/router-loader';
5
5
 
6
6
  export const RouterRenderComponent: RouterLoaderRender = (route) => {
7
7
  const Component = route.element();
@@ -1,12 +1,12 @@
1
1
  import { IOC } from '@/core/IOC';
2
- import { useController } from '@lib/fe-react-controller';
3
- import { ThemeController } from '@lib/fe-react-theme/ThemeController';
2
+ import { ThemeService } from '@qlover/corekit-bridge';
3
+ import { useSliceStore } from '@qlover/slice-store-react';
4
4
  import { useTranslation } from 'react-i18next';
5
5
 
6
6
  export default function ThemeSwitcher() {
7
- const controller = useController(IOC(ThemeController));
8
- const { theme } = controller.getState();
9
- const themes = controller.getSupportedThemes();
7
+ const themeService = IOC(ThemeService);
8
+ const { theme } = useSliceStore(themeService);
9
+ const themes = themeService.getSupportedThemes();
10
10
  const { t } = useTranslation('common');
11
11
 
12
12
  return (
@@ -17,7 +17,7 @@ export default function ThemeSwitcher() {
17
17
  <select
18
18
  id="theme-select"
19
19
  value={theme}
20
- onChange={(e) => controller.changeTheme(e.target.value)}
20
+ onChange={(e) => themeService.changeTheme(e.target.value)}
21
21
  >
22
22
  {themes.map((theme) => (
23
23
  <option key={theme} value={theme}>
@@ -4,10 +4,13 @@ import { BasePageProvider } from '@/base/types/Page';
4
4
  import { RouteMeta } from '@/base/types/Page';
5
5
  import { createContext } from 'react';
6
6
  import merge from 'lodash/merge';
7
- import { i18nConfig } from '@config/i18n';
7
+ import i18nConfig from '@config/i18n';
8
+ import { WITHIN_PAGE_PROVIDER } from '@config/ErrorIdentifier';
9
+
10
+ const { defaultNS } = i18nConfig;
8
11
 
9
12
  const defaultBaseRoutemeta = {
10
- localNamespace: i18nConfig.defaultNS,
13
+ localNamespace: defaultNS,
11
14
  title: '',
12
15
  icon: ''
13
16
  };
@@ -18,7 +21,7 @@ export function useBaseRoutePage(): BasePageProvider {
18
21
  const meta = useContext(BaseRoutePageContext);
19
22
 
20
23
  if (!meta) {
21
- throw new Error('useBaseRoutePage must be used within a PageProvider');
24
+ throw new Error(WITHIN_PAGE_PROVIDER);
22
25
  }
23
26
 
24
27
  const _meta = useMemo(() => merge({}, defaultBaseRoutemeta, meta), [meta]);
@@ -1,24 +1,21 @@
1
- import { FeController } from '@lib/fe-react-controller';
2
- import { FeApi } from '@/base/apis/feApi';
1
+ import { FeApi } from '@/base/apis/feApi/FeApi';
3
2
  import cloneDeep from 'lodash/cloneDeep';
4
3
  import {
5
4
  ExecutorPlugin,
6
5
  RequestAdapterResponse,
7
6
  RequestAdapterFetchConfig
8
- } from '@qlover/fe-utils';
9
-
10
- function createDefaultState() {
11
- return {
12
- helloState: {
13
- loading: false,
14
- result: null as unknown,
15
- error: null as unknown
16
- }
7
+ } from '@qlover/fe-corekit';
8
+ import { inject, injectable } from 'inversify';
9
+ import { SliceStore } from '@qlover/slice-store-react';
10
+
11
+ class ExecutorControllerState {
12
+ helloState = {
13
+ loading: false,
14
+ result: null as Record<string, unknown> | null,
15
+ error: null as unknown
17
16
  };
18
17
  }
19
18
 
20
- export type ExecutorControllerState = ReturnType<typeof createDefaultState>;
21
-
22
19
  const TestPlugin: ExecutorPlugin<RequestAdapterFetchConfig> = {
23
20
  pluginName: 'test',
24
21
  async onSuccess({ parameters, returnValue }) {
@@ -33,24 +30,57 @@ const TestPlugin: ExecutorPlugin<RequestAdapterFetchConfig> = {
33
30
  }
34
31
  };
35
32
 
36
- export class ExecutorController extends FeController<ExecutorControllerState> {
37
- private feApi: FeApi;
33
+ @injectable()
34
+ export class ExecutorController extends SliceStore<ExecutorControllerState> {
35
+ selector = {
36
+ helloState: (state: ExecutorControllerState) => state.helloState
37
+ };
38
38
 
39
- constructor(feApi: FeApi) {
40
- super(createDefaultState);
39
+ constructor(@inject(FeApi) private feApi: FeApi) {
40
+ super(ExecutorController.create);
41
41
 
42
+ // FIXME: not cloneDeep, create a new instance
42
43
  this.feApi = cloneDeep(feApi);
43
44
 
44
45
  this.feApi.usePlugin(TestPlugin);
45
46
  }
46
47
 
47
- onTestPlugins = async () => {
48
- console.log('onTestPlugins');
48
+ static create(): ExecutorControllerState {
49
+ return new ExecutorControllerState();
50
+ }
49
51
 
50
- const res = await this.feApi.get('/api/v1/executor/test', {
51
- responseType: 'text'
52
+ onTestPlugins = async () => {
53
+ this.emit({
54
+ ...this.state,
55
+ helloState: {
56
+ loading: true,
57
+ result: null,
58
+ error: null
59
+ }
52
60
  });
53
61
 
54
- console.log('res', res);
62
+ try {
63
+ const res = await this.feApi.get('/api/v1/executor/test', {
64
+ responseType: 'text'
65
+ });
66
+
67
+ this.emit({
68
+ ...this.state,
69
+ helloState: {
70
+ loading: false,
71
+ result: res,
72
+ error: null
73
+ }
74
+ });
75
+ } catch (error) {
76
+ this.emit({
77
+ ...this.state,
78
+ helloState: {
79
+ loading: false,
80
+ result: null,
81
+ error
82
+ }
83
+ });
84
+ }
55
85
  };
56
86
  }
@@ -1,5 +1,5 @@
1
- import { FeController } from '@lib/fe-react-controller';
2
- import { JSONStorage } from '@qlover/fe-utils';
1
+ import { JSONStorage } from '@qlover/fe-corekit';
2
+ import { SliceStore } from '@qlover/slice-store-react';
3
3
  import { random } from 'lodash';
4
4
 
5
5
  interface JSONStoragePageState {
@@ -9,7 +9,11 @@ interface JSONStoragePageState {
9
9
  requestTimeout: number;
10
10
  }
11
11
 
12
- export class JSONStorageController extends FeController<JSONStoragePageState> {
12
+ export class JSONStorageController extends SliceStore<JSONStoragePageState> {
13
+ selector = {
14
+ requestTimeout: (state: JSONStoragePageState) => state.requestTimeout
15
+ };
16
+
13
17
  constructor(private storage: JSONStorage) {
14
18
  super(() => ({
15
19
  testKey1: storage.getItem('testKey1') ?? 0,