@qlover/create-app 0.7.10 → 0.7.11

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 (92) hide show
  1. package/CHANGELOG.md +74 -0
  2. package/dist/index.cjs +1 -1
  3. package/dist/index.js +2 -2
  4. package/dist/templates/next-app/config/Identifier/api.ts +14 -0
  5. package/dist/templates/next-app/config/Identifier/common.ts +7 -0
  6. package/dist/templates/next-app/eslint.config.mjs +17 -0
  7. package/dist/templates/next-app/migrations/schema/UserSchema.ts +21 -10
  8. package/dist/templates/next-app/next.config.ts +1 -0
  9. package/dist/templates/next-app/package.json +1 -0
  10. package/dist/templates/next-app/public/locales/en.json +4 -1
  11. package/dist/templates/next-app/public/locales/zh.json +4 -1
  12. package/dist/templates/next-app/src/app/[locale]/admin/layout.tsx +4 -7
  13. package/dist/templates/next-app/src/app/[locale]/admin/page.tsx +16 -4
  14. package/dist/templates/next-app/src/app/[locale]/admin/users/page.tsx +62 -0
  15. package/dist/templates/next-app/src/app/[locale]/layout.tsx +1 -1
  16. package/dist/templates/next-app/src/app/[locale]/login/LoginForm.tsx +1 -1
  17. package/dist/templates/next-app/src/app/[locale]/login/page.tsx +1 -1
  18. package/dist/templates/next-app/src/app/[locale]/page.tsx +3 -3
  19. package/dist/templates/next-app/src/app/[locale]/register/RegisterForm.tsx +1 -1
  20. package/dist/templates/next-app/src/app/[locale]/register/page.tsx +1 -1
  21. package/dist/templates/next-app/src/app/api/admin/users/route.ts +39 -0
  22. package/dist/templates/next-app/src/base/cases/AdminPageManager.ts +40 -0
  23. package/dist/templates/next-app/src/base/cases/AppConfig.ts +1 -1
  24. package/dist/templates/next-app/src/base/cases/DialogErrorPlugin.ts +13 -2
  25. package/dist/templates/next-app/src/base/cases/PageParams.ts +1 -1
  26. package/dist/templates/next-app/src/base/cases/RequestState.ts +20 -0
  27. package/dist/templates/next-app/src/base/cases/UserServiceApi.ts +1 -1
  28. package/dist/templates/next-app/src/base/port/AdminLayoutInterface.ts +26 -0
  29. package/dist/templates/next-app/src/base/port/AdminPageInterface.ts +87 -0
  30. package/dist/templates/next-app/src/base/port/AppApiInterface.ts +1 -1
  31. package/dist/templates/next-app/src/base/port/AppUserApiInterface.ts +4 -4
  32. package/dist/templates/next-app/src/base/port/AsyncStateInterface.ts +7 -0
  33. package/dist/templates/next-app/src/base/port/DBBridgeInterface.ts +3 -0
  34. package/dist/templates/next-app/src/base/port/PaginationInterface.ts +6 -0
  35. package/dist/templates/next-app/src/base/services/AdminUserService.ts +45 -0
  36. package/dist/templates/next-app/src/base/services/UserService.ts +1 -1
  37. package/dist/templates/next-app/src/base/services/adminApi/AdminApiRequester.ts +21 -0
  38. package/dist/templates/next-app/src/base/services/adminApi/AdminUserApi.ts +34 -0
  39. package/dist/templates/next-app/src/base/services/appApi/AppApiPlugin.ts +2 -2
  40. package/dist/templates/next-app/src/base/services/appApi/AppApiRequester.ts +56 -0
  41. package/dist/templates/next-app/src/base/services/appApi/AppUserApi.ts +30 -31
  42. package/dist/templates/next-app/src/base/services/appApi/AppUserApiBootstrap.ts +5 -4
  43. package/dist/templates/next-app/src/core/bootstraps/BootstrapClient.ts +1 -1
  44. package/dist/templates/next-app/src/core/bootstraps/BootstrapServer.ts +3 -14
  45. package/dist/templates/next-app/src/core/bootstraps/BootstrapsRegistry.ts +2 -2
  46. package/dist/templates/next-app/src/core/bootstraps/PrintBootstrap.ts +1 -1
  47. package/dist/templates/next-app/src/core/clientIoc/ClientIOC.ts +1 -1
  48. package/dist/templates/next-app/src/core/clientIoc/ClientIOCRegister.ts +1 -1
  49. package/dist/templates/next-app/src/core/globals.ts +1 -1
  50. package/dist/templates/next-app/src/core/serverIoc/ServerIOC.ts +1 -1
  51. package/dist/templates/next-app/src/core/serverIoc/ServerIOCRegister.ts +1 -1
  52. package/dist/templates/next-app/src/server/ServerAuth.ts +13 -3
  53. package/dist/templates/next-app/src/server/SupabaseBridge.ts +37 -2
  54. package/dist/templates/next-app/src/server/UserCredentialToken.ts +1 -1
  55. package/dist/templates/next-app/src/server/port/DBTableInterface.ts +10 -0
  56. package/dist/templates/next-app/src/server/port/{UserAuthInterface.ts → ServerAuthInterface.ts} +3 -1
  57. package/dist/templates/next-app/src/server/port/UserRepositoryInterface.ts +1 -1
  58. package/dist/templates/next-app/src/server/repositorys/UserRepository.ts +32 -1
  59. package/dist/templates/next-app/src/server/services/AdminAuthPlugin.ts +19 -0
  60. package/dist/templates/next-app/src/server/services/ApiUserService.ts +26 -0
  61. package/dist/templates/next-app/src/server/services/UserService.ts +3 -3
  62. package/dist/templates/next-app/src/server/validators/PaginationValidator.ts +48 -0
  63. package/dist/templates/next-app/src/styles/css/antd-themes/{_default.css → _common/_default.css} +74 -1
  64. package/dist/templates/next-app/src/styles/css/antd-themes/{dark.css → _common/dark.css} +73 -0
  65. package/dist/templates/next-app/src/styles/css/antd-themes/_common/index.css +3 -0
  66. package/dist/templates/next-app/src/styles/css/antd-themes/{pink.css → _common/pink.css} +70 -0
  67. package/dist/templates/next-app/src/styles/css/antd-themes/index.css +4 -3
  68. package/dist/templates/next-app/src/styles/css/antd-themes/menu/_default.css +108 -0
  69. package/dist/templates/next-app/src/styles/css/antd-themes/menu/dark.css +67 -0
  70. package/dist/templates/next-app/src/styles/css/antd-themes/menu/index.css +3 -0
  71. package/dist/templates/next-app/src/styles/css/antd-themes/menu/pink.css +67 -0
  72. package/dist/templates/next-app/src/styles/css/antd-themes/pagination/_default.css +33 -0
  73. package/dist/templates/next-app/src/styles/css/antd-themes/pagination/dark.css +32 -0
  74. package/dist/templates/next-app/src/styles/css/antd-themes/pagination/index.css +3 -0
  75. package/dist/templates/next-app/src/styles/css/antd-themes/pagination/pink.css +35 -0
  76. package/dist/templates/next-app/src/styles/css/antd-themes/table/_default.css +44 -0
  77. package/dist/templates/next-app/src/styles/css/antd-themes/table/dark.css +43 -0
  78. package/dist/templates/next-app/src/styles/css/antd-themes/table/index.css +3 -0
  79. package/dist/templates/next-app/src/styles/css/antd-themes/table/pink.css +43 -0
  80. package/dist/templates/next-app/src/uikit/components/AdminLayout.tsx +106 -0
  81. package/dist/templates/next-app/src/uikit/components/BaseHeader.tsx +68 -17
  82. package/dist/templates/next-app/src/uikit/components/BaseLayout.tsx +6 -1
  83. package/dist/templates/next-app/src/uikit/components/BootstrapsProvider.tsx +1 -1
  84. package/dist/templates/next-app/src/uikit/components/ComboProvider.tsx +11 -3
  85. package/dist/templates/next-app/src/uikit/components/LanguageSwitcher.tsx +1 -1
  86. package/dist/templates/next-app/src/uikit/components/LogoutButton.tsx +1 -1
  87. package/dist/templates/next-app/src/uikit/hook/useIOC.ts +1 -1
  88. package/dist/templates/next-app/src/uikit/hook/useMountedClient.ts +7 -1
  89. package/package.json +1 -1
  90. package/dist/templates/next-app/src/base/cases/ServerErrorHandler.ts +0 -27
  91. package/dist/templates/next-app/src/base/port/DBTableInterface.ts +0 -3
  92. package/dist/templates/next-app/src/base/services/appApi/AppUserType.ts +0 -51
