@qlover/create-app 0.7.6 → 0.7.8

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 (104) hide show
  1. package/CHANGELOG.md +328 -0
  2. package/dist/index.cjs +1 -1
  3. package/dist/index.js +1 -1
  4. package/dist/templates/next-app/.env.template +22 -0
  5. package/dist/templates/next-app/.prettierignore +58 -0
  6. package/dist/templates/next-app/README.md +36 -0
  7. package/dist/templates/next-app/build/generateLocales.ts +25 -0
  8. package/dist/templates/next-app/config/IOCIdentifier.ts +58 -0
  9. package/dist/templates/next-app/config/Identifier/common.error.ts +41 -0
  10. package/dist/templates/next-app/config/Identifier/common.ts +62 -0
  11. package/dist/templates/next-app/config/Identifier/index.ts +10 -0
  12. package/dist/templates/next-app/config/Identifier/page.about.ts +181 -0
  13. package/dist/templates/next-app/config/Identifier/page.executor.ts +272 -0
  14. package/dist/templates/next-app/config/Identifier/page.home.ts +63 -0
  15. package/dist/templates/next-app/config/Identifier/page.identifiter.ts +39 -0
  16. package/dist/templates/next-app/config/Identifier/page.jsonStorage.ts +72 -0
  17. package/dist/templates/next-app/config/Identifier/page.login.ts +165 -0
  18. package/dist/templates/next-app/config/Identifier/page.register.ts +147 -0
  19. package/dist/templates/next-app/config/Identifier/page.request.ts +182 -0
  20. package/dist/templates/next-app/config/common.ts +34 -0
  21. package/dist/templates/next-app/config/i18n/PageI18nInterface.ts +51 -0
  22. package/dist/templates/next-app/config/i18n/i18nConfig.ts +12 -0
  23. package/dist/templates/next-app/config/i18n/index.ts +3 -0
  24. package/dist/templates/next-app/config/i18n/loginI18n.ts +42 -0
  25. package/dist/templates/next-app/config/theme.ts +23 -0
  26. package/dist/templates/next-app/docs/env.md +94 -0
  27. package/dist/templates/next-app/eslint.config.mjs +181 -0
  28. package/dist/templates/next-app/next.config.ts +21 -0
  29. package/dist/templates/next-app/package.json +58 -0
  30. package/dist/templates/next-app/postcss.config.mjs +5 -0
  31. package/dist/templates/next-app/public/file.svg +1 -0
  32. package/dist/templates/next-app/public/globe.svg +1 -0
  33. package/dist/templates/next-app/public/locales/en.json +184 -0
  34. package/dist/templates/next-app/public/locales/zh.json +184 -0
  35. package/dist/templates/next-app/public/next.svg +1 -0
  36. package/dist/templates/next-app/public/vercel.svg +1 -0
  37. package/dist/templates/next-app/public/window.svg +1 -0
  38. package/dist/templates/next-app/src/app/[locale]/favicon.ico +0 -0
  39. package/dist/templates/next-app/src/app/[locale]/layout.tsx +32 -0
  40. package/dist/templates/next-app/src/app/[locale]/login/FeatureItem.tsx +13 -0
  41. package/dist/templates/next-app/src/app/[locale]/login/LoginForm.tsx +114 -0
  42. package/dist/templates/next-app/src/app/[locale]/login/page.tsx +86 -0
  43. package/dist/templates/next-app/src/app/[locale]/not-found.tsx +24 -0
  44. package/dist/templates/next-app/src/app/[locale]/page.tsx +119 -0
  45. package/dist/templates/next-app/src/base/cases/AppConfig.ts +15 -0
  46. package/dist/templates/next-app/src/base/cases/DialogHandler.ts +92 -0
  47. package/dist/templates/next-app/src/base/cases/InversifyContainer.ts +33 -0
  48. package/dist/templates/next-app/src/base/cases/NavigateBridge.ts +16 -0
  49. package/dist/templates/next-app/src/base/cases/PageParams.ts +74 -0
  50. package/dist/templates/next-app/src/base/cases/RouterService.ts +35 -0
  51. package/dist/templates/next-app/src/base/cases/ServerAuth.ts +17 -0
  52. package/dist/templates/next-app/src/base/cases/ServerErrorHandler.ts +27 -0
  53. package/dist/templates/next-app/src/base/port/I18nServiceInterface.ts +25 -0
  54. package/dist/templates/next-app/src/base/port/IOCInterface.ts +24 -0
  55. package/dist/templates/next-app/src/base/port/ParamsHandlerInterface.ts +11 -0
  56. package/dist/templates/next-app/src/base/port/RouterInterface.ts +11 -0
  57. package/dist/templates/next-app/src/base/port/ServerAuthInterface.ts +3 -0
  58. package/dist/templates/next-app/src/base/port/ServerInterface.ts +12 -0
  59. package/dist/templates/next-app/src/base/port/UserServiceInterface.ts +11 -0
  60. package/dist/templates/next-app/src/base/services/I18nService.ts +115 -0
  61. package/dist/templates/next-app/src/base/services/UserService.ts +23 -0
  62. package/dist/templates/next-app/src/base/types/PageProps.ts +9 -0
  63. package/dist/templates/next-app/src/core/bootstraps/BootstrapClient.ts +61 -0
  64. package/dist/templates/next-app/src/core/bootstraps/BootstrapServer.ts +78 -0
  65. package/dist/templates/next-app/src/core/bootstraps/BootstrapsRegistry.ts +47 -0
  66. package/dist/templates/next-app/src/core/bootstraps/IocIdentifierTest.ts +26 -0
  67. package/dist/templates/next-app/src/core/bootstraps/PrintBootstrap.ts +18 -0
  68. package/dist/templates/next-app/src/core/clientIoc/ClientIOC.ts +37 -0
  69. package/dist/templates/next-app/src/core/clientIoc/ClientIOCRegister.ts +97 -0
  70. package/dist/templates/next-app/src/core/globals.ts +24 -0
  71. package/dist/templates/next-app/src/core/serverIoc/ServerIOC.ts +52 -0
  72. package/dist/templates/next-app/src/core/serverIoc/ServerIOCRegister.ts +63 -0
  73. package/dist/templates/next-app/src/i18n/request.ts +21 -0
  74. package/dist/templates/next-app/src/i18n/routing.ts +30 -0
  75. package/dist/templates/next-app/src/middleware.ts +25 -0
  76. package/dist/templates/next-app/src/styles/css/antd-themes/_default.css +239 -0
  77. package/dist/templates/next-app/src/styles/css/antd-themes/dark.css +178 -0
  78. package/dist/templates/next-app/src/styles/css/antd-themes/index.css +3 -0
  79. package/dist/templates/next-app/src/styles/css/antd-themes/no-context.css +34 -0
  80. package/dist/templates/next-app/src/styles/css/antd-themes/pink.css +204 -0
  81. package/dist/templates/next-app/src/styles/css/index.css +6 -0
  82. package/dist/templates/next-app/src/styles/css/page.css +19 -0
  83. package/dist/templates/next-app/src/styles/css/tailwind.css +5 -0
  84. package/dist/templates/next-app/src/styles/css/themes/_default.css +29 -0
  85. package/dist/templates/next-app/src/styles/css/themes/dark.css +29 -0
  86. package/dist/templates/next-app/src/styles/css/themes/index.css +3 -0
  87. package/dist/templates/next-app/src/styles/css/themes/pink.css +29 -0
  88. package/dist/templates/next-app/src/styles/css/zIndex.css +9 -0
  89. package/dist/templates/next-app/src/uikit/components/BaseHeader.tsx +47 -0
  90. package/dist/templates/next-app/src/uikit/components/BootstrapsProvider.tsx +37 -0
  91. package/dist/templates/next-app/src/uikit/components/ComboProvider.tsx +50 -0
  92. package/dist/templates/next-app/src/uikit/components/LanguageSwitcher.tsx +52 -0
  93. package/dist/templates/next-app/src/uikit/components/LocaleLink.tsx +51 -0
  94. package/dist/templates/next-app/src/uikit/components/LogoutButton.tsx +34 -0
  95. package/dist/templates/next-app/src/uikit/components/NextIntlProvider.tsx +21 -0
  96. package/dist/templates/next-app/src/uikit/components/ThemeSwitcher.tsx +99 -0
  97. package/dist/templates/next-app/src/uikit/context/IOCContext.ts +13 -0
  98. package/dist/templates/next-app/src/uikit/hook/useI18nInterface.ts +28 -0
  99. package/dist/templates/next-app/src/uikit/hook/useIOC.ts +37 -0
  100. package/dist/templates/next-app/src/uikit/hook/useMountedClient.ts +11 -0
  101. package/dist/templates/next-app/src/uikit/hook/useStore.ts +15 -0
  102. package/dist/templates/next-app/tailwind.config.ts +8 -0
  103. package/dist/templates/next-app/tsconfig.json +36 -0
  104. package/package.json +1 -1
