@qlover/create-app 0.4.1 → 0.4.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 +37 -0
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/templates/react-app/.env +1 -2
- package/templates/react-app/README.md +70 -8
- package/templates/react-app/config/app.router.json +13 -13
- package/templates/react-app/config/common.ts +8 -0
- package/templates/react-app/config/i18n.ts +3 -1
- package/templates/react-app/config/{Identifier.I18n.ts → identifier/I18n.ts} +321 -3
- package/templates/react-app/index.html +1 -1
- package/templates/react-app/public/locales/en/common.json +54 -8
- package/templates/react-app/public/locales/zh/common.json +54 -8
- package/templates/react-app/public/router-root/logo.svg +1 -0
- package/templates/react-app/src/App.tsx +6 -3
- package/templates/react-app/src/base/cases/PublicAssetsPath.ts +17 -0
- package/templates/react-app/src/base/port/LoginInterface.ts +8 -0
- package/templates/react-app/src/base/port/StoreInterface.ts +58 -0
- package/templates/react-app/src/base/services/I18nService.ts +15 -9
- package/templates/react-app/src/{uikit/controllers/RouterController.ts → base/services/RouteService.ts} +12 -12
- package/templates/react-app/src/{uikit/controllers/UserController.ts → base/services/UserService.ts} +21 -11
- package/templates/react-app/src/core/bootstrap.ts +1 -1
- package/templates/react-app/src/core/registers/RegisterCommon.ts +11 -1
- package/templates/react-app/src/core/registers/RegisterControllers.ts +3 -12
- package/templates/react-app/src/pages/auth/Layout.tsx +5 -5
- package/templates/react-app/src/pages/auth/Login.tsx +50 -29
- package/templates/react-app/src/pages/auth/Register.tsx +238 -1
- package/templates/react-app/src/pages/base/About.tsx +1 -1
- package/templates/react-app/src/pages/base/ErrorIdentifier.tsx +2 -2
- package/templates/react-app/src/pages/base/Executor.tsx +4 -4
- package/templates/react-app/src/pages/base/Home.tsx +1 -1
- package/templates/react-app/src/pages/base/JSONStorage.tsx +3 -3
- package/templates/react-app/src/pages/base/Request.tsx +4 -4
- package/templates/react-app/src/pages/base/components/BaseHeader.tsx +8 -2
- package/templates/react-app/src/uikit/components/LanguageSwitcher.tsx +7 -7
- package/templates/react-app/src/uikit/components/ThemeSwitcher.tsx +3 -3
- package/templates/react-app/src/uikit/contexts/BaseRouteContext.ts +1 -1
- package/templates/react-app/src/uikit/controllers/ExecutorController.ts +6 -3
- package/templates/react-app/src/uikit/controllers/JSONStorageController.ts +6 -3
- package/templates/react-app/src/uikit/controllers/RequestController.ts +3 -4
- package/templates/react-app/src/uikit/hooks/useDocumentTitle.ts +15 -0
- package/templates/react-app/src/uikit/hooks/useStore.ts +12 -0
- package/templates/react-app/src/uikit/providers/BaseRouteProvider.tsx +7 -1
- package/templates/react-app/src/uikit/providers/ProcessProvider.tsx +7 -7
- package/templates/react-app/vite.config.ts +20 -11
- /package/templates/react-app/config/{Identifier.Error.ts → identifier/Error.ts} +0 -0
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
|
-
<title>
|
|
7
|
+
<title>react-app</title>
|
|
8
8
|
</head>
|
|
9
9
|
<body>
|
|
10
10
|
<div id="root"></div>
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
|
-
"404.notComponent": "Component not found",
|
|
3
|
-
"404.notPage": "Page not found",
|
|
4
|
-
"500.title": "Server Error",
|
|
5
|
-
"header.theme.label": "Switch Theme",
|
|
6
2
|
"err.ioc.not.implemented": "IOC not implemented",
|
|
7
3
|
"err.local.no.user.token": "Local no user token",
|
|
8
4
|
"err.global.no.window": "Global no window",
|
|
9
5
|
"err.within.page.provider": "Must be used in PageProvider",
|
|
10
6
|
"page.about.title": "About",
|
|
11
7
|
"page.about.description": "Learn more about our project and team information",
|
|
8
|
+
"404.notComponent": "Component not found",
|
|
9
|
+
"404.notPage": "Page not found",
|
|
10
|
+
"500.title": "Server Error",
|
|
11
|
+
"header.theme.label": "Switch Theme",
|
|
12
12
|
"about.title": "About",
|
|
13
13
|
"jsonStorage.title": "JSONStorage Demo",
|
|
14
14
|
"jsonStorage.title2": "Permanent storage test",
|
|
@@ -54,7 +54,6 @@
|
|
|
54
54
|
"page.request.title": "Request Examples",
|
|
55
55
|
"page.request.description": "Demonstrate various request examples and usage",
|
|
56
56
|
"page.executor.title": "Executor Examples",
|
|
57
|
-
"page.executor.description": "A powerful task executor supporting multiple task types and state management",
|
|
58
57
|
"page.error.identifier.title": "Error Identifier",
|
|
59
58
|
"page.error.identifier.description": "Error identifier usage and examples",
|
|
60
59
|
"home.explore": "Explore",
|
|
@@ -88,6 +87,7 @@
|
|
|
88
87
|
"page.request.trigger_api_catch": "Trigger API Catch Result",
|
|
89
88
|
"page.request.stop_api_catch": "Stop API Catch Result",
|
|
90
89
|
"page.executor.main_title": "Executor",
|
|
90
|
+
"page.executor.description": "A powerful task executor supporting multiple task types and state management",
|
|
91
91
|
"page.executor.test_plugin.title": "Test Plugin",
|
|
92
92
|
"page.executor.task.status.pending": "Pending",
|
|
93
93
|
"page.executor.task.status.running": "Running",
|
|
@@ -120,7 +120,7 @@
|
|
|
120
120
|
"page.executor.view_guide": "View Guide",
|
|
121
121
|
"page.executor.contact_support": "Contact Support",
|
|
122
122
|
"page.error.identifier.main_title": "Error Identifier",
|
|
123
|
-
"page.error.identifier.source_description": "Identifier From: '@config/Identifier
|
|
123
|
+
"page.error.identifier.source_description": "Identifier From: '@config/Identifier/Error'",
|
|
124
124
|
"page.error.identifier.help.title": "Need Help?",
|
|
125
125
|
"page.error.identifier.help.description": "If you encounter any issues while using error identifiers, please contact our support team",
|
|
126
126
|
"page.error.identifier.contact_support": "Contact Support",
|
|
@@ -128,7 +128,9 @@
|
|
|
128
128
|
"about.notification.title": "Notification Title",
|
|
129
129
|
"about.notification.description": "This is a test notification",
|
|
130
130
|
"about.button.message": "Show Message",
|
|
131
|
+
"about.button.message2": "Static Message",
|
|
131
132
|
"about.button.notification": "Show Notification",
|
|
133
|
+
"about.button.notification2": "Static Notification",
|
|
132
134
|
"about.tooltip.text": "This is a tooltip text",
|
|
133
135
|
"about.button.tooltip": "Hover to show Tooltip",
|
|
134
136
|
"about.modal.title": "Modal Example",
|
|
@@ -146,6 +148,50 @@
|
|
|
146
148
|
"about.alert.message": "This is a warning alert",
|
|
147
149
|
"common.ok": "OK",
|
|
148
150
|
"common.cancel": "Cancel",
|
|
149
|
-
"
|
|
150
|
-
"
|
|
151
|
+
"login.welcome": "Welcome to the future of learning",
|
|
152
|
+
"login.subtitle": "Unlock personalized AI-powered learning experiences designed to accelerate your knowledge journey.",
|
|
153
|
+
"login.forgot_password": "Forgot your password?",
|
|
154
|
+
"login.continue_with": "or continue with",
|
|
155
|
+
"login.with_google": "Sign in with Google",
|
|
156
|
+
"login.no_account": "Don't have an account?",
|
|
157
|
+
"login.create_account": "Create one here",
|
|
158
|
+
"login.email_required": "Please input your email!",
|
|
159
|
+
"login.password_required": "Please input your password!",
|
|
160
|
+
"login.feature.ai_paths": "AI-powered personalized learning paths",
|
|
161
|
+
"login.feature.smart_recommendations": "Smart content recommendations",
|
|
162
|
+
"login.feature.progress_tracking": "Real-time progress tracking",
|
|
163
|
+
"login.email.title": "Enter email",
|
|
164
|
+
"login.password.title": "Enter password",
|
|
165
|
+
"login.forgot_password.title": "Reset password",
|
|
166
|
+
"login.button.title": "Sign in to account",
|
|
167
|
+
"login.with_google.title": "Sign in with Google account",
|
|
168
|
+
"login.create_account.title": "Create new account",
|
|
169
|
+
"register.title": "Create Account",
|
|
170
|
+
"register.subtitle": "Start your learning journey",
|
|
171
|
+
"register.username": "Username",
|
|
172
|
+
"register.username_required": "Please input your username!",
|
|
173
|
+
"register.email": "Email",
|
|
174
|
+
"register.email_required": "Please input your email!",
|
|
175
|
+
"register.password": "Password",
|
|
176
|
+
"register.password_required": "Please input your password!",
|
|
177
|
+
"register.confirm_password": "Confirm Password",
|
|
178
|
+
"register.confirm_password_required": "Please confirm your password!",
|
|
179
|
+
"register.password_mismatch": "The passwords you entered don't match!",
|
|
180
|
+
"register.button": "Register",
|
|
181
|
+
"register.terms_prefix": "By registering, you agree to our",
|
|
182
|
+
"register.terms_link": "Terms of Service",
|
|
183
|
+
"register.terms_and": "and",
|
|
184
|
+
"register.privacy_link": "Privacy Policy",
|
|
185
|
+
"register.have_account": "Already have an account?",
|
|
186
|
+
"register.login_link": "Sign in",
|
|
187
|
+
"register.feature.personalized": "Personalized Learning Experience",
|
|
188
|
+
"register.feature.support": "Expert Support and Guidance",
|
|
189
|
+
"register.feature.community": "Active Learning Community",
|
|
190
|
+
"register.terms_required": "Please agree to the Terms of Service and Privacy Policy",
|
|
191
|
+
"page.error_identifier.title": "Error Identifier",
|
|
192
|
+
"page.home.title": "Home",
|
|
193
|
+
"page.404.title": "404 - Page Not Found",
|
|
194
|
+
"page.500.title": "500 - Server Error",
|
|
195
|
+
"page.login.title": "Login",
|
|
196
|
+
"page.register.title": "Register"
|
|
151
197
|
}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
|
-
"404.notComponent": "组件不存在",
|
|
3
|
-
"404.notPage": "页面不存在",
|
|
4
|
-
"500.title": "服务器错误",
|
|
5
|
-
"header.theme.label": "切换主题",
|
|
6
2
|
"err.ioc.not.implemented": "IOC 未实现",
|
|
7
3
|
"err.local.no.user.token": "本地未找到 user token",
|
|
8
4
|
"err.global.no.window": "全局未找到 window",
|
|
9
5
|
"err.within.page.provider": "必须在 PageProvider 中使用",
|
|
10
6
|
"page.about.title": "关于",
|
|
11
7
|
"page.about.description": "了解更多关于我们的项目和团队信息",
|
|
8
|
+
"404.notComponent": "组件不存在",
|
|
9
|
+
"404.notPage": "页面不存在",
|
|
10
|
+
"500.title": "服务器错误",
|
|
11
|
+
"header.theme.label": "切换主题",
|
|
12
12
|
"about.title": "关于我们",
|
|
13
13
|
"jsonStorage.title": "JSONStorage Demo",
|
|
14
14
|
"jsonStorage.title2": "永久存储测试",
|
|
@@ -54,7 +54,6 @@
|
|
|
54
54
|
"page.request.title": "请求示例",
|
|
55
55
|
"page.request.description": "展示各种请求示例和用法",
|
|
56
56
|
"page.executor.title": "执行器示例",
|
|
57
|
-
"page.executor.description": "一个强大的任务执行器,支持多种任务类型和状态管理",
|
|
58
57
|
"page.error.identifier.title": "错误标识符",
|
|
59
58
|
"page.error.identifier.description": "错误标识符的使用和示例",
|
|
60
59
|
"home.explore": "探索",
|
|
@@ -88,6 +87,7 @@
|
|
|
88
87
|
"page.request.trigger_api_catch": "触发 API 捕获结果",
|
|
89
88
|
"page.request.stop_api_catch": "停止 API 捕获结果",
|
|
90
89
|
"page.executor.main_title": "执行器",
|
|
90
|
+
"page.executor.description": "一个强大的任务执行器,支持多种任务类型和状态管理",
|
|
91
91
|
"page.executor.test_plugin.title": "测试插件",
|
|
92
92
|
"page.executor.task.status.pending": "等待中",
|
|
93
93
|
"page.executor.task.status.running": "运行中",
|
|
@@ -120,7 +120,7 @@
|
|
|
120
120
|
"page.executor.view_guide": "查看指南",
|
|
121
121
|
"page.executor.contact_support": "联系支持",
|
|
122
122
|
"page.error.identifier.main_title": "错误标识符",
|
|
123
|
-
"page.error.identifier.source_description": "来自 '@config/Identifier
|
|
123
|
+
"page.error.identifier.source_description": "来自 '@config/Identifier/Error' 的标识符",
|
|
124
124
|
"page.error.identifier.help.title": "需要帮助?",
|
|
125
125
|
"page.error.identifier.help.description": "如果您在使用错误标识符时遇到问题,请联系我们的支持团队",
|
|
126
126
|
"page.error.identifier.contact_support": "联系支持",
|
|
@@ -128,7 +128,9 @@
|
|
|
128
128
|
"about.notification.title": "通知标题",
|
|
129
129
|
"about.notification.description": "这是一条测试通知",
|
|
130
130
|
"about.button.message": "显示 Message",
|
|
131
|
+
"about.button.message2": "静态消息",
|
|
131
132
|
"about.button.notification": "显示 Notification",
|
|
133
|
+
"about.button.notification2": "静态通知",
|
|
132
134
|
"about.tooltip.text": "这是一个提示文本",
|
|
133
135
|
"about.button.tooltip": "悬停显示 Tooltip",
|
|
134
136
|
"about.modal.title": "模态框示例",
|
|
@@ -146,6 +148,50 @@
|
|
|
146
148
|
"about.alert.message": "这是一个警告提示",
|
|
147
149
|
"common.ok": "确定",
|
|
148
150
|
"common.cancel": "取消",
|
|
149
|
-
"
|
|
150
|
-
"
|
|
151
|
+
"login.welcome": "欢迎来到未来学习",
|
|
152
|
+
"login.subtitle": "解锁个性化 AI 驱动的学习体验,加速您的知识旅程。",
|
|
153
|
+
"login.forgot_password": "忘记密码?",
|
|
154
|
+
"login.continue_with": "或继续使用",
|
|
155
|
+
"login.with_google": "使用 Google 登录",
|
|
156
|
+
"login.no_account": "还没有账号?",
|
|
157
|
+
"login.create_account": "在此创建",
|
|
158
|
+
"login.email_required": "请输入您的邮箱!",
|
|
159
|
+
"login.password_required": "请输入您的密码!",
|
|
160
|
+
"login.feature.ai_paths": "AI驱动的个性化学习路径",
|
|
161
|
+
"login.feature.smart_recommendations": "智能内容推荐",
|
|
162
|
+
"login.feature.progress_tracking": "实时进度追踪",
|
|
163
|
+
"login.email.title": "输入邮箱",
|
|
164
|
+
"login.password.title": "输入密码",
|
|
165
|
+
"login.forgot_password.title": "重置密码",
|
|
166
|
+
"login.button.title": "登录账号",
|
|
167
|
+
"login.with_google.title": "使用Google账号登录",
|
|
168
|
+
"login.create_account.title": "创建新账号",
|
|
169
|
+
"register.title": "创建账号",
|
|
170
|
+
"register.subtitle": "开始您的学习之旅",
|
|
171
|
+
"register.username": "用户名",
|
|
172
|
+
"register.username_required": "请输入用户名!",
|
|
173
|
+
"register.email": "邮箱",
|
|
174
|
+
"register.email_required": "请输入邮箱!",
|
|
175
|
+
"register.password": "密码",
|
|
176
|
+
"register.password_required": "请输入密码!",
|
|
177
|
+
"register.confirm_password": "确认密码",
|
|
178
|
+
"register.confirm_password_required": "请确认密码!",
|
|
179
|
+
"register.password_mismatch": "两次输入的密码不匹配!",
|
|
180
|
+
"register.button": "注册",
|
|
181
|
+
"register.terms_prefix": "注册即表示您同意我们的",
|
|
182
|
+
"register.terms_link": "服务条款",
|
|
183
|
+
"register.terms_and": "和",
|
|
184
|
+
"register.privacy_link": "隐私政策",
|
|
185
|
+
"register.have_account": "已有账号?",
|
|
186
|
+
"register.login_link": "登录",
|
|
187
|
+
"register.feature.personalized": "个性化学习体验",
|
|
188
|
+
"register.feature.support": "专家支持和指导",
|
|
189
|
+
"register.feature.community": "活跃的学习社区",
|
|
190
|
+
"register.terms_required": "请同意服务条款和隐私政策",
|
|
191
|
+
"page.error_identifier.title": "错误标识符",
|
|
192
|
+
"page.home.title": "首页",
|
|
193
|
+
"page.404.title": "404 - 页面未找到",
|
|
194
|
+
"page.500.title": "500 - 服务器错误",
|
|
195
|
+
"page.login.title": "登录",
|
|
196
|
+
"page.register.title": "注册"
|
|
151
197
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path><text x="50" y="50" font-family="Arial" font-size="24" fill="black">fe</text></svg>
|
|
@@ -3,9 +3,10 @@ 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 {
|
|
6
|
+
import { RouteService } from './base/services/RouteService';
|
|
7
7
|
import { RouterLoader, type ComponentValue } from '@/base/cases/RouterLoader';
|
|
8
8
|
import { AntdThemeProvider } from '@brain-toolkit/antd-theme-override/react';
|
|
9
|
+
import { routerPrefix } from '@config/common';
|
|
9
10
|
|
|
10
11
|
function getAllPages() {
|
|
11
12
|
const modules = import.meta.glob('./pages/**/*.tsx');
|
|
@@ -28,10 +29,12 @@ const routerLoader = new RouterLoader({
|
|
|
28
29
|
|
|
29
30
|
function App() {
|
|
30
31
|
const routerBase = useMemo(() => {
|
|
31
|
-
const routes = IOC(
|
|
32
|
+
const routes = IOC(RouteService)
|
|
32
33
|
.getRoutes()
|
|
33
34
|
.map((route) => routerLoader.toRoute(route));
|
|
34
|
-
const router = createBrowserRouter(routes
|
|
35
|
+
const router = createBrowserRouter(routes, {
|
|
36
|
+
basename: routerPrefix
|
|
37
|
+
});
|
|
35
38
|
return router;
|
|
36
39
|
}, []);
|
|
37
40
|
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { routerPrefix } from '@config/common';
|
|
2
|
+
import { injectable } from 'inversify';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Get Publish Assets Path
|
|
6
|
+
*
|
|
7
|
+
* - If router has prefix, the path will be `prefix/path`
|
|
8
|
+
* - If router has no prefix, the path will be `path`
|
|
9
|
+
*/
|
|
10
|
+
@injectable()
|
|
11
|
+
export class PublicAssetsPath {
|
|
12
|
+
constructor(protected prefix: string = routerPrefix) {}
|
|
13
|
+
|
|
14
|
+
getPath(path: string): string {
|
|
15
|
+
return this.prefix + `/${path}`;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -1,4 +1,12 @@
|
|
|
1
|
+
export interface RegisterFormData {
|
|
2
|
+
username: string;
|
|
3
|
+
email: string;
|
|
4
|
+
password: string;
|
|
5
|
+
confirmPassword: string;
|
|
6
|
+
agreeToTerms: boolean;
|
|
7
|
+
}
|
|
1
8
|
export interface LoginInterface {
|
|
2
9
|
login(params: { username: string; password: string }): Promise<unknown>;
|
|
3
10
|
logout(): void;
|
|
11
|
+
register(params: RegisterFormData): Promise<unknown>;
|
|
4
12
|
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { SliceStore } from '@qlover/slice-store-react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Store State Interface
|
|
5
|
+
*
|
|
6
|
+
* This interface is used to define the state of a store.
|
|
7
|
+
* It is used to define the state of a store.
|
|
8
|
+
*
|
|
9
|
+
* must implement the copyWith method
|
|
10
|
+
*/
|
|
11
|
+
export interface StoreStateInterface {
|
|
12
|
+
// You can define your own properties here
|
|
13
|
+
// ...
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Store Interface
|
|
18
|
+
*
|
|
19
|
+
* This class is used to define the store interface.
|
|
20
|
+
* It is used to define the store interface.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```ts
|
|
24
|
+
* class ChatStoreState implements StoreStateInterface {
|
|
25
|
+
* isChatRunning: boolean = false;
|
|
26
|
+
*
|
|
27
|
+
* copyWith(state: { isChatRunning?: boolean }): this {
|
|
28
|
+
* return Object.assign(new ChatStoreState(), this, state);
|
|
29
|
+
* }
|
|
30
|
+
* }
|
|
31
|
+
*
|
|
32
|
+
* export class ChatStore extends StoreInterface<ChatStoreState> {
|
|
33
|
+
* constructor() {
|
|
34
|
+
* super(() => new ChatStoreState());
|
|
35
|
+
* }
|
|
36
|
+
* }
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export abstract class StoreInterface<
|
|
40
|
+
T extends StoreStateInterface
|
|
41
|
+
> extends SliceStore<T> {
|
|
42
|
+
/**
|
|
43
|
+
* Constructor
|
|
44
|
+
*
|
|
45
|
+
* @param stateFactory - The factory function to create the initial state
|
|
46
|
+
*/
|
|
47
|
+
constructor(protected stateFactory: () => T) {
|
|
48
|
+
super(stateFactory);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Reset the state of the store
|
|
53
|
+
*/
|
|
54
|
+
resetState(): void {
|
|
55
|
+
// Create a new instance of initial state
|
|
56
|
+
this.emit(this.stateFactory());
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -4,22 +4,22 @@ import LanguageDetector from 'i18next-browser-languagedetector';
|
|
|
4
4
|
import HttpApi from 'i18next-http-backend';
|
|
5
5
|
import merge from 'lodash/merge';
|
|
6
6
|
import i18nConfig from '@config/i18n';
|
|
7
|
+
import type { BootstrapExecutorPlugin } from '@qlover/corekit-bridge';
|
|
7
8
|
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
} from '
|
|
11
|
-
|
|
9
|
+
StoreInterface,
|
|
10
|
+
StoreStateInterface
|
|
11
|
+
} from '@/base/port/StoreInterface';
|
|
12
12
|
const { supportedLngs, fallbackLng } = i18nConfig;
|
|
13
13
|
|
|
14
14
|
export type I18nServiceLocale = (typeof supportedLngs)[number];
|
|
15
15
|
|
|
16
|
-
export class I18nServiceState {
|
|
16
|
+
export class I18nServiceState implements StoreStateInterface {
|
|
17
17
|
loading: boolean = false;
|
|
18
18
|
constructor(public language: I18nServiceLocale) {}
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
export class I18nService
|
|
22
|
-
extends
|
|
22
|
+
extends StoreInterface<I18nServiceState>
|
|
23
23
|
implements BootstrapExecutorPlugin
|
|
24
24
|
{
|
|
25
25
|
readonly pluginName = 'I18nService';
|
|
@@ -57,9 +57,15 @@ export class I18nService
|
|
|
57
57
|
const pathLanguageDetector = {
|
|
58
58
|
name: 'pathLanguageDetector',
|
|
59
59
|
lookup: () => {
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
60
|
+
const paths = this.pathname.split('/');
|
|
61
|
+
|
|
62
|
+
for (const path of paths) {
|
|
63
|
+
if (I18nService.isValidLanguage(path)) {
|
|
64
|
+
return path;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return fallbackLng;
|
|
63
69
|
},
|
|
64
70
|
cacheUserLanguage() {
|
|
65
71
|
// no cache, because we get language from URL
|
|
@@ -4,32 +4,32 @@ import type { UIDependenciesInterface } from '@/base/port/UIDependenciesInterfac
|
|
|
4
4
|
import type { LoggerInterface } from '@qlover/logger';
|
|
5
5
|
import { I18nService } from '@/base/services/I18nService';
|
|
6
6
|
|
|
7
|
-
export type
|
|
7
|
+
export type RouterServiceDependencies = {
|
|
8
8
|
navigate: NavigateFunction;
|
|
9
9
|
};
|
|
10
10
|
|
|
11
|
-
export type
|
|
11
|
+
export type RouterServiceOptions = {
|
|
12
12
|
config: {
|
|
13
13
|
routes: RouteConfigValue[];
|
|
14
14
|
};
|
|
15
15
|
logger: LoggerInterface;
|
|
16
16
|
};
|
|
17
17
|
|
|
18
|
-
export type
|
|
18
|
+
export type RouterServiceState = {
|
|
19
19
|
routes: RouteConfigValue[];
|
|
20
20
|
};
|
|
21
21
|
|
|
22
|
-
export class
|
|
23
|
-
implements UIDependenciesInterface<
|
|
22
|
+
export class RouteService
|
|
23
|
+
implements UIDependenciesInterface<RouterServiceDependencies>
|
|
24
24
|
{
|
|
25
25
|
/**
|
|
26
26
|
* @override
|
|
27
27
|
*/
|
|
28
|
-
dependencies?:
|
|
28
|
+
dependencies?: RouterServiceDependencies;
|
|
29
29
|
|
|
30
|
-
state:
|
|
30
|
+
state: RouterServiceState;
|
|
31
31
|
|
|
32
|
-
constructor(private options:
|
|
32
|
+
constructor(private options: RouterServiceOptions) {
|
|
33
33
|
this.state = {
|
|
34
34
|
routes: options.config.routes
|
|
35
35
|
};
|
|
@@ -49,7 +49,7 @@ export class RouterController
|
|
|
49
49
|
return navigate;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
|
|
52
|
+
composePath(path: string): string {
|
|
53
53
|
const targetLang = I18nService.getCurrentLanguage();
|
|
54
54
|
return `/${targetLang}${path}`;
|
|
55
55
|
}
|
|
@@ -57,11 +57,11 @@ export class RouterController
|
|
|
57
57
|
/**
|
|
58
58
|
* @override
|
|
59
59
|
*/
|
|
60
|
-
setDependencies(dependencies: Partial<
|
|
60
|
+
setDependencies(dependencies: Partial<RouterServiceDependencies>): void {
|
|
61
61
|
this.dependencies = Object.assign(
|
|
62
62
|
this.dependencies || {},
|
|
63
63
|
dependencies
|
|
64
|
-
) as
|
|
64
|
+
) as RouterServiceDependencies;
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
getRoutes(): RouteConfigValue[] {
|
|
@@ -73,7 +73,7 @@ export class RouterController
|
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
goto(path: string, options?: NavigateOptions): void {
|
|
76
|
-
path =
|
|
76
|
+
path = this.composePath(path);
|
|
77
77
|
this.logger.debug('Goto path => ', path);
|
|
78
78
|
this.navigate?.(path, options);
|
|
79
79
|
}
|
package/templates/react-app/src/{uikit/controllers/UserController.ts → base/services/UserService.ts}
RENAMED
|
@@ -3,17 +3,17 @@ import type {
|
|
|
3
3
|
UserApiGetUserInfoTransaction,
|
|
4
4
|
UserApiLoginTransaction
|
|
5
5
|
} from '@/base/apis/userApi/UserApiType';
|
|
6
|
-
import {
|
|
6
|
+
import { RouteService } from './RouteService';
|
|
7
7
|
import { ThreadUtil, type StorageTokenInterface } from '@qlover/corekit-bridge';
|
|
8
8
|
import { inject, injectable } from 'inversify';
|
|
9
9
|
import { IOCIdentifier } from '@/core/IOC';
|
|
10
|
-
import { LoginInterface } from '@/base/port/LoginInterface';
|
|
10
|
+
import { LoginInterface, RegisterFormData } from '@/base/port/LoginInterface';
|
|
11
11
|
import { UserApi } from '@/base/apis/userApi/UserApi';
|
|
12
12
|
import { AppError } from '@/base/cases/AppError';
|
|
13
|
-
import { LOCAL_NO_USER_TOKEN } from '@config/Identifier
|
|
14
|
-
import {
|
|
13
|
+
import { LOCAL_NO_USER_TOKEN } from '@config/Identifier/Error';
|
|
14
|
+
import { StoreInterface, StoreStateInterface } from '../port/StoreInterface';
|
|
15
15
|
|
|
16
|
-
class
|
|
16
|
+
class UserServiceState implements StoreStateInterface {
|
|
17
17
|
success: boolean = false;
|
|
18
18
|
userInfo: UserApiGetUserInfoTransaction['response']['data'] = {
|
|
19
19
|
name: '',
|
|
@@ -23,22 +23,22 @@ class UserControllerState {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
@injectable()
|
|
26
|
-
export class
|
|
27
|
-
extends
|
|
26
|
+
export class UserService
|
|
27
|
+
extends StoreInterface<UserServiceState>
|
|
28
28
|
implements ExecutorPlugin, LoginInterface
|
|
29
29
|
{
|
|
30
|
-
readonly pluginName = '
|
|
30
|
+
readonly pluginName = 'UserService';
|
|
31
31
|
|
|
32
32
|
constructor(
|
|
33
33
|
@inject(UserApi) private userApi: UserApi,
|
|
34
|
-
@inject(
|
|
34
|
+
@inject(RouteService) private routerController: RouteService,
|
|
35
35
|
@inject(IOCIdentifier.FeApiToken)
|
|
36
36
|
private userToken: StorageTokenInterface<string>
|
|
37
37
|
) {
|
|
38
|
-
super(() => new
|
|
38
|
+
super(() => new UserServiceState());
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
setState(state: Partial<
|
|
41
|
+
setState(state: Partial<UserServiceState>): void {
|
|
42
42
|
this.emit({ ...this.state, ...state });
|
|
43
43
|
}
|
|
44
44
|
|
|
@@ -111,4 +111,14 @@ export class UserController
|
|
|
111
111
|
isAuthenticated(): boolean {
|
|
112
112
|
return this.state.success;
|
|
113
113
|
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* @override
|
|
117
|
+
*/
|
|
118
|
+
async register(params: RegisterFormData): Promise<unknown> {
|
|
119
|
+
return this.login({
|
|
120
|
+
username: params.username,
|
|
121
|
+
password: params.password
|
|
122
|
+
});
|
|
123
|
+
}
|
|
114
124
|
}
|
|
@@ -4,7 +4,7 @@ import { IOC } from './IOC';
|
|
|
4
4
|
import * as globals from '@/core/globals';
|
|
5
5
|
import { IocRegister } from './registers';
|
|
6
6
|
import { BootstrapsRegistry } from './bootstraps';
|
|
7
|
-
import { GLOBAL_NO_WINDOW } from '@config/Identifier
|
|
7
|
+
import { GLOBAL_NO_WINDOW } from '@config/Identifier/Error';
|
|
8
8
|
|
|
9
9
|
export default async function startup({
|
|
10
10
|
root,
|
|
@@ -17,8 +17,10 @@ import {
|
|
|
17
17
|
import mockDataJson from '@config/feapi.mock.json';
|
|
18
18
|
import { RequestStatusCatcher } from '@/base/cases/RequestStatusCatcher';
|
|
19
19
|
import themeConfig from '@config/theme.json';
|
|
20
|
-
import { localJsonStorage } from '../globals';
|
|
20
|
+
import { localJsonStorage, logger } from '../globals';
|
|
21
21
|
import { I18nService } from '@/base/services/I18nService';
|
|
22
|
+
import { RouteService } from '@/base/services/RouteService';
|
|
23
|
+
import { base as baseRoutes } from '@config/app.router.json';
|
|
22
24
|
|
|
23
25
|
export class RegisterCommon implements InversifyRegisterInterface {
|
|
24
26
|
register(
|
|
@@ -64,6 +66,14 @@ export class RegisterCommon implements InversifyRegisterInterface {
|
|
|
64
66
|
})
|
|
65
67
|
);
|
|
66
68
|
|
|
69
|
+
container.bind(
|
|
70
|
+
RouteService,
|
|
71
|
+
new RouteService({
|
|
72
|
+
config: baseRoutes,
|
|
73
|
+
logger
|
|
74
|
+
})
|
|
75
|
+
);
|
|
76
|
+
|
|
67
77
|
container.bind(I18nService, new I18nService(options.pathname));
|
|
68
78
|
}
|
|
69
79
|
}
|
|
@@ -1,24 +1,15 @@
|
|
|
1
|
-
import { localJsonStorage
|
|
2
|
-
import { RouterController } from '@/uikit/controllers/RouterController';
|
|
1
|
+
import { localJsonStorage } from '../globals';
|
|
3
2
|
import { JSONStorageController } from '@/uikit/controllers/JSONStorageController';
|
|
4
3
|
import { ProcesserService } from '@/base/services/ProcesserService';
|
|
5
|
-
import {
|
|
6
|
-
import { base as baseRoutes } from '@config/app.router.json';
|
|
4
|
+
import { UserService } from '@/base/services/UserService';
|
|
7
5
|
import { InversifyRegisterInterface } from '../IOC';
|
|
8
6
|
import { InversifyContainer } from '../IOC';
|
|
9
|
-
|
|
10
7
|
export class RegisterControllers implements InversifyRegisterInterface {
|
|
11
8
|
register(container: InversifyContainer): void {
|
|
12
|
-
const routerController = new RouterController({
|
|
13
|
-
config: baseRoutes,
|
|
14
|
-
logger
|
|
15
|
-
});
|
|
16
|
-
|
|
17
9
|
const jsonStorageController = new JSONStorageController(localJsonStorage);
|
|
18
10
|
|
|
19
|
-
container.bind(RouterController, routerController);
|
|
20
11
|
container.bind(JSONStorageController, jsonStorageController);
|
|
21
12
|
|
|
22
|
-
container.get(ProcesserService).use(container.get(
|
|
13
|
+
container.get(ProcesserService).use(container.get(UserService));
|
|
23
14
|
}
|
|
24
15
|
}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { IOC } from '@/core/IOC';
|
|
2
|
-
import {
|
|
2
|
+
import { UserService } from '@/base/services/UserService';
|
|
3
3
|
import { Navigate, Outlet } from 'react-router-dom';
|
|
4
|
-
import {
|
|
4
|
+
import { useStore } from '@/uikit/hooks/useStore';
|
|
5
5
|
|
|
6
6
|
export default function Layout() {
|
|
7
|
-
const
|
|
8
|
-
|
|
7
|
+
const userService = IOC(UserService);
|
|
8
|
+
useStore(userService, (state) => state.success);
|
|
9
9
|
|
|
10
10
|
// If user is authenticated, redirect to home page
|
|
11
|
-
if (
|
|
11
|
+
if (userService.isAuthenticated()) {
|
|
12
12
|
return <Navigate to="/" replace />;
|
|
13
13
|
}
|
|
14
14
|
|