@@ -1,13 +1,24 @@
1
- export interface UserSchema {
2
- id: number;
3
- role: string;
4
- email: string;
5
- password: string;
1
+ import { z } from 'zod';
2
+
3
+ export const UserRole = {
4
+ ADMIN: 0,
5
+ USER: 1
6
+ } as const;
7
+
8
+ export type UserRoleType = (typeof UserRole)[keyof typeof UserRole];
9
+
10
+ export const userSchema = z.object({
11
+ id: z.number(),
12
+ role: z.enum(UserRole),
13
+ email: z.email(),
14
+ password: z.string(),
6
15
  /**
7
16
  * 加密的token, 包含token, 过期时间
8
17
  */
9
- credential_token: string;
10
- email_confirmed_at: number;
11
- created_at?: number;
12
- updated_at?: number;
13
- }
18
+ credential_token: z.string(),
19
+ email_confirmed_at: z.number(),
20
+ created_at: z.number(),
21
+ updated_at: z.number()
22
+ });
23
+
24
+ export type UserSchema = z.infer<typeof userSchema>;
@@ -10,6 +10,7 @@ generateLocales().catch((error) => {
10
10
  });
11
11
 
12
12
  const nextConfig: NextConfig = {
13
+ // reactStrictMode: false,
13
14
  turbopack: {
14
15
  root: __dirname // 明确指定根目录
15
16
  },
@@ -56,6 +56,7 @@
56
56
  "eslint": "^9",
57
57
  "eslint-config-next": "15.5.0",
58
58
  "eslint-config-prettier": "^10.1.8",
59
+ "eslint-import-resolver-typescript": "^4.4.4",
59
60
  "eslint-plugin-import": "^2.32.0",
60
61
  "eslint-plugin-prettier": "^5.5.4",
61
62
  "eslint-plugin-unused-imports": "^4.2.0",
@@ -195,5 +195,8 @@
195
195
  "register__welcome": "Welcome to our platform",
196
196
  "register__feature__ai_paths": "AI Paths",
197
197
  "register__feature__smart_recommendations": "Smart Recommendations",
198
- "register__feature__progress_tracking": "Progress Tracking"
198
+ "register__feature__progress_tracking": "Progress Tracking",
199
+ "page__head__admin__title": "Admin Backend",
200
+ "api__not_authorized": "Not authorized",
201
+ "api__page__invalid": "Page number is incorrect"
199
202
  }
