Spaces:
Sleeping
Sleeping
Merge remote-tracking branch 'origin/main'
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- Dockerfile +2 -0
- backend/.env.example +6 -1
- backend/package-lock.json +0 -0
- backend/package.json +95 -91
- backend/src/app.module.ts +45 -43
- backend/src/common/enums/MenuItemType.enum.ts +4 -4
- backend/src/common/enums/OrderStatus.enum.ts +5 -5
- backend/src/common/enums/OrderType.enum.ts +3 -3
- backend/src/common/enums/PaymentMethod.enum.ts +3 -3
- backend/src/common/enums/VnpCardType.enum.ts +5 -0
- backend/src/common/enums/role.enum.ts +7 -7
- backend/src/common/interfaces/jwt-payload.interface.ts +5 -0
- backend/src/config/config.ts +5 -0
- backend/src/entities/branch-menu.entity.ts +3 -0
- backend/src/entities/branch.entity.ts +14 -3
- backend/src/entities/feed.entity.ts +11 -1
- backend/src/entities/menu-item.entity.ts +6 -2
- backend/src/entities/order.entity.ts +21 -7
- backend/src/entities/payment.entity.ts +27 -7
- backend/src/entities/receipt.entity.ts +57 -0
- backend/src/entities/user.entity.ts +8 -4
- backend/src/migrations/1730547520878-AddReceipt.ts +30 -0
- backend/src/migrations/1730549959767-RemoveEnums.ts +36 -0
- backend/src/migrations/1730651201156-modify_payment.ts +32 -0
- backend/src/migrations/1730865796585-AddBranchAndOrderFields.ts +22 -0
- backend/src/migrations/1730895089788-UpdateOrderFieldTime.ts +25 -0
- backend/src/modules/authentication/authentication.service.ts +1 -1
- backend/src/modules/authentication/authorization/roles.guard.ts +1 -1
- backend/src/modules/branch-menus/branch-menus.service.ts +15 -4
- backend/src/modules/branch/branch.controller.ts +6 -1
- backend/src/modules/branch/branch.service.ts +9 -3
- backend/src/modules/branch/dto/create-branch.dto.ts +3 -0
- backend/src/modules/branch/dto/update-branch.dto.ts +4 -0
- backend/src/modules/menu-item/dto/create-menu-item.dto.ts +2 -2
- backend/src/modules/menu-item/dto/update-menu-item.dto.ts +2 -2
- backend/src/modules/order/dto/create-order.dto.ts +6 -2
- backend/src/modules/order/dto/update-order.dto.ts +14 -0
- backend/src/modules/order/order.controller.ts +41 -15
- backend/src/modules/order/order.module.ts +3 -2
- backend/src/modules/order/order.service.ts +204 -72
- backend/src/modules/user/dto/update-users.dto.ts +37 -0
- backend/src/modules/user/user.controller.ts +61 -23
- backend/src/modules/user/user.service.ts +167 -66
- backend/src/payment/dto/create-payment-url.dto.ts +22 -0
- backend/src/payment/dto/create-payment.dto..ts +38 -0
- backend/src/payment/payment.controller.ts +39 -0
- backend/src/payment/payment.module.ts +11 -0
- backend/src/payment/payment.service.ts +200 -0
- frontend/.gitignore +4 -1
- frontend/package-lock.json +662 -0
Dockerfile
CHANGED
@@ -7,6 +7,8 @@ WORKDIR /app
|
|
7 |
# Copy package.json and package-lock.json
|
8 |
COPY backend/package*.json ./
|
9 |
|
|
|
|
|
10 |
# Install dependencies
|
11 |
RUN npm install
|
12 |
|
|
|
7 |
# Copy package.json and package-lock.json
|
8 |
COPY backend/package*.json ./
|
9 |
|
10 |
+
RUN npm install -g npm@10.9.0
|
11 |
+
|
12 |
# Install dependencies
|
13 |
RUN npm install
|
14 |
|
backend/.env.example
CHANGED
@@ -4,4 +4,9 @@ DB_USER='pbl6_jw8s_user'
|
|
4 |
DB_PASSWORD=''
|
5 |
DB_NAME='pbl6_jw8s'
|
6 |
JWT_KEY= ''
|
7 |
-
DB_SSL_ENABLED=true # default is true to connect with remote database
|
|
|
|
|
|
|
|
|
|
|
|
4 |
DB_PASSWORD=''
|
5 |
DB_NAME='pbl6_jw8s'
|
6 |
JWT_KEY= ''
|
7 |
+
DB_SSL_ENABLED=true # default is true to connect with remote database
|
8 |
+
#Payment
|
9 |
+
VNP_TMNCODE = ''
|
10 |
+
VNP_HASHSECRET = ''
|
11 |
+
VNP_URL = ''
|
12 |
+
VNP_RETURNURL = ''
|
backend/package-lock.json
CHANGED
The diff for this file is too large to render.
See raw diff
|
|
backend/package.json
CHANGED
@@ -1,91 +1,95 @@
|
|
1 |
-
{
|
2 |
-
"type": "module",
|
3 |
-
"name": "backend",
|
4 |
-
"version": "0.0.1",
|
5 |
-
"description": "",
|
6 |
-
"author": "",
|
7 |
-
"private": true,
|
8 |
-
"license": "UNLICENSED",
|
9 |
-
"scripts": {
|
10 |
-
"build": "nest build",
|
11 |
-
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
|
12 |
-
"start": "nest start",
|
13 |
-
"start:dev": "nest start --watch",
|
14 |
-
"start:debug": "nest start --debug --watch",
|
15 |
-
"start:prod": "node dist/main",
|
16 |
-
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
|
17 |
-
"test": "jest",
|
18 |
-
"test:watch": "jest --watch",
|
19 |
-
"test:cov": "jest --coverage",
|
20 |
-
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
|
21 |
-
"test:e2e": "jest --config ./test/jest-e2e.json",
|
22 |
-
"typeorm": "typeorm-ts-node-esm",
|
23 |
-
"db:migrate:generate": "npm run typeorm migration:generate -- -d ./src/config/typeorm.ts",
|
24 |
-
"db:migrate:up": "npm run typeorm migration:run -- -d ./src/config/typeorm.ts",
|
25 |
-
"db:migrate:down": "npm run typeorm migration:revert -- -d ./src/config/typeorm.ts"
|
26 |
-
},
|
27 |
-
"dependencies": {
|
28 |
-
"@nestjs/common": "^10.0.0",
|
29 |
-
"@nestjs/config": "^3.2.3",
|
30 |
-
"@nestjs/core": "^10.0.0",
|
31 |
-
"@nestjs/jwt": "^10.2.0",
|
32 |
-
"@nestjs/mapped-types": "*",
|
33 |
-
"@nestjs/passport": "^10.0.3",
|
34 |
-
"@nestjs/platform-express": "^10.0.0",
|
35 |
-
"@nestjs/typeorm": "^10.0.2",
|
36 |
-
"bcrypt": "^5.1.1",
|
37 |
-
"class-transformer": "^0.5.1",
|
38 |
-
"class-validator": "^0.14.1",
|
39 |
-
"
|
40 |
-
"
|
41 |
-
"
|
42 |
-
"
|
43 |
-
"
|
44 |
-
"passport
|
45 |
-
"passport-
|
46 |
-
"
|
47 |
-
"
|
48 |
-
"
|
49 |
-
"
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
"@nestjs/
|
55 |
-
"@
|
56 |
-
"@
|
57 |
-
"@types/
|
58 |
-
"@types/
|
59 |
-
"@
|
60 |
-
"@
|
61 |
-
"
|
62 |
-
"
|
63 |
-
"eslint-plugin
|
64 |
-
"
|
65 |
-
"
|
66 |
-
"
|
67 |
-
"
|
68 |
-
"
|
69 |
-
"
|
70 |
-
"
|
71 |
-
"
|
72 |
-
"
|
73 |
-
|
74 |
-
|
75 |
-
"
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
"
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
"
|
90 |
-
|
91 |
-
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"type": "module",
|
3 |
+
"name": "backend",
|
4 |
+
"version": "0.0.1",
|
5 |
+
"description": "",
|
6 |
+
"author": "",
|
7 |
+
"private": true,
|
8 |
+
"license": "UNLICENSED",
|
9 |
+
"scripts": {
|
10 |
+
"build": "nest build",
|
11 |
+
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
|
12 |
+
"start": "nest start",
|
13 |
+
"start:dev": "nest start --watch",
|
14 |
+
"start:debug": "nest start --debug --watch",
|
15 |
+
"start:prod": "node dist/main",
|
16 |
+
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
|
17 |
+
"test": "jest",
|
18 |
+
"test:watch": "jest --watch",
|
19 |
+
"test:cov": "jest --coverage",
|
20 |
+
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
|
21 |
+
"test:e2e": "jest --config ./test/jest-e2e.json",
|
22 |
+
"typeorm": "typeorm-ts-node-esm",
|
23 |
+
"db:migrate:generate": "npm run typeorm migration:generate -- -d ./src/config/typeorm.ts",
|
24 |
+
"db:migrate:up": "npm run typeorm migration:run -- -d ./src/config/typeorm.ts",
|
25 |
+
"db:migrate:down": "npm run typeorm migration:revert -- -d ./src/config/typeorm.ts"
|
26 |
+
},
|
27 |
+
"dependencies": {
|
28 |
+
"@nestjs/common": "^10.0.0",
|
29 |
+
"@nestjs/config": "^3.2.3",
|
30 |
+
"@nestjs/core": "^10.0.0",
|
31 |
+
"@nestjs/jwt": "^10.2.0",
|
32 |
+
"@nestjs/mapped-types": "*",
|
33 |
+
"@nestjs/passport": "^10.0.3",
|
34 |
+
"@nestjs/platform-express": "^10.0.0",
|
35 |
+
"@nestjs/typeorm": "^10.0.2",
|
36 |
+
"bcrypt": "^5.1.1",
|
37 |
+
"class-transformer": "^0.5.1",
|
38 |
+
"class-validator": "^0.14.1",
|
39 |
+
"dateformat": "^5.0.3",
|
40 |
+
"dotenv": "^16.4.5",
|
41 |
+
"mysql2": "^3.11.3",
|
42 |
+
"nest-access-control": "^3.1.0",
|
43 |
+
"nestjs-paginate": "^9.3.0",
|
44 |
+
"passport": "^0.7.0",
|
45 |
+
"passport-jwt": "^4.0.1",
|
46 |
+
"passport-local": "^1.0.0",
|
47 |
+
"pg": "^8.13.0",
|
48 |
+
"reflect-metadata": "^0.2.0",
|
49 |
+
"rxjs": "^7.8.1",
|
50 |
+
"typeorm": "^0.3.20",
|
51 |
+
"vnpay": "^1.6.0"
|
52 |
+
},
|
53 |
+
"devDependencies": {
|
54 |
+
"@nestjs/cli": "^10.0.0",
|
55 |
+
"@nestjs/schematics": "^10.0.0",
|
56 |
+
"@nestjs/testing": "^10.0.0",
|
57 |
+
"@types/dateformat": "^5.0.2",
|
58 |
+
"@types/express": "^4.17.17",
|
59 |
+
"@types/jest": "^29.5.2",
|
60 |
+
"@types/multer": "^1.4.12",
|
61 |
+
"@types/node": "^20.3.1",
|
62 |
+
"@types/supertest": "^6.0.0",
|
63 |
+
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
64 |
+
"@typescript-eslint/parser": "^8.0.0",
|
65 |
+
"eslint": "^8.42.0",
|
66 |
+
"eslint-config-prettier": "^9.0.0",
|
67 |
+
"eslint-plugin-prettier": "^5.0.0",
|
68 |
+
"jest": "^29.5.0",
|
69 |
+
"prettier": "^3.0.0",
|
70 |
+
"source-map-support": "^0.5.21",
|
71 |
+
"supertest": "^7.0.0",
|
72 |
+
"ts-jest": "^29.1.0",
|
73 |
+
"ts-loader": "^9.4.3",
|
74 |
+
"ts-node": "^10.9.2",
|
75 |
+
"tsconfig-paths": "^4.2.0",
|
76 |
+
"typescript": "^5.1.3"
|
77 |
+
},
|
78 |
+
"jest": {
|
79 |
+
"moduleFileExtensions": [
|
80 |
+
"js",
|
81 |
+
"json",
|
82 |
+
"ts"
|
83 |
+
],
|
84 |
+
"rootDir": "src",
|
85 |
+
"testRegex": ".*\\.spec\\.ts$",
|
86 |
+
"transform": {
|
87 |
+
"^.+\\.(t|j)s$": "ts-jest"
|
88 |
+
},
|
89 |
+
"collectCoverageFrom": [
|
90 |
+
"**/*.(t|j)s"
|
91 |
+
],
|
92 |
+
"coverageDirectory": "../coverage",
|
93 |
+
"testEnvironment": "node"
|
94 |
+
}
|
95 |
+
}
|
backend/src/app.module.ts
CHANGED
@@ -1,43 +1,45 @@
|
|
1 |
-
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
|
2 |
-
import { AppController } from './app.controller.js';
|
3 |
-
import { AppService } from './app.service.js';
|
4 |
-
import { ConfigModule } from '@nestjs/config';
|
5 |
-
import { TypeOrmModule } from '@nestjs/typeorm';
|
6 |
-
import { configuration } from './config/config.js';
|
7 |
-
import { DatabaseConfigService } from './config/database.js';
|
8 |
-
import { AppLoggerMiddleware } from './common/middlewares/app-logger.middleware.js';
|
9 |
-
import { DeviceInfoMiddleware } from './common/middlewares/device-info.middleware.js';
|
10 |
-
import { UserModule } from './modules/user/user.module.js';
|
11 |
-
import { BranchModule } from './modules/branch/branch.module.js';
|
12 |
-
import { AuthenticationModule } from './modules/authentication/authentication.module.js';
|
13 |
-
import { MenuItemModule } from './modules/menu-item/menu-item.module.js';
|
14 |
-
import { FeedsModule } from './modules/feeds/feeds.module.js';
|
15 |
-
import { OrderModule } from './modules/order/order.module.js';
|
16 |
-
import { BranchMenusModule } from './modules/branch-menus/branch-menus.module.js';
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
|
|
|
|
|
1 |
+
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
|
2 |
+
import { AppController } from './app.controller.js';
|
3 |
+
import { AppService } from './app.service.js';
|
4 |
+
import { ConfigModule } from '@nestjs/config';
|
5 |
+
import { TypeOrmModule } from '@nestjs/typeorm';
|
6 |
+
import { configuration } from './config/config.js';
|
7 |
+
import { DatabaseConfigService } from './config/database.js';
|
8 |
+
import { AppLoggerMiddleware } from './common/middlewares/app-logger.middleware.js';
|
9 |
+
import { DeviceInfoMiddleware } from './common/middlewares/device-info.middleware.js';
|
10 |
+
import { UserModule } from './modules/user/user.module.js';
|
11 |
+
import { BranchModule } from './modules/branch/branch.module.js';
|
12 |
+
import { AuthenticationModule } from './modules/authentication/authentication.module.js';
|
13 |
+
import { MenuItemModule } from './modules/menu-item/menu-item.module.js';
|
14 |
+
import { FeedsModule } from './modules/feeds/feeds.module.js';
|
15 |
+
import { OrderModule } from './modules/order/order.module.js';
|
16 |
+
import { BranchMenusModule } from './modules/branch-menus/branch-menus.module.js';
|
17 |
+
import { PaymentModule } from './payment/payment.module.js';
|
18 |
+
@Module({
|
19 |
+
imports: [
|
20 |
+
ConfigModule.forRoot({
|
21 |
+
isGlobal: true,
|
22 |
+
load: [configuration],
|
23 |
+
}),
|
24 |
+
TypeOrmModule.forRootAsync({
|
25 |
+
imports: [ConfigModule],
|
26 |
+
useClass: DatabaseConfigService,
|
27 |
+
}),
|
28 |
+
UserModule,
|
29 |
+
BranchModule,
|
30 |
+
AuthenticationModule,
|
31 |
+
MenuItemModule,
|
32 |
+
FeedsModule,
|
33 |
+
OrderModule,
|
34 |
+
BranchMenusModule,
|
35 |
+
PaymentModule,
|
36 |
+
],
|
37 |
+
controllers: [AppController],
|
38 |
+
providers: [AppService],
|
39 |
+
})
|
40 |
+
export class AppModule implements NestModule {
|
41 |
+
configure(consumer: MiddlewareConsumer): void {
|
42 |
+
consumer.apply(AppLoggerMiddleware).forRoutes('*');
|
43 |
+
consumer.apply(DeviceInfoMiddleware).forRoutes('*');
|
44 |
+
}
|
45 |
+
}
|
backend/src/common/enums/MenuItemType.enum.ts
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
export enum MenuItemType {
|
2 |
-
|
3 |
-
|
4 |
-
GIAI_KHAT =
|
5 |
-
|
6 |
}
|
|
|
1 |
export enum MenuItemType {
|
2 |
+
KHAC = 0, // khác
|
3 |
+
MON_CHINH = 1, // món chính
|
4 |
+
GIAI_KHAT = 2, // giải khát
|
5 |
+
TRANG_MIENG = 3, // tráng miệng
|
6 |
}
|
backend/src/common/enums/OrderStatus.enum.ts
CHANGED
@@ -1,7 +1,7 @@
|
|
1 |
export enum OrderStatus {
|
2 |
-
PENDING =
|
3 |
-
|
4 |
-
PREPARING =
|
5 |
-
DELIVERING =
|
6 |
-
DONE =
|
7 |
}
|
|
|
1 |
export enum OrderStatus {
|
2 |
+
PENDING = 0, // Khách hàng đặt hàng chưa thanh toán <online>
|
3 |
+
ONLINE_PAID = 1, // Khách hàng đã thanh toán và được nhân viên xác nhận <online>
|
4 |
+
PREPARING = 2, // nhân viên xác nhận và sang trạng thái preparing <online/offline>
|
5 |
+
DELIVERING = 3, // dang giao hàng <online>
|
6 |
+
DONE = 4, // <online, offline>
|
7 |
}
|
backend/src/common/enums/OrderType.enum.ts
CHANGED
@@ -1,5 +1,5 @@
|
|
1 |
export enum OrderType {
|
2 |
-
TAKE_AWAY =
|
3 |
-
OFFLINE =
|
4 |
-
ONLINE =
|
5 |
}
|
|
|
1 |
export enum OrderType {
|
2 |
+
TAKE_AWAY = 0,
|
3 |
+
OFFLINE = 1,
|
4 |
+
ONLINE = 2,
|
5 |
}
|
backend/src/common/enums/PaymentMethod.enum.ts
CHANGED
@@ -1,5 +1,5 @@
|
|
1 |
export enum PaymentMethod {
|
2 |
-
CASH =
|
3 |
-
CARD =
|
4 |
-
ONLINE_PAYMENT =
|
5 |
}
|
|
|
1 |
export enum PaymentMethod {
|
2 |
+
CASH = 0,
|
3 |
+
CARD = 1,
|
4 |
+
ONLINE_PAYMENT = 2,
|
5 |
}
|
backend/src/common/enums/VnpCardType.enum.ts
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
export enum VnpCardType {
|
2 |
+
None = 0,
|
3 |
+
ATM = 1,
|
4 |
+
QRCODE = 2
|
5 |
+
}
|
backend/src/common/enums/role.enum.ts
CHANGED
@@ -1,8 +1,8 @@
|
|
1 |
export enum Role {
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
|
|
1 |
export enum Role {
|
2 |
+
CUSTOMER = 'CUSTOMER',
|
3 |
+
ADMIN = 'ADMIN',
|
4 |
+
BRANCH_MANAGER = 'BRANCH_MANAGER',
|
5 |
+
AREA_MANAGER = 'AREA_MANAGER',
|
6 |
+
STAFF = 'STAFF',
|
7 |
+
SHIPPER = 'SHIPPER',
|
8 |
+
}
|
backend/src/common/interfaces/jwt-payload.interface.ts
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
interface JwtPayload {
|
2 |
+
sub: string; // id người dùng
|
3 |
+
role: string; // quyền người dùng
|
4 |
+
username: string;
|
5 |
+
}
|
backend/src/config/config.ts
CHANGED
@@ -8,5 +8,10 @@ export const configuration = () => {
|
|
8 |
'db.name': process.env.DB_NAME,
|
9 |
'db.slow_limit': Number(process.env.DB_SLOW_LIMIT) || 500, // ms
|
10 |
'db.ssl_enabled': process.env.DB_SSL_ENABLED,
|
|
|
|
|
|
|
|
|
|
|
11 |
};
|
12 |
};
|
|
|
8 |
'db.name': process.env.DB_NAME,
|
9 |
'db.slow_limit': Number(process.env.DB_SLOW_LIMIT) || 500, // ms
|
10 |
'db.ssl_enabled': process.env.DB_SSL_ENABLED,
|
11 |
+
// payment config
|
12 |
+
'vnp_TmnCode': process.env.VNP_TMNCODE,
|
13 |
+
'vnp_HashSecret': process.env.VNP_HASHSECRET,
|
14 |
+
'vnp_Url': process.env.VNP_URL,
|
15 |
+
'vnp_ReturnUrl': process.env.VNP_RETURNURL,
|
16 |
};
|
17 |
};
|
backend/src/entities/branch-menu.entity.ts
CHANGED
@@ -28,6 +28,9 @@ export class BranchMenuEntity extends BaseEntity {
|
|
28 |
@Column({ default: true })
|
29 |
is_open: boolean;
|
30 |
|
|
|
|
|
|
|
31 |
@ManyToOne(() => BranchEntity, (a) => a.menu_items)
|
32 |
@JoinColumn({ name: 'branch_id' })
|
33 |
branch: Relation<BranchEntity>;
|
|
|
28 |
@Column({ default: true })
|
29 |
is_open: boolean;
|
30 |
|
31 |
+
@Column({ default: 0 })
|
32 |
+
sold_count: number;
|
33 |
+
|
34 |
@ManyToOne(() => BranchEntity, (a) => a.menu_items)
|
35 |
@JoinColumn({ name: 'branch_id' })
|
36 |
branch: Relation<BranchEntity>;
|
backend/src/entities/branch.entity.ts
CHANGED
@@ -2,6 +2,7 @@ import {
|
|
2 |
BaseEntity,
|
3 |
Column,
|
4 |
CreateDateColumn,
|
|
|
5 |
Entity,
|
6 |
ManyToOne,
|
7 |
OneToMany,
|
@@ -11,19 +12,23 @@ import {
|
|
11 |
} from 'typeorm';
|
12 |
import { UserEntity } from './user.entity.js';
|
13 |
import { BranchMenuEntity } from './branch-menu.entity.js';
|
|
|
14 |
|
15 |
@Entity('branches')
|
16 |
export class BranchEntity extends BaseEntity {
|
17 |
@PrimaryColumn()
|
18 |
id: string;
|
19 |
|
20 |
-
@Column()
|
21 |
name: string;
|
22 |
|
23 |
-
@Column()
|
|
|
|
|
|
|
24 |
location: string;
|
25 |
|
26 |
-
@Column()
|
27 |
phone_number: string;
|
28 |
|
29 |
@ManyToOne(() => UserEntity, (user) => user.branches)
|
@@ -32,6 +37,12 @@ export class BranchEntity extends BaseEntity {
|
|
32 |
@OneToMany(() => BranchMenuEntity, (a) => a.branch)
|
33 |
menu_items: Relation<BranchMenuEntity>[];
|
34 |
|
|
|
|
|
|
|
35 |
@CreateDateColumn()
|
36 |
create_at: Date;
|
|
|
|
|
|
|
37 |
}
|
|
|
2 |
BaseEntity,
|
3 |
Column,
|
4 |
CreateDateColumn,
|
5 |
+
DeleteDateColumn,
|
6 |
Entity,
|
7 |
ManyToOne,
|
8 |
OneToMany,
|
|
|
12 |
} from 'typeorm';
|
13 |
import { UserEntity } from './user.entity.js';
|
14 |
import { BranchMenuEntity } from './branch-menu.entity.js';
|
15 |
+
import { ReceiptEntity } from './receipt.entity.js';
|
16 |
|
17 |
@Entity('branches')
|
18 |
export class BranchEntity extends BaseEntity {
|
19 |
@PrimaryColumn()
|
20 |
id: string;
|
21 |
|
22 |
+
@Column({ nullable: true })
|
23 |
name: string;
|
24 |
|
25 |
+
@Column({ nullable: true })
|
26 |
+
image_url: string;
|
27 |
+
|
28 |
+
@Column({ nullable: true })
|
29 |
location: string;
|
30 |
|
31 |
+
@Column({ nullable: true })
|
32 |
phone_number: string;
|
33 |
|
34 |
@ManyToOne(() => UserEntity, (user) => user.branches)
|
|
|
37 |
@OneToMany(() => BranchMenuEntity, (a) => a.branch)
|
38 |
menu_items: Relation<BranchMenuEntity>[];
|
39 |
|
40 |
+
@OneToMany(() => ReceiptEntity, (a) => a.branch)
|
41 |
+
receipts: Relation<ReceiptEntity>[];
|
42 |
+
|
43 |
@CreateDateColumn()
|
44 |
create_at: Date;
|
45 |
+
|
46 |
+
@DeleteDateColumn()
|
47 |
+
delete_at: Date;
|
48 |
}
|
backend/src/entities/feed.entity.ts
CHANGED
@@ -1,4 +1,11 @@
|
|
1 |
-
import {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2 |
|
3 |
@Entity('feeds')
|
4 |
export class FeedEntity extends BaseEntity {
|
@@ -19,4 +26,7 @@ export class FeedEntity extends BaseEntity {
|
|
19 |
|
20 |
@CreateDateColumn()
|
21 |
create_at: Date;
|
|
|
|
|
|
|
22 |
}
|
|
|
1 |
+
import {
|
2 |
+
Entity,
|
3 |
+
Column,
|
4 |
+
BaseEntity,
|
5 |
+
PrimaryGeneratedColumn,
|
6 |
+
CreateDateColumn,
|
7 |
+
DeleteDateColumn,
|
8 |
+
} from 'typeorm';
|
9 |
|
10 |
@Entity('feeds')
|
11 |
export class FeedEntity extends BaseEntity {
|
|
|
26 |
|
27 |
@CreateDateColumn()
|
28 |
create_at: Date;
|
29 |
+
|
30 |
+
@DeleteDateColumn()
|
31 |
+
delete_at: Date;
|
32 |
}
|
backend/src/entities/menu-item.entity.ts
CHANGED
@@ -2,6 +2,7 @@ import {
|
|
2 |
BaseEntity,
|
3 |
Column,
|
4 |
CreateDateColumn,
|
|
|
5 |
Entity,
|
6 |
OneToMany,
|
7 |
PrimaryColumn,
|
@@ -21,8 +22,8 @@ export class MenuItemEntity extends BaseEntity {
|
|
21 |
@Column({ nullable: true })
|
22 |
image_url: string;
|
23 |
|
24 |
-
@Column({
|
25 |
-
item_type:
|
26 |
|
27 |
@Column()
|
28 |
description: string;
|
@@ -35,4 +36,7 @@ export class MenuItemEntity extends BaseEntity {
|
|
35 |
|
36 |
@CreateDateColumn()
|
37 |
create_at: Date;
|
|
|
|
|
|
|
38 |
}
|
|
|
2 |
BaseEntity,
|
3 |
Column,
|
4 |
CreateDateColumn,
|
5 |
+
DeleteDateColumn,
|
6 |
Entity,
|
7 |
OneToMany,
|
8 |
PrimaryColumn,
|
|
|
22 |
@Column({ nullable: true })
|
23 |
image_url: string;
|
24 |
|
25 |
+
@Column({ default: 0 })
|
26 |
+
item_type: number;
|
27 |
|
28 |
@Column()
|
29 |
description: string;
|
|
|
36 |
|
37 |
@CreateDateColumn()
|
38 |
create_at: Date;
|
39 |
+
|
40 |
+
@DeleteDateColumn()
|
41 |
+
delete_at: Date;
|
42 |
}
|
backend/src/entities/order.entity.ts
CHANGED
@@ -2,6 +2,7 @@ import {
|
|
2 |
BaseEntity,
|
3 |
Column,
|
4 |
CreateDateColumn,
|
|
|
5 |
Entity,
|
6 |
JoinColumn,
|
7 |
ManyToOne,
|
@@ -9,6 +10,7 @@ import {
|
|
9 |
OneToOne,
|
10 |
PrimaryGeneratedColumn,
|
11 |
Relation,
|
|
|
12 |
} from 'typeorm';
|
13 |
import { BranchEntity } from './branch.entity.js';
|
14 |
import { UserEntity } from './user.entity.js';
|
@@ -46,17 +48,20 @@ export class OrderEntity extends BaseEntity {
|
|
46 |
@Column({ nullable: true })
|
47 |
table_number: number;
|
48 |
|
|
|
|
|
|
|
|
|
|
|
|
|
49 |
@Column()
|
50 |
total_value: number;
|
51 |
|
52 |
-
@
|
53 |
-
|
54 |
-
|
55 |
-
@Column({ type: 'enum', enum: OrderType, default: OrderType.ONLINE })
|
56 |
-
order_type: OrderType;
|
57 |
|
58 |
-
@Column({
|
59 |
-
order_status:
|
60 |
|
61 |
@OneToMany(() => OrderItemEntity, (a) => a.order)
|
62 |
order_items: Relation<OrderItemEntity>[];
|
@@ -67,4 +72,13 @@ export class OrderEntity extends BaseEntity {
|
|
67 |
@OneToOne(() => PaymentEntity, (a) => a.order)
|
68 |
@JoinColumn({ name: 'payment_id' })
|
69 |
payment: Relation<PaymentEntity>;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
70 |
}
|
|
|
2 |
BaseEntity,
|
3 |
Column,
|
4 |
CreateDateColumn,
|
5 |
+
DeleteDateColumn,
|
6 |
Entity,
|
7 |
JoinColumn,
|
8 |
ManyToOne,
|
|
|
10 |
OneToOne,
|
11 |
PrimaryGeneratedColumn,
|
12 |
Relation,
|
13 |
+
UpdateDateColumn,
|
14 |
} from 'typeorm';
|
15 |
import { BranchEntity } from './branch.entity.js';
|
16 |
import { UserEntity } from './user.entity.js';
|
|
|
48 |
@Column({ nullable: true })
|
49 |
table_number: number;
|
50 |
|
51 |
+
@Column({ nullable: true })
|
52 |
+
note: string;
|
53 |
+
|
54 |
+
@Column({ nullable: true })
|
55 |
+
rating: number;
|
56 |
+
|
57 |
@Column()
|
58 |
total_value: number;
|
59 |
|
60 |
+
@Column({ default: 0 })
|
61 |
+
order_type: number;
|
|
|
|
|
|
|
62 |
|
63 |
+
@Column({ default: 0 })
|
64 |
+
order_status: number;
|
65 |
|
66 |
@OneToMany(() => OrderItemEntity, (a) => a.order)
|
67 |
order_items: Relation<OrderItemEntity>[];
|
|
|
72 |
@OneToOne(() => PaymentEntity, (a) => a.order)
|
73 |
@JoinColumn({ name: 'payment_id' })
|
74 |
payment: Relation<PaymentEntity>;
|
75 |
+
|
76 |
+
@CreateDateColumn()
|
77 |
+
created_at: Date;
|
78 |
+
|
79 |
+
@UpdateDateColumn()
|
80 |
+
updated_at: Date;
|
81 |
+
|
82 |
+
@DeleteDateColumn()
|
83 |
+
deleted_at: Date;
|
84 |
}
|
backend/src/entities/payment.entity.ts
CHANGED
@@ -2,15 +2,11 @@ import {
|
|
2 |
BaseEntity,
|
3 |
Column,
|
4 |
Entity,
|
5 |
-
JoinColumn,
|
6 |
-
ManyToOne,
|
7 |
OneToOne,
|
8 |
PrimaryGeneratedColumn,
|
9 |
Relation,
|
10 |
} from 'typeorm';
|
11 |
-
import { BranchMenuEntity } from './branch-menu.entity.js';
|
12 |
import { OrderEntity } from './order.entity.js';
|
13 |
-
import { PaymentMethod } from '../common/enums/PaymentMethod.enum.js';
|
14 |
|
15 |
@Entity('payments')
|
16 |
export class PaymentEntity extends BaseEntity {
|
@@ -20,9 +16,33 @@ export class PaymentEntity extends BaseEntity {
|
|
20 |
@OneToOne(() => OrderEntity, (a) => a.payment)
|
21 |
order: Relation<OrderEntity>;
|
22 |
|
23 |
-
@Column({
|
24 |
-
payment_method:
|
25 |
|
26 |
@Column()
|
27 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
28 |
}
|
|
|
2 |
BaseEntity,
|
3 |
Column,
|
4 |
Entity,
|
|
|
|
|
5 |
OneToOne,
|
6 |
PrimaryGeneratedColumn,
|
7 |
Relation,
|
8 |
} from 'typeorm';
|
|
|
9 |
import { OrderEntity } from './order.entity.js';
|
|
|
10 |
|
11 |
@Entity('payments')
|
12 |
export class PaymentEntity extends BaseEntity {
|
|
|
16 |
@OneToOne(() => OrderEntity, (a) => a.payment)
|
17 |
order: Relation<OrderEntity>;
|
18 |
|
19 |
+
@Column({ default: 0 })
|
20 |
+
payment_method: number;
|
21 |
|
22 |
@Column()
|
23 |
+
vnp_amount: number
|
24 |
+
|
25 |
+
@Column({ nullable: true })
|
26 |
+
vnp_bank_code: string
|
27 |
+
|
28 |
+
@Column({ nullable: true })
|
29 |
+
vnp_bank_tran_no:string
|
30 |
+
|
31 |
+
@Column({ default: 0})
|
32 |
+
vnp_card_type: number
|
33 |
+
|
34 |
+
@Column({ nullable: true })
|
35 |
+
vnp_order_info: string //Nội dung giao dịch
|
36 |
+
|
37 |
+
@Column({ nullable: true })
|
38 |
+
vnp_paydate: string
|
39 |
+
|
40 |
+
@Column({ nullable: true })
|
41 |
+
vnp_response_code: number
|
42 |
+
|
43 |
+
@Column({ nullable: true })
|
44 |
+
vnp_transaction_no: string
|
45 |
+
|
46 |
+
@Column({ nullable: true })
|
47 |
+
vnp_transaction_status: number
|
48 |
}
|
backend/src/entities/receipt.entity.ts
ADDED
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import {
|
2 |
+
BaseEntity,
|
3 |
+
Column,
|
4 |
+
CreateDateColumn,
|
5 |
+
Entity,
|
6 |
+
JoinColumn,
|
7 |
+
ManyToOne,
|
8 |
+
PrimaryGeneratedColumn,
|
9 |
+
Relation,
|
10 |
+
} from 'typeorm';
|
11 |
+
import { BranchEntity } from './branch.entity.js';
|
12 |
+
import { UserEntity } from './user.entity.js';
|
13 |
+
|
14 |
+
@Entity('receipts')
|
15 |
+
export class ReceiptEntity extends BaseEntity {
|
16 |
+
@PrimaryGeneratedColumn()
|
17 |
+
id: number;
|
18 |
+
|
19 |
+
@Column()
|
20 |
+
branch_id: string;
|
21 |
+
|
22 |
+
@ManyToOne(() => BranchEntity, (a) => a.receipts)
|
23 |
+
@JoinColumn({ name: 'branch_id' })
|
24 |
+
branch: Relation<BranchEntity>;
|
25 |
+
|
26 |
+
@Column({ default: 0 })
|
27 |
+
income: number;
|
28 |
+
|
29 |
+
@Column({ default: 0 })
|
30 |
+
spend: number;
|
31 |
+
|
32 |
+
@Column({ nullable: true })
|
33 |
+
description: string;
|
34 |
+
|
35 |
+
@Column({ default: 0 })
|
36 |
+
type: number;
|
37 |
+
|
38 |
+
@Column({ default: 0 })
|
39 |
+
sub_type: number;
|
40 |
+
|
41 |
+
@Column({ nullable: true })
|
42 |
+
sender_id: string;
|
43 |
+
|
44 |
+
@Column({ nullable: true })
|
45 |
+
receiver_id: string;
|
46 |
+
|
47 |
+
@ManyToOne(() => UserEntity, (a) => a.out_receipts)
|
48 |
+
@JoinColumn({ name: 'sender_id' })
|
49 |
+
sender: Relation<UserEntity>;
|
50 |
+
|
51 |
+
@ManyToOne(() => UserEntity, (a) => a.in_receipts)
|
52 |
+
@JoinColumn({ name: 'receiver_id' })
|
53 |
+
receiver: Relation<UserEntity>;
|
54 |
+
|
55 |
+
@CreateDateColumn()
|
56 |
+
created_at: Date;
|
57 |
+
}
|
backend/src/entities/user.entity.ts
CHANGED
@@ -12,6 +12,7 @@ import {
|
|
12 |
import { BranchEntity } from './branch.entity.js';
|
13 |
import { IsOptional } from 'class-validator';
|
14 |
import { Role } from '../common/enums/role.enum.js';
|
|
|
15 |
|
16 |
@Entity('users')
|
17 |
export class UserEntity extends BaseEntity {
|
@@ -37,19 +38,22 @@ export class UserEntity extends BaseEntity {
|
|
37 |
|
38 |
@Column({ type: 'enum', enum: Role, default: 'CUSTOMER' })
|
39 |
role: Role;
|
40 |
-
|
41 |
@Column()
|
42 |
hash_password: string;
|
43 |
|
44 |
-
@IsOptional()
|
45 |
@Column({ default: true })
|
46 |
is_valid: boolean;
|
47 |
|
48 |
-
@IsOptional()
|
49 |
@CreateDateColumn()
|
50 |
create_at: Date;
|
51 |
|
52 |
-
@IsOptional()
|
53 |
@OneToMany(() => BranchEntity, (branch) => branch.owner)
|
54 |
branches: Relation<BranchEntity>[];
|
|
|
|
|
|
|
|
|
|
|
|
|
55 |
}
|
|
|
12 |
import { BranchEntity } from './branch.entity.js';
|
13 |
import { IsOptional } from 'class-validator';
|
14 |
import { Role } from '../common/enums/role.enum.js';
|
15 |
+
import { ReceiptEntity } from './receipt.entity.js';
|
16 |
|
17 |
@Entity('users')
|
18 |
export class UserEntity extends BaseEntity {
|
|
|
38 |
|
39 |
@Column({ type: 'enum', enum: Role, default: 'CUSTOMER' })
|
40 |
role: Role;
|
41 |
+
|
42 |
@Column()
|
43 |
hash_password: string;
|
44 |
|
|
|
45 |
@Column({ default: true })
|
46 |
is_valid: boolean;
|
47 |
|
|
|
48 |
@CreateDateColumn()
|
49 |
create_at: Date;
|
50 |
|
|
|
51 |
@OneToMany(() => BranchEntity, (branch) => branch.owner)
|
52 |
branches: Relation<BranchEntity>[];
|
53 |
+
|
54 |
+
@OneToMany(() => ReceiptEntity, (receipt) => receipt.sender)
|
55 |
+
in_receipts: Relation<ReceiptEntity>[];
|
56 |
+
|
57 |
+
@OneToMany(() => ReceiptEntity, (receipt) => receipt.receiver)
|
58 |
+
out_receipts: Relation<ReceiptEntity>[];
|
59 |
}
|
backend/src/migrations/1730547520878-AddReceipt.ts
ADDED
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { MigrationInterface, QueryRunner } from "typeorm";
|
2 |
+
|
3 |
+
export class AddReceipt1730547520878 implements MigrationInterface {
|
4 |
+
name = 'AddReceipt1730547520878'
|
5 |
+
|
6 |
+
public async up(queryRunner: QueryRunner): Promise<void> {
|
7 |
+
await queryRunner.query(`CREATE TABLE "receipts" ("id" SERIAL NOT NULL, "branch_id" character varying NOT NULL, "income" integer NOT NULL DEFAULT '0', "spend" integer NOT NULL DEFAULT '0', "description" character varying, "type" integer NOT NULL DEFAULT '0', "sub_type" integer NOT NULL DEFAULT '0', "sender_id" uuid, "receiver_id" uuid, "created_at" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "PK_5e8182d7c29e023da6e1ff33bfe" PRIMARY KEY ("id"))`);
|
8 |
+
await queryRunner.query(`ALTER TABLE "branches" ADD "image_url" character varying`);
|
9 |
+
await queryRunner.query(`ALTER TABLE "branch_menu" ADD "sold_count" integer NOT NULL DEFAULT '0'`);
|
10 |
+
await queryRunner.query(`ALTER TABLE "branches" ALTER COLUMN "name" DROP NOT NULL`);
|
11 |
+
await queryRunner.query(`ALTER TABLE "branches" ALTER COLUMN "location" DROP NOT NULL`);
|
12 |
+
await queryRunner.query(`ALTER TABLE "branches" ALTER COLUMN "phone_number" DROP NOT NULL`);
|
13 |
+
await queryRunner.query(`ALTER TABLE "receipts" ADD CONSTRAINT "FK_82e9dee911c0e7393154d1d98ad" FOREIGN KEY ("branch_id") REFERENCES "branches"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
|
14 |
+
await queryRunner.query(`ALTER TABLE "receipts" ADD CONSTRAINT "FK_eda4c4e486a25beef4dc82a41d5" FOREIGN KEY ("sender_id") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
|
15 |
+
await queryRunner.query(`ALTER TABLE "receipts" ADD CONSTRAINT "FK_366c3d3cf125da97552f40001a1" FOREIGN KEY ("receiver_id") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
|
16 |
+
}
|
17 |
+
|
18 |
+
public async down(queryRunner: QueryRunner): Promise<void> {
|
19 |
+
await queryRunner.query(`ALTER TABLE "receipts" DROP CONSTRAINT "FK_366c3d3cf125da97552f40001a1"`);
|
20 |
+
await queryRunner.query(`ALTER TABLE "receipts" DROP CONSTRAINT "FK_eda4c4e486a25beef4dc82a41d5"`);
|
21 |
+
await queryRunner.query(`ALTER TABLE "receipts" DROP CONSTRAINT "FK_82e9dee911c0e7393154d1d98ad"`);
|
22 |
+
await queryRunner.query(`ALTER TABLE "branches" ALTER COLUMN "phone_number" SET NOT NULL`);
|
23 |
+
await queryRunner.query(`ALTER TABLE "branches" ALTER COLUMN "location" SET NOT NULL`);
|
24 |
+
await queryRunner.query(`ALTER TABLE "branches" ALTER COLUMN "name" SET NOT NULL`);
|
25 |
+
await queryRunner.query(`ALTER TABLE "branch_menu" DROP COLUMN "sold_count"`);
|
26 |
+
await queryRunner.query(`ALTER TABLE "branches" DROP COLUMN "image_url"`);
|
27 |
+
await queryRunner.query(`DROP TABLE "receipts"`);
|
28 |
+
}
|
29 |
+
|
30 |
+
}
|
backend/src/migrations/1730549959767-RemoveEnums.ts
ADDED
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { MigrationInterface, QueryRunner } from "typeorm";
|
2 |
+
|
3 |
+
export class RemoveEnums1730549959767 implements MigrationInterface {
|
4 |
+
name = 'RemoveEnums1730549959767'
|
5 |
+
|
6 |
+
public async up(queryRunner: QueryRunner): Promise<void> {
|
7 |
+
await queryRunner.query(`ALTER TABLE "menu_items" DROP COLUMN "item_type"`);
|
8 |
+
await queryRunner.query(`DROP TYPE "public"."menu_items_item_type_enum"`);
|
9 |
+
await queryRunner.query(`ALTER TABLE "menu_items" ADD "item_type" integer NOT NULL DEFAULT '0'`);
|
10 |
+
await queryRunner.query(`ALTER TABLE "payments" DROP COLUMN "payment_method"`);
|
11 |
+
await queryRunner.query(`DROP TYPE "public"."payments_payment_method_enum"`);
|
12 |
+
await queryRunner.query(`ALTER TABLE "payments" ADD "payment_method" integer NOT NULL DEFAULT '0'`);
|
13 |
+
await queryRunner.query(`ALTER TABLE "orders" DROP COLUMN "order_type"`);
|
14 |
+
await queryRunner.query(`DROP TYPE "public"."orders_order_type_enum"`);
|
15 |
+
await queryRunner.query(`ALTER TABLE "orders" ADD "order_type" integer NOT NULL DEFAULT '0'`);
|
16 |
+
await queryRunner.query(`ALTER TABLE "orders" DROP COLUMN "order_status"`);
|
17 |
+
await queryRunner.query(`DROP TYPE "public"."orders_order_status_enum"`);
|
18 |
+
await queryRunner.query(`ALTER TABLE "orders" ADD "order_status" integer NOT NULL DEFAULT '0'`);
|
19 |
+
}
|
20 |
+
|
21 |
+
public async down(queryRunner: QueryRunner): Promise<void> {
|
22 |
+
await queryRunner.query(`ALTER TABLE "orders" DROP COLUMN "order_status"`);
|
23 |
+
await queryRunner.query(`CREATE TYPE "public"."orders_order_status_enum" AS ENUM('pending', 'confirmed', 'preparing', 'delivering', 'done')`);
|
24 |
+
await queryRunner.query(`ALTER TABLE "orders" ADD "order_status" "public"."orders_order_status_enum" NOT NULL DEFAULT 'pending'`);
|
25 |
+
await queryRunner.query(`ALTER TABLE "orders" DROP COLUMN "order_type"`);
|
26 |
+
await queryRunner.query(`CREATE TYPE "public"."orders_order_type_enum" AS ENUM('take_away', 'offline', 'online')`);
|
27 |
+
await queryRunner.query(`ALTER TABLE "orders" ADD "order_type" "public"."orders_order_type_enum" NOT NULL DEFAULT 'online'`);
|
28 |
+
await queryRunner.query(`ALTER TABLE "payments" DROP COLUMN "payment_method"`);
|
29 |
+
await queryRunner.query(`CREATE TYPE "public"."payments_payment_method_enum" AS ENUM('cash', 'card', 'online_payment')`);
|
30 |
+
await queryRunner.query(`ALTER TABLE "payments" ADD "payment_method" "public"."payments_payment_method_enum" NOT NULL DEFAULT 'cash'`);
|
31 |
+
await queryRunner.query(`ALTER TABLE "menu_items" DROP COLUMN "item_type"`);
|
32 |
+
await queryRunner.query(`CREATE TYPE "public"."menu_items_item_type_enum" AS ENUM('monchinh', 'trangmieng', 'giaikhat', 'khac')`);
|
33 |
+
await queryRunner.query(`ALTER TABLE "menu_items" ADD "item_type" "public"."menu_items_item_type_enum" NOT NULL DEFAULT 'khac'`);
|
34 |
+
}
|
35 |
+
|
36 |
+
}
|
backend/src/migrations/1730651201156-modify_payment.ts
ADDED
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { MigrationInterface, QueryRunner } from "typeorm";
|
2 |
+
|
3 |
+
export class ModifyPayment1730651201156 implements MigrationInterface {
|
4 |
+
name = 'ModifyPayment1730651201156'
|
5 |
+
|
6 |
+
public async up(queryRunner: QueryRunner): Promise<void> {
|
7 |
+
await queryRunner.query(`ALTER TABLE "payments" DROP COLUMN "value"`);
|
8 |
+
await queryRunner.query(`ALTER TABLE "payments" ADD "vnp_amount" integer NOT NULL`);
|
9 |
+
await queryRunner.query(`ALTER TABLE "payments" ADD "vnp_bank_code" character varying`);
|
10 |
+
await queryRunner.query(`ALTER TABLE "payments" ADD "vnp_bank_tran_no" character varying`);
|
11 |
+
await queryRunner.query(`ALTER TABLE "payments" ADD "vnp_card_type" integer NOT NULL DEFAULT '0'`);
|
12 |
+
await queryRunner.query(`ALTER TABLE "payments" ADD "vnp_order_info" character varying`);
|
13 |
+
await queryRunner.query(`ALTER TABLE "payments" ADD "vnp_paydate" character varying`);
|
14 |
+
await queryRunner.query(`ALTER TABLE "payments" ADD "vnp_response_code" integer`);
|
15 |
+
await queryRunner.query(`ALTER TABLE "payments" ADD "vnp_transaction_no" character varying`);
|
16 |
+
await queryRunner.query(`ALTER TABLE "payments" ADD "vnp_transaction_status" integer`);
|
17 |
+
}
|
18 |
+
|
19 |
+
public async down(queryRunner: QueryRunner): Promise<void> {
|
20 |
+
await queryRunner.query(`ALTER TABLE "payments" DROP COLUMN "vnp_transaction_status"`);
|
21 |
+
await queryRunner.query(`ALTER TABLE "payments" DROP COLUMN "vnp_transaction_no"`);
|
22 |
+
await queryRunner.query(`ALTER TABLE "payments" DROP COLUMN "vnp_response_code"`);
|
23 |
+
await queryRunner.query(`ALTER TABLE "payments" DROP COLUMN "vnp_paydate"`);
|
24 |
+
await queryRunner.query(`ALTER TABLE "payments" DROP COLUMN "vnp_order_info"`);
|
25 |
+
await queryRunner.query(`ALTER TABLE "payments" DROP COLUMN "vnp_card_type"`);
|
26 |
+
await queryRunner.query(`ALTER TABLE "payments" DROP COLUMN "vnp_bank_tran_no"`);
|
27 |
+
await queryRunner.query(`ALTER TABLE "payments" DROP COLUMN "vnp_bank_code"`);
|
28 |
+
await queryRunner.query(`ALTER TABLE "payments" DROP COLUMN "vnp_amount"`);
|
29 |
+
await queryRunner.query(`ALTER TABLE "payments" ADD "value" integer NOT NULL`);
|
30 |
+
}
|
31 |
+
|
32 |
+
}
|
backend/src/migrations/1730865796585-AddBranchAndOrderFields.ts
ADDED
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { MigrationInterface, QueryRunner } from "typeorm";
|
2 |
+
|
3 |
+
export class AddBranchAndOrderFields1730865796585 implements MigrationInterface {
|
4 |
+
name = 'AddBranchAndOrderFields1730865796585'
|
5 |
+
|
6 |
+
public async up(queryRunner: QueryRunner): Promise<void> {
|
7 |
+
await queryRunner.query(`ALTER TABLE "feeds" ADD "delete_at" TIMESTAMP`);
|
8 |
+
await queryRunner.query(`ALTER TABLE "menu_items" ADD "delete_at" TIMESTAMP`);
|
9 |
+
await queryRunner.query(`ALTER TABLE "branches" ADD "delete_at" TIMESTAMP`);
|
10 |
+
await queryRunner.query(`ALTER TABLE "orders" ADD "note" character varying`);
|
11 |
+
await queryRunner.query(`ALTER TABLE "orders" ADD "rating" integer`);
|
12 |
+
}
|
13 |
+
|
14 |
+
public async down(queryRunner: QueryRunner): Promise<void> {
|
15 |
+
await queryRunner.query(`ALTER TABLE "orders" DROP COLUMN "rating"`);
|
16 |
+
await queryRunner.query(`ALTER TABLE "orders" DROP COLUMN "note"`);
|
17 |
+
await queryRunner.query(`ALTER TABLE "branches" DROP COLUMN "delete_at"`);
|
18 |
+
await queryRunner.query(`ALTER TABLE "menu_items" DROP COLUMN "delete_at"`);
|
19 |
+
await queryRunner.query(`ALTER TABLE "feeds" DROP COLUMN "delete_at"`);
|
20 |
+
}
|
21 |
+
|
22 |
+
}
|
backend/src/migrations/1730895089788-UpdateOrderFieldTime.ts
ADDED
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { MigrationInterface, QueryRunner } from 'typeorm';
|
2 |
+
|
3 |
+
export class UpdateOrderFieldTime1730895089788 implements MigrationInterface {
|
4 |
+
name = 'UpdateOrderFieldTime1730895089788';
|
5 |
+
|
6 |
+
public async up(queryRunner: QueryRunner): Promise<void> {
|
7 |
+
await queryRunner.query(`ALTER TABLE "orders" DROP COLUMN "create_at"`);
|
8 |
+
await queryRunner.query(
|
9 |
+
`ALTER TABLE "orders" ADD "created_at" TIMESTAMP NOT NULL DEFAULT now()`,
|
10 |
+
);
|
11 |
+
await queryRunner.query(
|
12 |
+
`ALTER TABLE "orders" ADD "updated_at" TIMESTAMP NOT NULL DEFAULT now()`,
|
13 |
+
);
|
14 |
+
await queryRunner.query(`ALTER TABLE "orders" ADD "deleted_at" TIMESTAMP`);
|
15 |
+
}
|
16 |
+
|
17 |
+
public async down(queryRunner: QueryRunner): Promise<void> {
|
18 |
+
await queryRunner.query(
|
19 |
+
`ALTER TABLE "orders" ADD "create_at" TIMESTAMP NOT NULL DEFAULT now()`,
|
20 |
+
);
|
21 |
+
await queryRunner.query(`ALTER TABLE "orders" DROP COLUMN "deleted_at"`);
|
22 |
+
await queryRunner.query(`ALTER TABLE "orders" DROP COLUMN "updated_at"`);
|
23 |
+
await queryRunner.query(`ALTER TABLE "orders" DROP COLUMN "created_at"`);
|
24 |
+
}
|
25 |
+
}
|
backend/src/modules/authentication/authentication.service.ts
CHANGED
@@ -28,7 +28,7 @@ export class AuthenticationService {
|
|
28 |
if (!compare) {
|
29 |
throw new UnauthorizedException("Wrong password");
|
30 |
}
|
31 |
-
const payload = { sub: user.id, username: user.full_name,
|
32 |
return {
|
33 |
access_token: await this.jwtService.signAsync(payload),
|
34 |
};
|
|
|
28 |
if (!compare) {
|
29 |
throw new UnauthorizedException("Wrong password");
|
30 |
}
|
31 |
+
const payload = { sub: user.id, username: user.full_name, role: user.role};
|
32 |
return {
|
33 |
access_token: await this.jwtService.signAsync(payload),
|
34 |
};
|
backend/src/modules/authentication/authorization/roles.guard.ts
CHANGED
@@ -17,6 +17,6 @@ export class RolesGuard implements CanActivate {
|
|
17 |
return true;
|
18 |
}
|
19 |
const { user } = context.switchToHttp().getRequest();
|
20 |
-
return requiredRoles.some((role) => user.
|
21 |
}
|
22 |
}
|
|
|
17 |
return true;
|
18 |
}
|
19 |
const { user } = context.switchToHttp().getRequest();
|
20 |
+
return requiredRoles.some((role) => user.role.includes(role));
|
21 |
}
|
22 |
}
|
backend/src/modules/branch-menus/branch-menus.service.ts
CHANGED
@@ -4,7 +4,12 @@ import { UpdateBranchMenuDto } from './dto/update-branch-menu.dto.js';
|
|
4 |
import { BranchService } from '../branch/branch.service.js';
|
5 |
import { MenuItemService } from '../menu-item/menu-item.service.js';
|
6 |
import { BranchMenuEntity } from '../../entities/branch-menu.entity.js';
|
7 |
-
import {
|
|
|
|
|
|
|
|
|
|
|
8 |
import { isUUID } from 'class-validator';
|
9 |
|
10 |
@Injectable()
|
@@ -36,12 +41,18 @@ export class BranchMenusService {
|
|
36 |
const paginateConfig: PaginateConfig<BranchMenuEntity> = {
|
37 |
sortableColumns: ['id', 'branch_id', 'menu_id', 'description'],
|
38 |
nullSort: 'last',
|
39 |
-
defaultSortBy: [['
|
40 |
searchableColumns: ['description'],
|
41 |
filterableColumns: {
|
42 |
-
|
43 |
-
|
|
|
|
|
|
|
|
|
|
|
44 |
},
|
|
|
45 |
};
|
46 |
return paginate(
|
47 |
query,
|
|
|
4 |
import { BranchService } from '../branch/branch.service.js';
|
5 |
import { MenuItemService } from '../menu-item/menu-item.service.js';
|
6 |
import { BranchMenuEntity } from '../../entities/branch-menu.entity.js';
|
7 |
+
import {
|
8 |
+
FilterOperator,
|
9 |
+
paginate,
|
10 |
+
PaginateConfig,
|
11 |
+
PaginateQuery,
|
12 |
+
} from 'nestjs-paginate';
|
13 |
import { isUUID } from 'class-validator';
|
14 |
|
15 |
@Injectable()
|
|
|
41 |
const paginateConfig: PaginateConfig<BranchMenuEntity> = {
|
42 |
sortableColumns: ['id', 'branch_id', 'menu_id', 'description'],
|
43 |
nullSort: 'last',
|
44 |
+
defaultSortBy: [['menu_item.item_type', 'ASC']],
|
45 |
searchableColumns: ['description'],
|
46 |
filterableColumns: {
|
47 |
+
'menu_item.price': [
|
48 |
+
FilterOperator.LT,
|
49 |
+
FilterOperator.LTE,
|
50 |
+
FilterOperator.GT,
|
51 |
+
FilterOperator.GTE,
|
52 |
+
],
|
53 |
+
'menu_item.item_type': [FilterOperator.EQ, FilterOperator.IN],
|
54 |
},
|
55 |
+
relations: ['menu_item'],
|
56 |
};
|
57 |
return paginate(
|
58 |
query,
|
backend/src/modules/branch/branch.controller.ts
CHANGED
@@ -22,6 +22,11 @@ export class BranchController {
|
|
22 |
return this.branchService.create(createBranchDto);
|
23 |
}
|
24 |
|
|
|
|
|
|
|
|
|
|
|
25 |
@Get()
|
26 |
async findAll() {
|
27 |
return this.branchService.findAll();
|
@@ -42,7 +47,7 @@ export class BranchController {
|
|
42 |
|
43 |
@Delete(':id')
|
44 |
async remove(@Param('id') id: string) {
|
45 |
-
return this.branchService.
|
46 |
}
|
47 |
|
48 |
@Post(':id/menu-items')
|
|
|
22 |
return this.branchService.create(createBranchDto);
|
23 |
}
|
24 |
|
25 |
+
@Post(':id/restore')
|
26 |
+
restore(@Param('id') id: string) {
|
27 |
+
return this.branchService.restore(id);
|
28 |
+
}
|
29 |
+
|
30 |
@Get()
|
31 |
async findAll() {
|
32 |
return this.branchService.findAll();
|
|
|
47 |
|
48 |
@Delete(':id')
|
49 |
async remove(@Param('id') id: string) {
|
50 |
+
return this.branchService.softRemove(id);
|
51 |
}
|
52 |
|
53 |
@Post(':id/menu-items')
|
backend/src/modules/branch/branch.service.ts
CHANGED
@@ -46,8 +46,14 @@ export class BranchService {
|
|
46 |
return await branch.save();
|
47 |
}
|
48 |
|
49 |
-
async
|
50 |
-
let
|
51 |
-
return await
|
|
|
|
|
|
|
|
|
|
|
|
|
52 |
}
|
53 |
}
|
|
|
46 |
return await branch.save();
|
47 |
}
|
48 |
|
49 |
+
async softRemove(id: string) {
|
50 |
+
let branch = await this.getBranchOrError(id);
|
51 |
+
return await branch.softRemove();
|
52 |
+
}
|
53 |
+
|
54 |
+
async restore(id: string) {
|
55 |
+
let result = await BranchEntity.getRepository().restore(id);
|
56 |
+
let branch = await BranchEntity.findOneBy({ id });
|
57 |
+
return branch;
|
58 |
}
|
59 |
}
|
backend/src/modules/branch/dto/create-branch.dto.ts
CHANGED
@@ -7,6 +7,9 @@ export class CreateBranchDto {
|
|
7 |
@IsString()
|
8 |
name: string;
|
9 |
|
|
|
|
|
|
|
10 |
@IsString()
|
11 |
location: string;
|
12 |
|
|
|
7 |
@IsString()
|
8 |
name: string;
|
9 |
|
10 |
+
@IsString()
|
11 |
+
image_url: string;
|
12 |
+
|
13 |
@IsString()
|
14 |
location: string;
|
15 |
|
backend/src/modules/branch/dto/update-branch.dto.ts
CHANGED
@@ -5,6 +5,10 @@ export class UpdateBranchDto {
|
|
5 |
@IsOptional()
|
6 |
name?: string;
|
7 |
|
|
|
|
|
|
|
|
|
8 |
@IsString()
|
9 |
@IsOptional()
|
10 |
location?: string;
|
|
|
5 |
@IsOptional()
|
6 |
name?: string;
|
7 |
|
8 |
+
@IsString()
|
9 |
+
@IsOptional()
|
10 |
+
image_url?: string;
|
11 |
+
|
12 |
@IsString()
|
13 |
@IsOptional()
|
14 |
location?: string;
|
backend/src/modules/menu-item/dto/create-menu-item.dto.ts
CHANGED
@@ -10,9 +10,9 @@ export class CreateMenuItemDto {
|
|
10 |
@IsUrl()
|
11 |
image_url: string;
|
12 |
|
13 |
-
@
|
14 |
@IsOptional()
|
15 |
-
|
16 |
|
17 |
@IsString()
|
18 |
@IsOptional()
|
|
|
10 |
@IsUrl()
|
11 |
image_url: string;
|
12 |
|
13 |
+
@IsNumber()
|
14 |
@IsOptional()
|
15 |
+
item_type?: number;
|
16 |
|
17 |
@IsString()
|
18 |
@IsOptional()
|
backend/src/modules/menu-item/dto/update-menu-item.dto.ts
CHANGED
@@ -9,9 +9,9 @@ export class UpdateMenuItemDto {
|
|
9 |
@IsOptional()
|
10 |
image_url: string;
|
11 |
|
12 |
-
@
|
13 |
@IsOptional()
|
14 |
-
|
15 |
|
16 |
@IsString()
|
17 |
@IsOptional()
|
|
|
9 |
@IsOptional()
|
10 |
image_url: string;
|
11 |
|
12 |
+
@IsNumber()
|
13 |
@IsOptional()
|
14 |
+
item_type?: number;
|
15 |
|
16 |
@IsString()
|
17 |
@IsOptional()
|
backend/src/modules/order/dto/create-order.dto.ts
CHANGED
@@ -16,8 +16,12 @@ export class CreateOrderDto {
|
|
16 |
@IsNumber()
|
17 |
table_number?: number;
|
18 |
|
19 |
-
@
|
20 |
-
|
|
|
|
|
|
|
|
|
21 |
|
22 |
@IsArray()
|
23 |
@ValidateNested()
|
|
|
16 |
@IsNumber()
|
17 |
table_number?: number;
|
18 |
|
19 |
+
@IsOptional()
|
20 |
+
@IsString()
|
21 |
+
note?: string;
|
22 |
+
|
23 |
+
@IsNumber()
|
24 |
+
order_type: number;
|
25 |
|
26 |
@IsArray()
|
27 |
@ValidateNested()
|
backend/src/modules/order/dto/update-order.dto.ts
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { IsArray, IsNumber, IsOptional, ValidateNested } from 'class-validator';
|
2 |
+
import { OrderItemsDto } from './order-items.dto.js';
|
3 |
+
import { Type } from 'class-transformer';
|
4 |
+
|
5 |
+
export class UpdateOrderDto {
|
6 |
+
@IsOptional()
|
7 |
+
@IsNumber()
|
8 |
+
table_number?: number;
|
9 |
+
|
10 |
+
@IsArray()
|
11 |
+
@ValidateNested()
|
12 |
+
@Type(() => OrderItemsDto)
|
13 |
+
order_items: OrderItemsDto[];
|
14 |
+
}
|
backend/src/modules/order/order.controller.ts
CHANGED
@@ -11,9 +11,12 @@ import {
|
|
11 |
import { OrderService } from './order.service.js';
|
12 |
import { CreateOrderDto } from './dto/create-order.dto.js';
|
13 |
import { Role } from '../../common/enums/role.enum.js';
|
|
|
|
|
|
|
14 |
|
15 |
@Controller('branchs/:branchId/orders')
|
16 |
-
export class
|
17 |
constructor(private readonly orderService: OrderService) {}
|
18 |
|
19 |
@Post()
|
@@ -23,15 +26,16 @@ export class OrderController {
|
|
23 |
@Body() createOrderDto: CreateOrderDto,
|
24 |
) {
|
25 |
const userId = req['user'].sub;
|
26 |
-
const role = req['user'].
|
27 |
console.log(req['user']);
|
28 |
-
if (role
|
|
|
29 |
return this.orderService.createFromCustomer(
|
30 |
branchId,
|
31 |
userId,
|
32 |
createOrderDto,
|
33 |
);
|
34 |
-
else
|
35 |
return this.orderService.createFromStaff(
|
36 |
branchId,
|
37 |
userId,
|
@@ -39,28 +43,50 @@ export class OrderController {
|
|
39 |
);
|
40 |
}
|
41 |
|
42 |
-
@Get()
|
43 |
-
async
|
|
|
|
|
|
|
|
|
|
|
44 |
const userId = req['user'].sub;
|
45 |
console.log(req['user']);
|
46 |
-
return this.orderService.
|
47 |
}
|
48 |
|
49 |
@Get(':id')
|
50 |
async findOne(@Param('id') id: string) {
|
51 |
return this.orderService.findOne(+id);
|
52 |
}
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
// @Body() updateOrderDto: UpdateOrderDto,
|
58 |
-
// ) {
|
59 |
-
// return this.orderService.update(+id, updateOrderDto);
|
60 |
-
// }
|
61 |
|
62 |
@Delete(':id')
|
63 |
remove(@Param('id') id: string) {
|
64 |
return this.orderService.remove(+id);
|
65 |
}
|
66 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
11 |
import { OrderService } from './order.service.js';
|
12 |
import { CreateOrderDto } from './dto/create-order.dto.js';
|
13 |
import { Role } from '../../common/enums/role.enum.js';
|
14 |
+
import { paginate, Paginate, PaginateQuery } from 'nestjs-paginate';
|
15 |
+
import { Roles } from '../authentication/authorization/roles.decorator.js';
|
16 |
+
import { UpdateOrderDto } from './dto/update-order.dto.js';
|
17 |
|
18 |
@Controller('branchs/:branchId/orders')
|
19 |
+
export class BranchOrderController {
|
20 |
constructor(private readonly orderService: OrderService) {}
|
21 |
|
22 |
@Post()
|
|
|
26 |
@Body() createOrderDto: CreateOrderDto,
|
27 |
) {
|
28 |
const userId = req['user'].sub;
|
29 |
+
const role = req['user'].role;
|
30 |
console.log(req['user']);
|
31 |
+
if (role === Role.CUSTOMER) {
|
32 |
+
console.log('customer');
|
33 |
return this.orderService.createFromCustomer(
|
34 |
branchId,
|
35 |
userId,
|
36 |
createOrderDto,
|
37 |
);
|
38 |
+
} else
|
39 |
return this.orderService.createFromStaff(
|
40 |
branchId,
|
41 |
userId,
|
|
|
43 |
);
|
44 |
}
|
45 |
|
46 |
+
@Get('history')
|
47 |
+
async findHistory(
|
48 |
+
@Req() req: Request,
|
49 |
+
@Param('branchId') branchId: string,
|
50 |
+
@Paginate() paginateQuery: PaginateQuery,
|
51 |
+
) {
|
52 |
+
// order history of user.
|
53 |
const userId = req['user'].sub;
|
54 |
console.log(req['user']);
|
55 |
+
return this.orderService.findHistory(paginateQuery, userId, branchId);
|
56 |
}
|
57 |
|
58 |
@Get(':id')
|
59 |
async findOne(@Param('id') id: string) {
|
60 |
return this.orderService.findOne(+id);
|
61 |
}
|
62 |
+
@Patch(':id')
|
63 |
+
update(@Param('id') id: string, @Body() updateOrderDto: UpdateOrderDto) {
|
64 |
+
return this.orderService.updateOrder(+id, updateOrderDto);
|
65 |
+
}
|
|
|
|
|
|
|
|
|
66 |
|
67 |
@Delete(':id')
|
68 |
remove(@Param('id') id: string) {
|
69 |
return this.orderService.remove(+id);
|
70 |
}
|
71 |
}
|
72 |
+
|
73 |
+
@Roles(Role.ADMIN, Role.STAFF, Role.BRANCH_MANAGER, Role.SHIPPER)
|
74 |
+
@Controller('orders')
|
75 |
+
export class OrderController {
|
76 |
+
constructor(private readonly orderService: OrderService) {}
|
77 |
+
|
78 |
+
@Get()
|
79 |
+
async findAll(@Paginate() paginateQuery: PaginateQuery) {
|
80 |
+
return this.orderService.findAll(paginateQuery);
|
81 |
+
}
|
82 |
+
|
83 |
+
@Get(':id')
|
84 |
+
async findOne(@Param('id') id: string) {
|
85 |
+
return this.orderService.findOne(+id);
|
86 |
+
}
|
87 |
+
|
88 |
+
@Patch(':id')
|
89 |
+
update(@Param('id') id: string, @Body() updateOrderDto: UpdateOrderDto) {
|
90 |
+
return this.orderService.updateOrder(+id, updateOrderDto);
|
91 |
+
}
|
92 |
+
}
|
backend/src/modules/order/order.module.ts
CHANGED
@@ -1,12 +1,13 @@
|
|
1 |
import { Module } from '@nestjs/common';
|
2 |
import { OrderService } from './order.service.js';
|
3 |
-
import { OrderController } from './order.controller.js';
|
4 |
import { BranchModule } from '../branch/branch.module.js';
|
5 |
import { BranchMenusModule } from '../branch-menus/branch-menus.module.js';
|
6 |
|
7 |
@Module({
|
8 |
imports: [BranchModule, BranchMenusModule],
|
9 |
-
controllers: [OrderController],
|
10 |
providers: [OrderService],
|
|
|
11 |
})
|
12 |
export class OrderModule {}
|
|
|
1 |
import { Module } from '@nestjs/common';
|
2 |
import { OrderService } from './order.service.js';
|
3 |
+
import { BranchOrderController, OrderController } from './order.controller.js';
|
4 |
import { BranchModule } from '../branch/branch.module.js';
|
5 |
import { BranchMenusModule } from '../branch-menus/branch-menus.module.js';
|
6 |
|
7 |
@Module({
|
8 |
imports: [BranchModule, BranchMenusModule],
|
9 |
+
controllers: [BranchOrderController, OrderController],
|
10 |
providers: [OrderService],
|
11 |
+
exports: [OrderService],
|
12 |
})
|
13 |
export class OrderModule {}
|
backend/src/modules/order/order.service.ts
CHANGED
@@ -1,4 +1,8 @@
|
|
1 |
-
import {
|
|
|
|
|
|
|
|
|
2 |
import { CreateOrderDto } from './dto/create-order.dto.js';
|
3 |
import { UserEntity } from '../../entities/user.entity.js';
|
4 |
import { OrderEntity } from '../../entities/order.entity.js';
|
@@ -8,16 +12,27 @@ import { isUUID } from 'class-validator';
|
|
8 |
import { OrderItemEntity } from '../../entities/order-item.entity.js';
|
9 |
import { BranchMenuEntity } from '../../entities/branch-menu.entity.js';
|
10 |
import { OrderStatus } from '../../common/enums/OrderStatus.enum.js';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
11 |
|
12 |
@Injectable()
|
13 |
export class OrderService {
|
14 |
-
constructor(
|
|
|
|
|
|
|
15 |
async createFromCustomer(
|
16 |
branchId: string,
|
17 |
userId: string,
|
18 |
createOrderDto: CreateOrderDto,
|
19 |
) {
|
20 |
-
|
21 |
if (createOrderDto.order_type != OrderType.ONLINE) {
|
22 |
throw new BadRequestException('customer cannot create offline order');
|
23 |
}
|
@@ -30,44 +45,60 @@ export class OrderService {
|
|
30 |
if (!branch) {
|
31 |
throw new BadRequestException('Branch not found');
|
32 |
}
|
33 |
-
const
|
34 |
-
order.branch = branch;
|
35 |
-
order.customer = user;
|
36 |
-
order.order_type = createOrderDto.order_type;
|
37 |
-
order.order_status = OrderStatus.PENDING;
|
38 |
-
order.total_value = 0;
|
39 |
-
await order.save();
|
40 |
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
58 |
}
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
orderItems
|
|
|
|
|
|
|
|
|
66 |
}
|
67 |
-
await order.save();
|
68 |
-
order.total_value = totalValue;
|
69 |
-
await OrderItemEntity.save(orderItems);
|
70 |
-
return { ...order, order_items: orderItems };
|
71 |
}
|
72 |
|
73 |
async createFromStaff(
|
@@ -89,49 +120,150 @@ export class OrderService {
|
|
89 |
order.table_number = createOrderDto.table_number;
|
90 |
order.order_status = OrderStatus.PREPARING;
|
91 |
order.total_value = 0;
|
92 |
-
await order.save();
|
93 |
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
111 |
}
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
119 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
120 |
await order.save();
|
121 |
-
order.total_value = totalValue;
|
122 |
-
await OrderItemEntity.save(orderItems);
|
123 |
-
return { ...order, order_items: orderItems };
|
124 |
}
|
125 |
|
126 |
-
|
127 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
128 |
}
|
129 |
|
130 |
-
findOne(id: number) {
|
131 |
-
return
|
132 |
}
|
133 |
|
134 |
-
remove(id: number) {
|
135 |
-
|
|
|
136 |
}
|
137 |
}
|
|
|
1 |
+
import {
|
2 |
+
BadRequestException,
|
3 |
+
Injectable,
|
4 |
+
NotFoundException,
|
5 |
+
} from '@nestjs/common';
|
6 |
import { CreateOrderDto } from './dto/create-order.dto.js';
|
7 |
import { UserEntity } from '../../entities/user.entity.js';
|
8 |
import { OrderEntity } from '../../entities/order.entity.js';
|
|
|
12 |
import { OrderItemEntity } from '../../entities/order-item.entity.js';
|
13 |
import { BranchMenuEntity } from '../../entities/branch-menu.entity.js';
|
14 |
import { OrderStatus } from '../../common/enums/OrderStatus.enum.js';
|
15 |
+
import {
|
16 |
+
FilterOperator,
|
17 |
+
paginate,
|
18 |
+
PaginateConfig,
|
19 |
+
PaginateQuery,
|
20 |
+
} from 'nestjs-paginate';
|
21 |
+
import { UpdateOrderDto } from './dto/update-order.dto.js';
|
22 |
+
import { DataSource } from 'typeorm';
|
23 |
|
24 |
@Injectable()
|
25 |
export class OrderService {
|
26 |
+
constructor(
|
27 |
+
private readonly branchService: BranchService,
|
28 |
+
private readonly datasource: DataSource,
|
29 |
+
) {}
|
30 |
async createFromCustomer(
|
31 |
branchId: string,
|
32 |
userId: string,
|
33 |
createOrderDto: CreateOrderDto,
|
34 |
) {
|
35 |
+
// Chưa được update đầy đủ, còn thiếu thông tin thanh toán.
|
36 |
if (createOrderDto.order_type != OrderType.ONLINE) {
|
37 |
throw new BadRequestException('customer cannot create offline order');
|
38 |
}
|
|
|
45 |
if (!branch) {
|
46 |
throw new BadRequestException('Branch not found');
|
47 |
}
|
48 |
+
const queryRunner = this.datasource.createQueryRunner();
|
|
|
|
|
|
|
|
|
|
|
|
|
49 |
|
50 |
+
// Start a new transaction
|
51 |
+
await queryRunner.connect();
|
52 |
+
await queryRunner.startTransaction();
|
53 |
+
|
54 |
+
try {
|
55 |
+
console.log('Customer create order');
|
56 |
+
const order = OrderEntity.create();
|
57 |
+
order.branch = branch;
|
58 |
+
order.customer = user;
|
59 |
+
order.order_type = createOrderDto.order_type;
|
60 |
+
order.order_status = OrderStatus.PENDING;
|
61 |
+
order.total_value = 0;
|
62 |
+
await queryRunner.manager.save(order);
|
63 |
+
|
64 |
+
let orderItems: OrderItemEntity[] = [];
|
65 |
+
let totalValue = 0;
|
66 |
+
for (const item of createOrderDto.order_items) {
|
67 |
+
let branchMenu: BranchMenuEntity;
|
68 |
+
if (!isUUID(item.menu_id)) {
|
69 |
+
branchMenu = await BranchMenuEntity.findOne({
|
70 |
+
where: { branch_id: branchId, menu_id: item.menu_id },
|
71 |
+
relations: ['menu_item'],
|
72 |
+
});
|
73 |
+
} else {
|
74 |
+
branchMenu = await BranchMenuEntity.findOne({
|
75 |
+
where: { branch_id: branchId, id: item.menu_id },
|
76 |
+
relations: ['menu_item'],
|
77 |
+
});
|
78 |
+
}
|
79 |
+
if (!branchMenu) {
|
80 |
+
throw new BadRequestException('Item not found in branch menu');
|
81 |
+
}
|
82 |
+
const orderItem = OrderItemEntity.create();
|
83 |
+
orderItem.branch_menu = branchMenu;
|
84 |
+
orderItem.price = branchMenu.menu_item.price;
|
85 |
+
orderItem.quantity = item.quantity;
|
86 |
+
orderItem.order_id = order.id;
|
87 |
+
totalValue += orderItem.price * orderItem.quantity;
|
88 |
+
orderItems.push(orderItem);
|
89 |
}
|
90 |
+
await queryRunner.manager.save(order);
|
91 |
+
order.total_value = totalValue;
|
92 |
+
await queryRunner.manager.save(orderItems);
|
93 |
+
|
94 |
+
await queryRunner.commitTransaction();
|
95 |
+
await queryRunner.release();
|
96 |
+
return { ...order, order_items: orderItems };
|
97 |
+
} catch (err) {
|
98 |
+
await queryRunner.rollbackTransaction();
|
99 |
+
await queryRunner.release();
|
100 |
+
throw err;
|
101 |
}
|
|
|
|
|
|
|
|
|
102 |
}
|
103 |
|
104 |
async createFromStaff(
|
|
|
120 |
order.table_number = createOrderDto.table_number;
|
121 |
order.order_status = OrderStatus.PREPARING;
|
122 |
order.total_value = 0;
|
|
|
123 |
|
124 |
+
const queryRunner = this.datasource.createQueryRunner();
|
125 |
+
|
126 |
+
// Start a new transaction
|
127 |
+
await queryRunner.connect();
|
128 |
+
await queryRunner.startTransaction();
|
129 |
+
|
130 |
+
try {
|
131 |
+
await queryRunner.manager.save(order);
|
132 |
+
|
133 |
+
let orderItems: OrderItemEntity[] = [];
|
134 |
+
let totalValue = 0;
|
135 |
+
for (const item of createOrderDto.order_items) {
|
136 |
+
let branchMenu: BranchMenuEntity;
|
137 |
+
if (!isUUID(item.menu_id)) {
|
138 |
+
branchMenu = await BranchMenuEntity.findOne({
|
139 |
+
where: { branch_id: branchId, menu_id: item.menu_id },
|
140 |
+
relations: ['menu_item'],
|
141 |
+
});
|
142 |
+
} else {
|
143 |
+
branchMenu = await BranchMenuEntity.findOne({
|
144 |
+
where: { branch_id: branchId, id: item.menu_id },
|
145 |
+
relations: ['menu_item'],
|
146 |
+
});
|
147 |
+
}
|
148 |
+
if (!branchMenu) {
|
149 |
+
throw new BadRequestException('Item not found in branch menu');
|
150 |
+
}
|
151 |
+
const orderItem = OrderItemEntity.create();
|
152 |
+
orderItem.branch_menu = branchMenu;
|
153 |
+
orderItem.price = branchMenu.menu_item.price;
|
154 |
+
orderItem.quantity = item.quantity;
|
155 |
+
orderItem.order_id = order.id;
|
156 |
+
totalValue += orderItem.price * orderItem.quantity;
|
157 |
+
orderItems.push(orderItem);
|
158 |
}
|
159 |
+
await queryRunner.manager.save(order);
|
160 |
+
order.total_value = totalValue;
|
161 |
+
await queryRunner.manager.save(orderItems);
|
162 |
+
|
163 |
+
await queryRunner.commitTransaction();
|
164 |
+
await queryRunner.release();
|
165 |
+
|
166 |
+
return { ...order, order_items: orderItems };
|
167 |
+
} catch (err) {
|
168 |
+
await queryRunner.rollbackTransaction();
|
169 |
+
await queryRunner.release();
|
170 |
+
throw err;
|
171 |
+
}
|
172 |
+
}
|
173 |
+
|
174 |
+
/**
|
175 |
+
* Lấy order kèm với list item
|
176 |
+
*/
|
177 |
+
async findOrderOrError(id: number) {
|
178 |
+
const order = await OrderEntity.findOne({
|
179 |
+
where: { id },
|
180 |
+
relations: [
|
181 |
+
'order_items',
|
182 |
+
'order_items.branch_menu',
|
183 |
+
'order_items.branch_menu.menu_item',
|
184 |
+
],
|
185 |
+
});
|
186 |
+
if (!order) {
|
187 |
+
throw new NotFoundException('Order id not found');
|
188 |
}
|
189 |
+
return order;
|
190 |
+
}
|
191 |
+
|
192 |
+
async updateOrder(id: number, updateOrderDto: UpdateOrderDto) {
|
193 |
+
// update vì
|
194 |
+
// Offline
|
195 |
+
// Người dùng chuyển bàn
|
196 |
+
// Người dùng thêm/xoá món
|
197 |
+
// Cập nhật trạng thái
|
198 |
+
// Cập nhật rating
|
199 |
+
// Cập nhật note
|
200 |
+
}
|
201 |
+
|
202 |
+
async updateOrderPayment(id: number, paymentId: number) {
|
203 |
+
const order = await this.findOrderOrError(id);
|
204 |
+
order.payment_id = paymentId; // i'm trust that paymentId existed.
|
205 |
+
order.order_status = OrderStatus.ONLINE_PAID;
|
206 |
await order.save();
|
|
|
|
|
|
|
207 |
}
|
208 |
|
209 |
+
getOrderPaginteConfig() {
|
210 |
+
const paginateConfig: PaginateConfig<OrderEntity> = {
|
211 |
+
sortableColumns: ['id', 'created_at', 'total_value'],
|
212 |
+
filterableColumns: {
|
213 |
+
order_status: [FilterOperator.EQ],
|
214 |
+
create_at: [FilterOperator.GTE, FilterOperator.LTE],
|
215 |
+
updated_at: [FilterOperator.GTE, FilterOperator.LTE],
|
216 |
+
customer_id: [FilterOperator.EQ],
|
217 |
+
branch_id: [FilterOperator.EQ],
|
218 |
+
staff_id: [FilterOperator.EQ],
|
219 |
+
table_number: [FilterOperator.EQ],
|
220 |
+
rating: [FilterOperator.EQ, FilterOperator.GTE, FilterOperator.LTE],
|
221 |
+
total_value: [
|
222 |
+
FilterOperator.EQ,
|
223 |
+
FilterOperator.GTE,
|
224 |
+
FilterOperator.LTE,
|
225 |
+
],
|
226 |
+
},
|
227 |
+
defaultSortBy: [['id', 'DESC']],
|
228 |
+
};
|
229 |
+
return paginateConfig;
|
230 |
+
}
|
231 |
+
|
232 |
+
getOrderQueryBuilder(branchId?: string) {
|
233 |
+
//
|
234 |
+
let queryBuilder = OrderEntity.createQueryBuilder('od')
|
235 |
+
.leftJoinAndSelect('od.order_items', 'order_items')
|
236 |
+
.leftJoinAndSelect('order_items.branch_menu', 'branch_menu')
|
237 |
+
.leftJoinAndSelect('branch_menu.menu_item', 'menu_item');
|
238 |
+
if (branchId) {
|
239 |
+
queryBuilder.andWhere('od.branch_id = :branchId', { branchId });
|
240 |
+
}
|
241 |
+
// console.log(queryBuilder.getQuery());
|
242 |
+
return queryBuilder;
|
243 |
+
}
|
244 |
+
|
245 |
+
async findAll(query: PaginateQuery, branchId?: string) {
|
246 |
+
const paginateConfig = this.getOrderPaginteConfig();
|
247 |
+
let queryBuilder = this.getOrderQueryBuilder(branchId);
|
248 |
+
if (branchId) {
|
249 |
+
queryBuilder.andWhere('od.branch_id = :branchId', { branchId });
|
250 |
+
}
|
251 |
+
return paginate(query, queryBuilder, paginateConfig);
|
252 |
+
}
|
253 |
+
|
254 |
+
async findHistory(query: PaginateQuery, userId: string, branchId?: string) {
|
255 |
+
const paginateConfig = this.getOrderPaginteConfig();
|
256 |
+
let queryBuilder = this.getOrderQueryBuilder(branchId);
|
257 |
+
queryBuilder.andWhere('od.customer_id = :userId', { userId });
|
258 |
+
return paginate(query, queryBuilder, paginateConfig);
|
259 |
}
|
260 |
|
261 |
+
async findOne(id: number) {
|
262 |
+
return this.findOrderOrError(id);
|
263 |
}
|
264 |
|
265 |
+
async remove(id: number) {
|
266 |
+
const order = await this.findOrderOrError(id);
|
267 |
+
return await OrderEntity.softRemove(order);
|
268 |
}
|
269 |
}
|
backend/src/modules/user/dto/update-users.dto.ts
ADDED
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { IsEmail, IsMobilePhone, IsOptional, IsString } from "class-validator";
|
2 |
+
import { Role } from "../../../common/enums/role.enum.js";
|
3 |
+
export class UpdateUsersDto {
|
4 |
+
|
5 |
+
@IsString()
|
6 |
+
id: string;
|
7 |
+
|
8 |
+
@IsOptional()
|
9 |
+
avatar: string;
|
10 |
+
|
11 |
+
@IsString()
|
12 |
+
@IsOptional()
|
13 |
+
full_name?: string;
|
14 |
+
|
15 |
+
@IsMobilePhone('vi-VN')
|
16 |
+
@IsOptional()
|
17 |
+
phone_number: string
|
18 |
+
|
19 |
+
@IsString()
|
20 |
+
@IsOptional()
|
21 |
+
address: string;
|
22 |
+
|
23 |
+
@IsEmail()
|
24 |
+
@IsOptional()
|
25 |
+
email?: string;
|
26 |
+
|
27 |
+
@IsOptional()
|
28 |
+
role: Role;
|
29 |
+
|
30 |
+
@IsString()
|
31 |
+
@IsOptional()
|
32 |
+
hash_password?: string;
|
33 |
+
|
34 |
+
@IsOptional()
|
35 |
+
is_valid: boolean;
|
36 |
+
}
|
37 |
+
|
backend/src/modules/user/user.controller.ts
CHANGED
@@ -1,23 +1,61 @@
|
|
1 |
-
import { Body, Controller, Get,
|
2 |
-
import { UserService } from './user.service.js';
|
3 |
-
import { UserEntity } from 'src/entities/user.entity.js';
|
4 |
-
import {
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
)
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { Body, Controller, Get, Post, Put, Query, Request } from '@nestjs/common';
|
2 |
+
import { UserService } from './user.service.js';
|
3 |
+
import { UserEntity } from 'src/entities/user.entity.js';
|
4 |
+
import { Roles } from '../authentication/authorization/roles.decorator.js';
|
5 |
+
import { Role } from '../../common/enums/role.enum.js';
|
6 |
+
import { Paginate, PaginateQuery } from 'nestjs-paginate';
|
7 |
+
import { UpdateUsersDto } from './dto/update-users.dto.js';
|
8 |
+
|
9 |
+
@Controller('users')
|
10 |
+
export class UsersController {
|
11 |
+
constructor(private readonly usersService: UserService) {}
|
12 |
+
|
13 |
+
@Get('id')
|
14 |
+
@Roles(Role.ADMIN, Role.AREA_MANAGER, Role.BRANCH_MANAGER)
|
15 |
+
async getUserById(
|
16 |
+
@Query('id') id: string
|
17 |
+
): Promise<UserEntity | undefined> {
|
18 |
+
return this.usersService.findOneByField("id", id)
|
19 |
+
}
|
20 |
+
|
21 |
+
@Get('fullname')
|
22 |
+
@Roles(Role.ADMIN, Role.AREA_MANAGER, Role.BRANCH_MANAGER)
|
23 |
+
async getUserByFullname( @Query('full_name') fullName: string, @Paginate() query: PaginateQuery, ) {
|
24 |
+
console.log(fullName)
|
25 |
+
return this.usersService.findAllByName(fullName, query);
|
26 |
+
}
|
27 |
+
|
28 |
+
@Post('updateUser')
|
29 |
+
async updateUser(@Request() req){
|
30 |
+
const userId = req.user.sub;
|
31 |
+
const updateUserDto = req.body;
|
32 |
+
return this.usersService.updateUserById(userId, updateUserDto);
|
33 |
+
}
|
34 |
+
|
35 |
+
@Put('updateList')
|
36 |
+
@Roles(Role.ADMIN)
|
37 |
+
async updateUsers(
|
38 |
+
@Body() updateUsersDto: UpdateUsersDto[],
|
39 |
+
) {
|
40 |
+
return this.usersService.updateUsers(updateUsersDto);
|
41 |
+
}
|
42 |
+
|
43 |
+
@Get('getAll')
|
44 |
+
@Roles(Role.ADMIN, Role.AREA_MANAGER, Role.BRANCH_MANAGER)
|
45 |
+
async findAllUser(@Paginate() query: PaginateQuery) {
|
46 |
+
return this.usersService.findAllUser(query);
|
47 |
+
}
|
48 |
+
|
49 |
+
@Get('role')
|
50 |
+
@Roles(Role.ADMIN, Role.AREA_MANAGER, Role.BRANCH_MANAGER)
|
51 |
+
async getUserByRole( @Query('role') role: string, @Paginate() query: PaginateQuery, ) {
|
52 |
+
console.log(role)
|
53 |
+
return this.usersService.findAllByRole(role, query);
|
54 |
+
}
|
55 |
+
|
56 |
+
@Get('role')
|
57 |
+
@Roles(Role.ADMIN, Role.AREA_MANAGER)
|
58 |
+
async getUsersByBranch(@Query('branchId') branchId: string, @Paginate() query: PaginateQuery){
|
59 |
+
|
60 |
+
}
|
61 |
+
}
|
backend/src/modules/user/user.service.ts
CHANGED
@@ -1,66 +1,167 @@
|
|
1 |
-
import { Body, forwardRef, Inject, Injectable, NotFoundException } from '@nestjs/common';
|
2 |
-
import { UserEntity } from '../../entities/user.entity.js';
|
3 |
-
import { SignUpDto } from '../authentication/dto/sign-up.dto.js';
|
4 |
-
import { UpdateUserDto } from './dto/update-user-dto.js';
|
5 |
-
import { ValidateService } from '../../validate/validate.service.js';
|
6 |
-
import * as bcrypt from 'bcrypt';
|
7 |
-
import { JwtService } from '@nestjs/jwt';
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
export
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
await this.validateService.checkExistField('
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
const
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { Body, forwardRef, HttpStatus, Inject, Injectable, NotFoundException } from '@nestjs/common';
|
2 |
+
import { UserEntity } from '../../entities/user.entity.js';
|
3 |
+
import { SignUpDto } from '../authentication/dto/sign-up.dto.js';
|
4 |
+
import { UpdateUserDto } from './dto/update-user-dto.js';
|
5 |
+
import { ValidateService } from '../../validate/validate.service.js';
|
6 |
+
import * as bcrypt from 'bcrypt';
|
7 |
+
import { JwtService } from '@nestjs/jwt';
|
8 |
+
import { FilterOperator, paginate, PaginateConfig, PaginateQuery } from 'nestjs-paginate';
|
9 |
+
import { UpdateUsersDto } from './dto/update-users.dto.js';
|
10 |
+
import { In } from 'typeorm';
|
11 |
+
|
12 |
+
export type User = any;
|
13 |
+
|
14 |
+
@Injectable()
|
15 |
+
export class UserService {
|
16 |
+
constructor(
|
17 |
+
private validateService: ValidateService,
|
18 |
+
private jwtService: JwtService,
|
19 |
+
) { }
|
20 |
+
|
21 |
+
async create(signUpDto: SignUpDto): Promise<UserEntity | undefined> {
|
22 |
+
return UserEntity.create({
|
23 |
+
full_name: signUpDto.full_name,
|
24 |
+
phone_number: signUpDto.phone_number,
|
25 |
+
email: signUpDto.email,
|
26 |
+
hash_password: signUpDto.password
|
27 |
+
})
|
28 |
+
}
|
29 |
+
|
30 |
+
async save(userEntity: UserEntity): Promise<UserEntity | undefined> {
|
31 |
+
return UserEntity.save(userEntity);
|
32 |
+
}
|
33 |
+
|
34 |
+
async findOneByField(field: string, value: any): Promise<UserEntity | undefined> {
|
35 |
+
return UserEntity.findOne({
|
36 |
+
where: { [field]: value }
|
37 |
+
});
|
38 |
+
}
|
39 |
+
|
40 |
+
async updateUserById(userId: string, updateUserDto: UpdateUserDto) {
|
41 |
+
|
42 |
+
await this.validateService.checkExistField('email', updateUserDto.email);
|
43 |
+
await this.validateService.checkExistField('phone_number', updateUserDto.phone_number);
|
44 |
+
|
45 |
+
const user = await UserEntity.findOne({
|
46 |
+
where: { id: userId }
|
47 |
+
});
|
48 |
+
if (!user) {
|
49 |
+
throw new NotFoundException(`User with ID ${userId} not found`);
|
50 |
+
}
|
51 |
+
|
52 |
+
Object.assign(user, updateUserDto);
|
53 |
+
if (updateUserDto.hash_password) {
|
54 |
+
const saltRounds = 10;
|
55 |
+
user.hash_password = await bcrypt.hash(updateUserDto.hash_password, saltRounds); // Mã hóa mật khẩu
|
56 |
+
}
|
57 |
+
await UserEntity.save(user);
|
58 |
+
|
59 |
+
const payload = { sub: user.id, username: user.full_name, roles: user.role };
|
60 |
+
const token = await this.jwtService.signAsync(payload)
|
61 |
+
return {
|
62 |
+
access_token: token
|
63 |
+
};
|
64 |
+
}
|
65 |
+
|
66 |
+
async findAllUser(query: PaginateQuery) {
|
67 |
+
const paginateConfig: PaginateConfig<UserEntity> = {
|
68 |
+
sortableColumns: ['id', 'full_name', 'phone_number', 'email'],
|
69 |
+
nullSort: 'last',
|
70 |
+
defaultSortBy: [['id', 'DESC']],
|
71 |
+
searchableColumns: ['full_name'],
|
72 |
+
filterableColumns: {
|
73 |
+
full_name: [
|
74 |
+
FilterOperator.LT,
|
75 |
+
FilterOperator.LTE,
|
76 |
+
FilterOperator.GT,
|
77 |
+
FilterOperator.GTE,
|
78 |
+
],
|
79 |
+
item_type: [FilterOperator.EQ]
|
80 |
+
},
|
81 |
+
};
|
82 |
+
return paginate(query, UserEntity.createQueryBuilder(), paginateConfig);
|
83 |
+
}
|
84 |
+
|
85 |
+
async findAllByName(fullName: string, query: PaginateQuery) {
|
86 |
+
const queryBuilder = UserEntity.createQueryBuilder('users')
|
87 |
+
.where('users.full_name = :fullName', { fullName });
|
88 |
+
const paginateConfig: PaginateConfig<UserEntity> = {
|
89 |
+
sortableColumns: ['id', 'full_name', 'phone_number', 'email'],
|
90 |
+
nullSort: 'last',
|
91 |
+
defaultSortBy: [['id', 'DESC']],
|
92 |
+
searchableColumns: ['full_name'],
|
93 |
+
filterableColumns: {
|
94 |
+
full_name: [
|
95 |
+
FilterOperator.LT,
|
96 |
+
FilterOperator.LTE,
|
97 |
+
FilterOperator.GT,
|
98 |
+
FilterOperator.GTE,
|
99 |
+
],
|
100 |
+
item_type: [FilterOperator.EQ]
|
101 |
+
},
|
102 |
+
};
|
103 |
+
return paginate(query, queryBuilder, paginateConfig);
|
104 |
+
}
|
105 |
+
|
106 |
+
async findAllByRole(role: string, query: PaginateQuery) {
|
107 |
+
const queryBuilder = UserEntity.createQueryBuilder('users')
|
108 |
+
.where('users.role = :role', { role });
|
109 |
+
const paginateConfig: PaginateConfig<UserEntity> = {
|
110 |
+
sortableColumns: ['id', 'full_name', 'phone_number', 'email'],
|
111 |
+
nullSort: 'last',
|
112 |
+
defaultSortBy: [['id', 'DESC']],
|
113 |
+
searchableColumns: ['full_name'],
|
114 |
+
filterableColumns: {
|
115 |
+
full_name: [
|
116 |
+
FilterOperator.LT,
|
117 |
+
FilterOperator.LTE,
|
118 |
+
FilterOperator.GT,
|
119 |
+
FilterOperator.GTE,
|
120 |
+
],
|
121 |
+
item_type: [FilterOperator.EQ]
|
122 |
+
},
|
123 |
+
};
|
124 |
+
return paginate(query, queryBuilder, paginateConfig);
|
125 |
+
}
|
126 |
+
|
127 |
+
async updateUsers(updateUsersDto: UpdateUsersDto[]) {
|
128 |
+
try {
|
129 |
+
//Lấy ra id trong updateUsersDto
|
130 |
+
const userIds = updateUsersDto.map(user => user.id).filter(id => id !== undefined);
|
131 |
+
|
132 |
+
//Lấy ra các user tồn tại trong db
|
133 |
+
const existingUsers = await UserEntity.find({
|
134 |
+
where: { id: In(userIds) },
|
135 |
+
});
|
136 |
+
|
137 |
+
// Bước 2: Kết hợp dữ liệu từ yêu cầu với dữ liệu hiện có
|
138 |
+
const mergedData: Partial<User>[] = [];
|
139 |
+
for (const userData of updateUsersDto) {
|
140 |
+
if (userData.hash_password)
|
141 |
+
userData.hash_password = await bcrypt.hash(userData.hash_password, 10);
|
142 |
+
|
143 |
+
const existingUser = existingUsers.find(user => user.id === userData.id);
|
144 |
+
|
145 |
+
if (existingUser) {
|
146 |
+
// Merge data: keep existing fields for any data not included in the request
|
147 |
+
mergedData.push({ ...existingUser, ...userData });
|
148 |
+
} else {
|
149 |
+
// For new users, use only the request data
|
150 |
+
mergedData.push(userData);
|
151 |
+
}
|
152 |
+
}
|
153 |
+
|
154 |
+
console.log(updateUsersDto)
|
155 |
+
UserEntity.upsert(mergedData, ['id'])
|
156 |
+
return {
|
157 |
+
statusCode: HttpStatus.OK,
|
158 |
+
message: "Thành công"
|
159 |
+
}
|
160 |
+
} catch (error) {
|
161 |
+
return {
|
162 |
+
statusCode: HttpStatus.OK,
|
163 |
+
message: "Thất bại"
|
164 |
+
}
|
165 |
+
}
|
166 |
+
}
|
167 |
+
}
|
backend/src/payment/dto/create-payment-url.dto.ts
ADDED
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { IsNumber, IsOptional, IsString } from "class-validator"
|
2 |
+
|
3 |
+
export class CreatePaymentUrlDto {
|
4 |
+
@IsNumber()
|
5 |
+
amount: number
|
6 |
+
|
7 |
+
@IsOptional()
|
8 |
+
@IsString()
|
9 |
+
bankCode: string
|
10 |
+
|
11 |
+
@IsString()
|
12 |
+
orderId: string
|
13 |
+
|
14 |
+
@IsString()
|
15 |
+
orderDescription: string
|
16 |
+
|
17 |
+
@IsString()
|
18 |
+
orderType: string
|
19 |
+
|
20 |
+
@IsString()
|
21 |
+
language: string
|
22 |
+
}
|
backend/src/payment/dto/create-payment.dto..ts
ADDED
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { IsNumber, IsOptional, IsString } from 'class-validator';
|
2 |
+
|
3 |
+
export class CreatePaymentDto {
|
4 |
+
@IsNumber()
|
5 |
+
payment_method: number;
|
6 |
+
|
7 |
+
@IsNumber()
|
8 |
+
vnp_amount: number
|
9 |
+
|
10 |
+
@IsString()
|
11 |
+
vnp_bank_code: string
|
12 |
+
|
13 |
+
@IsString()
|
14 |
+
vnp_bank_tran_no: string
|
15 |
+
|
16 |
+
@IsNumber()
|
17 |
+
vnp_card_type: number
|
18 |
+
|
19 |
+
@IsString()
|
20 |
+
@IsOptional()
|
21 |
+
vnp_order_info: string //Nội dung giao dịch
|
22 |
+
|
23 |
+
@IsString()
|
24 |
+
@IsOptional()
|
25 |
+
vnp_paydate: string
|
26 |
+
|
27 |
+
@IsString()
|
28 |
+
@IsOptional()
|
29 |
+
vnp_response_code: number
|
30 |
+
|
31 |
+
@IsString()
|
32 |
+
@IsOptional()
|
33 |
+
vnp_transaction_no: string
|
34 |
+
|
35 |
+
@IsString()
|
36 |
+
@IsOptional()
|
37 |
+
vnp_transaction_status: number
|
38 |
+
}
|
backend/src/payment/payment.controller.ts
ADDED
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
// payment.controller.ts
|
2 |
+
import { Controller, Post, Body, Req, Res, Get } from '@nestjs/common';
|
3 |
+
import { PaymentService } from './payment.service.js';
|
4 |
+
import { Request, Response } from 'express';
|
5 |
+
import { Public } from '../modules/authentication/authentication.decorator.js';
|
6 |
+
import { CreatePaymentUrlDto } from './dto/create-payment-url.dto.js';
|
7 |
+
|
8 |
+
@Controller('payment')
|
9 |
+
export class PaymentController {
|
10 |
+
constructor(private readonly paymentService: PaymentService) {}
|
11 |
+
|
12 |
+
@Public()
|
13 |
+
@Post('create_payment_url')
|
14 |
+
async createPaymentUrl(@Req() req: Request, @Body() body: CreatePaymentUrlDto) {
|
15 |
+
|
16 |
+
const ipAddr =
|
17 |
+
req.headers['x-forwarded-for'] ||
|
18 |
+
req.socket.remoteAddress ||
|
19 |
+
req.socket?.remoteAddress;
|
20 |
+
|
21 |
+
return await this.paymentService.createPaymentUrl(
|
22 |
+
body.amount,
|
23 |
+
body.orderId,
|
24 |
+
body.orderDescription,
|
25 |
+
body.orderType,
|
26 |
+
body.language,
|
27 |
+
ipAddr as string,
|
28 |
+
);
|
29 |
+
}
|
30 |
+
|
31 |
+
@Public()
|
32 |
+
@Get('vnpay_ipn')
|
33 |
+
async vnpayIpn(@Req() req: Request, @Body() body: any){
|
34 |
+
const reqQuery = req.query;
|
35 |
+
const res = await this.paymentService.vnpayIpn(reqQuery)
|
36 |
+
console.log(res);
|
37 |
+
return res;
|
38 |
+
}
|
39 |
+
}
|
backend/src/payment/payment.module.ts
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { Module } from '@nestjs/common';
|
2 |
+
import { PaymentService } from './payment.service.js';
|
3 |
+
import { PaymentController } from './payment.controller.js';
|
4 |
+
import { OrderService } from '../modules/order/order.service.js';
|
5 |
+
import { BranchService } from '../modules/branch/branch.service.js';
|
6 |
+
|
7 |
+
@Module({
|
8 |
+
controllers: [PaymentController],
|
9 |
+
providers: [PaymentService, OrderService, BranchService],
|
10 |
+
})
|
11 |
+
export class PaymentModule {}
|
backend/src/payment/payment.service.ts
ADDED
@@ -0,0 +1,200 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
// payment.service.ts
|
2 |
+
import { HttpStatus, Injectable } from '@nestjs/common';
|
3 |
+
import { ConfigService } from '@nestjs/config';
|
4 |
+
import * as querystring from 'qs';
|
5 |
+
import * as crypto from 'crypto';
|
6 |
+
import { OrderService } from '../modules/order/order.service.js';
|
7 |
+
import { PaymentEntity } from '../entities/payment.entity.js';
|
8 |
+
import { CreatePaymentUrlDto } from './dto/create-payment-url.dto.js';
|
9 |
+
import { CreatePaymentDto } from './dto/create-payment.dto..js';
|
10 |
+
import { VnpCardType } from '../common/enums/VnpCardType.enum.js';
|
11 |
+
|
12 |
+
@Injectable()
|
13 |
+
export class PaymentService {
|
14 |
+
constructor(
|
15 |
+
private readonly configService: ConfigService,
|
16 |
+
private readonly orderService: OrderService
|
17 |
+
) { }
|
18 |
+
|
19 |
+
async createPaymentUrl(amount: number, orderId: string, orderDescription: string, orderType: string, language: string, ipAddr: string) {
|
20 |
+
|
21 |
+
const tmnCode = this.configService.get<string>('vnp_TmnCode');
|
22 |
+
const secretKey = this.configService.get<string>('vnp_HashSecret');
|
23 |
+
const vnpUrl = this.configService.get<string>('vnp_Url');
|
24 |
+
const returnUrl = this.configService.get<string>('vnp_ReturnUrl');
|
25 |
+
|
26 |
+
const date = new Date();
|
27 |
+
const createDate = this.formatDate(date, 'yyyymmddHHmmss');
|
28 |
+
const locale = language || 'vn';
|
29 |
+
const currCode = 'VND';
|
30 |
+
|
31 |
+
const vnp_Params: Record<string, string> = {
|
32 |
+
vnp_Version: '2.1.0',
|
33 |
+
vnp_Command: 'pay',
|
34 |
+
vnp_TmnCode: tmnCode,
|
35 |
+
vnp_Locale: locale,
|
36 |
+
vnp_CurrCode: currCode,
|
37 |
+
vnp_TxnRef: orderId,
|
38 |
+
vnp_OrderInfo: orderDescription,
|
39 |
+
vnp_OrderType: orderType,
|
40 |
+
vnp_Amount: (amount * 100).toString(),
|
41 |
+
vnp_ReturnUrl: returnUrl,
|
42 |
+
vnp_IpAddr: ipAddr,
|
43 |
+
vnp_CreateDate: createDate,
|
44 |
+
};
|
45 |
+
console.log("3")
|
46 |
+
|
47 |
+
const sortedParams = this.sortObject(vnp_Params);
|
48 |
+
|
49 |
+
// Sign the data
|
50 |
+
const signData = querystring.stringify(sortedParams, { encode: false });
|
51 |
+
const hmac = crypto.createHmac('sha512', secretKey);
|
52 |
+
const signed = hmac.update(Buffer.from(signData, 'utf-8')).digest('hex');
|
53 |
+
sortedParams['vnp_SecureHash'] = signed;
|
54 |
+
|
55 |
+
// Create the URL
|
56 |
+
const res = `${vnpUrl}?${querystring.stringify(sortedParams, { encode: false })}`;
|
57 |
+
|
58 |
+
return res;
|
59 |
+
}
|
60 |
+
|
61 |
+
|
62 |
+
async vnpayIpn(reqQuery) {
|
63 |
+
console.log("helloooo")
|
64 |
+
let vnp_Params = reqQuery;
|
65 |
+
let secureHash = vnp_Params['vnp_SecureHash'];
|
66 |
+
|
67 |
+
let orderId = vnp_Params['vnp_TxnRef'];
|
68 |
+
let rspCode = vnp_Params['vnp_ResponseCode'];
|
69 |
+
|
70 |
+
delete vnp_Params['vnp_SecureHash'];
|
71 |
+
delete vnp_Params['vnp_SecureHashType'];
|
72 |
+
|
73 |
+
vnp_Params = this.sortObject(vnp_Params);
|
74 |
+
let secretKey = this.configService.get('vnp_HashSecret');
|
75 |
+
let signData = querystring.stringify(vnp_Params, { encode: false });
|
76 |
+
let hmac = crypto.createHmac("sha512", secretKey);
|
77 |
+
let signed = hmac.update(Buffer.from(signData, 'utf-8')).digest("hex");
|
78 |
+
|
79 |
+
let paymentStatus = '0'; // Giả sử '0' là trạng thái khởi tạo giao dịch, chưa có IPN. Trạng thái này được lưu khi yêu cầu thanh toán chuyển hướng sang Cổng thanh toán VNPAY tại đầu khởi tạo đơn hàng.
|
80 |
+
//let paymentStatus = '1'; // Giả sử '1' là trạng thái thành công bạn cập nhật sau IPN được gọi và trả kết quả về nó
|
81 |
+
//let paymentStatus = '2'; // Giả sử '2' là trạng thái thất bại bạn cập nhật sau IPN được gọi và trả kết quả về nó
|
82 |
+
//Kiểm tra có đúng order id không
|
83 |
+
let checkOrderId = true;
|
84 |
+
let order;
|
85 |
+
try {
|
86 |
+
order = await this.orderService.findOne(orderId)
|
87 |
+
} catch (error) {
|
88 |
+
return {
|
89 |
+
statusCode: HttpStatus.OK,
|
90 |
+
message: 'Order not found',
|
91 |
+
};
|
92 |
+
}
|
93 |
+
console.log("order = ", order);
|
94 |
+
console.log("order total value ", order.total_value);
|
95 |
+
// Kiểm tra số tiền "giá trị của vnp_Amout/100" trùng khớp với số tiền của đơn hàng trong CSDL của bạn
|
96 |
+
let checkAmount = order.total_value == parseFloat(vnp_Params["vnp_Amount"])/100;
|
97 |
+
if (secureHash === signed) { //kiểm tra checksum
|
98 |
+
if (checkOrderId) {
|
99 |
+
if (checkAmount) {
|
100 |
+
if (paymentStatus == "0") { //kiểm tra tình trạng giao dịch trước khi cập nhật tình trạng thanh toán
|
101 |
+
if (rspCode == "00") {
|
102 |
+
//thanh cong
|
103 |
+
//paymentStatus = '1'
|
104 |
+
// Ở đây cập nhật trạng thái giao dịch thanh toán thành công vào CSDL của bạn
|
105 |
+
const payment = await this.create({
|
106 |
+
payment_method: 2,
|
107 |
+
vnp_amount: parseFloat(vnp_Params["vnp_Amount"])/100,
|
108 |
+
vnp_bank_code: vnp_Params['vnp_BankCode'],
|
109 |
+
vnp_bank_tran_no: vnp_Params['vnp_BankTranNo'],
|
110 |
+
vnp_card_type: VnpCardType[vnp_Params['vnp_CardType'] as keyof typeof VnpCardType],
|
111 |
+
vnp_order_info: vnp_Params['vnp_BankTranNo'],
|
112 |
+
vnp_paydate: vnp_Params['vnp_PayDate'],
|
113 |
+
vnp_response_code: vnp_Params['vnp_ResponseCode'],
|
114 |
+
vnp_transaction_no: vnp_Params['vnp_TransactionNo'],
|
115 |
+
vnp_transaction_status: vnp_Params['vnp_TransactionStatus'],
|
116 |
+
});
|
117 |
+
await PaymentEntity.save(payment);
|
118 |
+
this.orderService.updateOrderPayment(orderId, payment.id)
|
119 |
+
return {
|
120 |
+
statusCode: HttpStatus.OK,
|
121 |
+
message: 'Thành công!',
|
122 |
+
};
|
123 |
+
}
|
124 |
+
else {
|
125 |
+
//that bai
|
126 |
+
//paymentStatus = '2'
|
127 |
+
// Ở đây cập nhật trạng thái giao dịch thanh toán thất bại vào CSDL của bạn
|
128 |
+
return {
|
129 |
+
statusCode: HttpStatus.OK,
|
130 |
+
message: 'Thất bại',
|
131 |
+
};
|
132 |
+
}
|
133 |
+
}
|
134 |
+
else {
|
135 |
+
return {
|
136 |
+
statusCode: HttpStatus.OK,
|
137 |
+
message: 'This order has been updated to the payment status',
|
138 |
+
};
|
139 |
+
}
|
140 |
+
}
|
141 |
+
else {
|
142 |
+
return {
|
143 |
+
statusCode: HttpStatus.OK,
|
144 |
+
message: 'Amount invalid',
|
145 |
+
};
|
146 |
+
}
|
147 |
+
}
|
148 |
+
else {
|
149 |
+
return {
|
150 |
+
statusCode: HttpStatus.OK,
|
151 |
+
message: 'Order not found',
|
152 |
+
};
|
153 |
+
}
|
154 |
+
}
|
155 |
+
else {
|
156 |
+
return {
|
157 |
+
statusCode: HttpStatus.OK,
|
158 |
+
message: 'Checksum failed!',
|
159 |
+
};
|
160 |
+
}
|
161 |
+
}
|
162 |
+
|
163 |
+
// Format date helper function
|
164 |
+
formatDate(date: Date, format: string): string {
|
165 |
+
const yyyymmdd = date.toISOString().slice(0, 10).replace(/-/g, ''); // YYYYMMDD
|
166 |
+
const hhmmss = date.toTimeString().slice(0, 8).replace(/:/g, ''); // HHMMSS
|
167 |
+
return format === 'yyyymmddHHmmss' ? yyyymmdd + hhmmss : hhmmss;
|
168 |
+
}
|
169 |
+
|
170 |
+
sortObject(obj) {
|
171 |
+
let sorted = {};
|
172 |
+
let str = [];
|
173 |
+
let key;
|
174 |
+
for (key in obj) {
|
175 |
+
if (obj.hasOwnProperty(key)) {
|
176 |
+
str.push(encodeURIComponent(key));
|
177 |
+
}
|
178 |
+
}
|
179 |
+
str.sort();
|
180 |
+
for (key = 0; key < str.length; key++) {
|
181 |
+
sorted[str[key]] = encodeURIComponent(obj[str[key]]).replace(/%20/g, "+");
|
182 |
+
}
|
183 |
+
return sorted;
|
184 |
+
}
|
185 |
+
|
186 |
+
async create(createPaymentDto: CreatePaymentDto): Promise<PaymentEntity | undefined> {
|
187 |
+
return PaymentEntity.create({
|
188 |
+
payment_method: 2,
|
189 |
+
vnp_amount: createPaymentDto.vnp_amount,
|
190 |
+
vnp_bank_code: createPaymentDto.vnp_bank_code,
|
191 |
+
vnp_bank_tran_no: createPaymentDto.vnp_bank_tran_no,
|
192 |
+
vnp_card_type: createPaymentDto.vnp_card_type,
|
193 |
+
vnp_order_info: createPaymentDto.vnp_order_info,
|
194 |
+
vnp_paydate: createPaymentDto.vnp_paydate,
|
195 |
+
vnp_response_code: createPaymentDto.vnp_response_code,
|
196 |
+
vnp_transaction_no: createPaymentDto.vnp_transaction_no,
|
197 |
+
vnp_transaction_status: createPaymentDto.vnp_transaction_status
|
198 |
+
})
|
199 |
+
}
|
200 |
+
}
|
frontend/.gitignore
CHANGED
@@ -24,4 +24,7 @@ yarn-error.log*
|
|
24 |
|
25 |
.env
|
26 |
note_dev.txt
|
27 |
-
object.json
|
|
|
|
|
|
|
|
24 |
|
25 |
.env
|
26 |
note_dev.txt
|
27 |
+
object.json
|
28 |
+
test.json
|
29 |
+
data.json
|
30 |
+
# src/data/*
|
frontend/package-lock.json
CHANGED
@@ -8,11 +8,13 @@
|
|
8 |
"name": "test-app",
|
9 |
"version": "0.1.0",
|
10 |
"dependencies": {
|
|
|
11 |
"@testing-library/jest-dom": "^5.17.0",
|
12 |
"@testing-library/react": "^13.4.0",
|
13 |
"@testing-library/user-event": "^13.5.0",
|
14 |
"axios": "^1.7.7",
|
15 |
"bootstrap": "^5.3.3",
|
|
|
16 |
"js-cookie": "^3.0.5",
|
17 |
"jsonwebtoken": "^9.0.2",
|
18 |
"react": "^18.3.1",
|
@@ -3433,6 +3435,20 @@
|
|
3433 |
"node": ">= 8"
|
3434 |
}
|
3435 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3436 |
"node_modules/@pkgjs/parseargs": {
|
3437 |
"version": "0.11.0",
|
3438 |
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
|
@@ -4774,6 +4790,13 @@
|
|
4774 |
"integrity": "sha512-7i+zxXdPD0T4cKDuxCUXJ4wHcsJLwENa6Z3dCu8cfCK743OGy5Nu1RmAGqDPsoTDINVEcdXKRvR/zre+P2Ku1A==",
|
4775 |
"license": "MIT"
|
4776 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4777 |
"node_modules/@types/range-parser": {
|
4778 |
"version": "1.2.7",
|
4779 |
"resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz",
|
@@ -5813,6 +5836,18 @@
|
|
5813 |
"node": ">= 4.0.0"
|
5814 |
}
|
5815 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5816 |
"node_modules/autoprefixer": {
|
5817 |
"version": "10.4.20",
|
5818 |
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz",
|
@@ -6218,6 +6253,15 @@
|
|
6218 |
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
6219 |
"license": "MIT"
|
6220 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6221 |
"node_modules/batch": {
|
6222 |
"version": "0.6.1",
|
6223 |
"resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz",
|
@@ -6261,6 +6305,12 @@
|
|
6261 |
"url": "https://github.com/sponsors/sindresorhus"
|
6262 |
}
|
6263 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
6264 |
"node_modules/bluebird": {
|
6265 |
"version": "3.7.2",
|
6266 |
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
|
@@ -6431,6 +6481,18 @@
|
|
6431 |
"node-int64": "^0.4.0"
|
6432 |
}
|
6433 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6434 |
"node_modules/buffer-equal-constant-time": {
|
6435 |
"version": "1.0.1",
|
6436 |
"resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
|
@@ -6555,6 +6617,33 @@
|
|
6555 |
],
|
6556 |
"license": "CC-BY-4.0"
|
6557 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6558 |
"node_modules/case-sensitive-paths-webpack-plugin": {
|
6559 |
"version": "2.4.0",
|
6560 |
"resolved": "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz",
|
@@ -7032,6 +7121,15 @@
|
|
7032 |
"postcss": "^8.4"
|
7033 |
}
|
7034 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
7035 |
"node_modules/css-loader": {
|
7036 |
"version": "6.11.0",
|
7037 |
"resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.11.0.tgz",
|
@@ -7365,6 +7463,416 @@
|
|
7365 |
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
|
7366 |
"license": "MIT"
|
7367 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
7368 |
"node_modules/damerau-levenshtein": {
|
7369 |
"version": "1.0.8",
|
7370 |
"resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz",
|
@@ -7567,6 +8075,15 @@
|
|
7567 |
"url": "https://github.com/sponsors/ljharb"
|
7568 |
}
|
7569 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
7570 |
"node_modules/delayed-stream": {
|
7571 |
"version": "1.0.0",
|
7572 |
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
@@ -7796,6 +8313,13 @@
|
|
7796 |
"url": "https://github.com/fb55/domhandler?sponsor=1"
|
7797 |
}
|
7798 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
7799 |
"node_modules/domutils": {
|
7800 |
"version": "2.8.0",
|
7801 |
"resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
|
@@ -8149,6 +8673,12 @@
|
|
8149 |
"url": "https://github.com/sponsors/ljharb"
|
8150 |
}
|
8151 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
8152 |
"node_modules/escalade": {
|
8153 |
"version": "3.2.0",
|
8154 |
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
|
@@ -9132,6 +9662,12 @@
|
|
9132 |
"bser": "2.1.1"
|
9133 |
}
|
9134 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
9135 |
"node_modules/file-entry-cache": {
|
9136 |
"version": "6.0.1",
|
9137 |
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
|
@@ -10149,6 +10685,30 @@
|
|
10149 |
}
|
10150 |
}
|
10151 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
10152 |
"node_modules/htmlparser2": {
|
10153 |
"version": "6.1.0",
|
10154 |
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz",
|
@@ -10430,6 +10990,24 @@
|
|
10430 |
"node": ">= 0.4"
|
10431 |
}
|
10432 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
10433 |
"node_modules/invariant": {
|
10434 |
"version": "2.2.4",
|
10435 |
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
|
@@ -10957,6 +11535,12 @@
|
|
10957 |
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
|
10958 |
"license": "ISC"
|
10959 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
10960 |
"node_modules/istanbul-lib-coverage": {
|
10961 |
"version": "3.2.2",
|
10962 |
"resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz",
|
@@ -13425,6 +14009,24 @@
|
|
13425 |
"npm": ">=6"
|
13426 |
}
|
13427 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
13428 |
"node_modules/jsx-ast-utils": {
|
13429 |
"version": "3.3.5",
|
13430 |
"resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz",
|
@@ -17002,6 +17604,16 @@
|
|
17002 |
"node": ">=0.10.0"
|
17003 |
}
|
17004 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
17005 |
"node_modules/rimraf": {
|
17006 |
"version": "3.0.2",
|
17007 |
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
@@ -17018,6 +17630,12 @@
|
|
17018 |
"url": "https://github.com/sponsors/isaacs"
|
17019 |
}
|
17020 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
17021 |
"node_modules/rollup": {
|
17022 |
"version": "2.79.2",
|
17023 |
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.2.tgz",
|
@@ -17116,6 +17734,12 @@
|
|
17116 |
"queue-microtask": "^1.2.2"
|
17117 |
}
|
17118 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
17119 |
"node_modules/safe-array-concat": {
|
17120 |
"version": "1.1.2",
|
17121 |
"resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz",
|
@@ -17735,6 +18359,16 @@
|
|
17735 |
"node": ">=8"
|
17736 |
}
|
17737 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
17738 |
"node_modules/stackframe": {
|
17739 |
"version": "1.3.4",
|
17740 |
"resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz",
|
@@ -18291,6 +18925,16 @@
|
|
18291 |
"integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==",
|
18292 |
"license": "MIT"
|
18293 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
18294 |
"node_modules/svgo": {
|
18295 |
"version": "1.3.2",
|
18296 |
"resolved": "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz",
|
@@ -18575,6 +19219,15 @@
|
|
18575 |
"node": ">=8"
|
18576 |
}
|
18577 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
18578 |
"node_modules/text-table": {
|
18579 |
"version": "0.2.0",
|
18580 |
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
|
@@ -19113,6 +19766,15 @@
|
|
19113 |
"node": ">= 0.4.0"
|
19114 |
}
|
19115 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
19116 |
"node_modules/uuid": {
|
19117 |
"version": "8.3.2",
|
19118 |
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
|
|
|
8 |
"name": "test-app",
|
9 |
"version": "0.1.0",
|
10 |
"dependencies": {
|
11 |
+
"@observablehq/plot": "^0.6.16",
|
12 |
"@testing-library/jest-dom": "^5.17.0",
|
13 |
"@testing-library/react": "^13.4.0",
|
14 |
"@testing-library/user-event": "^13.5.0",
|
15 |
"axios": "^1.7.7",
|
16 |
"bootstrap": "^5.3.3",
|
17 |
+
"html2pdf.js": "^0.10.2",
|
18 |
"js-cookie": "^3.0.5",
|
19 |
"jsonwebtoken": "^9.0.2",
|
20 |
"react": "^18.3.1",
|
|
|
3435 |
"node": ">= 8"
|
3436 |
}
|
3437 |
},
|
3438 |
+
"node_modules/@observablehq/plot": {
|
3439 |
+
"version": "0.6.16",
|
3440 |
+
"resolved": "https://registry.npmjs.org/@observablehq/plot/-/plot-0.6.16.tgz",
|
3441 |
+
"integrity": "sha512-LRi9Rn93yUx90MIo2Md7+vazxO3Wiat14but2ttCER0xVS+jnfoUjuCGoz6H7bz/lgI9CFcW0HWlvWjMFjAv8g==",
|
3442 |
+
"license": "ISC",
|
3443 |
+
"dependencies": {
|
3444 |
+
"d3": "^7.9.0",
|
3445 |
+
"interval-tree-1d": "^1.0.0",
|
3446 |
+
"isoformat": "^0.2.0"
|
3447 |
+
},
|
3448 |
+
"engines": {
|
3449 |
+
"node": ">=12"
|
3450 |
+
}
|
3451 |
+
},
|
3452 |
"node_modules/@pkgjs/parseargs": {
|
3453 |
"version": "0.11.0",
|
3454 |
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
|
|
|
4790 |
"integrity": "sha512-7i+zxXdPD0T4cKDuxCUXJ4wHcsJLwENa6Z3dCu8cfCK743OGy5Nu1RmAGqDPsoTDINVEcdXKRvR/zre+P2Ku1A==",
|
4791 |
"license": "MIT"
|
4792 |
},
|
4793 |
+
"node_modules/@types/raf": {
|
4794 |
+
"version": "3.4.3",
|
4795 |
+
"resolved": "https://registry.npmjs.org/@types/raf/-/raf-3.4.3.tgz",
|
4796 |
+
"integrity": "sha512-c4YAvMedbPZ5tEyxzQdMoOhhJ4RD3rngZIdwC2/qDN3d7JpEhB6fiBRKVY1lg5B7Wk+uPBjn5f39j1/2MY1oOw==",
|
4797 |
+
"license": "MIT",
|
4798 |
+
"optional": true
|
4799 |
+
},
|
4800 |
"node_modules/@types/range-parser": {
|
4801 |
"version": "1.2.7",
|
4802 |
"resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz",
|
|
|
5836 |
"node": ">= 4.0.0"
|
5837 |
}
|
5838 |
},
|
5839 |
+
"node_modules/atob": {
|
5840 |
+
"version": "2.1.2",
|
5841 |
+
"resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
|
5842 |
+
"integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
|
5843 |
+
"license": "(MIT OR Apache-2.0)",
|
5844 |
+
"bin": {
|
5845 |
+
"atob": "bin/atob.js"
|
5846 |
+
},
|
5847 |
+
"engines": {
|
5848 |
+
"node": ">= 4.5.0"
|
5849 |
+
}
|
5850 |
+
},
|
5851 |
"node_modules/autoprefixer": {
|
5852 |
"version": "10.4.20",
|
5853 |
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz",
|
|
|
6253 |
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
6254 |
"license": "MIT"
|
6255 |
},
|
6256 |
+
"node_modules/base64-arraybuffer": {
|
6257 |
+
"version": "1.0.2",
|
6258 |
+
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
|
6259 |
+
"integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==",
|
6260 |
+
"license": "MIT",
|
6261 |
+
"engines": {
|
6262 |
+
"node": ">= 0.6.0"
|
6263 |
+
}
|
6264 |
+
},
|
6265 |
"node_modules/batch": {
|
6266 |
"version": "0.6.1",
|
6267 |
"resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz",
|
|
|
6305 |
"url": "https://github.com/sponsors/sindresorhus"
|
6306 |
}
|
6307 |
},
|
6308 |
+
"node_modules/binary-search-bounds": {
|
6309 |
+
"version": "2.0.5",
|
6310 |
+
"resolved": "https://registry.npmjs.org/binary-search-bounds/-/binary-search-bounds-2.0.5.tgz",
|
6311 |
+
"integrity": "sha512-H0ea4Fd3lS1+sTEB2TgcLoK21lLhwEJzlQv3IN47pJS976Gx4zoWe0ak3q+uYh60ppQxg9F16Ri4tS1sfD4+jA==",
|
6312 |
+
"license": "MIT"
|
6313 |
+
},
|
6314 |
"node_modules/bluebird": {
|
6315 |
"version": "3.7.2",
|
6316 |
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
|
|
|
6481 |
"node-int64": "^0.4.0"
|
6482 |
}
|
6483 |
},
|
6484 |
+
"node_modules/btoa": {
|
6485 |
+
"version": "1.2.1",
|
6486 |
+
"resolved": "https://registry.npmjs.org/btoa/-/btoa-1.2.1.tgz",
|
6487 |
+
"integrity": "sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==",
|
6488 |
+
"license": "(MIT OR Apache-2.0)",
|
6489 |
+
"bin": {
|
6490 |
+
"btoa": "bin/btoa.js"
|
6491 |
+
},
|
6492 |
+
"engines": {
|
6493 |
+
"node": ">= 0.4.0"
|
6494 |
+
}
|
6495 |
+
},
|
6496 |
"node_modules/buffer-equal-constant-time": {
|
6497 |
"version": "1.0.1",
|
6498 |
"resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
|
|
|
6617 |
],
|
6618 |
"license": "CC-BY-4.0"
|
6619 |
},
|
6620 |
+
"node_modules/canvg": {
|
6621 |
+
"version": "3.0.10",
|
6622 |
+
"resolved": "https://registry.npmjs.org/canvg/-/canvg-3.0.10.tgz",
|
6623 |
+
"integrity": "sha512-qwR2FRNO9NlzTeKIPIKpnTY6fqwuYSequ8Ru8c0YkYU7U0oW+hLUvWadLvAu1Rl72OMNiFhoLu4f8eUjQ7l/+Q==",
|
6624 |
+
"license": "MIT",
|
6625 |
+
"optional": true,
|
6626 |
+
"dependencies": {
|
6627 |
+
"@babel/runtime": "^7.12.5",
|
6628 |
+
"@types/raf": "^3.4.0",
|
6629 |
+
"core-js": "^3.8.3",
|
6630 |
+
"raf": "^3.4.1",
|
6631 |
+
"regenerator-runtime": "^0.13.7",
|
6632 |
+
"rgbcolor": "^1.0.1",
|
6633 |
+
"stackblur-canvas": "^2.0.0",
|
6634 |
+
"svg-pathdata": "^6.0.3"
|
6635 |
+
},
|
6636 |
+
"engines": {
|
6637 |
+
"node": ">=10.0.0"
|
6638 |
+
}
|
6639 |
+
},
|
6640 |
+
"node_modules/canvg/node_modules/regenerator-runtime": {
|
6641 |
+
"version": "0.13.11",
|
6642 |
+
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
|
6643 |
+
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==",
|
6644 |
+
"license": "MIT",
|
6645 |
+
"optional": true
|
6646 |
+
},
|
6647 |
"node_modules/case-sensitive-paths-webpack-plugin": {
|
6648 |
"version": "2.4.0",
|
6649 |
"resolved": "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz",
|
|
|
7121 |
"postcss": "^8.4"
|
7122 |
}
|
7123 |
},
|
7124 |
+
"node_modules/css-line-break": {
|
7125 |
+
"version": "2.1.0",
|
7126 |
+
"resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz",
|
7127 |
+
"integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==",
|
7128 |
+
"license": "MIT",
|
7129 |
+
"dependencies": {
|
7130 |
+
"utrie": "^1.0.2"
|
7131 |
+
}
|
7132 |
+
},
|
7133 |
"node_modules/css-loader": {
|
7134 |
"version": "6.11.0",
|
7135 |
"resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.11.0.tgz",
|
|
|
7463 |
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
|
7464 |
"license": "MIT"
|
7465 |
},
|
7466 |
+
"node_modules/d3": {
|
7467 |
+
"version": "7.9.0",
|
7468 |
+
"resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz",
|
7469 |
+
"integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==",
|
7470 |
+
"license": "ISC",
|
7471 |
+
"dependencies": {
|
7472 |
+
"d3-array": "3",
|
7473 |
+
"d3-axis": "3",
|
7474 |
+
"d3-brush": "3",
|
7475 |
+
"d3-chord": "3",
|
7476 |
+
"d3-color": "3",
|
7477 |
+
"d3-contour": "4",
|
7478 |
+
"d3-delaunay": "6",
|
7479 |
+
"d3-dispatch": "3",
|
7480 |
+
"d3-drag": "3",
|
7481 |
+
"d3-dsv": "3",
|
7482 |
+
"d3-ease": "3",
|
7483 |
+
"d3-fetch": "3",
|
7484 |
+
"d3-force": "3",
|
7485 |
+
"d3-format": "3",
|
7486 |
+
"d3-geo": "3",
|
7487 |
+
"d3-hierarchy": "3",
|
7488 |
+
"d3-interpolate": "3",
|
7489 |
+
"d3-path": "3",
|
7490 |
+
"d3-polygon": "3",
|
7491 |
+
"d3-quadtree": "3",
|
7492 |
+
"d3-random": "3",
|
7493 |
+
"d3-scale": "4",
|
7494 |
+
"d3-scale-chromatic": "3",
|
7495 |
+
"d3-selection": "3",
|
7496 |
+
"d3-shape": "3",
|
7497 |
+
"d3-time": "3",
|
7498 |
+
"d3-time-format": "4",
|
7499 |
+
"d3-timer": "3",
|
7500 |
+
"d3-transition": "3",
|
7501 |
+
"d3-zoom": "3"
|
7502 |
+
},
|
7503 |
+
"engines": {
|
7504 |
+
"node": ">=12"
|
7505 |
+
}
|
7506 |
+
},
|
7507 |
+
"node_modules/d3-array": {
|
7508 |
+
"version": "3.2.4",
|
7509 |
+
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz",
|
7510 |
+
"integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==",
|
7511 |
+
"license": "ISC",
|
7512 |
+
"dependencies": {
|
7513 |
+
"internmap": "1 - 2"
|
7514 |
+
},
|
7515 |
+
"engines": {
|
7516 |
+
"node": ">=12"
|
7517 |
+
}
|
7518 |
+
},
|
7519 |
+
"node_modules/d3-axis": {
|
7520 |
+
"version": "3.0.0",
|
7521 |
+
"resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz",
|
7522 |
+
"integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==",
|
7523 |
+
"license": "ISC",
|
7524 |
+
"engines": {
|
7525 |
+
"node": ">=12"
|
7526 |
+
}
|
7527 |
+
},
|
7528 |
+
"node_modules/d3-brush": {
|
7529 |
+
"version": "3.0.0",
|
7530 |
+
"resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz",
|
7531 |
+
"integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==",
|
7532 |
+
"license": "ISC",
|
7533 |
+
"dependencies": {
|
7534 |
+
"d3-dispatch": "1 - 3",
|
7535 |
+
"d3-drag": "2 - 3",
|
7536 |
+
"d3-interpolate": "1 - 3",
|
7537 |
+
"d3-selection": "3",
|
7538 |
+
"d3-transition": "3"
|
7539 |
+
},
|
7540 |
+
"engines": {
|
7541 |
+
"node": ">=12"
|
7542 |
+
}
|
7543 |
+
},
|
7544 |
+
"node_modules/d3-chord": {
|
7545 |
+
"version": "3.0.1",
|
7546 |
+
"resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz",
|
7547 |
+
"integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==",
|
7548 |
+
"license": "ISC",
|
7549 |
+
"dependencies": {
|
7550 |
+
"d3-path": "1 - 3"
|
7551 |
+
},
|
7552 |
+
"engines": {
|
7553 |
+
"node": ">=12"
|
7554 |
+
}
|
7555 |
+
},
|
7556 |
+
"node_modules/d3-color": {
|
7557 |
+
"version": "3.1.0",
|
7558 |
+
"resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",
|
7559 |
+
"integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==",
|
7560 |
+
"license": "ISC",
|
7561 |
+
"engines": {
|
7562 |
+
"node": ">=12"
|
7563 |
+
}
|
7564 |
+
},
|
7565 |
+
"node_modules/d3-contour": {
|
7566 |
+
"version": "4.0.2",
|
7567 |
+
"resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz",
|
7568 |
+
"integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==",
|
7569 |
+
"license": "ISC",
|
7570 |
+
"dependencies": {
|
7571 |
+
"d3-array": "^3.2.0"
|
7572 |
+
},
|
7573 |
+
"engines": {
|
7574 |
+
"node": ">=12"
|
7575 |
+
}
|
7576 |
+
},
|
7577 |
+
"node_modules/d3-delaunay": {
|
7578 |
+
"version": "6.0.4",
|
7579 |
+
"resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz",
|
7580 |
+
"integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==",
|
7581 |
+
"license": "ISC",
|
7582 |
+
"dependencies": {
|
7583 |
+
"delaunator": "5"
|
7584 |
+
},
|
7585 |
+
"engines": {
|
7586 |
+
"node": ">=12"
|
7587 |
+
}
|
7588 |
+
},
|
7589 |
+
"node_modules/d3-dispatch": {
|
7590 |
+
"version": "3.0.1",
|
7591 |
+
"resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz",
|
7592 |
+
"integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==",
|
7593 |
+
"license": "ISC",
|
7594 |
+
"engines": {
|
7595 |
+
"node": ">=12"
|
7596 |
+
}
|
7597 |
+
},
|
7598 |
+
"node_modules/d3-drag": {
|
7599 |
+
"version": "3.0.0",
|
7600 |
+
"resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz",
|
7601 |
+
"integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==",
|
7602 |
+
"license": "ISC",
|
7603 |
+
"dependencies": {
|
7604 |
+
"d3-dispatch": "1 - 3",
|
7605 |
+
"d3-selection": "3"
|
7606 |
+
},
|
7607 |
+
"engines": {
|
7608 |
+
"node": ">=12"
|
7609 |
+
}
|
7610 |
+
},
|
7611 |
+
"node_modules/d3-dsv": {
|
7612 |
+
"version": "3.0.1",
|
7613 |
+
"resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz",
|
7614 |
+
"integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==",
|
7615 |
+
"license": "ISC",
|
7616 |
+
"dependencies": {
|
7617 |
+
"commander": "7",
|
7618 |
+
"iconv-lite": "0.6",
|
7619 |
+
"rw": "1"
|
7620 |
+
},
|
7621 |
+
"bin": {
|
7622 |
+
"csv2json": "bin/dsv2json.js",
|
7623 |
+
"csv2tsv": "bin/dsv2dsv.js",
|
7624 |
+
"dsv2dsv": "bin/dsv2dsv.js",
|
7625 |
+
"dsv2json": "bin/dsv2json.js",
|
7626 |
+
"json2csv": "bin/json2dsv.js",
|
7627 |
+
"json2dsv": "bin/json2dsv.js",
|
7628 |
+
"json2tsv": "bin/json2dsv.js",
|
7629 |
+
"tsv2csv": "bin/dsv2dsv.js",
|
7630 |
+
"tsv2json": "bin/dsv2json.js"
|
7631 |
+
},
|
7632 |
+
"engines": {
|
7633 |
+
"node": ">=12"
|
7634 |
+
}
|
7635 |
+
},
|
7636 |
+
"node_modules/d3-dsv/node_modules/commander": {
|
7637 |
+
"version": "7.2.0",
|
7638 |
+
"resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
|
7639 |
+
"integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
|
7640 |
+
"license": "MIT",
|
7641 |
+
"engines": {
|
7642 |
+
"node": ">= 10"
|
7643 |
+
}
|
7644 |
+
},
|
7645 |
+
"node_modules/d3-ease": {
|
7646 |
+
"version": "3.0.1",
|
7647 |
+
"resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz",
|
7648 |
+
"integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==",
|
7649 |
+
"license": "BSD-3-Clause",
|
7650 |
+
"engines": {
|
7651 |
+
"node": ">=12"
|
7652 |
+
}
|
7653 |
+
},
|
7654 |
+
"node_modules/d3-fetch": {
|
7655 |
+
"version": "3.0.1",
|
7656 |
+
"resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz",
|
7657 |
+
"integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==",
|
7658 |
+
"license": "ISC",
|
7659 |
+
"dependencies": {
|
7660 |
+
"d3-dsv": "1 - 3"
|
7661 |
+
},
|
7662 |
+
"engines": {
|
7663 |
+
"node": ">=12"
|
7664 |
+
}
|
7665 |
+
},
|
7666 |
+
"node_modules/d3-force": {
|
7667 |
+
"version": "3.0.0",
|
7668 |
+
"resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz",
|
7669 |
+
"integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==",
|
7670 |
+
"license": "ISC",
|
7671 |
+
"dependencies": {
|
7672 |
+
"d3-dispatch": "1 - 3",
|
7673 |
+
"d3-quadtree": "1 - 3",
|
7674 |
+
"d3-timer": "1 - 3"
|
7675 |
+
},
|
7676 |
+
"engines": {
|
7677 |
+
"node": ">=12"
|
7678 |
+
}
|
7679 |
+
},
|
7680 |
+
"node_modules/d3-format": {
|
7681 |
+
"version": "3.1.0",
|
7682 |
+
"resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz",
|
7683 |
+
"integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==",
|
7684 |
+
"license": "ISC",
|
7685 |
+
"engines": {
|
7686 |
+
"node": ">=12"
|
7687 |
+
}
|
7688 |
+
},
|
7689 |
+
"node_modules/d3-geo": {
|
7690 |
+
"version": "3.1.1",
|
7691 |
+
"resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz",
|
7692 |
+
"integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==",
|
7693 |
+
"license": "ISC",
|
7694 |
+
"dependencies": {
|
7695 |
+
"d3-array": "2.5.0 - 3"
|
7696 |
+
},
|
7697 |
+
"engines": {
|
7698 |
+
"node": ">=12"
|
7699 |
+
}
|
7700 |
+
},
|
7701 |
+
"node_modules/d3-hierarchy": {
|
7702 |
+
"version": "3.1.2",
|
7703 |
+
"resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz",
|
7704 |
+
"integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==",
|
7705 |
+
"license": "ISC",
|
7706 |
+
"engines": {
|
7707 |
+
"node": ">=12"
|
7708 |
+
}
|
7709 |
+
},
|
7710 |
+
"node_modules/d3-interpolate": {
|
7711 |
+
"version": "3.0.1",
|
7712 |
+
"resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
|
7713 |
+
"integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
|
7714 |
+
"license": "ISC",
|
7715 |
+
"dependencies": {
|
7716 |
+
"d3-color": "1 - 3"
|
7717 |
+
},
|
7718 |
+
"engines": {
|
7719 |
+
"node": ">=12"
|
7720 |
+
}
|
7721 |
+
},
|
7722 |
+
"node_modules/d3-path": {
|
7723 |
+
"version": "3.1.0",
|
7724 |
+
"resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz",
|
7725 |
+
"integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==",
|
7726 |
+
"license": "ISC",
|
7727 |
+
"engines": {
|
7728 |
+
"node": ">=12"
|
7729 |
+
}
|
7730 |
+
},
|
7731 |
+
"node_modules/d3-polygon": {
|
7732 |
+
"version": "3.0.1",
|
7733 |
+
"resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz",
|
7734 |
+
"integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==",
|
7735 |
+
"license": "ISC",
|
7736 |
+
"engines": {
|
7737 |
+
"node": ">=12"
|
7738 |
+
}
|
7739 |
+
},
|
7740 |
+
"node_modules/d3-quadtree": {
|
7741 |
+
"version": "3.0.1",
|
7742 |
+
"resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz",
|
7743 |
+
"integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==",
|
7744 |
+
"license": "ISC",
|
7745 |
+
"engines": {
|
7746 |
+
"node": ">=12"
|
7747 |
+
}
|
7748 |
+
},
|
7749 |
+
"node_modules/d3-random": {
|
7750 |
+
"version": "3.0.1",
|
7751 |
+
"resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz",
|
7752 |
+
"integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==",
|
7753 |
+
"license": "ISC",
|
7754 |
+
"engines": {
|
7755 |
+
"node": ">=12"
|
7756 |
+
}
|
7757 |
+
},
|
7758 |
+
"node_modules/d3-scale": {
|
7759 |
+
"version": "4.0.2",
|
7760 |
+
"resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz",
|
7761 |
+
"integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==",
|
7762 |
+
"license": "ISC",
|
7763 |
+
"dependencies": {
|
7764 |
+
"d3-array": "2.10.0 - 3",
|
7765 |
+
"d3-format": "1 - 3",
|
7766 |
+
"d3-interpolate": "1.2.0 - 3",
|
7767 |
+
"d3-time": "2.1.1 - 3",
|
7768 |
+
"d3-time-format": "2 - 4"
|
7769 |
+
},
|
7770 |
+
"engines": {
|
7771 |
+
"node": ">=12"
|
7772 |
+
}
|
7773 |
+
},
|
7774 |
+
"node_modules/d3-scale-chromatic": {
|
7775 |
+
"version": "3.1.0",
|
7776 |
+
"resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz",
|
7777 |
+
"integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==",
|
7778 |
+
"license": "ISC",
|
7779 |
+
"dependencies": {
|
7780 |
+
"d3-color": "1 - 3",
|
7781 |
+
"d3-interpolate": "1 - 3"
|
7782 |
+
},
|
7783 |
+
"engines": {
|
7784 |
+
"node": ">=12"
|
7785 |
+
}
|
7786 |
+
},
|
7787 |
+
"node_modules/d3-selection": {
|
7788 |
+
"version": "3.0.0",
|
7789 |
+
"resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
|
7790 |
+
"integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
|
7791 |
+
"license": "ISC",
|
7792 |
+
"engines": {
|
7793 |
+
"node": ">=12"
|
7794 |
+
}
|
7795 |
+
},
|
7796 |
+
"node_modules/d3-shape": {
|
7797 |
+
"version": "3.2.0",
|
7798 |
+
"resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz",
|
7799 |
+
"integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==",
|
7800 |
+
"license": "ISC",
|
7801 |
+
"dependencies": {
|
7802 |
+
"d3-path": "^3.1.0"
|
7803 |
+
},
|
7804 |
+
"engines": {
|
7805 |
+
"node": ">=12"
|
7806 |
+
}
|
7807 |
+
},
|
7808 |
+
"node_modules/d3-time": {
|
7809 |
+
"version": "3.1.0",
|
7810 |
+
"resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz",
|
7811 |
+
"integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==",
|
7812 |
+
"license": "ISC",
|
7813 |
+
"dependencies": {
|
7814 |
+
"d3-array": "2 - 3"
|
7815 |
+
},
|
7816 |
+
"engines": {
|
7817 |
+
"node": ">=12"
|
7818 |
+
}
|
7819 |
+
},
|
7820 |
+
"node_modules/d3-time-format": {
|
7821 |
+
"version": "4.1.0",
|
7822 |
+
"resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz",
|
7823 |
+
"integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==",
|
7824 |
+
"license": "ISC",
|
7825 |
+
"dependencies": {
|
7826 |
+
"d3-time": "1 - 3"
|
7827 |
+
},
|
7828 |
+
"engines": {
|
7829 |
+
"node": ">=12"
|
7830 |
+
}
|
7831 |
+
},
|
7832 |
+
"node_modules/d3-timer": {
|
7833 |
+
"version": "3.0.1",
|
7834 |
+
"resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz",
|
7835 |
+
"integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==",
|
7836 |
+
"license": "ISC",
|
7837 |
+
"engines": {
|
7838 |
+
"node": ">=12"
|
7839 |
+
}
|
7840 |
+
},
|
7841 |
+
"node_modules/d3-transition": {
|
7842 |
+
"version": "3.0.1",
|
7843 |
+
"resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz",
|
7844 |
+
"integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==",
|
7845 |
+
"license": "ISC",
|
7846 |
+
"dependencies": {
|
7847 |
+
"d3-color": "1 - 3",
|
7848 |
+
"d3-dispatch": "1 - 3",
|
7849 |
+
"d3-ease": "1 - 3",
|
7850 |
+
"d3-interpolate": "1 - 3",
|
7851 |
+
"d3-timer": "1 - 3"
|
7852 |
+
},
|
7853 |
+
"engines": {
|
7854 |
+
"node": ">=12"
|
7855 |
+
},
|
7856 |
+
"peerDependencies": {
|
7857 |
+
"d3-selection": "2 - 3"
|
7858 |
+
}
|
7859 |
+
},
|
7860 |
+
"node_modules/d3-zoom": {
|
7861 |
+
"version": "3.0.0",
|
7862 |
+
"resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz",
|
7863 |
+
"integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==",
|
7864 |
+
"license": "ISC",
|
7865 |
+
"dependencies": {
|
7866 |
+
"d3-dispatch": "1 - 3",
|
7867 |
+
"d3-drag": "2 - 3",
|
7868 |
+
"d3-interpolate": "1 - 3",
|
7869 |
+
"d3-selection": "2 - 3",
|
7870 |
+
"d3-transition": "2 - 3"
|
7871 |
+
},
|
7872 |
+
"engines": {
|
7873 |
+
"node": ">=12"
|
7874 |
+
}
|
7875 |
+
},
|
7876 |
"node_modules/damerau-levenshtein": {
|
7877 |
"version": "1.0.8",
|
7878 |
"resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz",
|
|
|
8075 |
"url": "https://github.com/sponsors/ljharb"
|
8076 |
}
|
8077 |
},
|
8078 |
+
"node_modules/delaunator": {
|
8079 |
+
"version": "5.0.1",
|
8080 |
+
"resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz",
|
8081 |
+
"integrity": "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==",
|
8082 |
+
"license": "ISC",
|
8083 |
+
"dependencies": {
|
8084 |
+
"robust-predicates": "^3.0.2"
|
8085 |
+
}
|
8086 |
+
},
|
8087 |
"node_modules/delayed-stream": {
|
8088 |
"version": "1.0.0",
|
8089 |
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
|
|
8313 |
"url": "https://github.com/fb55/domhandler?sponsor=1"
|
8314 |
}
|
8315 |
},
|
8316 |
+
"node_modules/dompurify": {
|
8317 |
+
"version": "2.5.7",
|
8318 |
+
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.5.7.tgz",
|
8319 |
+
"integrity": "sha512-2q4bEI+coQM8f5ez7kt2xclg1XsecaV9ASJk/54vwlfRRNQfDqJz2pzQ8t0Ix/ToBpXlVjrRIx7pFC/o8itG2Q==",
|
8320 |
+
"license": "(MPL-2.0 OR Apache-2.0)",
|
8321 |
+
"optional": true
|
8322 |
+
},
|
8323 |
"node_modules/domutils": {
|
8324 |
"version": "2.8.0",
|
8325 |
"resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
|
|
|
8673 |
"url": "https://github.com/sponsors/ljharb"
|
8674 |
}
|
8675 |
},
|
8676 |
+
"node_modules/es6-promise": {
|
8677 |
+
"version": "4.2.8",
|
8678 |
+
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz",
|
8679 |
+
"integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==",
|
8680 |
+
"license": "MIT"
|
8681 |
+
},
|
8682 |
"node_modules/escalade": {
|
8683 |
"version": "3.2.0",
|
8684 |
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
|
|
|
9662 |
"bser": "2.1.1"
|
9663 |
}
|
9664 |
},
|
9665 |
+
"node_modules/fflate": {
|
9666 |
+
"version": "0.8.2",
|
9667 |
+
"resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz",
|
9668 |
+
"integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==",
|
9669 |
+
"license": "MIT"
|
9670 |
+
},
|
9671 |
"node_modules/file-entry-cache": {
|
9672 |
"version": "6.0.1",
|
9673 |
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
|
|
|
10685 |
}
|
10686 |
}
|
10687 |
},
|
10688 |
+
"node_modules/html2canvas": {
|
10689 |
+
"version": "1.4.1",
|
10690 |
+
"resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz",
|
10691 |
+
"integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==",
|
10692 |
+
"license": "MIT",
|
10693 |
+
"dependencies": {
|
10694 |
+
"css-line-break": "^2.1.0",
|
10695 |
+
"text-segmentation": "^1.0.3"
|
10696 |
+
},
|
10697 |
+
"engines": {
|
10698 |
+
"node": ">=8.0.0"
|
10699 |
+
}
|
10700 |
+
},
|
10701 |
+
"node_modules/html2pdf.js": {
|
10702 |
+
"version": "0.10.2",
|
10703 |
+
"resolved": "https://registry.npmjs.org/html2pdf.js/-/html2pdf.js-0.10.2.tgz",
|
10704 |
+
"integrity": "sha512-WyHVeMb18Bp7vYTmBv1GVsThH//K7SRfHdSdhHPkl4JvyQarNQXnailkYn0QUbRRmnN5rdbbmSIGEsPZtzPy2Q==",
|
10705 |
+
"license": "MIT",
|
10706 |
+
"dependencies": {
|
10707 |
+
"es6-promise": "^4.2.5",
|
10708 |
+
"html2canvas": "^1.0.0",
|
10709 |
+
"jspdf": "^2.3.1"
|
10710 |
+
}
|
10711 |
+
},
|
10712 |
"node_modules/htmlparser2": {
|
10713 |
"version": "6.1.0",
|
10714 |
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz",
|
|
|
10990 |
"node": ">= 0.4"
|
10991 |
}
|
10992 |
},
|
10993 |
+
"node_modules/internmap": {
|
10994 |
+
"version": "2.0.3",
|
10995 |
+
"resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",
|
10996 |
+
"integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==",
|
10997 |
+
"license": "ISC",
|
10998 |
+
"engines": {
|
10999 |
+
"node": ">=12"
|
11000 |
+
}
|
11001 |
+
},
|
11002 |
+
"node_modules/interval-tree-1d": {
|
11003 |
+
"version": "1.0.4",
|
11004 |
+
"resolved": "https://registry.npmjs.org/interval-tree-1d/-/interval-tree-1d-1.0.4.tgz",
|
11005 |
+
"integrity": "sha512-wY8QJH+6wNI0uh4pDQzMvl+478Qh7Rl4qLmqiluxALlNvl+I+o5x38Pw3/z7mDPTPS1dQalZJXsmbvxx5gclhQ==",
|
11006 |
+
"license": "MIT",
|
11007 |
+
"dependencies": {
|
11008 |
+
"binary-search-bounds": "^2.0.0"
|
11009 |
+
}
|
11010 |
+
},
|
11011 |
"node_modules/invariant": {
|
11012 |
"version": "2.2.4",
|
11013 |
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
|
|
|
11535 |
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
|
11536 |
"license": "ISC"
|
11537 |
},
|
11538 |
+
"node_modules/isoformat": {
|
11539 |
+
"version": "0.2.1",
|
11540 |
+
"resolved": "https://registry.npmjs.org/isoformat/-/isoformat-0.2.1.tgz",
|
11541 |
+
"integrity": "sha512-tFLRAygk9NqrRPhJSnNGh7g7oaVWDwR0wKh/GM2LgmPa50Eg4UfyaCO4I8k6EqJHl1/uh2RAD6g06n5ygEnrjQ==",
|
11542 |
+
"license": "ISC"
|
11543 |
+
},
|
11544 |
"node_modules/istanbul-lib-coverage": {
|
11545 |
"version": "3.2.2",
|
11546 |
"resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz",
|
|
|
14009 |
"npm": ">=6"
|
14010 |
}
|
14011 |
},
|
14012 |
+
"node_modules/jspdf": {
|
14013 |
+
"version": "2.5.2",
|
14014 |
+
"resolved": "https://registry.npmjs.org/jspdf/-/jspdf-2.5.2.tgz",
|
14015 |
+
"integrity": "sha512-myeX9c+p7znDWPk0eTrujCzNjT+CXdXyk7YmJq5nD5V7uLLKmSXnlQ/Jn/kuo3X09Op70Apm0rQSnFWyGK8uEQ==",
|
14016 |
+
"license": "MIT",
|
14017 |
+
"dependencies": {
|
14018 |
+
"@babel/runtime": "^7.23.2",
|
14019 |
+
"atob": "^2.1.2",
|
14020 |
+
"btoa": "^1.2.1",
|
14021 |
+
"fflate": "^0.8.1"
|
14022 |
+
},
|
14023 |
+
"optionalDependencies": {
|
14024 |
+
"canvg": "^3.0.6",
|
14025 |
+
"core-js": "^3.6.0",
|
14026 |
+
"dompurify": "^2.5.4",
|
14027 |
+
"html2canvas": "^1.0.0-rc.5"
|
14028 |
+
}
|
14029 |
+
},
|
14030 |
"node_modules/jsx-ast-utils": {
|
14031 |
"version": "3.3.5",
|
14032 |
"resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz",
|
|
|
17604 |
"node": ">=0.10.0"
|
17605 |
}
|
17606 |
},
|
17607 |
+
"node_modules/rgbcolor": {
|
17608 |
+
"version": "1.0.1",
|
17609 |
+
"resolved": "https://registry.npmjs.org/rgbcolor/-/rgbcolor-1.0.1.tgz",
|
17610 |
+
"integrity": "sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw==",
|
17611 |
+
"license": "MIT OR SEE LICENSE IN FEEL-FREE.md",
|
17612 |
+
"optional": true,
|
17613 |
+
"engines": {
|
17614 |
+
"node": ">= 0.8.15"
|
17615 |
+
}
|
17616 |
+
},
|
17617 |
"node_modules/rimraf": {
|
17618 |
"version": "3.0.2",
|
17619 |
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
|
|
17630 |
"url": "https://github.com/sponsors/isaacs"
|
17631 |
}
|
17632 |
},
|
17633 |
+
"node_modules/robust-predicates": {
|
17634 |
+
"version": "3.0.2",
|
17635 |
+
"resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz",
|
17636 |
+
"integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==",
|
17637 |
+
"license": "Unlicense"
|
17638 |
+
},
|
17639 |
"node_modules/rollup": {
|
17640 |
"version": "2.79.2",
|
17641 |
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.2.tgz",
|
|
|
17734 |
"queue-microtask": "^1.2.2"
|
17735 |
}
|
17736 |
},
|
17737 |
+
"node_modules/rw": {
|
17738 |
+
"version": "1.3.3",
|
17739 |
+
"resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz",
|
17740 |
+
"integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==",
|
17741 |
+
"license": "BSD-3-Clause"
|
17742 |
+
},
|
17743 |
"node_modules/safe-array-concat": {
|
17744 |
"version": "1.1.2",
|
17745 |
"resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz",
|
|
|
18359 |
"node": ">=8"
|
18360 |
}
|
18361 |
},
|
18362 |
+
"node_modules/stackblur-canvas": {
|
18363 |
+
"version": "2.7.0",
|
18364 |
+
"resolved": "https://registry.npmjs.org/stackblur-canvas/-/stackblur-canvas-2.7.0.tgz",
|
18365 |
+
"integrity": "sha512-yf7OENo23AGJhBriGx0QivY5JP6Y1HbrrDI6WLt6C5auYZXlQrheoY8hD4ibekFKz1HOfE48Ww8kMWMnJD/zcQ==",
|
18366 |
+
"license": "MIT",
|
18367 |
+
"optional": true,
|
18368 |
+
"engines": {
|
18369 |
+
"node": ">=0.1.14"
|
18370 |
+
}
|
18371 |
+
},
|
18372 |
"node_modules/stackframe": {
|
18373 |
"version": "1.3.4",
|
18374 |
"resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz",
|
|
|
18925 |
"integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==",
|
18926 |
"license": "MIT"
|
18927 |
},
|
18928 |
+
"node_modules/svg-pathdata": {
|
18929 |
+
"version": "6.0.3",
|
18930 |
+
"resolved": "https://registry.npmjs.org/svg-pathdata/-/svg-pathdata-6.0.3.tgz",
|
18931 |
+
"integrity": "sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw==",
|
18932 |
+
"license": "MIT",
|
18933 |
+
"optional": true,
|
18934 |
+
"engines": {
|
18935 |
+
"node": ">=12.0.0"
|
18936 |
+
}
|
18937 |
+
},
|
18938 |
"node_modules/svgo": {
|
18939 |
"version": "1.3.2",
|
18940 |
"resolved": "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz",
|
|
|
19219 |
"node": ">=8"
|
19220 |
}
|
19221 |
},
|
19222 |
+
"node_modules/text-segmentation": {
|
19223 |
+
"version": "1.0.3",
|
19224 |
+
"resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz",
|
19225 |
+
"integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==",
|
19226 |
+
"license": "MIT",
|
19227 |
+
"dependencies": {
|
19228 |
+
"utrie": "^1.0.2"
|
19229 |
+
}
|
19230 |
+
},
|
19231 |
"node_modules/text-table": {
|
19232 |
"version": "0.2.0",
|
19233 |
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
|
|
|
19766 |
"node": ">= 0.4.0"
|
19767 |
}
|
19768 |
},
|
19769 |
+
"node_modules/utrie": {
|
19770 |
+
"version": "1.0.2",
|
19771 |
+
"resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz",
|
19772 |
+
"integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==",
|
19773 |
+
"license": "MIT",
|
19774 |
+
"dependencies": {
|
19775 |
+
"base64-arraybuffer": "^1.0.2"
|
19776 |
+
}
|
19777 |
+
},
|
19778 |
"node_modules/uuid": {
|
19779 |
"version": "8.3.2",
|
19780 |
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
|