@qlover/create-app 0.1.10 → 0.1.12
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 +9 -0
- package/package.json +1 -1
- package/templates/react-app/.env +23 -2
- package/templates/react-app/config/common.ts +3 -0
- package/templates/react-app/lib/bootstrap/Bootstrap.ts +36 -0
- package/templates/react-app/lib/bootstrap/BootstrapExecutorPlugin.ts +20 -0
- package/templates/react-app/lib/bootstrap/IOCContainerInterface.ts +33 -0
- package/templates/react-app/lib/bootstrap/IOCManagerInterface.ts +12 -0
- package/templates/react-app/lib/bootstrap/index.ts +7 -0
- package/templates/react-app/lib/bootstrap/plugins/InjectEnv.ts +61 -0
- package/templates/react-app/lib/bootstrap/plugins/InjectGlobal.ts +36 -0
- package/templates/react-app/lib/bootstrap/plugins/InjectIOC.ts +24 -0
- package/templates/react-app/lib/env-config/injectPkgConfig.ts +11 -0
- package/templates/react-app/lib/openAiApi/OpenAIClient.ts +1 -1
- package/templates/react-app/lib/router-loader/Page.ts +64 -0
- package/templates/react-app/lib/router-loader/RouterLoader.ts +90 -0
- package/templates/react-app/package.json +3 -1
- package/templates/react-app/src/App.tsx +27 -6
- package/templates/react-app/src/base/consts/IOCIdentifier.ts +16 -0
- package/templates/react-app/src/base/port/IOCFunctionInterface.ts +39 -0
- package/templates/react-app/src/base/port/InversifyIocInterface.ts +9 -0
- package/templates/react-app/src/base/port/StorageTokenInterface.ts +22 -0
- package/templates/react-app/src/base/types/Page.ts +5 -17
- package/templates/react-app/src/base/types/global.d.ts +2 -1
- package/templates/react-app/src/components/RouterRenderComponent.tsx +16 -0
- package/templates/react-app/src/components/ThemeSwitcher.tsx +1 -1
- package/templates/react-app/src/core/AppConfig.ts +27 -0
- package/templates/react-app/src/core/AppIOCContainer.ts +30 -0
- package/templates/react-app/src/core/IOC.ts +48 -0
- package/templates/react-app/src/core/bootstrap.ts +60 -15
- package/templates/react-app/src/core/globals.ts +1 -1
- package/templates/react-app/src/core/registers/RegisterApi.ts +59 -0
- package/templates/react-app/src/core/registers/RegisterCommon.ts +21 -0
- package/templates/react-app/src/core/{feIOC → registers}/RegisterControllers.ts +16 -11
- package/templates/react-app/src/core/registers/RegisterGlobals.ts +20 -0
- package/templates/react-app/src/core/registers/index.ts +17 -0
- package/templates/react-app/src/main.tsx +4 -3
- package/templates/react-app/src/pages/auth/Layout.tsx +1 -1
- package/templates/react-app/src/pages/auth/Login.tsx +4 -4
- package/templates/react-app/src/pages/base/Executor.tsx +1 -1
- package/templates/react-app/src/pages/base/JSONStorage.tsx +1 -1
- package/templates/react-app/src/pages/base/Request.tsx +1 -1
- package/templates/react-app/src/services/{i18n/index.ts → I18nService.ts} +25 -17
- package/templates/react-app/src/services/processer/ProcesserService.ts +2 -2
- package/templates/react-app/src/uikit/contexts/BaseRouteContext.ts +7 -1
- package/templates/react-app/src/uikit/controllers/RouterController.ts +13 -16
- package/templates/react-app/src/uikit/hooks/useLanguageGuard.ts +1 -1
- package/templates/react-app/src/uikit/providers/ProcessProvider.tsx +2 -4
- package/templates/react-app/src/uikit/utils/RequestLogger.ts +4 -1
- package/templates/react-app/tsconfig.json +2 -1
- package/templates/react-app/tsconfig.node.json +6 -2
- package/templates/react-app/vite.config.ts +14 -0
- package/templates/react-app/config/app.common.ts +0 -52
- package/templates/react-app/src/base/port/IOCInterface.ts +0 -53
- package/templates/react-app/src/core/feIOC/FeIOC.ts +0 -32
- package/templates/react-app/src/core/feIOC/RegisterApi.ts +0 -41
- package/templates/react-app/src/core/feIOC/RegisterCommon.ts +0 -20
- package/templates/react-app/src/core/index.ts +0 -31
- package/templates/react-app/src/pages/index.tsx +0 -113
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Suspense } from 'react';
|
|
2
|
+
import { Loading } from './Loading';
|
|
3
|
+
import { RouterLoaderRender } from '@lib/router-loader/RouterLoader';
|
|
4
|
+
import BaseRouteProvider from '@/uikit/providers/BaseRouteProvider';
|
|
5
|
+
|
|
6
|
+
export const RouterRenderComponent: RouterLoaderRender = (route) => {
|
|
7
|
+
const Component = route.element();
|
|
8
|
+
|
|
9
|
+
return (
|
|
10
|
+
<Suspense fallback={<Loading fullscreen />}>
|
|
11
|
+
<BaseRouteProvider {...route.meta}>
|
|
12
|
+
<Component />
|
|
13
|
+
</BaseRouteProvider>
|
|
14
|
+
</Suspense>
|
|
15
|
+
);
|
|
16
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { EnvConfigInterface } from '@lib/bootstrap';
|
|
2
|
+
|
|
3
|
+
class AppConfig implements EnvConfigInterface {
|
|
4
|
+
readonly appName = '';
|
|
5
|
+
readonly appVersion = '';
|
|
6
|
+
/**
|
|
7
|
+
* vite mode
|
|
8
|
+
*/
|
|
9
|
+
readonly env = import.meta.env.VITE_USER_NODE_ENV;
|
|
10
|
+
|
|
11
|
+
readonly userTokenStorageKey = 'fe_user_token';
|
|
12
|
+
readonly openAiModels = [
|
|
13
|
+
'gpt-4o-mini',
|
|
14
|
+
'gpt-3.5-turbo',
|
|
15
|
+
'gpt-3.5-turbo-2',
|
|
16
|
+
'gpt-4',
|
|
17
|
+
'gpt-4-32k'
|
|
18
|
+
];
|
|
19
|
+
readonly openAiBaseUrl = '';
|
|
20
|
+
readonly openAiToken = '';
|
|
21
|
+
readonly openAiTokenPrefix = '';
|
|
22
|
+
readonly openAiRequireToken = true;
|
|
23
|
+
readonly loginUser = '';
|
|
24
|
+
readonly loginPassword = '';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export default new AppConfig();
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { InversifyRegisterInterface } from '@/base/port/InversifyIocInterface';
|
|
2
|
+
import type { IOCContainerInterface } from '@lib/bootstrap';
|
|
3
|
+
import { ServiceIdentifier, Container } from 'inversify';
|
|
4
|
+
|
|
5
|
+
export class AppIOCContainer implements IOCContainerInterface {
|
|
6
|
+
private container: Container;
|
|
7
|
+
|
|
8
|
+
constructor() {
|
|
9
|
+
this.container = new Container({
|
|
10
|
+
// allow `@injectable` decorator, auto bind injectable classes
|
|
11
|
+
autobind: true,
|
|
12
|
+
// use singleton scope
|
|
13
|
+
defaultScope: 'Singleton'
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
configure(registers?: InversifyRegisterInterface[]): void {
|
|
18
|
+
if (registers) {
|
|
19
|
+
registers.forEach((register) => register.register(this.container, this));
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
bind<T>(key: ServiceIdentifier<T>, value: T): void {
|
|
24
|
+
this.container.bind<T>(key).toConstantValue(value);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
get<T>(key: string): T {
|
|
28
|
+
return this.container.get<T>(key);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// ! dont't import tsx, only ts file
|
|
2
|
+
import type { IOCContainerInterface } from '@lib/bootstrap';
|
|
3
|
+
import type {
|
|
4
|
+
IOCIdentifierMap,
|
|
5
|
+
IOCFunctionInterface
|
|
6
|
+
} from '@/base/port/IOCFunctionInterface';
|
|
7
|
+
import type { ServiceIdentifier } from 'inversify';
|
|
8
|
+
|
|
9
|
+
let implemention: IOCContainerInterface | null;
|
|
10
|
+
|
|
11
|
+
function ioc<T>(serviceIdentifier: ServiceIdentifier<T>): T;
|
|
12
|
+
function ioc<K extends keyof IOCIdentifierMap>(
|
|
13
|
+
serviceIdentifier: K
|
|
14
|
+
): IOCIdentifierMap[K];
|
|
15
|
+
function ioc<T, K extends keyof IOCIdentifierMap>(
|
|
16
|
+
serviceIdentifier: ServiceIdentifier<T> | K
|
|
17
|
+
): T | IOCIdentifierMap[K] {
|
|
18
|
+
if (!implemention) {
|
|
19
|
+
throw new Error('IOC is not implemented');
|
|
20
|
+
}
|
|
21
|
+
return implemention.get(serviceIdentifier);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* IOC manager function.
|
|
26
|
+
*
|
|
27
|
+
* eg.
|
|
28
|
+
* ```ts
|
|
29
|
+
* class A {}
|
|
30
|
+
* const a = IOC(A);
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* or
|
|
34
|
+
* ```ts
|
|
35
|
+
* const a = IOC<A>();
|
|
36
|
+
* ```
|
|
37
|
+
*
|
|
38
|
+
* or use get(),
|
|
39
|
+
*/
|
|
40
|
+
export const IOC: IOCFunctionInterface = Object.assign(ioc, {
|
|
41
|
+
get implemention() {
|
|
42
|
+
return implemention;
|
|
43
|
+
},
|
|
44
|
+
implement: (container: IOCContainerInterface) => {
|
|
45
|
+
implemention = container;
|
|
46
|
+
},
|
|
47
|
+
get: ioc
|
|
48
|
+
});
|
|
@@ -1,21 +1,66 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import {
|
|
2
|
+
Bootstrap,
|
|
3
|
+
BootstrapExecutorPlugin,
|
|
4
|
+
InjectEnv,
|
|
5
|
+
InjectIOC,
|
|
6
|
+
InjectGlobal
|
|
7
|
+
} from '@lib/bootstrap';
|
|
8
|
+
import { AppIOCContainer } from '@/core/AppIOCContainer';
|
|
9
|
+
import AppConfig from '@/core/AppConfig';
|
|
10
|
+
import { envPrefix, browserGlobalsName } from '@config/common';
|
|
11
|
+
import { IOC } from './IOC';
|
|
12
|
+
import * as globals from '@/core/globals';
|
|
13
|
+
import { I18nService } from '@/services/I18nService';
|
|
14
|
+
import { registerList } from './registers';
|
|
5
15
|
|
|
6
|
-
|
|
7
|
-
|
|
16
|
+
/**
|
|
17
|
+
* Bootstrap
|
|
18
|
+
*
|
|
19
|
+
* 1. inject env config to AppConfig
|
|
20
|
+
* 2. inject IOC to Application
|
|
21
|
+
* 3. inject globals to window
|
|
22
|
+
*
|
|
23
|
+
*/
|
|
24
|
+
export default function startup(root: typeof globalThis) {
|
|
25
|
+
const window =
|
|
26
|
+
typeof root !== 'undefined' && root instanceof Window ? root : undefined;
|
|
8
27
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
28
|
+
if (!window) {
|
|
29
|
+
throw new Error('Not Found Window');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const bootstrap = new Bootstrap(new AppIOCContainer());
|
|
14
33
|
|
|
15
|
-
|
|
16
|
-
|
|
34
|
+
/**
|
|
35
|
+
* bootstrap start list
|
|
36
|
+
*
|
|
37
|
+
* - inject env config to AppConfig
|
|
38
|
+
* - inject IOC to Application
|
|
39
|
+
* - inject globals to window
|
|
40
|
+
* - inject i18n service to Application
|
|
41
|
+
*/
|
|
42
|
+
const bootstrapList: BootstrapExecutorPlugin[] = [
|
|
43
|
+
new InjectEnv(AppConfig, import.meta.env, envPrefix),
|
|
44
|
+
new InjectIOC(IOC, registerList),
|
|
45
|
+
new InjectGlobal(globals, browserGlobalsName),
|
|
46
|
+
new I18nService(window.location.pathname)
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
if (AppConfig.env !== 'production') {
|
|
50
|
+
bootstrapList.push({
|
|
51
|
+
pluginName: 'InjectDevTools',
|
|
52
|
+
onBefore() {
|
|
53
|
+
console.log(AppConfig);
|
|
54
|
+
},
|
|
55
|
+
onError({ error }) {
|
|
56
|
+
console.error(`${AppConfig.appName} starup error:`, error);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
}
|
|
17
60
|
|
|
18
|
-
|
|
19
|
-
|
|
61
|
+
try {
|
|
62
|
+
bootstrap.use(bootstrapList).start(root);
|
|
63
|
+
} catch (error) {
|
|
64
|
+
console.error(`${AppConfig.appName} starup error:`, error);
|
|
20
65
|
}
|
|
21
66
|
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
InversifyRegisterInterface,
|
|
3
|
+
InversifyRegisterContainer
|
|
4
|
+
} from '@/base/port/InversifyIocInterface';
|
|
5
|
+
|
|
6
|
+
import { RequestLogger } from '@/uikit/utils/RequestLogger';
|
|
7
|
+
import { localJsonStorage } from '../globals';
|
|
8
|
+
import { FetchAbortPlugin, FetchURLPlugin } from '@qlover/fe-utils';
|
|
9
|
+
import { FeApiMockPlugin } from '@/base/apis/feApi';
|
|
10
|
+
import { OpenAIClient } from '@lib/openAiApi';
|
|
11
|
+
import { FeApi } from '@/base/apis/feApi';
|
|
12
|
+
import { RequestCommonPlugin } from '@lib/request-common-plugin';
|
|
13
|
+
import mockDataJson from '@config/feapi.mock.json';
|
|
14
|
+
import AppConfig from '@/core/AppConfig';
|
|
15
|
+
|
|
16
|
+
export class RegisterApi implements InversifyRegisterInterface {
|
|
17
|
+
register(container: InversifyRegisterContainer): void {
|
|
18
|
+
const openAiApi = new OpenAIClient({
|
|
19
|
+
baseURL: AppConfig.openAiBaseUrl,
|
|
20
|
+
models: AppConfig.openAiModels,
|
|
21
|
+
commonPluginConfig: {
|
|
22
|
+
tokenPrefix: AppConfig.openAiTokenPrefix,
|
|
23
|
+
token: AppConfig.openAiToken,
|
|
24
|
+
defaultHeaders: {
|
|
25
|
+
'Content-Type': 'application/json'
|
|
26
|
+
},
|
|
27
|
+
defaultRequestData: {
|
|
28
|
+
model: AppConfig.openAiModels[0],
|
|
29
|
+
stream: true
|
|
30
|
+
},
|
|
31
|
+
requiredToken: true,
|
|
32
|
+
requestDataSerializer: (data) => JSON.stringify(data)
|
|
33
|
+
}
|
|
34
|
+
}).usePlugin(container.get(RequestLogger));
|
|
35
|
+
|
|
36
|
+
const abortPlugin = container.get(FetchAbortPlugin);
|
|
37
|
+
const feApi = new FeApi({
|
|
38
|
+
abortPlugin,
|
|
39
|
+
config: {
|
|
40
|
+
responseType: 'json',
|
|
41
|
+
baseURL: 'https://api.example.com/'
|
|
42
|
+
}
|
|
43
|
+
})
|
|
44
|
+
.usePlugin(new FetchURLPlugin())
|
|
45
|
+
.usePlugin(
|
|
46
|
+
new RequestCommonPlugin({
|
|
47
|
+
tokenPrefix: AppConfig.openAiTokenPrefix,
|
|
48
|
+
requiredToken: true,
|
|
49
|
+
token: () => localJsonStorage.getItem('fe_user_token')
|
|
50
|
+
})
|
|
51
|
+
)
|
|
52
|
+
.usePlugin(new FeApiMockPlugin(mockDataJson))
|
|
53
|
+
.usePlugin(container.get(RequestLogger))
|
|
54
|
+
.usePlugin(abortPlugin);
|
|
55
|
+
|
|
56
|
+
container.bind(OpenAIClient).toConstantValue(openAiApi);
|
|
57
|
+
container.bind(FeApi).toConstantValue(feApi);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
InversifyRegisterInterface,
|
|
3
|
+
InversifyRegisterContainer
|
|
4
|
+
} from '@/base/port/InversifyIocInterface';
|
|
5
|
+
|
|
6
|
+
import { FetchAbortPlugin, JSONStorage } from '@qlover/fe-utils';
|
|
7
|
+
import { UserToken } from '@/base/cases/UserToken';
|
|
8
|
+
import AppConfig from '@/core/AppConfig';
|
|
9
|
+
|
|
10
|
+
export class RegisterCommon implements InversifyRegisterInterface {
|
|
11
|
+
register(container: InversifyRegisterContainer): void {
|
|
12
|
+
const feApiAbort = new FetchAbortPlugin();
|
|
13
|
+
const userToken = new UserToken({
|
|
14
|
+
storageKey: AppConfig.userTokenStorageKey,
|
|
15
|
+
storage: container.get(JSONStorage)
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
container.bind(FetchAbortPlugin).toConstantValue(feApiAbort);
|
|
19
|
+
container.bind(UserToken).toConstantValue(userToken);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type {
|
|
2
|
+
InversifyRegisterInterface,
|
|
3
|
+
InversifyRegisterContainer
|
|
4
|
+
} from '@/base/port/InversifyIocInterface';
|
|
5
|
+
|
|
2
6
|
import { localJsonStorage, logger } from '../globals';
|
|
3
7
|
import { RouterController } from '@/uikit/controllers/RouterController';
|
|
4
8
|
import { JSONStorageController } from '@/uikit/controllers/JSONStorageController';
|
|
@@ -10,13 +14,12 @@ import { ThemeController } from '@lib/fe-react-theme/ThemeController';
|
|
|
10
14
|
import { RouteConfig } from '@/base/types/Page';
|
|
11
15
|
import { OpenAIClient } from '@lib/openAiApi';
|
|
12
16
|
import { FeApi } from '@/base/apis/feApi';
|
|
13
|
-
|
|
14
17
|
import appRouterConfig from '@config/app.router.json';
|
|
15
18
|
import themeConfigJson from '@config/theme.json';
|
|
16
19
|
import { UserToken } from '@/base/cases/UserToken';
|
|
17
20
|
|
|
18
|
-
export class RegisterControllers implements
|
|
19
|
-
register(container:
|
|
21
|
+
export class RegisterControllers implements InversifyRegisterInterface {
|
|
22
|
+
register(container: InversifyRegisterContainer): void {
|
|
20
23
|
const routerController = new RouterController({
|
|
21
24
|
config: appRouterConfig.base as RouteConfig,
|
|
22
25
|
logger
|
|
@@ -42,12 +45,14 @@ export class RegisterControllers implements IOCRegisterInterface {
|
|
|
42
45
|
logger
|
|
43
46
|
}).usePlugin(userController);
|
|
44
47
|
|
|
45
|
-
container.bind(RouterController
|
|
46
|
-
container
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
container.bind(
|
|
50
|
-
container.bind(
|
|
51
|
-
container.bind(
|
|
48
|
+
container.bind(RouterController).toConstantValue(routerController);
|
|
49
|
+
container
|
|
50
|
+
.bind(JSONStorageController)
|
|
51
|
+
.toConstantValue(jsonStorageController);
|
|
52
|
+
container.bind(RequestController).toConstantValue(requestController);
|
|
53
|
+
container.bind(ExecutorController).toConstantValue(executorController);
|
|
54
|
+
container.bind(UserController).toConstantValue(userController);
|
|
55
|
+
container.bind(ThemeController).toConstantValue(themeController);
|
|
56
|
+
container.bind(ProcesserService).toConstantValue(pageProcesser);
|
|
52
57
|
}
|
|
53
58
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
InversifyRegisterInterface,
|
|
3
|
+
InversifyRegisterContainer
|
|
4
|
+
} from '@/base/port/InversifyIocInterface';
|
|
5
|
+
import { JSON, localJsonStorage, logger } from '../globals';
|
|
6
|
+
import { JSONSerializer, JSONStorage, Logger } from '@qlover/fe-utils';
|
|
7
|
+
import { IOCIdentifier } from '@/base/consts/IOCIdentifier';
|
|
8
|
+
|
|
9
|
+
export class RegisterGlobals implements InversifyRegisterInterface {
|
|
10
|
+
register(container: InversifyRegisterContainer): void {
|
|
11
|
+
container.bind(JSONSerializer).toConstantValue(JSON);
|
|
12
|
+
container.bind(IOCIdentifier.JSON).toConstantValue(JSON);
|
|
13
|
+
|
|
14
|
+
container.bind(Logger).toConstantValue(logger);
|
|
15
|
+
container.bind(IOCIdentifier.Logger).toConstantValue(logger);
|
|
16
|
+
|
|
17
|
+
container.bind(JSONStorage).toConstantValue(localJsonStorage);
|
|
18
|
+
container.bind(IOCIdentifier.JSONStorage).toConstantValue(localJsonStorage);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { InversifyRegisterInterface } from '@/base/port/InversifyIocInterface';
|
|
2
|
+
import { RegisterGlobals } from './RegisterGlobals';
|
|
3
|
+
import { RegisterCommon } from './RegisterCommon';
|
|
4
|
+
import { RegisterApi } from './RegisterApi';
|
|
5
|
+
import { RegisterControllers } from './RegisterControllers';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Register List
|
|
9
|
+
*
|
|
10
|
+
* Register List is used to register dependencies in bootstrap
|
|
11
|
+
*/
|
|
12
|
+
export const registerList: InversifyRegisterInterface[] = [
|
|
13
|
+
new RegisterGlobals(),
|
|
14
|
+
new RegisterCommon(),
|
|
15
|
+
new RegisterApi(),
|
|
16
|
+
new RegisterControllers()
|
|
17
|
+
];
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
// !only this file use `window`, `document` ...global variables
|
|
2
|
+
import 'reflect-metadata';
|
|
1
3
|
import { StrictMode } from 'react';
|
|
2
4
|
import { createRoot } from 'react-dom/client';
|
|
3
5
|
import App from './App.tsx';
|
|
4
|
-
import
|
|
5
|
-
import { FeIOC } from './core/feIOC/FeIOC';
|
|
6
|
+
import startup from './core/bootstrap';
|
|
6
7
|
|
|
7
|
-
|
|
8
|
+
startup(window);
|
|
8
9
|
|
|
9
10
|
createRoot(document.getElementById('root')!).render(
|
|
10
11
|
<StrictMode>
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { useState } from 'react';
|
|
2
2
|
import { useController } from '@lib/fe-react-controller';
|
|
3
|
-
import { IOC } from '@/core';
|
|
3
|
+
import { IOC } from '@/core/IOC';
|
|
4
4
|
import { useBaseRoutePage } from '@/uikit/contexts/BaseRouteContext';
|
|
5
|
-
import { defaultLoginInfo } from '@config/app.common';
|
|
6
5
|
import { RouterController } from '@/uikit/controllers/RouterController';
|
|
7
6
|
import { UserController } from '@/uikit/controllers/UserController';
|
|
7
|
+
import AppConfig from '@/core/AppConfig';
|
|
8
8
|
|
|
9
9
|
export default function Login() {
|
|
10
10
|
const { t } = useBaseRoutePage();
|
|
11
11
|
const controller = useController(IOC(UserController));
|
|
12
|
-
const [email, setEmail] = useState(
|
|
13
|
-
const [password, setPassword] = useState(
|
|
12
|
+
const [email, setEmail] = useState(AppConfig.loginUser);
|
|
13
|
+
const [password, setPassword] = useState(AppConfig.loginPassword);
|
|
14
14
|
const [loading, setLoading] = useState(false);
|
|
15
15
|
|
|
16
16
|
const handleLogin = async () => {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { IOC } from '@/core';
|
|
1
|
+
import { IOC } from '@/core/IOC';
|
|
2
2
|
import { useControllerState } from '@lib/fe-react-controller';
|
|
3
3
|
import { useBaseRoutePage } from '@/uikit/contexts/BaseRouteContext';
|
|
4
4
|
import { JSONStorageController } from '@/uikit/controllers/JSONStorageController';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { IOC } from '@/core';
|
|
1
|
+
import { IOC } from '@/core/IOC';
|
|
2
2
|
import { useControllerState } from '@lib/fe-react-controller';
|
|
3
3
|
import { useBaseRoutePage } from '@/uikit/contexts/BaseRouteContext';
|
|
4
4
|
import { JSONStorageController } from '@/uikit/controllers/JSONStorageController';
|
|
@@ -4,24 +4,16 @@ 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, I18nServiceLocale } from '@config/i18n';
|
|
7
|
+
import type { BootstrapExecutorPlugin } from '@lib/bootstrap';
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
return I18nService.isValidLanguage(language)
|
|
15
|
-
? language
|
|
16
|
-
: i18nConfig.fallbackLng;
|
|
17
|
-
},
|
|
18
|
-
cacheUserLanguage() {
|
|
19
|
-
// no cache, because we get language from URL
|
|
20
|
-
}
|
|
21
|
-
};
|
|
9
|
+
const { supportedLngs, fallbackLng } = i18nConfig;
|
|
10
|
+
|
|
11
|
+
export class I18nService implements BootstrapExecutorPlugin {
|
|
12
|
+
readonly pluginName = 'I18nService';
|
|
13
|
+
|
|
14
|
+
constructor(private pathname: string) {}
|
|
22
15
|
|
|
23
|
-
|
|
24
|
-
static init(): void {
|
|
16
|
+
onBefore(): void {
|
|
25
17
|
i18n
|
|
26
18
|
.use(HttpApi)
|
|
27
19
|
.use(LanguageDetector)
|
|
@@ -36,15 +28,31 @@ export class I18nService {
|
|
|
36
28
|
);
|
|
37
29
|
|
|
38
30
|
// add custom detector
|
|
31
|
+
// custom language detector
|
|
32
|
+
const pathLanguageDetector = {
|
|
33
|
+
name: 'pathLanguageDetector',
|
|
34
|
+
lookup: () => {
|
|
35
|
+
const path = this.pathname.split('/');
|
|
36
|
+
const language = path[1];
|
|
37
|
+
return I18nService.isValidLanguage(language) ? language : fallbackLng;
|
|
38
|
+
},
|
|
39
|
+
cacheUserLanguage() {
|
|
40
|
+
// no cache, because we get language from URL
|
|
41
|
+
}
|
|
42
|
+
};
|
|
39
43
|
i18n.services.languageDetector.addDetector(pathLanguageDetector);
|
|
40
44
|
}
|
|
41
45
|
|
|
46
|
+
static getCurrentLanguage(): I18nServiceLocale {
|
|
47
|
+
return i18n.language as I18nServiceLocale;
|
|
48
|
+
}
|
|
49
|
+
|
|
42
50
|
/**
|
|
43
51
|
* check if the language is supported
|
|
44
52
|
* @param language - language to check
|
|
45
53
|
* @returns true if the language is supported, false otherwise
|
|
46
54
|
*/
|
|
47
55
|
static isValidLanguage(language: string): language is I18nServiceLocale {
|
|
48
|
-
return
|
|
56
|
+
return supportedLngs.includes(language as I18nServiceLocale);
|
|
49
57
|
}
|
|
50
58
|
}
|
|
@@ -22,8 +22,8 @@ export class ProcesserService {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
init(): Promise<unknown> {
|
|
25
|
-
return this.executor.exec(this.handler).catch(() => {
|
|
26
|
-
this.options.logger.error('PageProcesser init failed');
|
|
25
|
+
return this.executor.exec(this.handler).catch((err) => {
|
|
26
|
+
this.options.logger.error('PageProcesser init failed', err);
|
|
27
27
|
});
|
|
28
28
|
}
|
|
29
29
|
}
|
|
@@ -3,8 +3,14 @@ import { useContext, useMemo } from 'react';
|
|
|
3
3
|
import { BasePageProvider } from '@/base/types/Page';
|
|
4
4
|
import { RouteMeta } from '@/base/types/Page';
|
|
5
5
|
import { createContext } from 'react';
|
|
6
|
-
import { defaultBaseRoutemeta } from '@config/app.common';
|
|
7
6
|
import merge from 'lodash/merge';
|
|
7
|
+
import { i18nConfig } from '@config/i18n';
|
|
8
|
+
|
|
9
|
+
const defaultBaseRoutemeta = {
|
|
10
|
+
localNamespace: i18nConfig.defaultNS,
|
|
11
|
+
title: '',
|
|
12
|
+
icon: ''
|
|
13
|
+
};
|
|
8
14
|
|
|
9
15
|
export const BaseRoutePageContext = createContext<RouteMeta>({});
|
|
10
16
|
|
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
import { I18nService } from '@/services/
|
|
2
|
-
import { RouteConfig, RouteType } from '@/base/types/Page';
|
|
1
|
+
import { I18nService } from '@/services/I18nService';
|
|
3
2
|
import { UIDependenciesInterface } from '@/base/port/UIDependenciesInterface';
|
|
4
|
-
import { i18nConfig } from '@config/i18n';
|
|
5
3
|
import { Logger } from '@qlover/fe-utils';
|
|
6
4
|
import { NavigateFunction, NavigateOptions } from 'react-router-dom';
|
|
5
|
+
import { RouteConfigValue } from '@lib/router-loader/RouterLoader';
|
|
6
|
+
import { RouteConfig } from '@/base/types/Page';
|
|
7
7
|
|
|
8
8
|
export type RouterControllerDependencies = {
|
|
9
|
-
location: globalThis.Location;
|
|
10
9
|
navigate: NavigateFunction;
|
|
11
10
|
};
|
|
12
11
|
|
|
@@ -16,7 +15,7 @@ export type RouterControllerOptions = {
|
|
|
16
15
|
};
|
|
17
16
|
|
|
18
17
|
export type RouterControllerState = {
|
|
19
|
-
routes:
|
|
18
|
+
routes: RouteConfigValue[];
|
|
20
19
|
};
|
|
21
20
|
|
|
22
21
|
export class RouterController
|
|
@@ -49,6 +48,11 @@ export class RouterController
|
|
|
49
48
|
return navigate;
|
|
50
49
|
}
|
|
51
50
|
|
|
51
|
+
static composePath(path: string): string {
|
|
52
|
+
const targetLang = I18nService.getCurrentLanguage();
|
|
53
|
+
return `/${targetLang}${path}`;
|
|
54
|
+
}
|
|
55
|
+
|
|
52
56
|
/**
|
|
53
57
|
* @override
|
|
54
58
|
*/
|
|
@@ -59,24 +63,17 @@ export class RouterController
|
|
|
59
63
|
) as RouterControllerDependencies;
|
|
60
64
|
}
|
|
61
65
|
|
|
62
|
-
getRoutes():
|
|
66
|
+
getRoutes(): RouteConfigValue[] {
|
|
63
67
|
return this.state.routes;
|
|
64
68
|
}
|
|
65
69
|
|
|
66
|
-
changeRoutes(routes:
|
|
70
|
+
changeRoutes(routes: RouteConfigValue[]): void {
|
|
67
71
|
this.state.routes = routes;
|
|
68
72
|
}
|
|
69
73
|
|
|
70
74
|
goto(path: string, options?: NavigateOptions): void {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
const tryLng = path.split('/')[0];
|
|
74
|
-
if (!I18nService.isValidLanguage(tryLng)) {
|
|
75
|
-
path = i18nConfig.fallbackLng + path.replace(tryLng, '');
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
this.navigate?.(path, options);
|
|
79
|
-
|
|
75
|
+
path = RouterController.composePath(path);
|
|
76
|
+
this.logger.debug('Goto path => ', path);
|
|
80
77
|
this.navigate?.(path, options);
|
|
81
78
|
}
|
|
82
79
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createContext, useEffect } from 'react';
|
|
2
|
-
import { IOC } from '@/core';
|
|
2
|
+
import { IOC } from '@/core/IOC';
|
|
3
3
|
import { useLanguageGuard } from '@/uikit/hooks/useLanguageGuard';
|
|
4
4
|
import { useStrictEffect } from '@/uikit/hooks/useStrictEffect';
|
|
5
5
|
import { ProcesserService } from '@/services/processer/ProcesserService';
|
|
@@ -26,9 +26,7 @@ export function ProcessProvider({ children }: { children: React.ReactNode }) {
|
|
|
26
26
|
}, []);
|
|
27
27
|
|
|
28
28
|
useEffect(() => {
|
|
29
|
-
IOC(RouterController).setDependencies({
|
|
30
|
-
navigate
|
|
31
|
-
});
|
|
29
|
+
IOC(RouterController).setDependencies({ navigate });
|
|
32
30
|
}, [navigate]);
|
|
33
31
|
|
|
34
32
|
if (!success) {
|