@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.
Files changed (144) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/bin/create-app.js +28 -0
  3. package/dist/cjs/index.d.ts +67 -0
  4. package/dist/cjs/index.js +1 -0
  5. package/dist/es/index.d.ts +67 -0
  6. package/dist/es/index.js +1 -0
  7. package/package.json +60 -0
  8. package/templates/fe-react/.env +3 -0
  9. package/templates/fe-react/README.md +50 -0
  10. package/templates/fe-react/config/app.common.ts +52 -0
  11. package/templates/fe-react/config/app.router.json +150 -0
  12. package/templates/fe-react/config/feapi.mock.json +14 -0
  13. package/templates/fe-react/config/i18n.ts +21 -0
  14. package/templates/fe-react/config/theme.json +90 -0
  15. package/templates/fe-react/eslint.config.js +31 -0
  16. package/templates/fe-react/index.html +13 -0
  17. package/templates/fe-react/lib/fe-react-controller/FeController.ts +15 -0
  18. package/templates/fe-react/lib/fe-react-controller/index.ts +2 -0
  19. package/templates/fe-react/lib/fe-react-controller/useController.ts +71 -0
  20. package/templates/fe-react/lib/fe-react-theme/ThemeController.ts +40 -0
  21. package/templates/fe-react/lib/fe-react-theme/ThemeStateGetter.ts +53 -0
  22. package/templates/fe-react/lib/fe-react-theme/index.ts +3 -0
  23. package/templates/fe-react/lib/fe-react-theme/tw-generator.js +239 -0
  24. package/templates/fe-react/lib/fe-react-theme/type.ts +21 -0
  25. package/templates/fe-react/lib/openAiApi/OpenAIAuthPlugin.ts +29 -0
  26. package/templates/fe-react/lib/openAiApi/OpenAIClient.ts +51 -0
  27. package/templates/fe-react/lib/openAiApi/StreamProcessor.ts +81 -0
  28. package/templates/fe-react/lib/openAiApi/index.ts +3 -0
  29. package/templates/fe-react/lib/request-common-plugin/index.ts +169 -0
  30. package/templates/fe-react/lib/tw-root10px/index.css +4 -0
  31. package/templates/fe-react/lib/tw-root10px/index.js +178 -0
  32. package/templates/fe-react/package.json +49 -0
  33. package/templates/fe-react/postcss.config.js +6 -0
  34. package/templates/fe-react/public/locales/en/about.json +3 -0
  35. package/templates/fe-react/public/locales/en/common.json +6 -0
  36. package/templates/fe-react/public/locales/en/executor.json +6 -0
  37. package/templates/fe-react/public/locales/en/home.json +10 -0
  38. package/templates/fe-react/public/locales/en/jsonStorage.json +11 -0
  39. package/templates/fe-react/public/locales/en/login.json +7 -0
  40. package/templates/fe-react/public/locales/en/request.json +15 -0
  41. package/templates/fe-react/public/locales/zh/about.json +3 -0
  42. package/templates/fe-react/public/locales/zh/common.json +7 -0
  43. package/templates/fe-react/public/locales/zh/executor.json +7 -0
  44. package/templates/fe-react/public/locales/zh/home.json +10 -0
  45. package/templates/fe-react/public/locales/zh/jsonStorage.json +11 -0
  46. package/templates/fe-react/public/locales/zh/login.json +8 -0
  47. package/templates/fe-react/public/locales/zh/request.json +15 -0
  48. package/templates/fe-react/public/logo.svg +1 -0
  49. package/templates/fe-react/src/App.tsx +20 -0
  50. package/templates/fe-react/src/assets/react.svg +1 -0
  51. package/templates/fe-react/src/components/Loading.tsx +41 -0
  52. package/templates/fe-react/src/components/LocaleLink.tsx +42 -0
  53. package/templates/fe-react/src/components/ProcessProvider.tsx +41 -0
  54. package/templates/fe-react/src/components/ThemeSwitcher.tsx +29 -0
  55. package/templates/fe-react/src/containers/context/BaseRouteContext.ts +27 -0
  56. package/templates/fe-react/src/containers/context/BaseRouteProvider.tsx +11 -0
  57. package/templates/fe-react/src/containers/globals.ts +31 -0
  58. package/templates/fe-react/src/containers/index.ts +71 -0
  59. package/templates/fe-react/src/hooks/useLanguageGuard.ts +25 -0
  60. package/templates/fe-react/src/hooks/useStrictEffect.ts +29 -0
  61. package/templates/fe-react/src/main.tsx +15 -0
  62. package/templates/fe-react/src/pages/404.tsx +14 -0
  63. package/templates/fe-react/src/pages/500.tsx +14 -0
  64. package/templates/fe-react/src/pages/auth/Layout.tsx +14 -0
  65. package/templates/fe-react/src/pages/auth/Login.tsx +62 -0
  66. package/templates/fe-react/src/pages/auth/Register.tsx +3 -0
  67. package/templates/fe-react/src/pages/base/About.tsx +12 -0
  68. package/templates/fe-react/src/pages/base/Executor.tsx +38 -0
  69. package/templates/fe-react/src/pages/base/Home.tsx +78 -0
  70. package/templates/fe-react/src/pages/base/JSONStorage.tsx +124 -0
  71. package/templates/fe-react/src/pages/base/Layout.tsx +17 -0
  72. package/templates/fe-react/src/pages/base/RedirectPathname.tsx +15 -0
  73. package/templates/fe-react/src/pages/base/Request.tsx +91 -0
  74. package/templates/fe-react/src/pages/base/components/BaseHeader.tsx +19 -0
  75. package/templates/fe-react/src/pages/index.tsx +108 -0
  76. package/templates/fe-react/src/services/controllers/ExecutorController.ts +56 -0
  77. package/templates/fe-react/src/services/controllers/JSONStorageController.ts +42 -0
  78. package/templates/fe-react/src/services/controllers/RequestController.ts +105 -0
  79. package/templates/fe-react/src/services/controllers/RouterController.ts +90 -0
  80. package/templates/fe-react/src/services/controllers/UserController.ts +146 -0
  81. package/templates/fe-react/src/services/feApi/FeApi.ts +51 -0
  82. package/templates/fe-react/src/services/feApi/FeApiMockPlugin.ts +42 -0
  83. package/templates/fe-react/src/services/feApi/FeApiType.ts +55 -0
  84. package/templates/fe-react/src/services/feApi/index.ts +2 -0
  85. package/templates/fe-react/src/services/i18n/index.ts +50 -0
  86. package/templates/fe-react/src/services/pageProcesser/PageProcesser.ts +29 -0
  87. package/templates/fe-react/src/services/pageProcesser/index.ts +1 -0
  88. package/templates/fe-react/src/styles/css/index.css +2 -0
  89. package/templates/fe-react/src/styles/css/page.css +3 -0
  90. package/templates/fe-react/src/styles/css/tailwind.css +3 -0
  91. package/templates/fe-react/src/types/Page.ts +49 -0
  92. package/templates/fe-react/src/types/UIDependenciesInterface.ts +31 -0
  93. package/templates/fe-react/src/types/global.d.ts +7 -0
  94. package/templates/fe-react/src/utils/RequestLogger.ts +34 -0
  95. package/templates/fe-react/src/utils/datetime.ts +25 -0
  96. package/templates/fe-react/src/utils/thread.ts +3 -0
  97. package/templates/fe-react/src/vite-env.d.ts +1 -0
  98. package/templates/fe-react/tailwind.config.js +18 -0
  99. package/templates/fe-react/tsconfig.app.json +29 -0
  100. package/templates/fe-react/tsconfig.json +7 -0
  101. package/templates/fe-react/tsconfig.node.json +22 -0
  102. package/templates/fe-react/vite.config.ts +32 -0
  103. package/templates/pack-app/.editorconfig +23 -0
  104. package/templates/pack-app/.env +5 -0
  105. package/templates/pack-app/.env.template +6 -0
  106. package/templates/pack-app/.gitattributes +2 -0
  107. package/templates/pack-app/.github/workflows/general-check.yml +50 -0
  108. package/templates/pack-app/.github/workflows/release.yml.template +110 -0
  109. package/templates/pack-app/.prettierignore +5 -0
  110. package/templates/pack-app/.prettierrc.js +3 -0
  111. package/templates/pack-app/CHANGELOG.md +0 -0
  112. package/templates/pack-app/README.md +1 -0
  113. package/templates/pack-app/eslint.config.js +77 -0
  114. package/templates/pack-app/fe-config.json +10 -0
  115. package/templates/pack-app/jest.config.js +31 -0
  116. package/templates/pack-app/package.json +66 -0
  117. package/templates/pack-app/packages/browser/__tests__/calc.test.ts +9 -0
  118. package/templates/pack-app/packages/browser/package.json +11 -0
  119. package/templates/pack-app/packages/browser/rollup.config.js +70 -0
  120. package/templates/pack-app/packages/browser/src/calc.ts +3 -0
  121. package/templates/pack-app/packages/browser/src/index.ts +1 -0
  122. package/templates/pack-app/packages/browser/tsconfig.json +15 -0
  123. package/templates/pack-app/packages/node/__tests__/readJson.test.ts +25 -0
  124. package/templates/pack-app/packages/node/package.json +11 -0
  125. package/templates/pack-app/packages/node/rollup.config.js +89 -0
  126. package/templates/pack-app/packages/node/src/index.ts +7 -0
  127. package/templates/pack-app/packages/node/src/readJson.ts +6 -0
  128. package/templates/pack-app/packages/node/tsconfig.json +17 -0
  129. package/templates/pack-app/packages/react-vite-lib/README.md +50 -0
  130. package/templates/pack-app/packages/react-vite-lib/__tests__/Sum.test.ts +9 -0
  131. package/templates/pack-app/packages/react-vite-lib/__tests__/Text.test.tsx +11 -0
  132. package/templates/pack-app/packages/react-vite-lib/eslint.config.js +28 -0
  133. package/templates/pack-app/packages/react-vite-lib/index.html +13 -0
  134. package/templates/pack-app/packages/react-vite-lib/package.json +30 -0
  135. package/templates/pack-app/packages/react-vite-lib/public/vite.svg +1 -0
  136. package/templates/pack-app/packages/react-vite-lib/src/calc.ts +3 -0
  137. package/templates/pack-app/packages/react-vite-lib/src/commponents/Text.tsx +7 -0
  138. package/templates/pack-app/packages/react-vite-lib/src/index.ts +2 -0
  139. package/templates/pack-app/packages/react-vite-lib/src/vite-env.d.ts +1 -0
  140. package/templates/pack-app/packages/react-vite-lib/tsconfig.json +25 -0
  141. package/templates/pack-app/packages/react-vite-lib/vite.config.ts +24 -0
  142. package/templates/pack-app/pnpm-workspace.yaml +2 -0
  143. package/templates/pack-app/tsconfig.json +9 -0
  144. 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,3 @@
1
+ export default function Register() {
2
+ return <div>Register</div>;
3
+ }
@@ -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
+ }