Spaces:
Sleeping
Sleeping
feat: add menu item and module and migrations
Browse files- backend/package-lock.json +7 -0
- backend/package.json +1 -0
- backend/src/app.controller.ts +2 -0
- backend/src/app.module.ts +2 -0
- backend/src/entities/branch-menu.entity.ts +35 -0
- backend/src/entities/branch.entity.ts +5 -0
- backend/src/entities/menu-item.entity.ts +37 -0
- backend/src/main.ts +2 -0
- backend/src/migrations/1729703705445-AddBranchMenus.ts +20 -0
- backend/src/modules/menu-item/dto/create-menu-item.dto.ts +23 -0
- backend/src/modules/menu-item/dto/update-menu-item.dto.ts +23 -0
- backend/src/modules/menu-item/menu-item.controller.ts +47 -0
- backend/src/modules/menu-item/menu-item.module.ts +9 -0
- backend/src/modules/menu-item/menu-item.service.ts +44 -0
backend/package-lock.json
CHANGED
@@ -17,6 +17,7 @@
|
|
17 |
"@nestjs/platform-express": "^10.0.0",
|
18 |
"@nestjs/typeorm": "^10.0.2",
|
19 |
"bcrypt": "^5.1.1",
|
|
|
20 |
"class-validator": "^0.14.1",
|
21 |
"dotenv": "^16.4.5",
|
22 |
"mysql2": "^3.11.3",
|
@@ -3394,6 +3395,12 @@
|
|
3394 |
"integrity": "sha512-cuSVIHi9/9E/+821Qjdvngor+xpnlwnuwIyZOaLmHBVdXL+gP+I6QQB9VkO7RI77YIcTV+S1W9AreJ5eN63JBA==",
|
3395 |
"dev": true
|
3396 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
3397 |
"node_modules/class-validator": {
|
3398 |
"version": "0.14.1",
|
3399 |
"resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.14.1.tgz",
|
|
|
17 |
"@nestjs/platform-express": "^10.0.0",
|
18 |
"@nestjs/typeorm": "^10.0.2",
|
19 |
"bcrypt": "^5.1.1",
|
20 |
+
"class-transformer": "^0.5.1",
|
21 |
"class-validator": "^0.14.1",
|
22 |
"dotenv": "^16.4.5",
|
23 |
"mysql2": "^3.11.3",
|
|
|
3395 |
"integrity": "sha512-cuSVIHi9/9E/+821Qjdvngor+xpnlwnuwIyZOaLmHBVdXL+gP+I6QQB9VkO7RI77YIcTV+S1W9AreJ5eN63JBA==",
|
3396 |
"dev": true
|
3397 |
},
|
3398 |
+
"node_modules/class-transformer": {
|
3399 |
+
"version": "0.5.1",
|
3400 |
+
"resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz",
|
3401 |
+
"integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==",
|
3402 |
+
"license": "MIT"
|
3403 |
+
},
|
3404 |
"node_modules/class-validator": {
|
3405 |
"version": "0.14.1",
|
3406 |
"resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.14.1.tgz",
|
backend/package.json
CHANGED
@@ -33,6 +33,7 @@
|
|
33 |
"@nestjs/platform-express": "^10.0.0",
|
34 |
"@nestjs/typeorm": "^10.0.2",
|
35 |
"bcrypt": "^5.1.1",
|
|
|
36 |
"class-validator": "^0.14.1",
|
37 |
"dotenv": "^16.4.5",
|
38 |
"mysql2": "^3.11.3",
|
|
|
33 |
"@nestjs/platform-express": "^10.0.0",
|
34 |
"@nestjs/typeorm": "^10.0.2",
|
35 |
"bcrypt": "^5.1.1",
|
36 |
+
"class-transformer": "^0.5.1",
|
37 |
"class-validator": "^0.14.1",
|
38 |
"dotenv": "^16.4.5",
|
39 |
"mysql2": "^3.11.3",
|
backend/src/app.controller.ts
CHANGED
@@ -1,10 +1,12 @@
|
|
1 |
import { Controller, Get } from '@nestjs/common';
|
2 |
import { AppService } from './app.service.js';
|
|
|
3 |
|
4 |
@Controller()
|
5 |
export class AppController {
|
6 |
constructor(private readonly appService: AppService) {}
|
7 |
|
|
|
8 |
@Get()
|
9 |
getHello(): string {
|
10 |
return this.appService.getHello();
|
|
|
1 |
import { Controller, Get } from '@nestjs/common';
|
2 |
import { AppService } from './app.service.js';
|
3 |
+
import { Public } from './modules/authentication/authentication.decorator.js';
|
4 |
|
5 |
@Controller()
|
6 |
export class AppController {
|
7 |
constructor(private readonly appService: AppService) {}
|
8 |
|
9 |
+
@Public()
|
10 |
@Get()
|
11 |
getHello(): string {
|
12 |
return this.appService.getHello();
|
backend/src/app.module.ts
CHANGED
@@ -10,6 +10,7 @@ import { DeviceInfoMiddleware } from './common/middlewares/device-info.middlewar
|
|
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 |
@Module({
|
14 |
imports: [
|
15 |
ConfigModule.forRoot({
|
@@ -23,6 +24,7 @@ import { AuthenticationModule } from './modules/authentication/authentication.mo
|
|
23 |
UserModule,
|
24 |
BranchModule,
|
25 |
AuthenticationModule,
|
|
|
26 |
],
|
27 |
controllers: [AppController],
|
28 |
providers: [AppService],
|
|
|
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 |
@Module({
|
15 |
imports: [
|
16 |
ConfigModule.forRoot({
|
|
|
24 |
UserModule,
|
25 |
BranchModule,
|
26 |
AuthenticationModule,
|
27 |
+
MenuItemModule,
|
28 |
],
|
29 |
controllers: [AppController],
|
30 |
providers: [AppService],
|
backend/src/entities/branch-menu.entity.ts
ADDED
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import {
|
2 |
+
BaseEntity,
|
3 |
+
Column,
|
4 |
+
Entity,
|
5 |
+
ManyToOne,
|
6 |
+
OneToMany,
|
7 |
+
PrimaryGeneratedColumn,
|
8 |
+
Relation,
|
9 |
+
} from 'typeorm';
|
10 |
+
import { BranchEntity } from './branch.entity.js';
|
11 |
+
import { MenuItemEntity } from './menu-item.entity.js';
|
12 |
+
|
13 |
+
@Entity('branch_menu')
|
14 |
+
export class BranchMenuEntity extends BaseEntity {
|
15 |
+
@PrimaryGeneratedColumn('uuid')
|
16 |
+
id: string;
|
17 |
+
|
18 |
+
@Column()
|
19 |
+
branch_id: string;
|
20 |
+
|
21 |
+
@Column()
|
22 |
+
menu_id: string;
|
23 |
+
|
24 |
+
@Column()
|
25 |
+
description: string;
|
26 |
+
|
27 |
+
@Column()
|
28 |
+
is_open: boolean;
|
29 |
+
|
30 |
+
@ManyToOne(() => BranchEntity, (a) => a.menu_items)
|
31 |
+
branch: Relation<BranchEntity>;
|
32 |
+
|
33 |
+
@ManyToOne(() => MenuItemEntity, (a) => a.branch_menus)
|
34 |
+
menu_item: Relation<MenuItemEntity>;
|
35 |
+
}
|
backend/src/entities/branch.entity.ts
CHANGED
@@ -3,10 +3,12 @@ import {
|
|
3 |
Column,
|
4 |
Entity,
|
5 |
ManyToOne,
|
|
|
6 |
PrimaryGeneratedColumn,
|
7 |
Relation,
|
8 |
} from 'typeorm';
|
9 |
import { UserEntity } from './user.entity.js';
|
|
|
10 |
|
11 |
@Entity('branches')
|
12 |
export class BranchEntity extends BaseEntity {
|
@@ -25,6 +27,9 @@ export class BranchEntity extends BaseEntity {
|
|
25 |
@ManyToOne(() => UserEntity, (user) => user.branches)
|
26 |
owner: Relation<UserEntity>;
|
27 |
|
|
|
|
|
|
|
28 |
@Column({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' })
|
29 |
create_at: Date;
|
30 |
}
|
|
|
3 |
Column,
|
4 |
Entity,
|
5 |
ManyToOne,
|
6 |
+
OneToMany,
|
7 |
PrimaryGeneratedColumn,
|
8 |
Relation,
|
9 |
} from 'typeorm';
|
10 |
import { UserEntity } from './user.entity.js';
|
11 |
+
import { BranchMenuEntity } from './branch-menu.entity.js';
|
12 |
|
13 |
@Entity('branches')
|
14 |
export class BranchEntity extends BaseEntity {
|
|
|
27 |
@ManyToOne(() => UserEntity, (user) => user.branches)
|
28 |
owner: Relation<UserEntity>;
|
29 |
|
30 |
+
@OneToMany(() => BranchMenuEntity, (a) => a.branch)
|
31 |
+
menu_items: Relation<BranchMenuEntity>[];
|
32 |
+
|
33 |
@Column({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' })
|
34 |
create_at: Date;
|
35 |
}
|
backend/src/entities/menu-item.entity.ts
ADDED
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import {
|
2 |
+
BaseEntity,
|
3 |
+
Column,
|
4 |
+
Entity,
|
5 |
+
ManyToOne,
|
6 |
+
OneToMany,
|
7 |
+
PrimaryColumn,
|
8 |
+
Relation,
|
9 |
+
} from 'typeorm';
|
10 |
+
import { BranchMenuEntity } from './branch-menu.entity.js';
|
11 |
+
|
12 |
+
@Entity('menu_items')
|
13 |
+
export class MenuItemEntity extends BaseEntity {
|
14 |
+
@PrimaryColumn()
|
15 |
+
id: string;
|
16 |
+
|
17 |
+
@Column()
|
18 |
+
item_name: string;
|
19 |
+
|
20 |
+
@Column({ nullable: true })
|
21 |
+
image_url: string;
|
22 |
+
|
23 |
+
@Column({ nullable: true })
|
24 |
+
item_group_id: string;
|
25 |
+
|
26 |
+
@Column()
|
27 |
+
description: string;
|
28 |
+
|
29 |
+
@Column()
|
30 |
+
price: number;
|
31 |
+
|
32 |
+
@OneToMany(() => BranchMenuEntity, (a) => a.menu_item)
|
33 |
+
branch_menus: Relation<BranchMenuEntity>[];
|
34 |
+
|
35 |
+
@Column({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' })
|
36 |
+
create_at: Date;
|
37 |
+
}
|
backend/src/main.ts
CHANGED
@@ -1,8 +1,10 @@
|
|
1 |
import { NestFactory } from '@nestjs/core';
|
2 |
import { AppModule } from './app.module.js';
|
|
|
3 |
|
4 |
async function bootstrap() {
|
5 |
const app = await NestFactory.create(AppModule, { cors: true });
|
|
|
6 |
await app.listen(3000);
|
7 |
}
|
8 |
bootstrap();
|
|
|
1 |
import { NestFactory } from '@nestjs/core';
|
2 |
import { AppModule } from './app.module.js';
|
3 |
+
import { ValidationPipe } from '@nestjs/common';
|
4 |
|
5 |
async function bootstrap() {
|
6 |
const app = await NestFactory.create(AppModule, { cors: true });
|
7 |
+
app.useGlobalPipes(new ValidationPipe());
|
8 |
await app.listen(3000);
|
9 |
}
|
10 |
bootstrap();
|
backend/src/migrations/1729703705445-AddBranchMenus.ts
ADDED
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { MigrationInterface, QueryRunner } from "typeorm";
|
2 |
+
|
3 |
+
export class AddBranchMenus1729703705445 implements MigrationInterface {
|
4 |
+
name = 'AddBranchMenus1729703705445'
|
5 |
+
|
6 |
+
public async up(queryRunner: QueryRunner): Promise<void> {
|
7 |
+
await queryRunner.query(`CREATE TABLE "menu_items" ("id" character varying NOT NULL, "item_name" character varying NOT NULL, "image_url" character varying, "item_group_id" character varying, "description" character varying NOT NULL, "price" integer NOT NULL, "create_at" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "PK_57e6188f929e5dc6919168620c8" PRIMARY KEY ("id"))`);
|
8 |
+
await queryRunner.query(`CREATE TABLE "branch_menu" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "branch_id" character varying NOT NULL, "menu_id" character varying NOT NULL, "description" character varying NOT NULL, "is_open" boolean NOT NULL, "branchId" uuid, "menuItemId" character varying, CONSTRAINT "PK_977becffe98bbc626a56031b9e7" PRIMARY KEY ("id"))`);
|
9 |
+
await queryRunner.query(`ALTER TABLE "branch_menu" ADD CONSTRAINT "FK_e0c721a124fa03ea4cef6f28f42" FOREIGN KEY ("branchId") REFERENCES "branches"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
|
10 |
+
await queryRunner.query(`ALTER TABLE "branch_menu" ADD CONSTRAINT "FK_cbfb42df5887653593974e3e285" FOREIGN KEY ("menuItemId") REFERENCES "menu_items"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
|
11 |
+
}
|
12 |
+
|
13 |
+
public async down(queryRunner: QueryRunner): Promise<void> {
|
14 |
+
await queryRunner.query(`ALTER TABLE "branch_menu" DROP CONSTRAINT "FK_cbfb42df5887653593974e3e285"`);
|
15 |
+
await queryRunner.query(`ALTER TABLE "branch_menu" DROP CONSTRAINT "FK_e0c721a124fa03ea4cef6f28f42"`);
|
16 |
+
await queryRunner.query(`DROP TABLE "branch_menu"`);
|
17 |
+
await queryRunner.query(`DROP TABLE "menu_items"`);
|
18 |
+
}
|
19 |
+
|
20 |
+
}
|
backend/src/modules/menu-item/dto/create-menu-item.dto.ts
ADDED
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { IsNumber, IsOptional, IsString, IsUrl } from 'class-validator';
|
2 |
+
|
3 |
+
export class CreateMenuItemDto {
|
4 |
+
@IsString()
|
5 |
+
id: string;
|
6 |
+
|
7 |
+
@IsString()
|
8 |
+
item_name: string;
|
9 |
+
|
10 |
+
@IsUrl()
|
11 |
+
image_url: string;
|
12 |
+
|
13 |
+
@IsString()
|
14 |
+
@IsOptional()
|
15 |
+
item_group_id?: string;
|
16 |
+
|
17 |
+
@IsString()
|
18 |
+
@IsOptional()
|
19 |
+
description?: string;
|
20 |
+
|
21 |
+
@IsNumber()
|
22 |
+
price: number;
|
23 |
+
}
|
backend/src/modules/menu-item/dto/update-menu-item.dto.ts
ADDED
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { IsNumber, IsOptional, IsString, IsUrl } from 'class-validator';
|
2 |
+
|
3 |
+
export class UpdateMenuItemDto {
|
4 |
+
@IsString()
|
5 |
+
@IsOptional()
|
6 |
+
item_name: string;
|
7 |
+
|
8 |
+
@IsUrl()
|
9 |
+
@IsOptional()
|
10 |
+
image_url: string;
|
11 |
+
|
12 |
+
@IsString()
|
13 |
+
@IsOptional()
|
14 |
+
item_group_id?: string;
|
15 |
+
|
16 |
+
@IsString()
|
17 |
+
@IsOptional()
|
18 |
+
description?: string;
|
19 |
+
|
20 |
+
@IsNumber()
|
21 |
+
@IsOptional()
|
22 |
+
price: number;
|
23 |
+
}
|
backend/src/modules/menu-item/menu-item.controller.ts
ADDED
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import {
|
2 |
+
Controller,
|
3 |
+
Get,
|
4 |
+
Post,
|
5 |
+
Body,
|
6 |
+
Patch,
|
7 |
+
Param,
|
8 |
+
Delete,
|
9 |
+
} from '@nestjs/common';
|
10 |
+
import { MenuItemService } from './menu-item.service.js';
|
11 |
+
import { CreateMenuItemDto } from './dto/create-menu-item.dto.js';
|
12 |
+
import { UpdateMenuItemDto } from './dto/update-menu-item.dto.js';
|
13 |
+
import { Public } from '../authentication/authentication.decorator.js';
|
14 |
+
|
15 |
+
@Public()
|
16 |
+
@Controller('menu-items')
|
17 |
+
export class MenuItemController {
|
18 |
+
constructor(private readonly menuItemService: MenuItemService) {}
|
19 |
+
|
20 |
+
@Post()
|
21 |
+
async create(@Body() createMenuItemDto: CreateMenuItemDto) {
|
22 |
+
return this.menuItemService.create(createMenuItemDto);
|
23 |
+
}
|
24 |
+
|
25 |
+
@Get()
|
26 |
+
async findAll() {
|
27 |
+
return this.menuItemService.findAll();
|
28 |
+
}
|
29 |
+
|
30 |
+
@Get(':id')
|
31 |
+
async findOne(@Param('id') id: string) {
|
32 |
+
return this.menuItemService.findOne(id);
|
33 |
+
}
|
34 |
+
|
35 |
+
@Patch(':id')
|
36 |
+
async update(
|
37 |
+
@Param('id') id: string,
|
38 |
+
@Body() updateMenuItemDto: UpdateMenuItemDto,
|
39 |
+
) {
|
40 |
+
return this.menuItemService.update(id, updateMenuItemDto);
|
41 |
+
}
|
42 |
+
|
43 |
+
@Delete(':id')
|
44 |
+
remove(@Param('id') id: string) {
|
45 |
+
return this.menuItemService.remove(id);
|
46 |
+
}
|
47 |
+
}
|
backend/src/modules/menu-item/menu-item.module.ts
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { Module } from '@nestjs/common';
|
2 |
+
import { MenuItemService } from './menu-item.service.js';
|
3 |
+
import { MenuItemController } from './menu-item.controller.js';
|
4 |
+
|
5 |
+
@Module({
|
6 |
+
controllers: [MenuItemController],
|
7 |
+
providers: [MenuItemService],
|
8 |
+
})
|
9 |
+
export class MenuItemModule {}
|
backend/src/modules/menu-item/menu-item.service.ts
ADDED
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { Injectable, NotFoundException } from '@nestjs/common';
|
2 |
+
import { CreateMenuItemDto } from './dto/create-menu-item.dto.js';
|
3 |
+
import { UpdateMenuItemDto } from './dto/update-menu-item.dto.js';
|
4 |
+
import { MenuItemEntity } from '../../entities/menu-item.entity.js';
|
5 |
+
import { Public } from '../authentication/authentication.decorator.js';
|
6 |
+
import { plainToClass } from 'class-transformer';
|
7 |
+
|
8 |
+
@Public()
|
9 |
+
@Injectable()
|
10 |
+
export class MenuItemService {
|
11 |
+
async create(createMenuItemDto: CreateMenuItemDto) {
|
12 |
+
return await MenuItemEntity.create({ ...createMenuItemDto }).save();
|
13 |
+
}
|
14 |
+
|
15 |
+
async findAll() {
|
16 |
+
return await MenuItemEntity.find();
|
17 |
+
}
|
18 |
+
|
19 |
+
async findOne(id: string) {
|
20 |
+
return await MenuItemEntity.findOneBy({ id: id });
|
21 |
+
}
|
22 |
+
|
23 |
+
async getMenuItemOrError(id: string) {
|
24 |
+
let menuItem = await MenuItemEntity.findOneBy({ id });
|
25 |
+
if (!menuItem) {
|
26 |
+
throw new NotFoundException('Menu item not found');
|
27 |
+
}
|
28 |
+
return menuItem;
|
29 |
+
}
|
30 |
+
|
31 |
+
async update(id: string, updateMenuItemDto: UpdateMenuItemDto) {
|
32 |
+
let menuItem = await this.getMenuItemOrError(id);
|
33 |
+
menuItem = plainToClass(MenuItemEntity, {
|
34 |
+
...menuItem,
|
35 |
+
...updateMenuItemDto,
|
36 |
+
});
|
37 |
+
return await menuItem.save();
|
38 |
+
}
|
39 |
+
|
40 |
+
async remove(id: string) {
|
41 |
+
let menuItem = await this.getMenuItemOrError(id);
|
42 |
+
return await menuItem.remove();
|
43 |
+
}
|
44 |
+
}
|