@@ -195,5 +195,8 @@
195
195
  "register__welcome": "欢迎来到我们的平台",
196
196
  "register__feature__ai_paths": "AI路径",
197
197
  "register__feature__smart_recommendations": "智能推荐",
198
- "register__feature__progress_tracking": "进度跟踪"
198
+ "register__feature__progress_tracking": "进度跟踪",
199
+ "page__head__admin__title": "管理后台",
200
+ "api__not_authorized": "未授权",
201
+ "api__page__invalid": "页码不正确"
199
202
  }
@@ -1,8 +1,9 @@
1
1
  import { PageParams } from '@/base/cases/PageParams';
2
2
  import type { PageLayoutProps } from '@/base/types/PageProps';
3
3
  import '@/styles/css/index.css';
4
+ import { AdminLayout } from '@/uikit/components/AdminLayout';
4
5
 
5
- export default async function AdminLayout({
6
+ export default async function AdminRootLayout({
6
7
  children,
7
8
  params
8
9
  }: PageLayoutProps) {
@@ -10,12 +11,8 @@ export default async function AdminLayout({
10
11
  const locale = pageParams.getLocale();
11
12
 
12
13
  return (
13
- <div
14
- data-testid="AdminRootLayout"
15
- lang={locale}
16
- className="min-h-screen bg-primary text-text"
17
- >
14
+ <AdminLayout data-testid="AdminRootLayout" lang={locale}>
18
15
  {children}
19
- </div>
16
+ </AdminLayout>
20
17
  );
21
18
  }
@@ -1,10 +1,22 @@
1
- 'use client';
2
- import Link from 'next/link';
1
+ import type { Metadata } from 'next';
2
+
3
+ function AdminPageClient() {
4
+ return <div data-testid="AdminPage">Admin Page</div>;
5
+ }
6
+
7
+ export const metadata: Metadata = {
8
+ title: 'Admin Dashboard',
9
+ description: 'Admin dashboard for managing application resources',
10
+ robots: {
11
+ index: false,
12
+ follow: false
13
+ }
14
+ };
3
15
 
4
16
  export default function AdminPage() {
5
17
  return (
6
- <div data-testid="AdminPage">
7
- <Link href="/admin/migrations">Migrations</Link>
18
+ <div data-testid="AdminPageWrapper">
19
+ <AdminPageClient />
8
20
  </div>
9
21
  );
10
22
  }
@@ -0,0 +1,62 @@
1
+ 'use client';
2
+
3
+ import { Table } from 'antd';
4
+ import { useEffect, useRef } from 'react';
5
+ import { AdminUserService } from '@/base/services/AdminUserService';
6
+ import { useIOC } from '@/uikit/hook/useIOC';
7
+ import { useStore } from '@/uikit/hook/useStore';
8
+ import { userSchema, type UserSchema } from '@migrations/schema/UserSchema';
9
+ import type { ColumnsType } from 'antd/es/table';
10
+
11
+ const baseColumns: ColumnsType<UserSchema> = Object.keys(
12
+ userSchema.omit({
13
+ password: true,
14
+ credential_token: true
15
+ }).shape
16
+ ).map((key) => ({
17
+ title: key,
18
+ dataIndex: key
19
+ }));
20
+
21
+ export default function UsersPage() {
22
+ const adminUserService = useIOC(AdminUserService);
23
+
24
+ const listParams = useStore(adminUserService, (state) => state.listParams);
25
+ const listState = useStore(adminUserService, (state) => state.listState);
26
+ const mouted = useRef(false);
27
+
28
+ useEffect(() => {
29
+ if (!mouted.current) {
30
+ mouted.current = true;
31
+ adminUserService.initialize();
32
+ }
33
+ }, []);
34
+
35
+ const dataSource = listState.result?.list as UserSchema[];
36
+
37
+ const columns: ColumnsType<UserSchema> = baseColumns;
38
+
39
+ return (
40
+ <div data-testid="UsersPage">
41
+ <Table
42
+ rowKey="id"
43
+ columns={columns}
44
+ dataSource={dataSource}
45
+ loading={listState.loading}
46
+ scroll={{ x: true }}
47
+ pagination={{
48
+ pageSizeOptions: [10, 20, 50],
49
+ current: listParams.page,
50
+ pageSize: listParams.pageSize,
51
+ total: listState.result?.total,
52
+ onChange: (page, pageSize) => {
53
+ adminUserService.changeListParams({
54
+ page,
55
+ pageSize
56
+ });
57
+ }
58
+ }}
59
+ />
60
+ </div>
61
+ );
62
+ }
@@ -1,8 +1,8 @@
1
1
  import { NextIntlClientProvider } from 'next-intl';
2
- import { themeConfig } from '@config/theme';
3
2
  import { PageParams } from '@/base/cases/PageParams';
4
3
  import type { PageLayoutProps } from '@/base/types/PageProps';
5
4
  import { ComboProvider } from '@/uikit/components/ComboProvider';
5
+ import { themeConfig } from '@config/theme';
6
6
  import '@/styles/css/index.css';
7
7
 
8
8
  export default async function RootLayout({
@@ -4,11 +4,11 @@ import { UserOutlined, LockOutlined, GoogleOutlined } from '@ant-design/icons';
4
4
  import { Form, Input, Button } from 'antd';
5
5
  import { useTranslations } from 'next-intl';
6
6
  import { useState } from 'react';
7
- import { I } from '@config/IOCIdentifier';
8
7
  import { LoginValidator } from '@/server/validators/LoginValidator';
9
8
  import { LocaleLink } from '@/uikit/components/LocaleLink';
10
9
  import { useIOC } from '@/uikit/hook/useIOC';
11
10
  import type { LoginI18nInterface } from '@config/i18n/loginI18n';
11
+ import { I } from '@config/IOCIdentifier';
12
12
 
13
13
  interface LoginFormData {
14
14
  email: string;
@@ -1,5 +1,4 @@
1
1
  import { notFound } from 'next/navigation';
2
- import { loginI18n, i18nConfig } from '@config/i18n';
3
2
  import { PageParams, type PageParamsType } from '@/base/cases/PageParams';
4
3
  import type { PageParamsProps } from '@/base/types/PageProps';
5
4
  import { BootstrapServer } from '@/core/bootstraps/BootstrapServer';
@@ -7,6 +6,7 @@ import { redirect } from '@/i18n/routing';
7
6
  import { ServerAuth } from '@/server/ServerAuth';
8
7
  import { BaseLayout } from '@/uikit/components/BaseLayout';
9
8
  import { FeatureItem } from '@/uikit/components/FeatureItem';
9
+ import { loginI18n, i18nConfig } from '@config/i18n';
10
10
  import { LoginForm } from './LoginForm';
11
11
  import type { Metadata } from 'next';
12
12
 
@@ -1,12 +1,12 @@
1
1
  import { Button } from 'antd';
2
- import { i18nConfig } from '@config/i18n';
3
- import { homeI18n } from '@config/i18n/HomeI18n ';
4
2
  import { PageParams, type PageParamsType } from '@/base/cases/PageParams';
5
3
  import type { PageParamsProps } from '@/base/types/PageProps';
6
4
  import { BootstrapServer } from '@/core/bootstraps/BootstrapServer';
7
5
  import { redirect } from '@/i18n/routing';
8
6
  import { ServerAuth } from '@/server/ServerAuth';
9
7
  import { BaseLayout } from '@/uikit/components/BaseLayout';
8
+ import { i18nConfig } from '@config/i18n';
9
+ import { homeI18n } from '@config/i18n/HomeI18n ';
10
10
  import type { Metadata } from 'next';
11
11
 
12
12
  // const navigationItems = [
@@ -50,7 +50,7 @@ export default async function Home({ params }: PageParamsProps) {
50
50
  }
51
51
 
52
52
  return (
53
- <BaseLayout data-testid="HomePage" showLogoutButton>
53
+ <BaseLayout data-testid="HomePage" showLogoutButton showAdminButton>
54
54
  {/* Hero Section */}
55
55
  <section className="py-16 px-4">
56
56
  <div className="max-w-4xl mx-auto text-center">
@@ -3,9 +3,9 @@
3
3
  import { LockOutlined, MailOutlined, UserOutlined } from '@ant-design/icons';
4
4
  import { Button, Checkbox, Form, Input } from 'antd';
5
5
  import { useState } from 'react';
6
- import { I } from '@config/IOCIdentifier';
7
6
  import { useIOC } from '@/uikit/hook/useIOC';
8
7
  import type { RegisterI18nInterface } from '@config/i18n';
8
+ import { I } from '@config/IOCIdentifier';
9
9
 
10
10
  export function RegisterForm(props: { tt: RegisterI18nInterface }) {
11
11
  const { tt } = props;
@@ -1,5 +1,4 @@
1
1
  import { notFound } from 'next/navigation';
2
- import { i18nConfig, register18n } from '@config/i18n';
3
2
  import { PageParams, type PageParamsType } from '@/base/cases/PageParams';
4
3
  import type { PageParamsProps } from '@/base/types/PageProps';
5
4
  import { BootstrapServer } from '@/core/bootstraps/BootstrapServer';
@@ -7,6 +6,7 @@ import { redirect } from '@/i18n/routing';
7
6
  import { ServerAuth } from '@/server/ServerAuth';
8
7
  import { BaseLayout } from '@/uikit/components/BaseLayout';
9
8
  import { FeatureItem } from '@/uikit/components/FeatureItem';
9
+ import { i18nConfig, register18n } from '@config/i18n';
10
10
  import { RegisterForm } from './RegisterForm';
11
11
  import type { Metadata } from 'next';
12
12
 
@@ -0,0 +1,39 @@
1
+ import { ExecutorError } from '@qlover/fe-corekit';
2
+ import { NextResponse } from 'next/server';
3
+ import { BootstrapServer } from '@/core/bootstraps/BootstrapServer';
4
+ import { AppErrorApi } from '@/server/AppErrorApi';
5
+ import { AppSuccessApi } from '@/server/AppSuccessApi';
6
+ import { AdminAuthPlugin } from '@/server/services/AdminAuthPlugin';
7
+ import { ApiUserService } from '@/server/services/ApiUserService';
8
+ import { PaginationValidator } from '@/server/validators/PaginationValidator';
9
+ import type { NextRequest } from 'next/server';
10
+
11
+ export async function GET(req: NextRequest) {
12
+ const server = new BootstrapServer();
13
+
14
+ const result = await server
15
+ .use(new AdminAuthPlugin())
16
+ .execNoError(async ({ parameters: { IOC } }) => {
17
+ const searchParams = Object.fromEntries(
18
+ req.nextUrl.searchParams.entries()
19
+ );
20
+ const paginationParams = IOC(PaginationValidator).getThrow(searchParams);
21
+
22
+ const apiUserService = IOC(ApiUserService);
23
+
24
+ const result = await apiUserService.getUsers({
25
+ page: paginationParams.page,
26
+ pageSize: paginationParams.pageSize
27
+ });
28
+
29
+ return result;
30
+ });
31
+
32
+ if (result instanceof ExecutorError) {
33
+ return NextResponse.json(new AppErrorApi(result.id, result.message), {
34
+ status: 400
35
+ });
36
+ }
37
+
38
+ return NextResponse.json(new AppSuccessApi(result));
39
+ }
@@ -0,0 +1,40 @@
1
+ import { injectable } from 'inversify';
2
+ import { AdminLayoutInterface } from '../port/AdminLayoutInterface';
3
+ import type {
4
+ NavItemInterface,
5
+ AdminPageState
6
+ } from '../port/AdminLayoutInterface';
7
+
8
+ const defaultNavItems: NavItemInterface[] = [
9
+ {
10
+ key: 'dashboard',
11
+ i18nKey: 'Dashboard',
12
+ pathname: '/admin'
13
+ },
14
+ {
15
+ key: 'users',
16
+ i18nKey: 'User Management',
17
+ pathname: '/admin/users'
18
+ }
19
+ ];
20
+
21
+ class AdminPageManagerState implements AdminPageState {
22
+ collapsedSidebar = false;
23
+
24
+ navItems: NavItemInterface[] = defaultNavItems;
25
+ }
26
+
27
+ @injectable()
28
+ export class AdminPageManager extends AdminLayoutInterface {
29
+ constructor() {
30
+ super(() => new AdminPageManagerState());
31
+ }
32
+
33
+ override toggleSidebar(): void {
34
+ this.emit(
35
+ this.cloneState({
36
+ collapsedSidebar: !this.state.collapsedSidebar
37
+ })
38
+ );
39
+ }
40
+ }
@@ -1,6 +1,6 @@
1
- import type { StringValue } from 'ms';
2
1
  import { name, version } from '../../../package.json';
3
2
  import type { EnvConfigInterface } from '@qlover/corekit-bridge';
3
+ import type { StringValue } from 'ms';
4
4
 
5
5
  export class AppConfig implements EnvConfigInterface {
6
6
  /**
@@ -28,8 +28,19 @@ export class DialogErrorPlugin implements ExecutorPlugin {
28
28
  const handleError = runtimesError || error;
29
29
 
30
30
  if (handleError instanceof ExecutorError) {
31
- const message = this.i18nService.t(handleError.id);
32
- this.dialogHandler.error(message);
31
+ if (this.isI18nMessage(handleError.message)) {
32
+ const message = this.i18nService.t(handleError.id);
33
+ this.dialogHandler.error(message);
34
+ } else {
35
+ this.dialogHandler.error(handleError.message);
36
+ }
33
37
  }
34
38
  }
39
+
40
+ protected isI18nMessage(message: string): boolean {
41
+ // Check if message follows the pattern of underscore-separated format
42
+ // e.g., "namespace_key" or "namespace__key"
43
+ const pattern = /^[a-zA-Z]+(?:_[a-zA-Z]+)+$|^[a-zA-Z]+(?:__[a-zA-Z]+)+$/;
44
+ return pattern.test(message);
45
+ }
35
46
  }
@@ -1,8 +1,8 @@
1
1
  import { notFound } from 'next/navigation';
2
2
  import { getMessages, getTranslations } from 'next-intl/server';
3
3
  import { i18nConfig } from '@config/i18n';
4
- import type { ParamsHandlerInterface as ParamsHandlerInterface } from '../port/ParamsHandlerInterface';
5
4
  import type { LocaleType, PageI18nInterface } from '@config/i18n';
5
+ import type { ParamsHandlerInterface as ParamsHandlerInterface } from '../port/ParamsHandlerInterface';
6
6
 
7
7
  export interface PageWithParams {
8
8
  params?: Promise<PageParamsType>;
@@ -0,0 +1,20 @@
1
+ import type { AsyncStateInterface } from '@/base/port/AsyncStateInterface';
2
+
3
+ export class RequestState<T = unknown> implements AsyncStateInterface<T> {
4
+ startTime: number;
5
+ endTime: number;
6
+
7
+ constructor(
8
+ public loading: boolean = false,
9
+ public result: T | null = null,
10
+ public error: unknown | null = null
11
+ ) {
12
+ this.startTime = Date.now();
13
+ this.endTime = 0;
14
+ }
15
+
16
+ end(): this {
17
+ this.endTime = Date.now();
18
+ return this;
19
+ }
20
+ }
@@ -1,8 +1,8 @@
1
1
  import { inject, injectable } from 'inversify';
2
+ import type { UserSchema } from '@migrations/schema/UserSchema';
2
3
  import { AppUserApi } from '../services/appApi/AppUserApi';
3
4
  import type { AppApiSuccessInterface } from '../port/AppApiInterface';
4
5
  import type { AppUserApiInterface } from '../port/AppUserApiInterface';
5
- import type { UserSchema } from '@migrations/schema/UserSchema';
6
6
  import type {
7
7
  LoginResponseData,
8
8
  UserAuthApiInterface,
@@ -0,0 +1,26 @@
1
+ import { StoreInterface } from '@qlover/corekit-bridge';
2
+ import type { StoreStateInterface } from '@qlover/corekit-bridge';
3
+
4
+ export interface NavItemInterface {
5
+ key: string;
6
+ /**
7
+ * 可以是 i18n key
8
+ */
9
+ i18nKey: string;
10
+
11
+ pathname?: string;
12
+ }
13
+
14
+ export interface AdminPageState extends StoreStateInterface {
15
+ navItems: NavItemInterface[];
16
+ collapsedSidebar: boolean;
17
+ }
18
+
19
+ export abstract class AdminLayoutInterface extends StoreInterface<AdminPageState> {
20
+ public readonly selectors = {
21
+ collapsedSidebar: (state: AdminPageState) => state.collapsedSidebar,
22
+ navItems: (state: AdminPageState) => state.navItems
23
+ };
24
+
25
+ abstract toggleSidebar(): void;
26
+ }
@@ -0,0 +1,87 @@
1
+ import { StoreInterface } from '@qlover/corekit-bridge';
2
+ import { RequestState } from '../cases/RequestState';
3
+ import type { PaginationInterface } from './PaginationInterface';
4
+ import type { StoreStateInterface } from '@qlover/corekit-bridge';
5
+
6
+ export interface AdminPageListParams {
7
+ page: number;
8
+ pageSize: number;
9
+ }
10
+
11
+ export class AdminPageState implements StoreStateInterface {
12
+ listParams: AdminPageListParams = {
13
+ page: 1,
14
+ pageSize: 10
15
+ };
16
+ initState = new RequestState<unknown>();
17
+ listState = new RequestState<PaginationInterface<unknown>>();
18
+ }
19
+
20
+ export abstract class AdminPageInterface<
21
+ S extends AdminPageState
22
+ > extends StoreInterface<S> {
23
+ /**
24
+ * 初始化
25
+ * @returns
26
+ */
27
+ async initialize(): Promise<unknown> {
28
+ this.emit(
29
+ this.cloneState({
30
+ initState: new RequestState(true)
31
+ } as Partial<S>)
32
+ );
33
+
34
+ try {
35
+ const result = await this.fetchList(this.state.listParams);
36
+
37
+ console.log('jj result', result);
38
+
39
+ this.emit(
40
+ this.cloneState({
41
+ initState: new RequestState(false, result).end()
42
+ } as Partial<S>)
43
+ );
44
+ return result;
45
+ } catch (error) {
46
+ this.emit(
47
+ this.cloneState({
48
+ initState: new RequestState(false, null, error).end()
49
+ } as Partial<S>)
50
+ );
51
+
52
+ return error;
53
+ }
54
+ }
55
+
56
+ /**
57
+ * 销毁
58
+ */
59
+ destroy(): void {
60
+ this.reset();
61
+ }
62
+
63
+ /**
64
+ * 获取列表
65
+ * @param params
66
+ * @returns
67
+ */
68
+ abstract fetchList(
69
+ params: Partial<AdminPageListParams>
70
+ ): Promise<PaginationInterface<unknown>>;
71
+
72
+ changeListState(state: RequestState<unknown>): void {
73
+ this.emit(
74
+ this.cloneState({
75
+ listState: state
76
+ } as Partial<S>)
77
+ );
78
+ }
79
+
80
+ changeListParams(params: Partial<AdminPageListParams>): void {
81
+ this.emit(
82
+ this.cloneState({
83
+ listParams: params
84
+ } as Partial<S>)
85
+ );
86
+ }
87
+ }
@@ -9,6 +9,6 @@ export interface AppApiSuccessInterface<T = unknown> {
9
9
  data?: T;
10
10
  }
11
11
 
12
- export type AppApiResponse<T = unknown> =
12
+ export type AppApiResult<T = unknown> =
13
13
  | AppApiErrorInterface
14
14
  | AppApiSuccessInterface<T>;
@@ -1,15 +1,15 @@
1
- import type { AppApiResponse } from './AppApiInterface';
1
+ import type { AppApiResult } from './AppApiInterface';
2
2
 
3
3
  export interface AppUserApiInterface {
4
4
  login(params: {
5
5
  email: string;
6
6
  password: string;
7
- }): Promise<AppApiResponse<unknown>>;
7
+ }): Promise<AppApiResult<unknown>>;
8
8
 
9
9
  register(params: {
10
10
  email: string;
11
11
  password: string;
12
- }): Promise<AppApiResponse<unknown>>;
12
+ }): Promise<AppApiResult<unknown>>;
13
13
 
14
- logout(): Promise<AppApiResponse<unknown>>;
14
+ logout(): Promise<AppApiResult<unknown>>;
15
15
  }
@@ -0,0 +1,7 @@
1
+ export interface AsyncStateInterface<T> {
2
+ loading: boolean;
3
+ result: T | null;
4
+ error: unknown | null;
5
+ startTime: number;
6
+ endTime: number;
7
+ }
@@ -8,6 +8,8 @@ export interface BridgeEvent {
8
8
  fields?: string | string[];
9
9
  where?: Where[];
10
10
  data?: unknown;
11
+ page?: number;
12
+ pageSize?: number;
11
13
  }
12
14
 
13
15
  export interface DBBridgeInterface {
@@ -15,4 +17,5 @@ export interface DBBridgeInterface {
15
17
  update(event: BridgeEvent): Promise<PostgrestSingleResponse<unknown>>;
16
18
  delete(event: BridgeEvent): Promise<PostgrestSingleResponse<unknown>>;
17
19
  get(event: BridgeEvent): Promise<PostgrestSingleResponse<unknown>>;
20
+ pagination(event: BridgeEvent): Promise<PostgrestSingleResponse<unknown>>;
18
21
  }
@@ -0,0 +1,6 @@
1
+ export interface PaginationInterface<T> {
2
+ list: T[];
3
+ total: number;
4
+ page: number;
5
+ pageSize: number;
6
+ }
@@ -0,0 +1,45 @@
1
+ import { inject, injectable } from 'inversify';
2
+ import {
3
+ AdminPageInterface,
4
+ type AdminPageListParams,
5
+ AdminPageState
6
+ } from '../port/AdminPageInterface';
7
+ import { AdminUserApi } from './adminApi/AdminUserApi';
8
+ import { RequestState } from '../cases/RequestState';
9
+ import type { PaginationInterface } from '../port/PaginationInterface';
10
+
11
+ @injectable()
12
+ export class AdminUserService extends AdminPageInterface<AdminPageState> {
13
+ constructor(@inject(AdminUserApi) protected adminUserApi: AdminUserApi) {
14
+ super(() => new AdminPageState());
15
+ }
16
+
17
+ override async fetchList(
18
+ params: Partial<AdminPageListParams>
19
+ ): Promise<PaginationInterface<unknown>> {
20
+ this.changeListState(new RequestState(true));
21
+
22
+ try {
23
+ const response = await this.adminUserApi.getUserList(
24
+ Object.assign({}, this.state.listParams, params)
25
+ );
26
+
27
+ if (response.data.success) {
28
+ const paginationData = response.data
29
+ .data as PaginationInterface<unknown>;
30
+
31
+ this.changeListState(new RequestState(false, paginationData));
32
+
33
+ return paginationData;
34
+ }
35
+
36
+ this.changeListState(
37
+ new RequestState(false, null, response.data.message)
38
+ );
39
+ } catch (error) {
40
+ this.changeListState(new RequestState(false, null, error));
41
+ }
42
+
43
+ return this.state.listState.result!;
44
+ }
45
+ }