@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.
Files changed (45) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/dist/index.js +1 -1
  3. package/package.json +1 -1
  4. package/templates/react-app/.env +1 -2
  5. package/templates/react-app/README.md +70 -8
  6. package/templates/react-app/config/app.router.json +13 -13
  7. package/templates/react-app/config/common.ts +8 -0
  8. package/templates/react-app/config/i18n.ts +3 -1
  9. package/templates/react-app/config/{Identifier.I18n.ts → identifier/I18n.ts} +321 -3
  10. package/templates/react-app/index.html +1 -1
  11. package/templates/react-app/public/locales/en/common.json +54 -8
  12. package/templates/react-app/public/locales/zh/common.json +54 -8
  13. package/templates/react-app/public/router-root/logo.svg +1 -0
  14. package/templates/react-app/src/App.tsx +6 -3
  15. package/templates/react-app/src/base/cases/PublicAssetsPath.ts +17 -0
  16. package/templates/react-app/src/base/port/LoginInterface.ts +8 -0
  17. package/templates/react-app/src/base/port/StoreInterface.ts +58 -0
  18. package/templates/react-app/src/base/services/I18nService.ts +15 -9
  19. package/templates/react-app/src/{uikit/controllers/RouterController.ts → base/services/RouteService.ts} +12 -12
  20. package/templates/react-app/src/{uikit/controllers/UserController.ts → base/services/UserService.ts} +21 -11
  21. package/templates/react-app/src/core/bootstrap.ts +1 -1
  22. package/templates/react-app/src/core/registers/RegisterCommon.ts +11 -1
  23. package/templates/react-app/src/core/registers/RegisterControllers.ts +3 -12
  24. package/templates/react-app/src/pages/auth/Layout.tsx +5 -5
  25. package/templates/react-app/src/pages/auth/Login.tsx +50 -29
  26. package/templates/react-app/src/pages/auth/Register.tsx +238 -1
  27. package/templates/react-app/src/pages/base/About.tsx +1 -1
  28. package/templates/react-app/src/pages/base/ErrorIdentifier.tsx +2 -2
  29. package/templates/react-app/src/pages/base/Executor.tsx +4 -4
  30. package/templates/react-app/src/pages/base/Home.tsx +1 -1
  31. package/templates/react-app/src/pages/base/JSONStorage.tsx +3 -3
  32. package/templates/react-app/src/pages/base/Request.tsx +4 -4
  33. package/templates/react-app/src/pages/base/components/BaseHeader.tsx +8 -2
  34. package/templates/react-app/src/uikit/components/LanguageSwitcher.tsx +7 -7
  35. package/templates/react-app/src/uikit/components/ThemeSwitcher.tsx +3 -3
  36. package/templates/react-app/src/uikit/contexts/BaseRouteContext.ts +1 -1
  37. package/templates/react-app/src/uikit/controllers/ExecutorController.ts +6 -3
  38. package/templates/react-app/src/uikit/controllers/JSONStorageController.ts +6 -3
  39. package/templates/react-app/src/uikit/controllers/RequestController.ts +3 -4
  40. package/templates/react-app/src/uikit/hooks/useDocumentTitle.ts +15 -0
  41. package/templates/react-app/src/uikit/hooks/useStore.ts +12 -0
  42. package/templates/react-app/src/uikit/providers/BaseRouteProvider.tsx +7 -1
  43. package/templates/react-app/src/uikit/providers/ProcessProvider.tsx +7 -7
  44. package/templates/react-app/vite.config.ts +20 -11
  45. /package/templates/react-app/config/{Identifier.Error.ts → identifier/Error.ts} +0 -0
@@ -3,9 +3,11 @@ import { Form, Input, Button } from 'antd';
3
3
  import { UserOutlined, LockOutlined, GoogleOutlined } from '@ant-design/icons';
4
4
  import { IOC } from '@/core/IOC';
5
5
  import { useBaseRoutePage } from '@/uikit/contexts/BaseRouteContext';
