@qlover/create-app 0.0.1
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 +10 -0
- package/bin/create-app.js +28 -0
- package/dist/cjs/index.d.ts +67 -0
- package/dist/cjs/index.js +1 -0
- package/dist/es/index.d.ts +67 -0
- package/dist/es/index.js +1 -0
- package/package.json +60 -0
- package/templates/fe-react/.env +3 -0
- package/templates/fe-react/README.md +50 -0
- package/templates/fe-react/config/app.common.ts +52 -0
- package/templates/fe-react/config/app.router.json +150 -0
- package/templates/fe-react/config/feapi.mock.json +14 -0
- package/templates/fe-react/config/i18n.ts +21 -0
- package/templates/fe-react/config/theme.json +90 -0
- package/templates/fe-react/eslint.config.js +31 -0
- package/templates/fe-react/index.html +13 -0
- package/templates/fe-react/lib/fe-react-controller/FeController.ts +15 -0
- package/templates/fe-react/lib/fe-react-controller/index.ts +2 -0
- package/templates/fe-react/lib/fe-react-controller/useController.ts +71 -0
- package/templates/fe-react/lib/fe-react-theme/ThemeController.ts +40 -0
- package/templates/fe-react/lib/fe-react-theme/ThemeStateGetter.ts +53 -0
- package/templates/fe-react/lib/fe-react-theme/index.ts +3 -0
- package/templates/fe-react/lib/fe-react-theme/tw-generator.js +239 -0
- package/templates/fe-react/lib/fe-react-theme/type.ts +21 -0
- package/templates/fe-react/lib/openAiApi/OpenAIAuthPlugin.ts +29 -0
- package/templates/fe-react/lib/openAiApi/OpenAIClient.ts +51 -0
- package/templates/fe-react/lib/openAiApi/StreamProcessor.ts +81 -0
- package/templates/fe-react/lib/openAiApi/index.ts +3 -0
- package/templates/fe-react/lib/request-common-plugin/index.ts +169 -0
- package/templates/fe-react/lib/tw-root10px/index.css +4 -0
- package/templates/fe-react/lib/tw-root10px/index.js +178 -0
- package/templates/fe-react/package.json +49 -0
- package/templates/fe-react/postcss.config.js +6 -0
- package/templates/fe-react/public/locales/en/about.json +3 -0
- package/templates/fe-react/public/locales/en/common.json +6 -0
- package/templates/fe-react/public/locales/en/executor.json +6 -0
- package/templates/fe-react/public/locales/en/home.json +10 -0
- package/templates/fe-react/public/locales/en/jsonStorage.json +11 -0
- package/templates/fe-react/public/locales/en/login.json +7 -0
- package/templates/fe-react/public/locales/en/request.json +15 -0
- package/templates/fe-react/public/locales/zh/about.json +3 -0
- package/templates/fe-react/public/locales/zh/common.json +7 -0
- package/templates/fe-react/public/locales/zh/executor.json +7 -0
- package/templates/fe-react/public/locales/zh/home.json +10 -0
- package/templates/fe-react/public/locales/zh/jsonStorage.json +11 -0
- package/templates/fe-react/public/locales/zh/login.json +8 -0
- package/templates/fe-react/public/locales/zh/request.json +15 -0
- package/templates/fe-react/public/logo.svg +1 -0
- package/templates/fe-react/src/App.tsx +20 -0
- package/templates/fe-react/src/assets/react.svg +1 -0
- package/templates/fe-react/src/components/Loading.tsx +41 -0
- package/templates/fe-react/src/components/LocaleLink.tsx +42 -0
- package/templates/fe-react/src/components/ProcessProvider.tsx +41 -0
- package/templates/fe-react/src/components/ThemeSwitcher.tsx +29 -0
- package/templates/fe-react/src/containers/context/BaseRouteContext.ts +27 -0
- package/templates/fe-react/src/containers/context/BaseRouteProvider.tsx +11 -0
- package/templates/fe-react/src/containers/globals.ts +31 -0
- package/templates/fe-react/src/containers/index.ts +71 -0
- package/templates/fe-react/src/hooks/useLanguageGuard.ts +25 -0
- package/templates/fe-react/src/hooks/useStrictEffect.ts +29 -0
- package/templates/fe-react/src/main.tsx +15 -0
- package/templates/fe-react/src/pages/404.tsx +14 -0
- package/templates/fe-react/src/pages/500.tsx +14 -0
- package/templates/fe-react/src/pages/auth/Layout.tsx +14 -0
- package/templates/fe-react/src/pages/auth/Login.tsx +62 -0
- package/templates/fe-react/src/pages/auth/Register.tsx +3 -0
- package/templates/fe-react/src/pages/base/About.tsx +12 -0
- package/templates/fe-react/src/pages/base/Executor.tsx +38 -0
- package/templates/fe-react/src/pages/base/Home.tsx +78 -0
- package/templates/fe-react/src/pages/base/JSONStorage.tsx +124 -0
- package/templates/fe-react/src/pages/base/Layout.tsx +17 -0
- package/templates/fe-react/src/pages/base/RedirectPathname.tsx +15 -0
- package/templates/fe-react/src/pages/base/Request.tsx +91 -0
- package/templates/fe-react/src/pages/base/components/BaseHeader.tsx +19 -0
- package/templates/fe-react/src/pages/index.tsx +108 -0
- package/templates/fe-react/src/services/controllers/ExecutorController.ts +56 -0
- package/templates/fe-react/src/services/controllers/JSONStorageController.ts +42 -0
- package/templates/fe-react/src/services/controllers/RequestController.ts +105 -0
- package/templates/fe-react/src/services/controllers/RouterController.ts +90 -0
- package/templates/fe-react/src/services/controllers/UserController.ts +146 -0
- package/templates/fe-react/src/services/feApi/FeApi.ts +51 -0
- package/templates/fe-react/src/services/feApi/FeApiMockPlugin.ts +42 -0
- package/templates/fe-react/src/services/feApi/FeApiType.ts +55 -0
- package/templates/fe-react/src/services/feApi/index.ts +2 -0
- package/templates/fe-react/src/services/i18n/index.ts +50 -0
- package/templates/fe-react/src/services/pageProcesser/PageProcesser.ts +29 -0
- package/templates/fe-react/src/services/pageProcesser/index.ts +1 -0
- package/templates/fe-react/src/styles/css/index.css +2 -0
- package/templates/fe-react/src/styles/css/page.css +3 -0
- package/templates/fe-react/src/styles/css/tailwind.css +3 -0
- package/templates/fe-react/src/types/Page.ts +49 -0
- package/templates/fe-react/src/types/UIDependenciesInterface.ts +31 -0
- package/templates/fe-react/src/types/global.d.ts +7 -0
- package/templates/fe-react/src/utils/RequestLogger.ts +34 -0
- package/templates/fe-react/src/utils/datetime.ts +25 -0
- package/templates/fe-react/src/utils/thread.ts +3 -0
- package/templates/fe-react/src/vite-env.d.ts +1 -0
- package/templates/fe-react/tailwind.config.js +18 -0
- package/templates/fe-react/tsconfig.app.json +29 -0
- package/templates/fe-react/tsconfig.json +7 -0
- package/templates/fe-react/tsconfig.node.json +22 -0
- package/templates/fe-react/vite.config.ts +32 -0
- package/templates/pack-app/.editorconfig +23 -0
- package/templates/pack-app/.env +5 -0
- package/templates/pack-app/.env.template +6 -0
- package/templates/pack-app/.gitattributes +2 -0
- package/templates/pack-app/.github/workflows/general-check.yml +50 -0
- package/templates/pack-app/.github/workflows/release.yml.template +110 -0
- package/templates/pack-app/.prettierignore +5 -0
- package/templates/pack-app/.prettierrc.js +3 -0
- package/templates/pack-app/CHANGELOG.md +0 -0
- package/templates/pack-app/README.md +1 -0
- package/templates/pack-app/eslint.config.js +77 -0
- package/templates/pack-app/fe-config.json +10 -0
- package/templates/pack-app/jest.config.js +31 -0
- package/templates/pack-app/package.json +66 -0
- package/templates/pack-app/packages/browser/__tests__/calc.test.ts +9 -0
- package/templates/pack-app/packages/browser/package.json +11 -0
- package/templates/pack-app/packages/browser/rollup.config.js +70 -0
- package/templates/pack-app/packages/browser/src/calc.ts +3 -0
- package/templates/pack-app/packages/browser/src/index.ts +1 -0
- package/templates/pack-app/packages/browser/tsconfig.json +15 -0
- package/templates/pack-app/packages/node/__tests__/readJson.test.ts +25 -0
- package/templates/pack-app/packages/node/package.json +11 -0
- package/templates/pack-app/packages/node/rollup.config.js +89 -0
- package/templates/pack-app/packages/node/src/index.ts +7 -0
- package/templates/pack-app/packages/node/src/readJson.ts +6 -0
- package/templates/pack-app/packages/node/tsconfig.json +17 -0
- package/templates/pack-app/packages/react-vite-lib/README.md +50 -0
- package/templates/pack-app/packages/react-vite-lib/__tests__/Sum.test.ts +9 -0
- package/templates/pack-app/packages/react-vite-lib/__tests__/Text.test.tsx +11 -0
- package/templates/pack-app/packages/react-vite-lib/eslint.config.js +28 -0
- package/templates/pack-app/packages/react-vite-lib/index.html +13 -0
- package/templates/pack-app/packages/react-vite-lib/package.json +30 -0
- package/templates/pack-app/packages/react-vite-lib/public/vite.svg +1 -0
- package/templates/pack-app/packages/react-vite-lib/src/calc.ts +3 -0
- package/templates/pack-app/packages/react-vite-lib/src/commponents/Text.tsx +7 -0
- package/templates/pack-app/packages/react-vite-lib/src/index.ts +2 -0
- package/templates/pack-app/packages/react-vite-lib/src/vite-env.d.ts +1 -0
- package/templates/pack-app/packages/react-vite-lib/tsconfig.json +25 -0
- package/templates/pack-app/packages/react-vite-lib/vite.config.ts +24 -0
- package/templates/pack-app/pnpm-workspace.yaml +2 -0
- package/templates/pack-app/tsconfig.json +9 -0
- package/templates/pack-app/tsconfig.test.json +10 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { createContext, useEffect } from 'react';
|
|
2
|
+
import { pageProcesser, routerController, userController } from '@/containers';
|
|
3
|
+
import { useLanguageGuard } from '@/hooks/useLanguageGuard';
|
|
4
|
+
import { useStrictEffect } from '@/hooks/useStrictEffect';
|
|
5
|
+
import { PageProcesser } from '@/services/pageProcesser';
|
|
6
|
+
import { Navigate, useNavigate } from 'react-router-dom';
|
|
7
|
+
import { useControllerState } from '@lib/fe-react-controller';
|
|
8
|
+
import { Loading } from './Loading';
|
|
9
|
+
|
|
10
|
+
const PageProcesserContext = createContext<PageProcesser>(pageProcesser);
|
|
11
|
+
|
|
12
|
+
export function ProcessProvider({ children }: { children: React.ReactNode }) {
|
|
13
|
+
useLanguageGuard();
|
|
14
|
+
const { success } = useControllerState(userController);
|
|
15
|
+
|
|
16
|
+
const navigate = useNavigate();
|
|
17
|
+
|
|
18
|
+
useStrictEffect(() => {
|
|
19
|
+
pageProcesser.init();
|
|
20
|
+
}, []);
|
|
21
|
+
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
routerController.setDependencies({
|
|
24
|
+
navigate
|
|
25
|
+
});
|
|
26
|
+
}, [navigate]);
|
|
27
|
+
|
|
28
|
+
if (!success) {
|
|
29
|
+
return <Loading fullscreen />;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (!userController.isAuthenticated()) {
|
|
33
|
+
return <Navigate to="/login" />;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<PageProcesserContext.Provider value={pageProcesser}>
|
|
38
|
+
{children}
|
|
39
|
+
</PageProcesserContext.Provider>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { themeController } from '@/containers';
|
|
2
|
+
import { useController } from '@lib/fe-react-controller';
|
|
3
|
+
import { useTranslation } from 'react-i18next';
|
|
4
|
+
|
|
5
|
+
export default function ThemeSwitcher() {
|
|
6
|
+
const controller = useController(themeController);
|
|
7
|
+
const { theme } = controller.getState();
|
|
8
|
+
const themes = controller.getSupportedThemes();
|
|
9
|
+
const { t } = useTranslation('common');
|
|
10
|
+
|
|
11
|
+
return (
|
|
12
|
+
<div className="flex items-center gap-2">
|
|
13
|
+
<label className="text-black" htmlFor="theme-select">
|
|
14
|
+
{t('header.theme.label')}
|
|
15
|
+
</label>
|
|
16
|
+
<select
|
|
17
|
+
id="theme-select"
|
|
18
|
+
value={theme}
|
|
19
|
+
onChange={(e) => controller.changeTheme(e.target.value)}
|
|
20
|
+
>
|
|
21
|
+
{themes.map((theme) => (
|
|
22
|
+
<option key={theme} value={theme}>
|
|
23
|
+
{theme}
|
|
24
|
+
</option>
|
|
25
|
+
))}
|
|
26
|
+
</select>
|
|
27
|
+
</div>
|
|
28
|
+
);
|
|
29
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { useTranslation } from 'react-i18next';
|
|
2
|
+
import { useContext } from 'react';
|
|
3
|
+
import { BasePageProvider } from '@/types/Page';
|
|
4
|
+
import { RouteMeta } from '@/types/Page';
|
|
5
|
+
import { createContext } from 'react';
|
|
6
|
+
import { defaultBaseRoutemeta } from '@config/app.common';
|
|
7
|
+
import merge from 'lodash/merge';
|
|
8
|
+
|
|
9
|
+
export const BaseRoutePageContext = createContext<RouteMeta>({});
|
|
10
|
+
|
|
11
|
+
export function useBaseRoutePage(): BasePageProvider {
|
|
12
|
+
const meta = useContext(BaseRoutePageContext);
|
|
13
|
+
|
|
14
|
+
if (!meta) {
|
|
15
|
+
throw new Error('useBaseRoutePage must be used within a PageProvider');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const _meta = merge({}, defaultBaseRoutemeta, meta);
|
|
19
|
+
|
|
20
|
+
const i18n = useTranslation(_meta.localNamespace);
|
|
21
|
+
|
|
22
|
+
return {
|
|
23
|
+
meta: _meta,
|
|
24
|
+
i18n,
|
|
25
|
+
t: i18n.t
|
|
26
|
+
};
|
|
27
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { PropsWithChildren } from 'react';
|
|
2
|
+
import { RouteMeta } from '@/types/Page';
|
|
3
|
+
import { BaseRoutePageContext } from './BaseRouteContext';
|
|
4
|
+
|
|
5
|
+
export default function BaseRouteProvider(props: PropsWithChildren<RouteMeta>) {
|
|
6
|
+
return (
|
|
7
|
+
<BaseRoutePageContext.Provider value={props}>
|
|
8
|
+
{props.children}
|
|
9
|
+
</BaseRoutePageContext.Provider>
|
|
10
|
+
);
|
|
11
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
//! global variables, don't import any dependencies and don't have side effects
|
|
2
|
+
import {
|
|
3
|
+
JSONStorage,
|
|
4
|
+
JSONSerializer,
|
|
5
|
+
Logger,
|
|
6
|
+
SyncStorage
|
|
7
|
+
} from '@qlover/fe-utils';
|
|
8
|
+
|
|
9
|
+
export const env = import.meta.env.VITE_NODE_ENV;
|
|
10
|
+
export const isProduction = env === 'production';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Global logger
|
|
14
|
+
*/
|
|
15
|
+
export const logger = new Logger({
|
|
16
|
+
silent: isProduction,
|
|
17
|
+
debug: !isProduction
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Override JSONSerializer to use the global logger
|
|
22
|
+
*/
|
|
23
|
+
export const JSON = new JSONSerializer();
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Override JSONStorage to use the global local storage
|
|
27
|
+
*/
|
|
28
|
+
export const localJsonStorage = new JSONStorage(
|
|
29
|
+
localStorage as SyncStorage<string, string>,
|
|
30
|
+
JSON
|
|
31
|
+
);
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
//! dont't import tsx, only ts file
|
|
2
|
+
import { FetchAbortPlugin, FetchURLPlugin } from '@qlover/fe-utils';
|
|
3
|
+
import { OpenAIClient } from '@lib/openAiApi';
|
|
4
|
+
import { ThemeController } from '@lib/fe-react-theme/ThemeController';
|
|
5
|
+
import { ExecutorController } from '@/services/controllers/ExecutorController';
|
|
6
|
+
import { JSONStorageController } from '@/services/controllers/JSONStorageController';
|
|
7
|
+
import { RequestController } from '@/services/controllers/RequestController';
|
|
8
|
+
import { UserController } from '@/services/controllers/UserController';
|
|
9
|
+
import { RouterController } from '@/services/controllers/RouterController';
|
|
10
|
+
import { FeApi, FeApiMockPlugin } from '@/services/feApi';
|
|
11
|
+
import { defaultFeApiConfig, openAiConfig } from '@config/app.common';
|
|
12
|
+
import themeConfigJson from '@config/theme.json';
|
|
13
|
+
import appRouterConfig from '@config/app.router.json';
|
|
14
|
+
import mockDataJson from '@config/feapi.mock.json';
|
|
15
|
+
import { RequestCommonPlugin } from '@lib/request-common-plugin';
|
|
16
|
+
import { localJsonStorage, logger } from './globals';
|
|
17
|
+
import { RequestLogger } from '@/utils/RequestLogger';
|
|
18
|
+
import { RouteConfig } from '@/types/Page';
|
|
19
|
+
import { PageProcesser } from '@/services/pageProcesser';
|
|
20
|
+
|
|
21
|
+
// common plugins
|
|
22
|
+
const requestLogger = new RequestLogger(logger);
|
|
23
|
+
export const feApiAbort = new FetchAbortPlugin();
|
|
24
|
+
|
|
25
|
+
// open ai api
|
|
26
|
+
openAiConfig.commonPluginConfig.requestDataSerializer = (data) =>
|
|
27
|
+
JSON.stringify(data);
|
|
28
|
+
export const openAiApi = new OpenAIClient(openAiConfig).usePlugin(
|
|
29
|
+
requestLogger
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
export const feApi = new FeApi({
|
|
33
|
+
abortPlugin: feApiAbort,
|
|
34
|
+
config: defaultFeApiConfig.adapter
|
|
35
|
+
})
|
|
36
|
+
.usePlugin(new FetchURLPlugin())
|
|
37
|
+
.usePlugin(
|
|
38
|
+
new RequestCommonPlugin({
|
|
39
|
+
...defaultFeApiConfig.commonPluginConfig,
|
|
40
|
+
token: () => localJsonStorage.getItem('fe_user_token')
|
|
41
|
+
})
|
|
42
|
+
)
|
|
43
|
+
.usePlugin(new FeApiMockPlugin(mockDataJson))
|
|
44
|
+
.usePlugin(requestLogger)
|
|
45
|
+
.usePlugin(feApiAbort);
|
|
46
|
+
|
|
47
|
+
// ui layer controller
|
|
48
|
+
export const routerController = new RouterController({
|
|
49
|
+
config: appRouterConfig.base as RouteConfig,
|
|
50
|
+
logger
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
export const jsonStorageController = new JSONStorageController(
|
|
54
|
+
localJsonStorage
|
|
55
|
+
);
|
|
56
|
+
export const requestController = new RequestController(openAiApi, feApi);
|
|
57
|
+
export const executorController = new ExecutorController(feApi);
|
|
58
|
+
export const userController = new UserController({
|
|
59
|
+
storageKey: 'fe_user_token',
|
|
60
|
+
storage: localJsonStorage,
|
|
61
|
+
feApi,
|
|
62
|
+
routerController
|
|
63
|
+
});
|
|
64
|
+
export const themeController = new ThemeController({
|
|
65
|
+
...themeConfigJson.override,
|
|
66
|
+
storage: localJsonStorage
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
export const pageProcesser = new PageProcesser({
|
|
70
|
+
logger
|
|
71
|
+
}).usePlugin(userController);
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { I18nService } from '@/services/i18n';
|
|
2
|
+
import { I18nServiceLocale } from '@config/i18n';
|
|
3
|
+
import { useEffect } from 'react';
|
|
4
|
+
import { useNavigate } from 'react-router-dom';
|
|
5
|
+
import { useParams } from 'react-router-dom';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Language Guard
|
|
9
|
+
*
|
|
10
|
+
* if language not found, redirect to 404 page
|
|
11
|
+
*
|
|
12
|
+
* TODO: if language not found, use default language
|
|
13
|
+
*/
|
|
14
|
+
export function useLanguageGuard() {
|
|
15
|
+
const { lng } = useParams<{ lng: I18nServiceLocale }>();
|
|
16
|
+
const navigate = useNavigate();
|
|
17
|
+
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
if (!lng) {
|
|
20
|
+
navigate('/404', { replace: true });
|
|
21
|
+
} else if (!I18nService.isValidLanguage(lng)) {
|
|
22
|
+
navigate('/404', { replace: true });
|
|
23
|
+
}
|
|
24
|
+
}, [lng, navigate]);
|
|
25
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { useEffect, useRef } from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* In React Strict Mode, ensure that the effect is executed only once when each dependency changes
|
|
5
|
+
* @param effect The effect function to execute
|
|
6
|
+
* @param deps The dependency array
|
|
7
|
+
*/
|
|
8
|
+
export const useStrictEffect = (
|
|
9
|
+
effect: () => void | (() => void),
|
|
10
|
+
deps?: React.DependencyList
|
|
11
|
+
) => {
|
|
12
|
+
const mountedRef = useRef(false);
|
|
13
|
+
const depsRef = useRef(deps);
|
|
14
|
+
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
// Check if the dependencies have changed
|
|
17
|
+
const depsChanged = !deps || !depsRef.current || deps.some((dep, i) => dep !== depsRef.current![i]);
|
|
18
|
+
|
|
19
|
+
// Update the dependency reference
|
|
20
|
+
depsRef.current = deps;
|
|
21
|
+
|
|
22
|
+
// If it's the first mount or the dependencies have changed, execute the effect
|
|
23
|
+
if (!mountedRef.current || depsChanged) {
|
|
24
|
+
mountedRef.current = true;
|
|
25
|
+
return effect();
|
|
26
|
+
}
|
|
27
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
28
|
+
}, deps);
|
|
29
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { StrictMode } from 'react'
|
|
2
|
+
import { createRoot } from 'react-dom/client'
|
|
3
|
+
import App from './App.tsx'
|
|
4
|
+
import * as feGlobals from '@/containers/globals';
|
|
5
|
+
|
|
6
|
+
// set global feGlobals
|
|
7
|
+
if (typeof window !== 'undefined') {
|
|
8
|
+
window.feGlobals = Object.freeze(Object.assign({}, feGlobals));
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
createRoot(document.getElementById('root')!).render(
|
|
12
|
+
<StrictMode>
|
|
13
|
+
<App />
|
|
14
|
+
</StrictMode>,
|
|
15
|
+
)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { useBaseRoutePage } from '@/containers/context/BaseRouteContext';
|
|
2
|
+
|
|
3
|
+
export default function NotFound({ route }: { route?: string }) {
|
|
4
|
+
const { t } = useBaseRoutePage();
|
|
5
|
+
return (
|
|
6
|
+
<div className="flex flex-col justify-center min-h-screen py-6 bg-background sm:py-12">
|
|
7
|
+
<div className="relative py-3 mx-auto sm:max-w-xl">
|
|
8
|
+
<h1 className="text-primary text-2xl font-bold text-center">
|
|
9
|
+
404 - {route ? `${t('404.notComponent')}: ${route}` : t('404.notPage')}
|
|
10
|
+
</h1>
|
|
11
|
+
</div>
|
|
12
|
+
</div>
|
|
13
|
+
);
|
|
14
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { useBaseRoutePage } from '@/containers/context/BaseRouteContext';
|
|
2
|
+
|
|
3
|
+
export default function NotFound500() {
|
|
4
|
+
const { t } = useBaseRoutePage();
|
|
5
|
+
return (
|
|
6
|
+
<div className="flex flex-col justify-center min-h-screen py-6 bg-background sm:py-12">
|
|
7
|
+
<div className="relative py-3 mx-auto sm:max-w-xl">
|
|
8
|
+
<h1 className="text-primary text-2xl font-bold text-center">
|
|
9
|
+
500 - {t('500.title')}
|
|
10
|
+
</h1>
|
|
11
|
+
</div>
|
|
12
|
+
</div>
|
|
13
|
+
);
|
|
14
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { userController } from '@/containers';
|
|
2
|
+
import { useController } from '@lib/fe-react-controller';
|
|
3
|
+
import { Navigate, Outlet } from 'react-router-dom';
|
|
4
|
+
|
|
5
|
+
export default function Layout() {
|
|
6
|
+
const controller = useController(userController);
|
|
7
|
+
|
|
8
|
+
// If user is authenticated, redirect to home page
|
|
9
|
+
if (controller.isAuthenticated()) {
|
|
10
|
+
return <Navigate to="/" replace />;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return <Outlet />;
|
|
14
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
import { useController } from '@lib/fe-react-controller';
|
|
3
|
+
import { routerController, userController } from '@/containers';
|
|
4
|
+
import { useBaseRoutePage } from '@/containers/context/BaseRouteContext';
|
|
5
|
+
import { defaultLoginInfo } from '@config/app.common';
|
|
6
|
+
|
|
7
|
+
export default function Login() {
|
|
8
|
+
const { t } = useBaseRoutePage();
|
|
9
|
+
const controller = useController(userController);
|
|
10
|
+
const [email, setEmail] = useState(defaultLoginInfo.name);
|
|
11
|
+
const [password, setPassword] = useState(defaultLoginInfo.password);
|
|
12
|
+
const [loading, setLoading] = useState(false);
|
|
13
|
+
|
|
14
|
+
const handleLogin = async () => {
|
|
15
|
+
try {
|
|
16
|
+
setLoading(true);
|
|
17
|
+
await controller.login({ username: email, password });
|
|
18
|
+
// Redirect or show success message
|
|
19
|
+
routerController.replaceToHome();
|
|
20
|
+
} catch (error) {
|
|
21
|
+
// Handle login error
|
|
22
|
+
console.error(error);
|
|
23
|
+
} finally {
|
|
24
|
+
setLoading(false);
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<div className="min-h-screen bg-gray-100 py-6 flex flex-col justify-center sm:py-12">
|
|
30
|
+
<div className="relative py-3 sm:max-w-xl sm:mx-auto">
|
|
31
|
+
<div className="bg-white shadow-lg rounded-lg px-8 py-6">
|
|
32
|
+
<h1 className="text-base font-bold text-center text-gray-800 mb-8">
|
|
33
|
+
{t('login')}
|
|
34
|
+
</h1>
|
|
35
|
+
<div className="space-y-6">
|
|
36
|
+
<input
|
|
37
|
+
type="email"
|
|
38
|
+
value={email}
|
|
39
|
+
onChange={(e) => setEmail(e.target.value)}
|
|
40
|
+
placeholder={t('email')}
|
|
41
|
+
className="w-full px-4 py-2 border rounded-lg"
|
|
42
|
+
/>
|
|
43
|
+
<input
|
|
44
|
+
type="password"
|
|
45
|
+
value={password}
|
|
46
|
+
onChange={(e) => setPassword(e.target.value)}
|
|
47
|
+
placeholder={t('password')}
|
|
48
|
+
className="w-full px-4 py-2 border rounded-lg"
|
|
49
|
+
/>
|
|
50
|
+
<button
|
|
51
|
+
onClick={handleLogin}
|
|
52
|
+
className="w-full px-6 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 transition-colors duration-200"
|
|
53
|
+
disabled={loading}
|
|
54
|
+
>
|
|
55
|
+
{loading ? 'Loading...' : t('login')}
|
|
56
|
+
</button>
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
);
|
|
62
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { useBaseRoutePage } from '@/containers/context/BaseRouteContext';
|
|
2
|
+
|
|
3
|
+
export default function About() {
|
|
4
|
+
const { t } = useBaseRoutePage();
|
|
5
|
+
return (
|
|
6
|
+
<div className="min-h-screen bg-gray-100 py-6 flex flex-col justify-center sm:py-12">
|
|
7
|
+
<div className="relative py-3 sm:max-w-xl sm:mx-auto">
|
|
8
|
+
<h1 className="text-2xl font-bold text-center">{t('title')}</h1>
|
|
9
|
+
</div>
|
|
10
|
+
</div>
|
|
11
|
+
);
|
|
12
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { executorController, jsonStorageController } from '@/containers';
|
|
2
|
+
import { useControllerState } from '@lib/fe-react-controller';
|
|
3
|
+
import { useBaseRoutePage } from '@/containers/context/BaseRouteContext';
|
|
4
|
+
|
|
5
|
+
export default function Executor() {
|
|
6
|
+
const jsonStorageControllerState = useControllerState(jsonStorageController);
|
|
7
|
+
const { t } = useBaseRoutePage();
|
|
8
|
+
|
|
9
|
+
return (
|
|
10
|
+
<div className="min-h-screen bg-gray-100 py-6 flex flex-col justify-center sm:py-12">
|
|
11
|
+
<div className="relative py-3 sm:max-w-xl sm:mx-auto">
|
|
12
|
+
<div className="bg-white shadow-lg rounded-lg px-8 py-6">
|
|
13
|
+
<h1 className="text-3xl font-bold text-center text-gray-800 mb-8">
|
|
14
|
+
{t('executorDemo')}
|
|
15
|
+
</h1>
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
<div className="bg-white shadow-lg rounded-lg px-8 py-6">
|
|
19
|
+
{t('requestTimeout')}: {jsonStorageControllerState.requestTimeout}
|
|
20
|
+
</div>
|
|
21
|
+
<div className="space-y-6">
|
|
22
|
+
<div className="p-6 bg-gray-50 rounded-lg">
|
|
23
|
+
<h2 className="text-xl font-semibold text-gray-800 mb-4">
|
|
24
|
+
{t('executorTestPlugin')}
|
|
25
|
+
</h2>
|
|
26
|
+
|
|
27
|
+
<button
|
|
28
|
+
className="bg-blue-500 text-white px-4 py-2 rounded-md"
|
|
29
|
+
onClick={executorController.onTestPlugins}
|
|
30
|
+
>
|
|
31
|
+
{t('testPlugin')}
|
|
32
|
+
</button>
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
37
|
+
);
|
|
38
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import LocaleLink from '@/components/LocaleLink';
|
|
2
|
+
import { useBaseRoutePage } from '../../containers/context/BaseRouteContext';
|
|
3
|
+
|
|
4
|
+
export default function Home() {
|
|
5
|
+
const { t } = useBaseRoutePage();
|
|
6
|
+
|
|
7
|
+
return (
|
|
8
|
+
<div className="min-h-screen bg-gray-100/90 py-6 flex flex-col justify-center sm:py-12">
|
|
9
|
+
<div className="relative py-3 sm:max-w-xl sm:mx-auto">
|
|
10
|
+
<div className="bg-white shadow-lg rounded-lg px-8 py-6">
|
|
11
|
+
<h1 className="text-3xl font-bold text-center text-gray-800 mb-8">
|
|
12
|
+
{t('welcome')}
|
|
13
|
+
</h1>
|
|
14
|
+
|
|
15
|
+
<div className="space-y-6">
|
|
16
|
+
<div className="text-center text-gray-600 mb-8">
|
|
17
|
+
{t('description')}
|
|
18
|
+
</div>
|
|
19
|
+
|
|
20
|
+
<div className="grid gap-4">
|
|
21
|
+
<LocaleLink
|
|
22
|
+
href="/about"
|
|
23
|
+
className="block p-4 bg-blue-50 rounded-lg hover:bg-blue-100 transition-colors duration-200"
|
|
24
|
+
>
|
|
25
|
+
<h2 className="text-xl font-semibold text-blue-700 mb-2">
|
|
26
|
+
{t('about')}
|
|
27
|
+
</h2>
|
|
28
|
+
<p className="text-gray-600">{t('about_description')}</p>
|
|
29
|
+
</LocaleLink>
|
|
30
|
+
|
|
31
|
+
<LocaleLink
|
|
32
|
+
href="/jsonstorage"
|
|
33
|
+
className="block p-4 bg-green-50 rounded-lg hover:bg-green-100 transition-colors duration-200"
|
|
34
|
+
>
|
|
35
|
+
<h2 className="text-xl font-semibold text-green-700 mb-2">
|
|
36
|
+
{t('jsonstorage')}
|
|
37
|
+
</h2>
|
|
38
|
+
<p className="text-gray-600">{t('jsonstorage_description')}</p>
|
|
39
|
+
</LocaleLink>
|
|
40
|
+
|
|
41
|
+
<LocaleLink
|
|
42
|
+
href="/request"
|
|
43
|
+
className="block p-4 bg-red-50 rounded-lg hover:bg-red-100 transition-colors duration-200"
|
|
44
|
+
>
|
|
45
|
+
<h2 className="text-xl font-semibold text-red-700 mb-2">
|
|
46
|
+
{t('request')}
|
|
47
|
+
</h2>
|
|
48
|
+
<p className="text-gray-600">{t('request_description')}</p>
|
|
49
|
+
</LocaleLink>
|
|
50
|
+
</div>
|
|
51
|
+
|
|
52
|
+
<div className="mt-8 text-center">
|
|
53
|
+
<LocaleLink
|
|
54
|
+
href="https://github.com/qlover/fe-base"
|
|
55
|
+
target="_blank"
|
|
56
|
+
rel="noopener noreferrer"
|
|
57
|
+
className="inline-flex items-center text-gray-600 hover:text-gray-800"
|
|
58
|
+
>
|
|
59
|
+
<svg
|
|
60
|
+
className="w-5 h-5 mr-2"
|
|
61
|
+
fill="currentColor"
|
|
62
|
+
viewBox="0 0 24 24"
|
|
63
|
+
>
|
|
64
|
+
<path
|
|
65
|
+
fillRule="evenodd"
|
|
66
|
+
d="M12 2C6.477 2 2 6.477 2 12c0 4.42 2.865 8.17 6.839 9.49.5.092.682-.217.682-.482 0-.237-.008-.866-.013-1.7-2.782.604-3.369-1.34-3.369-1.34-.454-1.156-1.11-1.464-1.11-1.464-.908-.62.069-.608.069-.608 1.003.07 1.531 1.03 1.531 1.03.892 1.529 2.341 1.087 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.11-4.555-4.943 0-1.091.39-1.984 1.029-2.683-.103-.253-.446-1.27.098-2.647 0 0 .84-.269 2.75 1.025A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.294 2.747-1.025 2.747-1.025.546 1.377.203 2.394.1 2.647.64.699 1.028 1.592 1.028 2.683 0 3.842-2.339 4.687-4.566 4.935.359.309.678.919.678 1.852 0 1.336-.012 2.415-.012 2.743 0 .267.18.578.688.48C19.138 20.167 22 16.418 22 12c0-5.523-4.477-10-10-10z"
|
|
67
|
+
clipRule="evenodd"
|
|
68
|
+
/>
|
|
69
|
+
</svg>
|
|
70
|
+
Visit GitHub
|
|
71
|
+
</LocaleLink>
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
|
77
|
+
);
|
|
78
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { jsonStorageController } from '@/containers';
|
|
2
|
+
import { useController, useControllerState } from '@lib/fe-react-controller';
|
|
3
|
+
import { useBaseRoutePage } from '@/containers/context/BaseRouteContext';
|
|
4
|
+
import template from 'lodash/template';
|
|
5
|
+
|
|
6
|
+
export default function JSONStorage() {
|
|
7
|
+
const controllerState = useControllerState(
|
|
8
|
+
useController(jsonStorageController)
|
|
9
|
+
);
|
|
10
|
+
|
|
11
|
+
const { t } = useBaseRoutePage();
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<div className="min-h-screen bg-gray-100 py-6 flex flex-col justify-center sm:py-12">
|
|
15
|
+
<div className="relative py-3 sm:max-w-xl sm:mx-auto">
|
|
16
|
+
<div className="bg-white shadow-lg rounded-lg px-8 py-6">
|
|
17
|
+
<h1 className="text-3xl font-bold text-center text-gray-800 mb-8">
|
|
18
|
+
{t('title')}
|
|
19
|
+
</h1>
|
|
20
|
+
|
|
21
|
+
<div className="space-y-6">
|
|
22
|
+
{/* 无过期时间的测试 */}
|
|
23
|
+
<div className="p-6 bg-gray-50 rounded-lg">
|
|
24
|
+
<h2 className="text-xl font-semibold text-gray-800 mb-4">
|
|
25
|
+
{t('title2')}
|
|
26
|
+
</h2>
|
|
27
|
+
<div className="text-gray-600 mb-4">
|
|
28
|
+
{template(t('format.title'))({
|
|
29
|
+
key: 'testKey1',
|
|
30
|
+
min: 100,
|
|
31
|
+
max: 9000,
|
|
32
|
+
})}
|
|
33
|
+
</div>
|
|
34
|
+
|
|
35
|
+
<div className="flex flex-col items-center space-y-4">
|
|
36
|
+
<button
|
|
37
|
+
onClick={jsonStorageController.changeRandomTestKey1}
|
|
38
|
+
className="px-6 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 transition-colors duration-200"
|
|
39
|
+
>
|
|
40
|
+
{t('setRandomValue')}
|
|
41
|
+
</button>
|
|
42
|
+
|
|
43
|
+
<div className="p-4 bg-white rounded-lg w-full text-center">
|
|
44
|
+
<span className="text-gray-600">{t('currentValue')}: </span>
|
|
45
|
+
<span className="font-semibold text-gray-800">
|
|
46
|
+
{controllerState.testKey1}
|
|
47
|
+
</span>
|
|
48
|
+
</div>
|
|
49
|
+
</div>
|
|
50
|
+
</div>
|
|
51
|
+
|
|
52
|
+
{/* 带过期时间的测试 */}
|
|
53
|
+
<div className="p-6 bg-gray-50 rounded-lg">
|
|
54
|
+
<h2 className="text-xl font-semibold text-gray-800 mb-4">
|
|
55
|
+
{t('title3')}
|
|
56
|
+
</h2>
|
|
57
|
+
<div className="text-gray-600 mb-4">
|
|
58
|
+
{template(t('format.title'))({
|
|
59
|
+
key: 'testKey2',
|
|
60
|
+
min: 100,
|
|
61
|
+
max: 9000,
|
|
62
|
+
})}
|
|
63
|
+
</div>
|
|
64
|
+
|
|
65
|
+
<div className="flex flex-col items-center space-y-4">
|
|
66
|
+
<div className="flex items-center space-x-4">
|
|
67
|
+
<input
|
|
68
|
+
type="number"
|
|
69
|
+
value={controllerState.expireTime}
|
|
70
|
+
onChange={(e) =>
|
|
71
|
+
jsonStorageController.changeExpireTime(
|
|
72
|
+
Number(e.target.value)
|
|
73
|
+
)
|
|
74
|
+
}
|
|
75
|
+
className="px-4 py-2 border rounded-lg w-32"
|
|
76
|
+
min="1000"
|
|
77
|
+
step="1000"
|
|
78
|
+
/>
|
|
79
|
+
<span className="text-gray-600">{t('ms')}</span>
|
|
80
|
+
</div>
|
|
81
|
+
|
|
82
|
+
<button
|
|
83
|
+
onClick={jsonStorageController.onChangeRandomTestKey2}
|
|
84
|
+
className="px-6 py-2 bg-green-500 text-white rounded-lg hover:bg-green-600 transition-colors duration-200"
|
|
85
|
+
>
|
|
86
|
+
{t('setExpireTime')}
|
|
87
|
+
</button>
|
|
88
|
+
|
|
89
|
+
<div className="p-4 bg-white rounded-lg w-full text-center">
|
|
90
|
+
<span className="text-gray-600">{t('currentValue')}: </span>
|
|
91
|
+
<span className="font-semibold text-gray-800">
|
|
92
|
+
{controllerState.testKey2}
|
|
93
|
+
</span>
|
|
94
|
+
</div>
|
|
95
|
+
</div>
|
|
96
|
+
</div>
|
|
97
|
+
|
|
98
|
+
{/* 请求超时时间设置 */}
|
|
99
|
+
<div className="p-6 bg-gray-50 rounded-lg">
|
|
100
|
+
<h2 className="text-xl font-semibold text-gray-800 mb-4">
|
|
101
|
+
{t('title4')}
|
|
102
|
+
</h2>
|
|
103
|
+
<div className="flex items-center space-x-4">
|
|
104
|
+
<input
|
|
105
|
+
type="number"
|
|
106
|
+
value={controllerState.requestTimeout}
|
|
107
|
+
onChange={(e) =>
|
|
108
|
+
jsonStorageController.changeRequestTimeout(
|
|
109
|
+
Number(e.target.value)
|
|
110
|
+
)
|
|
111
|
+
}
|
|
112
|
+
className="px-4 py-2 border rounded-lg w-32"
|
|
113
|
+
min="1000"
|
|
114
|
+
step="1000"
|
|
115
|
+
/>
|
|
116
|
+
<span className="text-gray-600">{t('ms')}</span>
|
|
117
|
+
</div>
|
|
118
|
+
</div>
|
|
119
|
+
</div>
|
|
120
|
+
</div>
|
|
121
|
+
</div>
|
|
122
|
+
</div>
|
|
123
|
+
);
|
|
124
|
+
}
|