@@ -0,0 +1,184 @@
1
+ {
2
+ "err__local__no__user__token": "本地未找到 user token",
3
+ "err__global__no__window": "全局未找到 window",
4
+ "err__within__page__provider": "必须在 PageProvider 中使用",
5
+ "err__response__no__token": "响应内容没有 token 值",
6
+ "err__not__found__component": "未找到组件",
7
+ "header__theme__default": "默认主题",
8
+ "header__theme__dark": "暗色主题",
9
+ "header__theme__pink": "粉色主题",
10
+ "auth__logout__dialog__title": "登出",
11
+ "auth__logout__dialog__content": "确定要登出吗?",
12
+ "page__404__title": "404 页面未找到",
13
+ "page__404__description": "404 页面描述",
14
+ "page__500__title": "500 页面错误",
15
+ "page__500__description": "500 页面描述",
16
+ "page__about__title": "关于我们",
17
+ "page__about__description": "了解更多关于我们的项目和团队信息",
18
+ "about__message__test": "这是一条测试消息",
19
+ "about__notification__title": "通知标题",
20
+ "about__notification__description": "这是一条测试通知",
21
+ "about__button__message": "显示 Message",
22
+ "about__button__message2": "静态消息",
23
+ "about__button__notification": "显示 Notification",
24
+ "about__button__notification2": "静态通知",
25
+ "about__tooltip__text": "这是一个提示文本",
26
+ "about__button__tooltip": "悬停显示 Tooltip",
27
+ "about__modal__title": "模态框示例",
28
+ "about__modal__content": "这是一个模态框的内容",
29
+ "about__button__modal": "显示 Modal",
30
+ "about__drawer__title": "抽屉示例",
31
+ "about__drawer__content": "这是一个抽屉的内容",
32
+ "about__button__drawer": "显示 Drawer",
33
+ "about__popover__content": "这是一个气泡卡片",
34
+ "about__popover__title": "Popover 标题",
35
+ "about__button__popover": "显示 Popover",
36
+ "about__popconfirm__title": "确认提示",
37
+ "about__popconfirm__description": "您确定要执行此操作吗?",
38
+ "about__button__popconfirm": "显示 Popconfirm",
39
+ "about__alert__message": "这是一个警告提示",
40
+ "about__ok__text": "确定",
41
+ "about__cancel__text": "取消",
42
+ "page__executor__title": "执行器示例",
43
+ "page__executor__description": "一个强大的任务执行器,支持多种任务类型和状态管理",
44
+ "page__error_identifier__title": "错误标识符",
45
+ "page__executor__main_title": "执行器",
46
+ "page__executor__test_plugin__title": "测试插件",
47
+ "page__executor__task__status__pending": "等待中",
48
+ "page__executor__task__status__running": "运行中",
49
+ "page__executor__task__status__completed": "已完成",
50
+ "page__executor__task__status__failed": "失败",
51
+ "page__executor__task__type__data_sync": "数据同步",
52
+ "page__executor__task__type__report": "报告生成",
53
+ "page__executor__task__type__maintenance": "系统维护",
54
+ "page__executor__task__type__backup": "数据备份",
55
+ "page__executor__task__duration__unit": "分钟",
56
+ "page__executor__task__start": "开始",
57
+ "page__executor__task__stop": "停止",
58
+ "page__executor__task__success": "任务 %{name} 执行成功",
59
+ "page__executor__task__failure": "任务 %{name} 执行失败",
60
+ "page__executor__plugin__test__success": "插件测试成功",
61
+ "page__executor__plugin__test__failure": "插件测试失败",
62
+ "page__executor__custom_task__url_required": "请输入URL",
63
+ "page__executor__custom_task__name": "自定义任务 %{method} %{url}",
64
+ "page__executor__create_task__title": "创建自定义任务",
65
+ "page__executor__create_button": "创建",
66
+ "page__executor__enter_url": "输入URL",
67
+ "page__executor__task_list__title": "任务列表",
68
+ "page__executor__task_stats__total": "总任务数",
69
+ "page__executor__task_stats__running": "运行中",
70
+ "page__executor__task_stats__completed": "已完成",
71
+ "page__executor__task_stats__failed": "失败",
72
+ "page__executor__task_history__title": "执行历史",
73
+ "page__executor__help__title": "需要帮助?",
74
+ "page__executor__help__description": "遇到问题?查看我们的任务执行指南或联系支持团队",
75
+ "page__executor__view_guide": "查看指南",
76
+ "page__executor__contact_support": "联系支持",
77
+ "page__executor__request__timeout": "请求超时时间",
78
+ "page__home__title": "首页",
79
+ "page__home__description": "一个现代前端实用库集合,提供各种实用工具和组件",
80
+ "home__welcome": "欢迎来到主页",
81
+ "home__description": "一个现代前端实用库集合,提供各种实用工具和组件",
82
+ "page__error__identifier__description": "错误标识符的使用和示例",
83
+ "home__explore": "探索",
84
+ "home__get_started__title": "准备开始使用?",
85
+ "home__get_started__description": "加入我们,探索实用工具的力量",
86
+ "home__get_started__button": "立即开始",
87
+ "page__error__identifier__main_title": "错误标识符",
88
+ "page__error__identifier__source_description": "来自 '@config/Identifier/error' 的标识符",
89
+ "page__error__identifier__help__title": "需要帮助?",
90
+ "page__error__identifier__help__description": "如果您在使用错误标识符时遇到问题,请联系我们的支持团队",
91
+ "page__error__identifier__contact_support": "联系支持",
92
+ "page__jsonstorage__title": "JSONStorage 页面",
93
+ "page__jsonstorage__description": "使用 JSONStorage 进行数据存储和管理",
94
+ "page__jsonstorage__main_title": "JSONStorage 演示",
95
+ "page__jsonstorage__permanent_title": "永久存储测试",
96
+ "page__jsonstorage__expire_title": "过期时间测试",
97
+ "page__jsonstorage__timeout_title": "请求超时时间设置",
98
+ "page__jsonstorage__format_title": "测试 key: ${key}, 随机值范围: ${min}~${max}",
99
+ "page__jsonstorage__set_random": "设置随机值",
100
+ "page__jsonstorage__current_value": "当前值",
101
+ "page__jsonstorage__set_expire": "设置随机值(带过期时间)",
102
+ "page__jsonstorage__ms": "毫秒",
103
+ "page__login__title": "登录",
104
+ "page__login__description": "登录页面描述",
105
+ "login__title": "登录",
106
+ "login__email": "邮箱",
107
+ "login__username": "用户名",
108
+ "login__password": "密码",
109
+ "login__login": "登录",
110
+ "login__welcome": "欢迎来到未来学习",
111
+ "login__subtitle": "解锁个性化 AI 驱动的学习体验,加速您的知识旅程。",
112
+ "login__forgot_password": "忘记密码?",
113
+ "login__continue_with": "或继续使用",
114
+ "login__with_google": "使用 Google 登录",
115
+ "login__no_account": "还没有账号?",
116
+ "login__create_account": "在此创建",
117
+ "login__email_required": "请输入您的邮箱!",
118
+ "login__password_required": "请输入您的密码!",
119
+ "login__feature__ai_paths": "AI驱动的个性化学习路径",
120
+ "login__feature__smart_recommendations": "智能内容推荐",
121
+ "login__feature__progress_tracking": "实时进度追踪",
122
+ "login__email__title": "输入邮箱",
123
+ "login__password__title": "输入密码",
124
+ "login__forgot_password__title": "重置密码",
125
+ "login__button__title": "登录账号",
126
+ "login__with_google__title": "使用Google账号登录",
127
+ "login__create_account__title": "创建新账号",
128
+ "page__register__title": "创建账号",
129
+ "page__register__description": "创建账号页面描述",
130
+ "register__title": "创建账号",
131
+ "register__subtitle": "开始您的学习之旅",
132
+ "register__username": "用户名",
133
+ "register__username_required": "请输入用户名!",
134
+ "register__email": "邮箱",
135
+ "register__email_required": "请输入邮箱!",
136
+ "register__password": "密码",
137
+ "register__password_required": "请输入密码!",
138
+ "register__confirm_password": "确认密码",
139
+ "register__confirm_password_required": "请确认密码!",
140
+ "register__password_mismatch": "两次输入的密码不匹配!",
141
+ "register__button": "注册",
142
+ "register__terms_prefix": "注册即表示您同意我们的",
143
+ "register__terms_link": "服务条款",
144
+ "register__terms_and": "和",
145
+ "register__privacy_link": "隐私政策",
146
+ "register__have_account": "已有账号?",
147
+ "register__login_link": "登录",
148
+ "register__feature__personalized": "个性化学习体验",
149
+ "register__feature__support": "专家支持和指导",
150
+ "register__feature__community": "活跃的学习社区",
151
+ "register__terms_required": "请同意服务条款和隐私政策",
152
+ "page__request__title": "请求示例",
153
+ "page__request__description": "展示各种请求示例和用法",
154
+ "request__requestTimeout": "请求超时",
155
+ "request__helloResult": "Hello 结果是",
156
+ "request__helloError": "Hello 错误是",
157
+ "request__ipInfoResult": "IpInfo 结果是",
158
+ "request__ipInfo": "IpInfo",
159
+ "request__randomUser": "随机用户",
160
+ "request__loading": "加载中______",
161
+ "request__randomUserResult": "随机用户 结果是",
162
+ "request__randomUserError": "随机用户 错误是",
163
+ "request__triggerAbortRequest": "触发中止请求",
164
+ "request__stopAbortRequest": "停止中止请求",
165
+ "request__abortRequestResult": "中止请求 结果是",
166
+ "request__abortRequestError": "中止请求 错误是",
167
+ "page__request__timeout": "请求超时时间",
168
+ "page__request__hello__title": "AI API: Hello",
169
+ "page__request__hello__description": "函数式 api, 使用了 FetchURLPlugin, RequestCommonPlugin, ApiMockPlugin, RequestLogger 插件",
170
+ "page__request__hello__button": "Hello",
171
+ "page__request__ip_info__title": "FeApi: IP Information",
172
+ "page__request__ip_info__description": "RequestScheduler 类式 api, 使用了 FetchURLPlugin, RequestCommonPlugin, RequestLogger, ApiPickDataPlugin 插件, 其中 ApiPickDataPlugin 插件可以将返回类型统一扁平到 data 字段",
173
+ "page__request__random_user__title": "UserApi: Random User",
174
+ "page__request__random_user__description": "RequestTransaction 类式 api, 使用了 FetchURLPlugin, RequestCommonPlugin, ApiMockPlugin, FetchAbortPlugin, RequestLogger, ApiCatchPlugin 插件, 其中 FetchAbortPlugin 可以中止请求, ApiCatchPlugin 可以将捕获的错误统一到 apiCatchResult 字段",
175
+ "page__request__api_catch__title": "UserApi: Api Catch Result",
176
+ "page__request__abort__title": "UserApi: Abort Request",
177
+ "page__request__trigger_abort": "触发中止请求",
178
+ "page__request__stop_abort": "停止中止请求",
179
+ "page__request__trigger_api_catch": "触发 API 捕获结果",
180
+ "page__request__stop_api_catch": "停止 API 捕获结果",
181
+ "page__login__content": "登录页面内容",
182
+ "page__login__keywords": "登录页面关键词",
183
+ "err__server__auth__error": "服务器认证错误"
184
+ }
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>
@@ -0,0 +1 @@
1
+ <svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1155 1000"><path d="m577.3 0 577.4 1000H0z" fill="#fff"/></svg>
@@ -0,0 +1 @@
1
+ <svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5" fill="#666"/></svg>
@@ -0,0 +1,32 @@
1
+ import { NextIntlClientProvider } from 'next-intl';
2
+ import { themeConfig } from '@config/theme';
3
+ import { PageParams } from '@/base/cases/PageParams';
4
+ import type { PageLayoutProps } from '@/base/types/PageProps';
5
+ import { BaseHeader } from '@/uikit/components/BaseHeader';
6
+ import { ComboProvider } from '@/uikit/components/ComboProvider';
7
+ import '@/styles/css/index.css';
8
+
9
+ export default async function RootLayout({
10
+ children,
11
+ params
12
+ }: PageLayoutProps) {
13
+ const pageParams = new PageParams(await params!);
14
+ const locale = pageParams.getLocale();
15
+ const messages = await pageParams.getI18nMessages();
16
+
17
+ // TODO: suppressHydrationWarning 暂时解决 hydration 问题
18
+ return (
19
+ <html data-testid="RootLayout" lang={locale} suppressHydrationWarning>
20
+ <body>
21
+ <NextIntlClientProvider locale={locale} messages={messages}>
22
+ <ComboProvider themeConfig={themeConfig}>
23
+ <div className="flex flex-col min-h-screen">
24
+ <BaseHeader showLogoutButton />
25
+ <div className="flex flex-col">{children}</div>
26
+ </div>
27
+ </ComboProvider>
28
+ </NextIntlClientProvider>
29
+ </body>
30
+ </html>
31
+ );
32
+ }
@@ -0,0 +1,13 @@
1
+ interface FeatureItemProps {
2
+ icon: string;
3
+ text: string;
4
+ }
5
+
6
+ export function FeatureItem({ icon, text }: FeatureItemProps) {
7
+ return (
8
+ <div data-testid="FeatureItem" className="flex items-start gap-4">
9
+ <div className="text-2xl">{icon}</div>
10
+ <p className="text-text-secondary">{text}</p>
11
+ </div>
12
+ );
13
+ }
@@ -0,0 +1,114 @@
1
+ 'use client';
2
+
3
+ import { UserOutlined, LockOutlined, GoogleOutlined } from '@ant-design/icons';
4
+ import { Form, Input, Button } from 'antd';
5
+ import { useState } from 'react';
6
+ import { I } from '@config/IOCIdentifier';
7
+ import { LocaleLink } from '@/uikit/components/LocaleLink';
8
+ import { useIOC } from '@/uikit/hook/useIOC';
9
+ import type { LoginI18nInterface } from '@config/i18n/loginI18n';
10
+
11
+ interface LoginFormData {
12
+ email: string;
13
+ password: string;
14
+ }
15
+
16
+ export function LoginForm(props: { tt: LoginI18nInterface }) {
17
+ const { tt } = props;
18
+ const userService = useIOC(I.UserServiceInterface);
19
+ const routerService = useIOC(I.RouterServiceInterface);
20
+ const [loading, setLoading] = useState(false);
21
+
22
+ const handleLogin = async (values: LoginFormData) => {
23
+ try {
24
+ setLoading(true);
25
+ await userService.login(values);
26
+ routerService.gotoHome();
27
+ } catch (error) {
28
+ console.error('Login error:', error);
29
+ } finally {
30
+ setLoading(false);
31
+ }
32
+ };
33
+
34
+ return (
35
+ <Form
36
+ data-testid="LoginForm"
37
+ name="login"
38
+ onFinish={handleLogin}
39
+ layout="vertical"
40
+ className="space-y-4"
41
+ >
42
+ <Form.Item
43
+ name="email"
44
+ rules={[{ required: true, message: tt.emailRequired }]}
45
+ >
46
+ <Input
47
+ prefix={<UserOutlined className="text-text-tertiary" />}
48
+ placeholder={tt.email}
49
+ title={tt.emailTitle}
50
+ className="h-12 text-base bg-secondary border-border"
51
+ autoComplete="off"
52
+ />
53
+ </Form.Item>
54
+
55
+ <Form.Item
56
+ name="password"
57
+ rules={[{ required: true, message: tt.passwordRequired }]}
58
+ >
59
+ <Input.Password
60
+ prefix={<LockOutlined />}
61
+ placeholder={tt.password}
62
+ title={tt.passwordTitle}
63
+ className="h-12 text-base"
64
+ autoComplete="new-password"
65
+ />
66
+ </Form.Item>
67
+
68
+ <div className="flex justify-end">
69
+ <LocaleLink
70
+ href="#"
71
+ className="text-brand hover:text-brand-hover"
72
+ title={tt.forgotPasswordTitle}
73
+ >
74
+ {tt.forgotPassword}
75
+ </LocaleLink>
76
+ </div>
77
+
78
+ <Form.Item>
79
+ <Button
80
+ type="primary"
81
+ htmlType="submit"
82
+ loading={loading}
83
+ title={tt.buttonTitle}
84
+ className="w-full h-12 text-base"
85
+ >
86
+ {tt.button}
87
+ </Button>
88
+ </Form.Item>
89
+
90
+ <div className="text-center text-text-tertiary my-4">
91
+ {tt.continueWith}
92
+ </div>
93
+
94
+ <Button
95
+ icon={<GoogleOutlined />}
96
+ className="w-full h-12 text-base"
97
+ title={tt.withGoogleTitle}
98
+ >
99
+ {tt.withGoogle}
100
+ </Button>
101
+
102
+ <div className="text-center mt-6">
103
+ <span className="text-text-tertiary">{tt.noAccount} </span>
104
+ <LocaleLink
105
+ href="/register"
106
+ className="text-brand hover:text-brand-hover"
107
+ title={tt.createAccountTitle}
108
+ >
109
+ {tt.createAccount}
110
+ </LocaleLink>
111
+ </div>
112
+ </Form>
113
+ );
114
+ }
@@ -0,0 +1,86 @@
1
+ import { notFound } from 'next/navigation';
2
+ import { loginI18n, i18nConfig } from '@config/i18n';
3
+ import { PageParams, type PageParamsType } from '@/base/cases/PageParams';
4
+ import { ServerAuth } from '@/base/cases/ServerAuth';
5
+ import type { PageParamsProps } from '@/base/types/PageProps';
6
+ import { BootstrapServer } from '@/core/bootstraps/BootstrapServer';
7
+ import { redirect } from '@/i18n/routing';
8
+ import { FeatureItem } from './FeatureItem';
9
+ import { LoginForm } from './LoginForm';
10
+ import type { Metadata } from 'next';
11
+
12
+ // Generate static params for all supported locales (used for SSG)
13
+ export async function generateStaticParams() {
14
+ // Return one entry for each supported locale
15
+ return i18nConfig.supportedLngs.map((locale) => ({ locale }));
16
+ }
17
+
18
+ // Allow Next.js to statically generate this page if possible (default behavior)
19
+ export const dynamic = 'auto'; // Enable static generation when possible, fallback to dynamic if needed
20
+
21
+ // Optional: Use revalidate if you want ISR (Incremental Static Regeneration)
22
+ // export const revalidate = 3600; // Rebuild every hour (optional)
23
+
24
+ // Generate localized SEO metadata per locale (Next.js 15+ best practice)
25
+ export async function generateMetadata({
26
+ params
27
+ }: {
28
+ params: Promise<PageParamsType>;
29
+ }): Promise<Metadata> {
30
+ const pageParams = new PageParams(await params);
31
+
32
+ const tt = await pageParams.getI18nInterface(loginI18n);
33
+
34
+ return tt;
35
+ }
36
+
37
+ export default async function LoginPage(props: PageParamsProps) {
38
+ if (!props.params) {
39
+ return notFound();
40
+ }
41
+
42
+ const params = await props.params;
43
+ const pageParams = new PageParams(params);
44
+
45
+ const server = new BootstrapServer();
46
+
47
+ if (await new ServerAuth(server).hasAuth()) {
48
+ return redirect({ href: '/', locale: params.locale! });
49
+ }
50
+
51
+ const tt = await pageParams.getI18nInterface(loginI18n);
52
+
53
+ return (
54
+ <div
55
+ data-testid="LoginPage"
56
+ className="flex text-xs1 bg-primary min-h-screen"
57
+ >
58
+ {/* Left side - Brand section */}
59
+ <div className="hidden lg:flex bg-secondary lg:w-1/2 p-12 flex-col">
60
+ <div className="flex items-center gap-3 mb-12">
61
+ <div className="w-10 h-10 bg-brand rounded-lg"></div>
62
+ <span className="text-2xl font-semibold text-text">
63
+ {'AppConfig.appName'}
64
+ </span>
65
+ </div>
66
+ <h1 className="text-4xl font-bold text-text mb-4">{tt.welcome}</h1>
67
+ <p className="text-text-secondary text-lg mb-8">{tt.subtitle}</p>
68
+ <div className="space-y-4">
69
+ <FeatureItem icon="🎯" text={tt.feature_ai_paths} />
70
+ <FeatureItem icon="🎯" text={tt.feature_smart_recommendations} />
71
+ <FeatureItem icon="📊" text={tt.feature_progress_tracking} />
72
+ </div>
73
+ </div>
74
+
75
+ {/* Right side - Login form */}
76
+ <div className="w-full lg:w-1/2 p-8 sm:p-12 flex items-center justify-center">
77
+ <div className="w-full max-w-[420px]">
78
+ <h2 className="text-2xl font-semibold mb-2 text-text">{tt.title}</h2>
79
+ <p className="text-text-secondary mb-8">{tt.subtitle}</p>
80
+
81
+ <LoginForm tt={tt} />
82
+ </div>
83
+ </div>
84
+ </div>
85
+ );
86
+ }
@@ -0,0 +1,24 @@
1
+ // import { useTranslations } from 'next-intl'; // Client-side translation hook
2
+ import { identity as t } from 'lodash';
3
+ import { Link } from '@/i18n/routing'; // i18n-aware Link component
4
+
5
+ export default function NotFound() {
6
+ return (
7
+ <div
8
+ data-testid="NotFound"
9
+ className="space-y-6 py-24 flex flex-col items-center"
10
+ >
11
+ {/* Large translated 404 headline */}
12
+ <h1 className="text-4xl font-bold text-center">{'title'}</h1>
13
+ {/* Description text, translated */}
14
+ <p className="text-lg text-gray-600 text-center">{t('description')}</p>
15
+ {/* Link back to home page, translated label */}
16
+ <Link
17
+ href="/"
18
+ className="inline-block rounded-xl bg-gray-900 text-white font-medium px-6 py-3 hover:bg-gray-800 transition"
19
+ >
20
+ {t('backToHome')}
21
+ </Link>
22
+ </div>
23
+ );
24
+ }
@@ -0,0 +1,119 @@
1
+ import Image from 'next/image';
2
+ import { PageParams } from '@/base/cases/PageParams';
3
+ import { ServerAuth } from '@/base/cases/ServerAuth';
4
+ import type { PageParamsProps } from '@/base/types/PageProps';
5
+ import { BootstrapServer } from '@/core/bootstraps/BootstrapServer';
6
+ import { redirect } from '@/i18n/routing';
7
+
8
+ export default async function Home({ params }: PageParamsProps) {
9
+ const server = new BootstrapServer();
10
+ const pageParams = new PageParams(await params!);
11
+ const locale = pageParams.getLocale();
12
+
13
+ if (!(await new ServerAuth(server).hasAuth())) {
14
+ return redirect({ href: '/login', locale });
15
+ }
16
+
17
+ return (
18
+ <div
19
+ data-testid="Home"
20
+ className="font-sans grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20"
21
+ >
22
+ <main className="flex flex-col gap-[32px] row-start-2 items-center sm:items-start">
23
+ <Image
24
+ className="dark:invert"
25
+ src="/next.svg"
26
+ alt="Next.js logo"
27
+ width={180}
28
+ height={38}
29
+ priority
30
+ />
31
+ <ol className="font-mono list-inside list-decimal text-sm/6 text-center sm:text-left">
32
+ <li className="mb-2 tracking-[-.01em]">
33
+ Get started by editing{' '}
34
+ <code className="bg-black/[.05] dark:bg-white/[.06] font-mono font-semibold px-1 py-0.5 rounded">
35
+ src/app/page.tsx
36
+ </code>
37
+ .
38
+ </li>
39
+ <li className="tracking-[-.01em]">
40
+ Save and see your changes instantly.
41
+ </li>
42
+ </ol>
43
+
44
+ <div className="flex gap-4 items-center flex-col sm:flex-row">
45
+ <a
46
+ className="rounded-full border border-solid border-transparent transition-colors flex items-center justify-center bg-foreground text-background gap-2 hover:bg-[#383838] dark:hover:bg-[#ccc] font-medium text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 sm:w-auto"
47
+ href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
48
+ target="_blank"
49
+ rel="noopener noreferrer"
50
+ >
51
+ <Image
52
+ className="dark:invert"
53
+ src="/vercel.svg"
54
+ alt="Vercel logomark"
55
+ width={20}
56
+ height={20}
57
+ />
58
+ Deploy now
59
+ </a>
60
+ <a
61
+ className="rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent font-medium text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 w-full sm:w-auto md:w-[158px]"
62
+ href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
63
+ target="_blank"
64
+ rel="noopener noreferrer"
65
+ >
66
+ Read our docs
67
+ </a>
68
+ </div>
69
+ </main>
70
+ <footer className="row-start-3 flex gap-[24px] flex-wrap items-center justify-center">
71
+ <a
72
+ className="flex items-center gap-2 hover:underline hover:underline-offset-4"
73
+ href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
74
+ target="_blank"
75
+ rel="noopener noreferrer"
76
+ >
77
+ <Image
78
+ aria-hidden
79
+ src="/file.svg"
80
+ alt="File icon"
81
+ width={16}
82
+ height={16}
83
+ />
84
+ Learn
85
+ </a>
86
+ <a
87
+ className="flex items-center gap-2 hover:underline hover:underline-offset-4"
88
+ href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
89
+ target="_blank"
90
+ rel="noopener noreferrer"
91
+ >
92
+ <Image
93
+ aria-hidden
94
+ src="/window.svg"
95
+ alt="Window icon"
96
+ width={16}
97
+ height={16}
98
+ />
99
+ Examples
100
+ </a>
101
+ <a
102
+ className="flex items-center gap-2 hover:underline hover:underline-offset-4"
103
+ href="https://nextjs.org?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
104
+ target="_blank"
105
+ rel="noopener noreferrer"
106
+ >
107
+ <Image
108
+ aria-hidden
109
+ src="/globe.svg"
110
+ alt="Globe icon"
111
+ width={16}
112
+ height={16}
113
+ />
114
+ Go to nextjs.org →
115
+ </a>
116
+ </footer>
117
+ </div>
118
+ );
119
+ }
@@ -0,0 +1,15 @@
1
+ import { name, version } from '../../../package.json';
2
+ import type { EnvConfigInterface } from '@qlover/corekit-bridge';
3
+
4
+ export class AppConfig implements EnvConfigInterface {
5
+ /**
6
+ * Current environment mode for Vite
7
+ * @description Represents the running environment (development, production, etc.)
8
+ * Automatically set based on the current .env file being used
9
+ */
10
+ readonly env: string = process.env.APP_ENV!;
11
+ readonly appName: string = name;
12
+ readonly appVersion: string = version;
13
+
14
+ readonly userTokenKey: string = '_user_token';
15
+ }