@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.
- package/CHANGELOG.md +74 -0
- package/dist/index.cjs +1 -1
- package/dist/index.js +2 -2
- package/dist/templates/next-app/config/Identifier/api.ts +14 -0
- package/dist/templates/next-app/config/Identifier/common.ts +7 -0
- package/dist/templates/next-app/eslint.config.mjs +17 -0
- package/dist/templates/next-app/migrations/schema/UserSchema.ts +21 -10
- package/dist/templates/next-app/next.config.ts +1 -0
- package/dist/templates/next-app/package.json +1 -0
- package/dist/templates/next-app/public/locales/en.json +4 -1
- package/dist/templates/next-app/public/locales/zh.json +4 -1
- package/dist/templates/next-app/src/app/[locale]/admin/layout.tsx +4 -7
- package/dist/templates/next-app/src/app/[locale]/admin/page.tsx +16 -4
- package/dist/templates/next-app/src/app/[locale]/admin/users/page.tsx +62 -0
- package/dist/templates/next-app/src/app/[locale]/layout.tsx +1 -1
- package/dist/templates/next-app/src/app/[locale]/login/LoginForm.tsx +1 -1
- package/dist/templates/next-app/src/app/[locale]/login/page.tsx +1 -1
- package/dist/templates/next-app/src/app/[locale]/page.tsx +3 -3
- package/dist/templates/next-app/src/app/[locale]/register/RegisterForm.tsx +1 -1
- package/dist/templates/next-app/src/app/[locale]/register/page.tsx +1 -1
- package/dist/templates/next-app/src/app/api/admin/users/route.ts +39 -0
- package/dist/templates/next-app/src/base/cases/AdminPageManager.ts +40 -0
- package/dist/templates/next-app/src/base/cases/AppConfig.ts +1 -1
- package/dist/templates/next-app/src/base/cases/DialogErrorPlugin.ts +13 -2
- package/dist/templates/next-app/src/base/cases/PageParams.ts +1 -1
- package/dist/templates/next-app/src/base/cases/RequestState.ts +20 -0
- package/dist/templates/next-app/src/base/cases/UserServiceApi.ts +1 -1
- package/dist/templates/next-app/src/base/port/AdminLayoutInterface.ts +26 -0
- package/dist/templates/next-app/src/base/port/AdminPageInterface.ts +87 -0
- package/dist/templates/next-app/src/base/port/AppApiInterface.ts +1 -1
- package/dist/templates/next-app/src/base/port/AppUserApiInterface.ts +4 -4
- package/dist/templates/next-app/src/base/port/AsyncStateInterface.ts +7 -0
- package/dist/templates/next-app/src/base/port/DBBridgeInterface.ts +3 -0
- package/dist/templates/next-app/src/base/port/PaginationInterface.ts +6 -0
- package/dist/templates/next-app/src/base/services/AdminUserService.ts +45 -0
- package/dist/templates/next-app/src/base/services/UserService.ts +1 -1
- package/dist/templates/next-app/src/base/services/adminApi/AdminApiRequester.ts +21 -0
- package/dist/templates/next-app/src/base/services/adminApi/AdminUserApi.ts +34 -0
- package/dist/templates/next-app/src/base/services/appApi/AppApiPlugin.ts +2 -2
- package/dist/templates/next-app/src/base/services/appApi/AppApiRequester.ts +56 -0
- package/dist/templates/next-app/src/base/services/appApi/AppUserApi.ts +30 -31
- package/dist/templates/next-app/src/base/services/appApi/AppUserApiBootstrap.ts +5 -4
- package/dist/templates/next-app/src/core/bootstraps/BootstrapClient.ts +1 -1
- package/dist/templates/next-app/src/core/bootstraps/BootstrapServer.ts +3 -14
- package/dist/templates/next-app/src/core/bootstraps/BootstrapsRegistry.ts +2 -2
- package/dist/templates/next-app/src/core/bootstraps/PrintBootstrap.ts +1 -1
- package/dist/templates/next-app/src/core/clientIoc/ClientIOC.ts +1 -1
- package/dist/templates/next-app/src/core/clientIoc/ClientIOCRegister.ts +1 -1
- package/dist/templates/next-app/src/core/globals.ts +1 -1
- package/dist/templates/next-app/src/core/serverIoc/ServerIOC.ts +1 -1
- package/dist/templates/next-app/src/core/serverIoc/ServerIOCRegister.ts +1 -1
- package/dist/templates/next-app/src/server/ServerAuth.ts +13 -3
- package/dist/templates/next-app/src/server/SupabaseBridge.ts +37 -2
- package/dist/templates/next-app/src/server/UserCredentialToken.ts +1 -1
- package/dist/templates/next-app/src/server/port/DBTableInterface.ts +10 -0
- package/dist/templates/next-app/src/server/port/{UserAuthInterface.ts → ServerAuthInterface.ts} +3 -1
- package/dist/templates/next-app/src/server/port/UserRepositoryInterface.ts +1 -1
- package/dist/templates/next-app/src/server/repositorys/UserRepository.ts +32 -1
- package/dist/templates/next-app/src/server/services/AdminAuthPlugin.ts +19 -0
- package/dist/templates/next-app/src/server/services/ApiUserService.ts +26 -0
- package/dist/templates/next-app/src/server/services/UserService.ts +3 -3
- package/dist/templates/next-app/src/server/validators/PaginationValidator.ts +48 -0
- package/dist/templates/next-app/src/styles/css/antd-themes/{_default.css → _common/_default.css} +74 -1
- package/dist/templates/next-app/src/styles/css/antd-themes/{dark.css → _common/dark.css} +73 -0
- package/dist/templates/next-app/src/styles/css/antd-themes/_common/index.css +3 -0
- package/dist/templates/next-app/src/styles/css/antd-themes/{pink.css → _common/pink.css} +70 -0
- package/dist/templates/next-app/src/styles/css/antd-themes/index.css +4 -3
- package/dist/templates/next-app/src/styles/css/antd-themes/menu/_default.css +108 -0
- package/dist/templates/next-app/src/styles/css/antd-themes/menu/dark.css +67 -0
- package/dist/templates/next-app/src/styles/css/antd-themes/menu/index.css +3 -0
- package/dist/templates/next-app/src/styles/css/antd-themes/menu/pink.css +67 -0
- package/dist/templates/next-app/src/styles/css/antd-themes/pagination/_default.css +33 -0
- package/dist/templates/next-app/src/styles/css/antd-themes/pagination/dark.css +32 -0
- package/dist/templates/next-app/src/styles/css/antd-themes/pagination/index.css +3 -0
- package/dist/templates/next-app/src/styles/css/antd-themes/pagination/pink.css +35 -0
- package/dist/templates/next-app/src/styles/css/antd-themes/table/_default.css +44 -0
- package/dist/templates/next-app/src/styles/css/antd-themes/table/dark.css +43 -0
- package/dist/templates/next-app/src/styles/css/antd-themes/table/index.css +3 -0
- package/dist/templates/next-app/src/styles/css/antd-themes/table/pink.css +43 -0
- package/dist/templates/next-app/src/uikit/components/AdminLayout.tsx +106 -0
- package/dist/templates/next-app/src/uikit/components/BaseHeader.tsx +68 -17
- package/dist/templates/next-app/src/uikit/components/BaseLayout.tsx +6 -1
- package/dist/templates/next-app/src/uikit/components/BootstrapsProvider.tsx +1 -1
- package/dist/templates/next-app/src/uikit/components/ComboProvider.tsx +11 -3
- package/dist/templates/next-app/src/uikit/components/LanguageSwitcher.tsx +1 -1
- package/dist/templates/next-app/src/uikit/components/LogoutButton.tsx +1 -1
- package/dist/templates/next-app/src/uikit/hook/useIOC.ts +1 -1
- package/dist/templates/next-app/src/uikit/hook/useMountedClient.ts +7 -1
- package/package.json +1 -1
- package/dist/templates/next-app/src/base/cases/ServerErrorHandler.ts +0 -27
- package/dist/templates/next-app/src/base/port/DBTableInterface.ts +0 -3
- package/dist/templates/next-app/src/base/services/appApi/AppUserType.ts +0 -51
|
@@ -1,13 +1,24 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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
|
|
12
|
-
updated_at
|
|
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>;
|
|
@@ -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
|
|
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
|
-
<
|
|
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
|
-
</
|
|
16
|
+
</AdminLayout>
|
|
20
17
|
);
|
|
21
18
|
}
|
|
@@ -1,10 +1,22 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
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="
|
|
7
|
-
<
|
|
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
|
-
|
|
32
|
-
|
|
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
|
+
}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import type {
|
|
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<
|
|
7
|
+
}): Promise<AppApiResult<unknown>>;
|
|
8
8
|
|
|
9
9
|
register(params: {
|
|
10
10
|
email: string;
|
|
11
11
|
password: string;
|
|
12
|
-
}): Promise<
|
|
12
|
+
}): Promise<AppApiResult<unknown>>;
|
|
13
13
|
|
|
14
|
-
logout(): Promise<
|
|
14
|
+
logout(): Promise<AppApiResult<unknown>>;
|
|
15
15
|
}
|
|
@@ -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,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
|
+
}
|