implement configurable menu
This commit is contained in:
parent
3e2bdaebc6
commit
b08663fb28
@ -44,7 +44,7 @@
|
|||||||
|
|
||||||
<!-- Main Content -->
|
<!-- Main Content -->
|
||||||
<main class="flex-1 p-6 bg-base-100 overflow-y-auto">
|
<main class="flex-1 p-6 bg-base-100 overflow-y-auto">
|
||||||
<ng-content></ng-content>
|
<ng-content select="[main-content]"></ng-content>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<!-- Footer -->
|
<!-- Footer -->
|
||||||
@ -57,27 +57,7 @@
|
|||||||
<!-- Sidebar -->
|
<!-- Sidebar -->
|
||||||
<div class="drawer-side">
|
<div class="drawer-side">
|
||||||
<label for="my-drawer-2" aria-label="close sidebar" class="drawer-overlay"></label>
|
<label for="my-drawer-2" aria-label="close sidebar" class="drawer-overlay"></label>
|
||||||
<ul class="menu p-4 w-80 min-h-full bg-base-300 text-base-content">
|
<ng-content select="[menu-content]"></ng-content>
|
||||||
<!-- Sidebar content here -->
|
|
||||||
<li class="text-lg font-bold p-4">My App</li>
|
|
||||||
<li><a>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
|
||||||
d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />
|
|
||||||
</svg>
|
|
||||||
Dashboard</a></li>
|
|
||||||
<li><a>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
|
||||||
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
|
||||||
</svg>
|
|
||||||
Analytics</a></li>
|
|
||||||
<li><a>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
|
||||||
d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />
|
|
||||||
</svg>
|
|
||||||
Reports</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -1,3 +1,10 @@
|
|||||||
<rs-daisy-admin-layout-rs1 (clickEvent)="logout()" [loggedIn]="loggedIn()">
|
<rs-daisy-admin-layout-rs1 (clickEvent)="logout()" [loggedIn]="loggedIn()">
|
||||||
<router-outlet />
|
|
||||||
|
<app-menu
|
||||||
|
[title]="menuTitle"
|
||||||
|
[menuItems]="menuConfig"
|
||||||
|
[userRoles]="currentUserRoles"
|
||||||
|
menu-content>
|
||||||
|
</app-menu>
|
||||||
|
<router-outlet main-content />
|
||||||
</rs-daisy-admin-layout-rs1>
|
</rs-daisy-admin-layout-rs1>
|
||||||
|
|||||||
@ -1,23 +1,36 @@
|
|||||||
import { Component, inject, signal } from '@angular/core';
|
import { Component, inject, signal } from '@angular/core';
|
||||||
import { Router, RouterOutlet } from '@angular/router';
|
import { Router, RouterOutlet } from '@angular/router';
|
||||||
import { MainMenu } from './components/main-menu/main-menu';
|
|
||||||
import { AuthService } from './auth/auth.service';
|
import { AuthService } from './auth/auth.service';
|
||||||
import { AdminLayout } from './layout/admin-layout/admin-layout';
|
|
||||||
import { finalize } from 'rxjs/operators';
|
|
||||||
import {Button} from '@rschneider/ng-daisyui';
|
|
||||||
import { AdminLayoutRs1 } from '../../projects/rschneider/ng-daisyui/src/lib/layout';
|
import { AdminLayoutRs1 } from '../../projects/rschneider/ng-daisyui/src/lib/layout';
|
||||||
|
import { Menu, MenuItem } from './components/menu/menu';
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-root',
|
selector: 'app-root',
|
||||||
|
|
||||||
imports: [RouterOutlet, MainMenu, Button, AdminLayoutRs1],
|
imports: [RouterOutlet, AdminLayoutRs1, Menu],
|
||||||
templateUrl: './app.html',
|
templateUrl: './app.html',
|
||||||
styleUrl: './app.css',
|
styleUrl: './app.css',
|
||||||
})
|
})
|
||||||
export class App {
|
export class App {
|
||||||
protected readonly title = signal('admin');
|
protected readonly title = signal('admin');
|
||||||
|
protected menuConfig: MenuItem[] = [
|
||||||
|
|
||||||
|
];
|
||||||
|
protected currentUserRoles: string[] = ['admin'];
|
||||||
|
protected menuTitle: string | undefined = 'Menü';
|
||||||
|
|
||||||
|
|
||||||
constructor(private authService: AuthService, private router: Router) {}
|
constructor(private authService: AuthService, private router: Router) {
|
||||||
|
this.menuConfig = [
|
||||||
|
{
|
||||||
|
menuText: 'Esemény típusok',
|
||||||
|
targetUrl: '/event-type/table',
|
||||||
|
svgIcon: `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="M6.429 9.75 2.25 12l4.179 2.25m0-4.5 5.571 3 5.571-3m-11.142 0L2.25 7.5 12 2.25l9.75 5.25-4.179 2.25m0 0L21.75 12l-4.179 2.25m0 0 4.179 2.25L12 21.75 2.25 16.5l4.179-2.25m11.142 0-5.571 3-5.571-3" />
|
||||||
|
</svg>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
logout(): void {
|
logout(): void {
|
||||||
// With the interceptor fixed, this is now the correct and robust way.
|
// With the interceptor fixed, this is now the correct and robust way.
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { Component, inject, input, OnInit } from '@angular/core';
|
import { Component, inject, input, OnInit } from '@angular/core';
|
||||||
import { Router, RouterLink } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
export interface ActionDefinition<T> {
|
export interface ActionDefinition<T> {
|
||||||
action: string;
|
action: string;
|
||||||
@ -10,7 +10,6 @@ export interface ActionDefinition<T> {
|
|||||||
@Component({
|
@Component({
|
||||||
selector: 'app-generic-action-column',
|
selector: 'app-generic-action-column',
|
||||||
imports: [
|
imports: [
|
||||||
RouterLink,
|
|
||||||
],
|
],
|
||||||
templateUrl: './generic-action-column.html',
|
templateUrl: './generic-action-column.html',
|
||||||
styleUrl: './generic-action-column.css',
|
styleUrl: './generic-action-column.css',
|
||||||
|
|||||||
@ -84,5 +84,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
} @else {
|
||||||
|
<div class="text-center p-8">
|
||||||
|
<span class="loading loading-spinner loading-lg"></span>
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1 +0,0 @@
|
|||||||
<p>main-menu works!</p>
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
import { Component } from '@angular/core';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-main-menu',
|
|
||||||
imports: [],
|
|
||||||
templateUrl: './main-menu.html',
|
|
||||||
styleUrl: './main-menu.css',
|
|
||||||
})
|
|
||||||
export class MainMenu {
|
|
||||||
|
|
||||||
}
|
|
||||||
52
admin/src/app/components/menu/menu.html
Normal file
52
admin/src/app/components/menu/menu.html
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
<!--
|
||||||
|
<ul class="menu p-4 w-80 min-h-full bg-base-300 text-base-content">
|
||||||
|
<li class="text-lg font-bold p-4">My App</li>
|
||||||
|
<li><a>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||||
|
d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />
|
||||||
|
</svg>
|
||||||
|
Dashboard</a></li>
|
||||||
|
<li><a>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||||
|
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||||
|
</svg>
|
||||||
|
Analytics</a></li>
|
||||||
|
<li><a>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||||
|
d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />
|
||||||
|
</svg>
|
||||||
|
Reports</a></li>
|
||||||
|
</ul>
|
||||||
|
-->
|
||||||
|
<ul class="menu p-4 w-80 min-h-full bg-base-300 text-base-content">
|
||||||
|
@if(title()){
|
||||||
|
<li class="text-lg font-bold p-4">{{ title() }}</li>
|
||||||
|
}
|
||||||
|
@for (item of menuItems(); track item.menuText) {
|
||||||
|
|
||||||
|
@if (isItemVisible(item)) {
|
||||||
|
<li [ngClass]="getItemStyleClass(item)">
|
||||||
|
|
||||||
|
@if (item.children && item.children.length > 0) {
|
||||||
|
<details [open]="isSubMenuActive(item)">
|
||||||
|
<summary>
|
||||||
|
<!-- Using [innerHTML] for SVG. Be cautious with non-trusted sources. -->
|
||||||
|
<span [innerHTML]="item.svgIcon | safeHtml"></span>
|
||||||
|
{{ item.menuText }}
|
||||||
|
</summary>
|
||||||
|
<!-- Recursive call to the menu component for nested items -->
|
||||||
|
<app-menu [menuItems]="item.children" [userRoles]="userRoles()"></app-menu>
|
||||||
|
</details>
|
||||||
|
} @else {
|
||||||
|
<a (click)="handleItemClick($event, item)" [class.menu-active]="isItemActive(item)">
|
||||||
|
<span [innerHTML]="item.svgIcon | safeHtml"></span>
|
||||||
|
{{ item.menuText }}
|
||||||
|
</a>
|
||||||
|
}
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
@ -1,18 +1,18 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
import { MainMenu } from './main-menu';
|
import { Menu } from './menu';
|
||||||
|
|
||||||
describe('MainMenu', () => {
|
describe('Menu', () => {
|
||||||
let component: MainMenu;
|
let component: Menu;
|
||||||
let fixture: ComponentFixture<MainMenu>;
|
let fixture: ComponentFixture<Menu>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
imports: [MainMenu]
|
imports: [Menu]
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
|
|
||||||
fixture = TestBed.createComponent(MainMenu);
|
fixture = TestBed.createComponent(Menu);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
82
admin/src/app/components/menu/menu.ts
Normal file
82
admin/src/app/components/menu/menu.ts
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
import { Component, inject, input } from '@angular/core';
|
||||||
|
|
||||||
|
import { Router, RouterModule } from '@angular/router';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { SafeHtmlPipe } from '../../pipes/safe-html-pipe';
|
||||||
|
|
||||||
|
export interface MenuItem {
|
||||||
|
id?: string;
|
||||||
|
menuText: string;
|
||||||
|
targetUrl?: string;
|
||||||
|
onClick?: (event: MouseEvent, item: MenuItem) => void;
|
||||||
|
roles?: string[];
|
||||||
|
children?: MenuItem[];
|
||||||
|
getStyleClass?: (item: MenuItem) => string;
|
||||||
|
isActive?: (item: MenuItem, router: Router) => boolean;
|
||||||
|
svgIcon?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-menu',
|
||||||
|
imports: [CommonModule, RouterModule, SafeHtmlPipe],
|
||||||
|
templateUrl: './menu.html',
|
||||||
|
styleUrl: './menu.css',
|
||||||
|
})
|
||||||
|
export class Menu {
|
||||||
|
title = input<string>();
|
||||||
|
// Use the input() function instead of the @Input() decorator.
|
||||||
|
// input.required makes sure that the menuItems array is always passed.
|
||||||
|
menuItems = input.required<MenuItem[]>();
|
||||||
|
|
||||||
|
// You can also provide a default value with input().
|
||||||
|
// In a real app, this would likely come from an authentication service.
|
||||||
|
userRoles = input<string[]>(['admin', 'user']);
|
||||||
|
|
||||||
|
// Use inject() for dependency injection instead of the constructor.
|
||||||
|
private router = inject(Router);
|
||||||
|
|
||||||
|
isItemVisible(item: MenuItem): boolean {
|
||||||
|
if (!item.roles || item.roles.length === 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Access the value of the signal by calling it as a function: this.userRoles()
|
||||||
|
return item.roles.some(role => this.userRoles().includes(role));
|
||||||
|
}
|
||||||
|
|
||||||
|
handleItemClick(event: MouseEvent, item: MenuItem): void {
|
||||||
|
if (item.onClick) {
|
||||||
|
item.onClick(event, item);
|
||||||
|
} else if (item.targetUrl) {
|
||||||
|
this.router.navigate([item.targetUrl]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getItemStyleClass(item: MenuItem): string {
|
||||||
|
return item.getStyleClass ? item.getStyleClass(item) : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
isItemActive(item: MenuItem): boolean {
|
||||||
|
if (item.isActive) {
|
||||||
|
return item.isActive(item, this.router);
|
||||||
|
}
|
||||||
|
if (item.targetUrl) {
|
||||||
|
// router.isActive is a boolean check, perfect for this use case.
|
||||||
|
return this.router.isActive(item.targetUrl, {
|
||||||
|
paths: 'exact',
|
||||||
|
queryParams: 'exact',
|
||||||
|
fragment: 'ignored',
|
||||||
|
matrixParams: 'ignored'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to determine if a dropdown should be open by default
|
||||||
|
isSubMenuActive(item: MenuItem): boolean {
|
||||||
|
if (!item.children) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Recursively check if any child or grandchild is active
|
||||||
|
return item.children.some(child => this.isItemActive(child) || this.isSubMenuActive(child));
|
||||||
|
}
|
||||||
|
}
|
||||||
8
admin/src/app/pipes/safe-html-pipe.spec.ts
Normal file
8
admin/src/app/pipes/safe-html-pipe.spec.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { SafeHtmlPipe } from './safe-html-pipe';
|
||||||
|
|
||||||
|
describe('SafeHtmlPipe', () => {
|
||||||
|
it('create an instance', () => {
|
||||||
|
const pipe = new SafeHtmlPipe();
|
||||||
|
expect(pipe).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
16
admin/src/app/pipes/safe-html-pipe.ts
Normal file
16
admin/src/app/pipes/safe-html-pipe.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { inject, Pipe, PipeTransform } from '@angular/core';
|
||||||
|
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
|
||||||
|
|
||||||
|
@Pipe({
|
||||||
|
name: 'safeHtml'
|
||||||
|
})
|
||||||
|
export class SafeHtmlPipe implements PipeTransform {
|
||||||
|
|
||||||
|
private sanitized = inject(DomSanitizer);
|
||||||
|
|
||||||
|
transform(value: string | undefined): SafeHtml {
|
||||||
|
if (!value) return '';
|
||||||
|
return this.sanitized.bypassSecurityTrustHtml(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,8 +1,11 @@
|
|||||||
|
// dvbooking-cli/src/templates/nestjs/entity.ts.tpl
|
||||||
|
|
||||||
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
|
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
|
||||||
import { IsString, IsNumber, IsBoolean, IsDate, IsOptional } from 'class-validator';
|
import { IsString, IsNumber, IsBoolean, IsDate, IsOptional } from 'class-validator';
|
||||||
|
|
||||||
@Entity({ name: 'event_type' })
|
@Entity({ name: 'event_type' })
|
||||||
export class EventType {
|
export class EventType {
|
||||||
|
|
||||||
@PrimaryGeneratedColumn()
|
@PrimaryGeneratedColumn()
|
||||||
id: number;
|
id: number;
|
||||||
|
|
||||||
@ -20,4 +23,4 @@ export class EventType {
|
|||||||
@IsString()
|
@IsString()
|
||||||
color: string | null;
|
color: string | null;
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -1,8 +1,11 @@
|
|||||||
|
// dvbooking-cli/src/templates/nestjs/entity.ts.tpl
|
||||||
|
|
||||||
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
|
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
|
||||||
import { IsString, IsNumber, IsBoolean, IsDate, IsOptional } from 'class-validator';
|
import { IsString, IsNumber, IsBoolean, IsDate, IsOptional } from 'class-validator';
|
||||||
|
|
||||||
@Entity({ name: 'products' })
|
@Entity({ name: 'products' })
|
||||||
export class Product {
|
export class Product {
|
||||||
|
|
||||||
@PrimaryGeneratedColumn()
|
@PrimaryGeneratedColumn()
|
||||||
id: number;
|
id: number;
|
||||||
|
|
||||||
@ -20,4 +23,4 @@ export class Product {
|
|||||||
@IsBoolean()
|
@IsBoolean()
|
||||||
is_available: boolean | null = true;
|
is_available: boolean | null = true;
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -1,10 +1,29 @@
|
|||||||
import { Controller, Get, Post, Body, Patch, Param, Delete, Query, ParseIntPipe, DefaultValuePipe } from '@nestjs/common';
|
import {
|
||||||
|
Controller,
|
||||||
|
Get,
|
||||||
|
Post,
|
||||||
|
Body,
|
||||||
|
Patch,
|
||||||
|
Param,
|
||||||
|
Delete,
|
||||||
|
Query,
|
||||||
|
ParseIntPipe,
|
||||||
|
DefaultValuePipe,
|
||||||
|
UseGuards,
|
||||||
|
} from '@nestjs/common';
|
||||||
import { EventTypesService } from './event-type.service';
|
import { EventTypesService } from './event-type.service';
|
||||||
import { CreateEventTypeDto } from './dto/create-event-type.dto';
|
import { CreateEventTypeDto } from './dto/create-event-type.dto';
|
||||||
import { UpdateEventTypeDto } from './dto/update-event-type.dto';
|
import { UpdateEventTypeDto } from './dto/update-event-type.dto';
|
||||||
import { QueryEventTypeDto } from './dto/query-event-type.dto';
|
import { QueryEventTypeDto } from './dto/query-event-type.dto';
|
||||||
|
|
||||||
|
import { JwtAuthGuard } from '../auth/jwt-auth.guard';
|
||||||
|
import { Roles } from '../auth/roles.decorator';
|
||||||
|
import { Role } from '../auth/role.enum';
|
||||||
|
import { RolesGuard } from '../auth/roles.guard';
|
||||||
|
|
||||||
@Controller('event-type')
|
@Controller('event-type')
|
||||||
|
@UseGuards(JwtAuthGuard, RolesGuard)
|
||||||
|
@Roles(Role.Admin)
|
||||||
export class EventTypesController {
|
export class EventTypesController {
|
||||||
constructor(private readonly eventTypesService: EventTypesService) {}
|
constructor(private readonly eventTypesService: EventTypesService) {}
|
||||||
|
|
||||||
@ -33,7 +52,10 @@ export class EventTypesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Patch(':id')
|
@Patch(':id')
|
||||||
update(@Param('id', ParseIntPipe) id: number, @Body() updateEventTypeDto: UpdateEventTypeDto) {
|
update(
|
||||||
|
@Param('id', ParseIntPipe) id: number,
|
||||||
|
@Body() updateEventTypeDto: UpdateEventTypeDto,
|
||||||
|
) {
|
||||||
return this.eventTypesService.update(id, updateEventTypeDto);
|
return this.eventTypesService.update(id, updateEventTypeDto);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,10 +1,29 @@
|
|||||||
import { Controller, Get, Post, Body, Patch, Param, Delete, Query, ParseIntPipe, DefaultValuePipe } from '@nestjs/common';
|
import {
|
||||||
|
Controller,
|
||||||
|
Get,
|
||||||
|
Post,
|
||||||
|
Body,
|
||||||
|
Patch,
|
||||||
|
Param,
|
||||||
|
Delete,
|
||||||
|
Query,
|
||||||
|
ParseIntPipe,
|
||||||
|
DefaultValuePipe,
|
||||||
|
UseGuards,
|
||||||
|
} from '@nestjs/common';
|
||||||
import { ProductsService } from './products.service';
|
import { ProductsService } from './products.service';
|
||||||
import { CreateProductDto } from './dto/create-product.dto';
|
import { CreateProductDto } from './dto/create-product.dto';
|
||||||
import { UpdateProductDto } from './dto/update-product.dto';
|
import { UpdateProductDto } from './dto/update-product.dto';
|
||||||
import { QueryProductDto } from './dto/query-product.dto';
|
import { QueryProductDto } from './dto/query-product.dto';
|
||||||
|
|
||||||
|
import { JwtAuthGuard } from '../auth/jwt-auth.guard';
|
||||||
|
import { Roles } from '../auth/roles.decorator';
|
||||||
|
import { Role } from '../auth/role.enum';
|
||||||
|
import { RolesGuard } from '../auth/roles.guard';
|
||||||
|
|
||||||
@Controller('products')
|
@Controller('products')
|
||||||
|
@UseGuards(JwtAuthGuard, RolesGuard)
|
||||||
|
@Roles(Role.Admin)
|
||||||
export class ProductsController {
|
export class ProductsController {
|
||||||
constructor(private readonly productsService: ProductsService) {}
|
constructor(private readonly productsService: ProductsService) {}
|
||||||
|
|
||||||
@ -33,7 +52,10 @@ export class ProductsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Patch(':id')
|
@Patch(':id')
|
||||||
update(@Param('id', ParseIntPipe) id: number, @Body() updateProductDto: UpdateProductDto) {
|
update(
|
||||||
|
@Param('id', ParseIntPipe) id: number,
|
||||||
|
@Body() updateProductDto: UpdateProductDto,
|
||||||
|
) {
|
||||||
return this.productsService.update(id, updateProductDto);
|
return this.productsService.update(id, updateProductDto);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,4 +63,4 @@ export class ProductsController {
|
|||||||
remove(@Param('id', ParseIntPipe) id: number) {
|
remove(@Param('id', ParseIntPipe) id: number) {
|
||||||
return this.productsService.remove(id);
|
return this.productsService.remove(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user