6
- import { RouterController } from '@/uikit/controllers/RouterController';
7
- import { UserController } from '@/uikit/controllers/UserController';
8
- import { useSliceStore } from '@qlover/slice-store-react';
6
+ import { RouteService } from '@/base/services/RouteService';
7
+ import { UserService } from '@/base/services/UserService';
8
+ import { useStore } from '@/uikit/hooks/useStore';
9
+ import * as i18nKeys from '@config/Identifier/I18n';
10
+ import LocaleLink from '@/uikit/components/LocaleLink';
9
11
 
10
12
  interface LoginFormData {
11
13
  email: string;
@@ -14,19 +16,19 @@ interface LoginFormData {
14
16
 
15
17
  export default function Login() {
16
18
  const { t } = useBaseRoutePage();
17
- const userController = IOC(UserController);
19
+ const userService = IOC(UserService);
18
20
  const AppConfig = IOC('AppConfig');
19
- useSliceStore(userController);
21
+ useStore(userService);
20
22
  const [loading, setLoading] = useState(false);
21
23
 
22
24
  const handleLogin = async (values: LoginFormData) => {
23
25
  try {
24
26
  setLoading(true);
25
- await userController.login({
27
+ await userService.login({
26
28
  username: values.email,
27
29
  password: values.password
28
30
  });
29
- IOC(RouterController).replaceToHome();
31
+ IOC(RouteService).replaceToHome();
30
32
  } catch (error) {
31
33
  console.error(error);
32
34
  } finally {
@@ -45,19 +47,21 @@ export default function Login() {
45
47
  </span>
46
48
  </div>
47
49
  <h1 className="text-4xl font-bold text-text mb-4">
48
- Welcome back to the future of learning
50
+ {t(i18nKeys.LOGIN_WELCOME)}
49
51
  </h1>
50
52
  <p className="text-text-secondary text-lg mb-8">
51
- Unlock personalized AI-powered learning experiences designed to
52
- accelerate your knowledge journey.
53
+ {t(i18nKeys.LOGIN_SUBTITLE)}
53
54
  </p>
54
55
  <div className="space-y-4">
56
+ <FeatureItem icon="🎯" text={t(i18nKeys.LOGIN_FEATURE_AI_PATHS)} />
55
57
  <FeatureItem
56
58
  icon="🎯"
57
- text="AI-powered personalized learning paths"
59
+ text={t(i18nKeys.LOGIN_FEATURE_SMART_RECOMMENDATIONS)}
60
+ />
61
+ <FeatureItem
62
+ icon="📊"
63
+ text={t(i18nKeys.LOGIN_FEATURE_PROGRESS_TRACKING)}
58
64
  />
59
- <FeatureItem icon="🎯" text="Smart content recommendations" />
60
- <FeatureItem icon="📊" text="Real-time progress tracking" />
61
65
  </div>
62
66
  </div>
63
67
 
@@ -65,10 +69,10 @@ export default function Login() {
65
69
  <div className="w-full lg:w-1/2 p-8 sm:p-12 flex items-center justify-center">
66
70
  <div className="w-full max-w-[420px]">
67
71
  <h2 className="text-2xl font-semibold mb-2 text-text">
68
- {t('Sign in to your account')}
72
+ {t(i18nKeys.LOGIN_TITLE)}
69
73
  </h2>
70
74
  <p className="text-text-secondary mb-8">
71
- Enter your credentials to access your dashboard
75
+ {t(i18nKeys.LOGIN_SUBTITLE)}
72
76
  </p>
73
77
 
74
78
  <Form
@@ -83,11 +87,14 @@ export default function Login() {
83
87
  >
84
88
  <Form.Item
85
89
  name="email"
86
- rules={[{ required: true, message: 'Please input your email!' }]}
90
+ rules={[
91
+ { required: true, message: t(i18nKeys.LOGIN_EMAIL_REQUIRED) }
92
+ ]}
87
93
  >
88
94
  <Input
89
95
  prefix={<UserOutlined className="text-text-tertiary" />}
90
- placeholder={t('email')}
96
+ placeholder={t(i18nKeys.LOGIN_EMAIL)}
97
+ title={t(i18nKeys.LOGIN_EMAIL_TITLE)}
91
98
  className="h-12 text-base bg-secondary border-border"
92
99
  />
93
100
  </Form.Item>
@@ -95,19 +102,24 @@ export default function Login() {
95
102
  <Form.Item
96
103
  name="password"
97
104
  rules={[
98
- { required: true, message: 'Please input your password!' }
105
+ { required: true, message: t(i18nKeys.LOGIN_PASSWORD_REQUIRED) }
99
106
  ]}
100
107
  >
101
108
  <Input.Password
102
109
  prefix={<LockOutlined />}
103
- placeholder={t('password')}
110
+ placeholder={t(i18nKeys.LOGIN_PASSWORD)}
111
+ title={t(i18nKeys.LOGIN_PASSWORD_TITLE)}
104
112
  className="h-12 text-base"
105
113
  />
106
114
  </Form.Item>
107
115
 
108
116
  <div className="flex justify-end">
109
- <a href="#" className="text-brand hover:text-brand-hover">
110
- {t('Forgot your password?')}
117
+ <a
118
+ href="#"
119
+ className="text-brand hover:text-brand-hover"
120
+ title={t(i18nKeys.LOGIN_FORGOT_PASSWORD_TITLE)}
121
+ >
122
+ {t(i18nKeys.LOGIN_FORGOT_PASSWORD)}
111
123
  </a>
112
124
  </div>
113
125
 
@@ -116,27 +128,36 @@ export default function Login() {
116
128
  type="primary"
117
129
  htmlType="submit"
118
130
  loading={loading}
131
+ title={t(i18nKeys.LOGIN_BUTTON_TITLE)}
119
132
  className="w-full h-12 text-base"
120
133
  >
121
- {t('Sign In')}
134
+ {t(i18nKeys.LOGIN_BUTTON)}
122
135
  </Button>
123
136
  </Form.Item>
124
137
 
125
138
  <div className="text-center text-text-tertiary my-4">
126
- or continue with
139
+ {t(i18nKeys.LOGIN_CONTINUE_WITH)}
127
140
  </div>
128
141
 
129
- <Button icon={<GoogleOutlined />} className="w-full h-12 text-base">
130
- Sign in with Google
142
+ <Button
143
+ icon={<GoogleOutlined />}
144
+ className="w-full h-12 text-base"
145
+ title={t(i18nKeys.LOGIN_WITH_GOOGLE_TITLE)}
146
+ >
147
+ {t(i18nKeys.LOGIN_WITH_GOOGLE)}
131
148
  </Button>
132
149
 
133
150
  <div className="text-center mt-6">
134
151
  <span className="text-text-tertiary">
135
- Don't have an account?{' '}
152
+ {t(i18nKeys.LOGIN_NO_ACCOUNT)}{' '}
136
153
  </span>
137
- <a href="#" className="text-brand hover:text-brand-hover">
138
- Create one here
139
- </a>
154
+ <LocaleLink
155
+ href="/register"
156
+ className="text-brand hover:text-brand-hover"
157
+ title={t(i18nKeys.LOGIN_CREATE_ACCOUNT_TITLE)}
158
+ >
159
+ {t(i18nKeys.LOGIN_CREATE_ACCOUNT)}
160
+ </LocaleLink>
140
161
  </div>
141
162
  </Form>
142
163
  </div>
@@ -1,3 +1,240 @@
1
+ import { useState } from 'react';
2
+ import { Form, Input, Button, Checkbox } from 'antd';
3
+ import { UserOutlined, LockOutlined, MailOutlined } from '@ant-design/icons';
4
+ import { IOC } from '@/core/IOC';
5
+ import { useBaseRoutePage } from '@/uikit/contexts/BaseRouteContext';
6
+ import { RouteService } from '@/base/services/RouteService';
7
+ import { UserService } from '@/base/services/UserService';
8
+ import { useStore } from '@/uikit/hooks/useStore';
9
+ import * as i18nKeys from '@config/Identifier/I18n';
10
+ import type { RegisterFormData } from '@/base/port/LoginInterface';
11
+
1
12
  export default function Register() {
2
- return <div>Register</div>;
13
+ const { t } = useBaseRoutePage();
14
+ const userService = IOC(UserService);
15
+ const AppConfig = IOC('AppConfig');
16
+ useStore(userService);
17
+ const [loading, setLoading] = useState(false);
18
+ const [form] = Form.useForm();
19
+
20
+ const handleRegister = async (values: RegisterFormData) => {
21
+ try {
22
+ setLoading(true);
23
+ await userService.register(values);
24
+ IOC(RouteService).replaceToHome();
25
+ } catch (error) {
26
+ console.error(error);
27
+ } finally {
28
+ setLoading(false);
29
+ }
30
+ };
31
+
32
+ const handleLoginClick = (e: React.MouseEvent) => {
33
+ e.preventDefault();
34
+ IOC(RouteService).gotoLogin();
35
+ };
36
+
37
+ return (
38
+ <div className="flex min-h-screen text-xs1 bg-primary">
39
+ {/* Left side - Brand section */}
40
+ <div className="hidden lg:flex lg:w-1/2 bg-secondary p-12 flex-col">
41
+ <div className="flex items-center gap-3 mb-12">
42
+ <div className="w-10 h-10 bg-brand rounded-lg"></div>
43
+ <span className="text-2xl font-semibold text-text">
44
+ {AppConfig.appName}
45
+ </span>
46
+ </div>
47
+ <h1 className="text-4xl font-bold text-text mb-4">
48
+ {t(i18nKeys.REGISTER_TITLE)}
49
+ </h1>
50
+ <p className="text-text-secondary text-lg mb-8">
51
+ {t(i18nKeys.REGISTER_SUBTITLE)}
52
+ </p>
53
+ <div className="space-y-4">
54
+ <FeatureItem
55
+ icon="🎯"
56
+ text={t(i18nKeys.REGISTER_FEATURE_PERSONALIZED)}
57
+ />
58
+ <FeatureItem icon="👨‍🏫" text={t(i18nKeys.REGISTER_FEATURE_SUPPORT)} />
59
+ <FeatureItem
60
+ icon="👥"
61
+ text={t(i18nKeys.REGISTER_FEATURE_COMMUNITY)}
62
+ />
63
+ </div>
64
+ </div>
65
+
66
+ {/* Right side - Registration form */}
67
+ <div className="w-full lg:w-1/2 p-8 sm:p-12 flex items-center justify-center">
68
+ <div className="w-full max-w-[420px]">
69
+ <h2 className="text-2xl font-semibold mb-2 text-text">
70
+ {t(i18nKeys.REGISTER_TITLE)}
71
+ </h2>
72
+ <p className="text-text-secondary mb-8">
73
+ {t(i18nKeys.REGISTER_SUBTITLE)}
74
+ </p>
75
+
76
+ <Form
77
+ form={form}
78
+ name="register"
79
+ onFinish={handleRegister}
80
+ layout="vertical"
81
+ className="space-y-4"
82
+ >
83
+ <Form.Item
84
+ name="username"
85
+ rules={[
86
+ {
87
+ required: true,
88
+ message: t(i18nKeys.REGISTER_USERNAME_REQUIRED)
89
+ }
90
+ ]}
91
+ >
92
+ <Input
93
+ prefix={<UserOutlined className="text-text-tertiary" />}
94
+ placeholder={t(i18nKeys.REGISTER_USERNAME)}
95
+ className="h-12 text-base bg-secondary border-border"
96
+ />
97
+ </Form.Item>
98
+
99
+ <Form.Item
100
+ name="email"
101
+ rules={[
102
+ {
103
+ required: true,
104
+ message: t(i18nKeys.REGISTER_EMAIL_REQUIRED)
105
+ },
106
+ {
107
+ type: 'email',
108
+ message: t(i18nKeys.REGISTER_EMAIL_REQUIRED)
109
+ }
110
+ ]}
111
+ >
112
+ <Input
113
+ prefix={<MailOutlined className="text-text-tertiary" />}
114
+ placeholder={t(i18nKeys.REGISTER_EMAIL)}
115
+ className="h-12 text-base bg-secondary border-border"
116
+ />
117
+ </Form.Item>
118
+
119
+ <Form.Item
120
+ name="password"
121
+ rules={[
122
+ {
123
+ required: true,
124
+ message: t(i18nKeys.REGISTER_PASSWORD_REQUIRED)
125
+ }
126
+ ]}
127
+ >
128
+ <Input.Password
129
+ prefix={<LockOutlined />}
130
+ placeholder={t(i18nKeys.REGISTER_PASSWORD)}
131
+ className="h-12 text-base"
132
+ />
133
+ </Form.Item>
134
+
135
+ <Form.Item
136
+ name="confirmPassword"
137
+ dependencies={['password']}
138
+ rules={[
139
+ {
140
+ required: true,
141
+ message: t(i18nKeys.REGISTER_CONFIRM_PASSWORD_REQUIRED)
142
+ },
143
+ ({ getFieldValue }) => ({
144
+ validator(_, value) {
145
+ if (!value || getFieldValue('password') === value) {
146
+ return Promise.resolve();
147
+ }
148
+ return Promise.reject(
149
+ t(i18nKeys.REGISTER_PASSWORD_MISMATCH)
150
+ );
151
+ }
152
+ })
153
+ ]}
154
+ >
155
+ <Input.Password
156
+ prefix={<LockOutlined />}
157
+ placeholder={t(i18nKeys.REGISTER_CONFIRM_PASSWORD)}
158
+ className="h-12 text-base"
159
+ />
160
+ </Form.Item>
161
+
162
+ <Form.Item
163
+ name="agreeToTerms"
164
+ valuePropName="checked"
165
+ rules={[
166
+ {
167
+ validator: (_, value) =>
168
+ value
169
+ ? Promise.resolve()
170
+ : Promise.reject(
171
+ new Error(t(i18nKeys.REGISTER_TERMS_REQUIRED))
172
+ )
173
+ }
174
+ ]}
175
+ >
176
+ <Checkbox>
177
+ <span className="text-text-secondary">
178
+ {t(i18nKeys.REGISTER_TERMS_PREFIX)}{' '}
179
+ <a
180
+ href="#"
181
+ className="text-brand hover:text-brand-hover"
182
+ target="_blank"
183
+ rel="noopener noreferrer"
184
+ >
185
+ {t(i18nKeys.REGISTER_TERMS_LINK)}
186
+ </a>{' '}
187
+ {t(i18nKeys.REGISTER_TERMS_AND)}{' '}
188
+ <a
189
+ href="#"
190
+ className="text-brand hover:text-brand-hover"
191
+ target="_blank"
192
+ rel="noopener noreferrer"
193
+ >
194
+ {t(i18nKeys.REGISTER_PRIVACY_LINK)}
195
+ </a>
196
+ </span>
197
+ </Checkbox>
198
+ </Form.Item>
199
+
200
+ <Form.Item>
201
+ <Button
202
+ type="primary"
203
+ htmlType="submit"
204
+ loading={loading}
205
+ className="w-full h-12 text-base"
206
+ >
207
+ {t(i18nKeys.REGISTER_BUTTON)}
208
+ </Button>
209
+ </Form.Item>
210
+
211
+ <div className="text-center mt-6">
212
+ <span className="text-text-tertiary">
213
+ {t(i18nKeys.REGISTER_HAVE_ACCOUNT)}{' '}
214
+ </span>
215
+ <a
216
+ href="#"
217
+ className="text-brand hover:text-brand-hover"
218
+ onClick={handleLoginClick}
219
+ >
220
+ {t(i18nKeys.REGISTER_LOGIN_LINK)}
221
+ </a>
222
+ </div>
223
+ </Form>
224
+ </div>
225
+ </div>
226
+ </div>
227
+ );
228
+ }
229
+
230
+ // Helper component for feature items
231
+ function FeatureItem({ icon, text }: { icon: string; text: string }) {
232
+ return (
233
+ <div className="flex items-center gap-3 text-text">
234
+ <div className="w-8 h-8 bg-elevated rounded-lg flex items-center justify-center">
235
+ {icon}
236
+ </div>
237
+ <span>{text}</span>
238
+ </div>
239
+ );
3
240
  }
@@ -1,5 +1,5 @@
1
1
  import { useBaseRoutePage } from '@/uikit/contexts/BaseRouteContext';
2
- import * as i18nKeys from '@config/Identifier.I18n';
2
+ import * as i18nKeys from '@config/Identifier/I18n';
3
3
  import {
4
4
  Button,
5
5
  Tooltip,
@@ -1,7 +1,7 @@
1
1
  import { Button } from 'antd';
2
2
  import { useBaseRoutePage } from '@/uikit/contexts/BaseRouteContext';
3
- import * as ErrorIdentifierList from '@config/Identifier.Error';
4
- import * as i18nKeys from '@config/Identifier.I18n';
3
+ import * as ErrorIdentifierList from '@config/Identifier/Error';
4
+ import * as i18nKeys from '@config/Identifier/I18n';
5
5
 
6
6
  export default function ErrorIdentifier() {
7
7
  const { t } = useBaseRoutePage();
@@ -4,8 +4,8 @@ import { useState, useEffect } from 'react';
4
4
  import { IOC } from '@/core/IOC';
5
5
  import { JSONStorageController } from '@/uikit/controllers/JSONStorageController';
6
6
  import { ExecutorController } from '@/uikit/controllers/ExecutorController';
7
- import { useSliceStore } from '@qlover/slice-store-react';
8
- import * as i18nKeys from '@config/Identifier.I18n';
7
+ import { useStore } from '@/uikit/hooks/useStore';
8
+ import * as i18nKeys from '@config/Identifier/I18n';
9
9
 
10
10
  interface Task {
11
11
  id: string;
@@ -24,12 +24,12 @@ export default function Executor() {
24
24
  const { t } = useBaseRoutePage();
25
25
  const executorController = IOC(ExecutorController);
26
26
  const jSONStorageController = IOC(JSONStorageController);
27
- const requestTimeout = useSliceStore(
27
+ const requestTimeout = useStore(
28
28
  jSONStorageController,
29
29
  jSONStorageController.selector.requestTimeout
30
30
  );
31
31
 
32
- const helloState = useSliceStore(
32
+ const helloState = useStore(
33
33
  executorController,
34
34
  executorController.selector.helloState
35
35
  );
@@ -2,7 +2,7 @@ import { Button } from 'antd';
2
2
  import { useBaseRoutePage } from '@/uikit/contexts/BaseRouteContext';
3
3
  import LocaleLink from '@/uikit/components/LocaleLink';
4
4
  import clsx from 'clsx';
5
- import * as i18nKeys from '@config/Identifier.I18n';
5
+ import * as i18nKeys from '@config/Identifier/I18n';
6
6
 
7
7
  export default function Home() {
8
8
  const { t } = useBaseRoutePage();
@@ -2,13 +2,13 @@ import { IOC } from '@/core/IOC';
2
2
  import { useBaseRoutePage } from '@/uikit/contexts/BaseRouteContext';
3
3
  import template from 'lodash/template';
4
4
  import { JSONStorageController } from '@/uikit/controllers/JSONStorageController';
5
- import { useSliceStore } from '@qlover/slice-store-react';
5
+ import { useStore } from '@/uikit/hooks/useStore';
6
6
  import { Button, Input } from 'antd';
7
- import * as i18nKeys from '@config/Identifier.I18n';
7
+ import * as i18nKeys from '@config/Identifier/I18n';
8
8
 
9
9
  export default function JSONStorage() {
10
10
  const jsonStorageController = IOC(JSONStorageController);
11
- const controllerState = useSliceStore(jsonStorageController);
11
+ const controllerState = useStore(jsonStorageController);
12
12
  const { t } = useBaseRoutePage();
13
13
 
14
14
  return (
@@ -3,10 +3,10 @@ import { useBaseRoutePage } from '@/uikit/contexts/BaseRouteContext';
3
3
  import { JSONStorageController } from '@/uikit/controllers/JSONStorageController';
4
4
  import { RequestController } from '@/uikit/controllers/RequestController';
5
5
  import { useMemo } from 'react';
6
- import { useSliceStore } from '@qlover/slice-store-react';
6
+ import { useStore } from '@/uikit/hooks/useStore';
7
7
  import { Button } from 'antd';
8
8
  import { LoadingOutlined } from '@ant-design/icons';
9
- import * as i18nKeys from '@config/Identifier.I18n';
9
+ import * as i18nKeys from '@config/Identifier/I18n';
10
10
 
11
11
  function JSONValue({ value }: { value: unknown }) {
12
12
  const output = useMemo(() => {
@@ -25,8 +25,8 @@ function JSONValue({ value }: { value: unknown }) {
25
25
 
26
26
  export default function Request() {
27
27
  const requestController = IOC(RequestController);
28
- const requestControllerState = useSliceStore(requestController);
29
- const jsonStorageControllerState = useSliceStore(IOC(JSONStorageController));
28
+ const requestControllerState = useStore(requestController);
29
+ const jsonStorageControllerState = useStore(IOC(JSONStorageController));
30
30
  const { t } = useBaseRoutePage();
31
31
 
32
32
  return (
@@ -1,6 +1,8 @@
1
1
  import ThemeSwitcher from '@/uikit/components/ThemeSwitcher';
2
2
  import LocaleLink from '@/uikit/components/LocaleLink';
3
3
  import LanguageSwitcher from '@/uikit/components/LanguageSwitcher';
4
+ import { PublicAssetsPath } from '@/base/cases/PublicAssetsPath';
5
+ import { IOC } from '@/core/IOC';
4
6
 
5
7
  export default function BaseHeader() {
6
8
  return (
@@ -11,9 +13,13 @@ export default function BaseHeader() {
11
13
  href="/"
12
14
  className="flex items-center hover:opacity-80 transition-opacity"
13
15
  >
14
- <img src="/logo.svg" alt="logo" className="h-8 w-auto" />
16
+ <img
17
+ src={IOC(PublicAssetsPath).getPath('/logo.svg')}
18
+ alt="logo"
19
+ className="h-8 w-auto"
20
+ />
15
21
  <span className="ml-2 text-lg font-semibold text-text">
16
- {'appName'}
22
+ {IOC('AppConfig').appName}
17
23
  </span>
18
24
  </LocaleLink>
19
25
  </div>
@@ -1,20 +1,20 @@
1
1
  import { Select } from 'antd';
2
2
  import { GlobalOutlined } from '@ant-design/icons';
3
- import { useNavigate, useParams } from 'react-router-dom';
3
+ import { useLocation, useNavigate, useParams } from 'react-router-dom';
4
4
  import i18nConfig from '@config/i18n';
5
5
  import { IOC } from '@/core/IOC';
6
6
  import { I18nService, I18nServiceLocale } from '@/base/services/I18nService';
7
7
  import { useCallback } from 'react';
8
- import { useSliceStore } from '@qlover/slice-store-react';
8
+ import { useStore } from '@/uikit/hooks/useStore';
9
9
 
10
10
  const { supportedLngs } = i18nConfig;
11
11
 
12
12
  export default function LanguageSwitcher() {
13
13
  const navigate = useNavigate();
14
14
  const i18nService = IOC(I18nService);
15
- const loading = useSliceStore(i18nService, i18nService.selector.loading);
15
+ const loading = useStore(i18nService, i18nService.selector.loading);
16
16
  const { lng } = useParams<{ lng: I18nServiceLocale }>();
17
- const currentPath = window.location.pathname;
17
+ const { pathname } = useLocation();
18
18
 
19
19
  const languageOptions = supportedLngs.map((lang) => ({
20
20
  key: lang,
@@ -33,11 +33,11 @@ export default function LanguageSwitcher() {
33
33
  // Change i18n language
34
34
  await i18nService.changeLanguage(newLang);
35
35
  // Update URL path
36
- const newPath = currentPath.replace(`/${lng}`, `/${newLang}`);
37
- navigate(newPath);
36
+ navigate(pathname.replace(`/${lng}`, `/${newLang}`));
37
+
38
38
  i18nService.changeLoading(false);
39
39
  },
40
- [lng, currentPath, navigate, i18nService]
40
+ [lng, pathname, navigate, i18nService]
41
41
  );
42
42
 
43
43
  return (
@@ -1,6 +1,6 @@
1
1
  import { IOC } from '@/core/IOC';
2
2
  import { ThemeService } from '@qlover/corekit-bridge';
3
- import { useSliceStore } from '@qlover/slice-store-react';
3
+ import { useStore } from '@/uikit/hooks/useStore';
4
4
  import { useTranslation } from 'react-i18next';
5
5
  import { Select } from 'antd';
6
6
  import {
@@ -11,7 +11,7 @@ import {
11
11
  } from '@ant-design/icons';
12
12
  import clsx from 'clsx';
13
13
  import { useMemo } from 'react';
14
- import * as i18nKeys from '@config/Identifier.I18n';
14
+ import * as i18nKeys from '@config/Identifier/I18n';
15
15
 
16
16
  const colorMap: Record<
17
17
  string,
@@ -36,7 +36,7 @@ const colorMap: Record<
36
36
 
37
37
  export default function ThemeSwitcher() {
38
38
  const themeService = IOC(ThemeService);
39
- const { theme } = useSliceStore(themeService);
39
+ const { theme } = useStore(themeService);
40
40
  const themes = themeService.getSupportedThemes();
41
41
  const { t } = useTranslation('common');
42
42
 
@@ -5,7 +5,7 @@ import { RouteMeta } from '@/base/types/Page';
5
5
  import { createContext } from 'react';
6
6
  import merge from 'lodash/merge';
7
7
  import i18nConfig from '@config/i18n';
8
- import { WITHIN_PAGE_PROVIDER } from '@config/Identifier.Error';
8
+ import { WITHIN_PAGE_PROVIDER } from '@config/Identifier/Error';
9
9
 
10
10
  const { defaultNS } = i18nConfig;
11
11
 
@@ -6,9 +6,12 @@ import {
6
6
  RequestAdapterFetchConfig
7
7
  } from '@qlover/fe-corekit';
8
8
  import { inject, injectable } from 'inversify';
9
- import { SliceStore } from '@qlover/slice-store-react';
9
+ import {
10
+ StoreInterface,
11
+ StoreStateInterface
12
+ } from '@/base/port/StoreInterface';
10
13
 
11
- class ExecutorControllerState {
14
+ class ExecutorControllerState implements StoreStateInterface {
12
15
  helloState = {
13
16
  loading: false,
14
17
  result: null as Record<string, unknown> | null,
@@ -31,7 +34,7 @@ const TestPlugin: ExecutorPlugin<RequestAdapterFetchConfig> = {
31
34
  };
32
35
 
33
36
  @injectable()
34
- export class ExecutorController extends SliceStore<ExecutorControllerState> {
37
+ export class ExecutorController extends StoreInterface<ExecutorControllerState> {
35
38
  selector = {
36
39
  helloState: (state: ExecutorControllerState) => state.helloState
37
40
  };
@@ -1,15 +1,18 @@
1
1
  import { JSONStorage } from '@qlover/fe-corekit';
2
- import { SliceStore } from '@qlover/slice-store-react';
2
+ import {
3
+ StoreInterface,
4
+ StoreStateInterface
5
+ } from '@/base/port/StoreInterface';
3
6
  import { random } from 'lodash';
4
7
 
5
- interface JSONStoragePageState {
8
+ interface JSONStoragePageState extends StoreStateInterface {
6
9
  testKey1?: number;
7
10
  testKey2?: number;
8
11
  expireTime: number;
9
12
  requestTimeout: number;
10
13
  }
11
14
 
12
- export class JSONStorageController extends SliceStore<JSONStoragePageState> {
15
+ export class JSONStorageController extends StoreInterface<JSONStoragePageState> {
13
16
  selector = {
14
17
  requestTimeout: (state: JSONStoragePageState) => state.requestTimeout
15
18
  };