From 942b89739b7e1edf55b5cc18f30ad3ec62554b04 Mon Sep 17 00:00:00 2001 From: Rudy Marchandise Date: Sat, 25 Jun 2022 00:41:02 +0200 Subject: [PATCH] feat(floors): content-range, delete endpoint and admin page --- admin/src/App.js | 14 +++- admin/src/themes/Menu.js | 15 +--- admin/src/views/floors.js | 82 +++++++++++++++++++ .../controllers/floors/floors.controller.ts | 41 ++++++---- .../floors/models/floor.response.body.ts | 4 + .../floors/models/mapper.profiles.ts | 4 + core/src/entities/floors/floors.service.ts | 8 ++ .../entities/floors/models/floor.entity.ts | 4 +- 8 files changed, 138 insertions(+), 34 deletions(-) create mode 100644 admin/src/views/floors.js diff --git a/admin/src/App.js b/admin/src/App.js index 9f606aa..2ab448a 100644 --- a/admin/src/App.js +++ b/admin/src/App.js @@ -7,9 +7,11 @@ import { BASE_URL } from "./configs/BaseUrl"; import { OpenGardenAdminLayout } from './themes/Layout'; -import { IoFlower, IoFlowerOutline } from "react-icons/io5"; +import { TbSeeding, TbTree, TbPlant2 } from 'react-icons/tb'; + import { PlantCreate, PlantEdit, PlantShow, PlantsList } from "./views/plants"; import { VarietyCreate, VarietyEdit, VarietyShow, VarietiesList } from "./views/varieties"; +import { FloorCreate, FloorEdit, FloorShow, FloorsList } from "./views/floors"; const App = () => ( ( edit={permissions.includes('ADMIN') ? PlantEdit : null} list={PlantsList} show={PlantShow} - icon={IoFlower} /> + icon={TbTree} /> + icon={TbSeeding} /> + )} diff --git a/admin/src/themes/Menu.js b/admin/src/themes/Menu.js index 9082956..ff30ee1 100644 --- a/admin/src/themes/Menu.js +++ b/admin/src/themes/Menu.js @@ -1,6 +1,6 @@ import * as React from 'react'; import { DashboardMenuItem, Menu, MenuItemLink, useResourceDefinitions, useSidebarState } from 'react-admin'; -import GitHubIcon from '@mui/icons-material/GitHub'; +import { TbBrandGithub, TbHome2 } from 'react-icons/tb'; import DefaultIcon from '@mui/icons-material/PlayArrow'; import { Capitalize } from '../helpers/Capitalize'; import Divider from '@mui/material/Divider'; @@ -9,18 +9,9 @@ export const OpenGardenAdminMenu = (props) => { const resources = useResourceDefinitions() const [open] = useSidebarState(); - const menuHeaderStyle = { - 'line-height': '3', - 'text-align': 'center' - } return ( - { - open && - 🏡 OpenGarden {process.env.APP_VERSION || "0.0.0"} - - } - + } /> {Object.keys(resources).map(name => ( { /> ))} - { window.location.replace("https://github.com/Ealenn/OpenGarden") }} to="/github" primaryText="GitHub" leftIcon={} /> + { window.location.replace("https://github.com/Ealenn/OpenGarden") }} to="/github" primaryText="GitHub" leftIcon={} /> ); }; diff --git a/admin/src/views/floors.js b/admin/src/views/floors.js new file mode 100644 index 0000000..2077eb1 --- /dev/null +++ b/admin/src/views/floors.js @@ -0,0 +1,82 @@ +import * as React from "react"; +import { + List, + Datagrid, + TextField, + DateField, + Show, + RichTextField, + ReferenceOneField, + ShowButton, + EditButton, + Create, + TextInput, + SingleFieldList, + ChipField, + ArrayField, + Edit, + TabbedShowLayout, + Tab +} from "react-admin"; +import { RichTextInput } from 'ra-input-rich-text'; +import { StringToLabelObject } from '../helpers/StringToLabelObject'; +import { GetPermissions } from "../helpers/GetPermissions"; + +export const FloorCreate = (props) => ( + + + + +); + +export const FloorEdit = (props) => ( + + + + +); + +export const FloorsList = (props) => { + const permissions = GetPermissions(); + return ( + + permissions.includes('ADMIN')}> + + + + + + + + {permissions.includes('ADMIN') && } + + + + ); +}; + +export const FloorShow = (props) => ( + + + + + + + + + + + + + + + + + + + + + + + +); diff --git a/core/src/controllers/floors/floors.controller.ts b/core/src/controllers/floors/floors.controller.ts index cc9e3ef..e3e6708 100644 --- a/core/src/controllers/floors/floors.controller.ts +++ b/core/src/controllers/floors/floors.controller.ts @@ -8,6 +8,7 @@ import { Post, Query, Response, + Delete, } from '@nestjs/common'; import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; import { Mapper } from '@automapper/core'; @@ -22,6 +23,7 @@ import { ErrorsRequestBody } from '../models/errors.response.body'; import { Response as Res } from 'express'; import { Roles } from '../../auth/roles/roles.decorator'; import { Role } from '../../auth/roles/role.enum'; +import { PublishedState } from '../../entities/base.published.entity'; @ApiBearerAuth() @ApiTags('Floors') @@ -35,15 +37,12 @@ export class FloorsController { @Roles(Role.ADMIN) @ApiResponse({ status: 403, description: 'Forbidden' }) @ApiResponse({ status: 201, type: FloorResponseBody }) - @ApiResponse({ - status: 400, - description: 'Bad Request', - type: ErrorsRequestBody, - }) + @ApiResponse({ status: 400, description: 'Bad Request', type: ErrorsRequestBody }) async createFloor(@Request() req, @Body() createFloorRequestBody: CreateFloorRequestBody) { const createFloor: Floor = { ...createFloorRequestBody, _id: null, + status: PublishedState.ONLINE, createdAt: new Date(), updatedAt: new Date(), createdBy: req.user._id, @@ -52,19 +51,31 @@ export class FloorsController { return this.mapper.map(floor, Floor, FloorResponseBody); } + @Delete(':floorId') + @Roles(Role.ADMIN) + @ApiResponse({ status: 403, description: 'Forbidden', type: ErrorsRequestBody }) + @ApiResponse({ status: 404, description: 'Not Found', type: ErrorsRequestBody }) + @ApiResponse({ status: 200, type: FloorResponseBody }) + async deletePlant(@Response() res: Res, @Request() req, @Param('floorId') plantId: string) { + const floor = await this.floorsService.deleteFloor(plantId); + if (!floor) { + throw new NotFoundException(); + } + + const body = this.mapper.map(floor, Floor, FloorResponseBody); + return res.set({ 'Content-Range': `elements 0-1/1` }).json(body); + } + @Get(':floorId') @ApiResponse({ status: 200, type: FloorResponseBody }) - @ApiResponse({ - status: 404, - description: 'Not Found', - type: ErrorsRequestBody, - }) - async getFloorById(@Param('floorId') floorId: string) { + @ApiResponse({ status: 404, description: 'Not Found', type: ErrorsRequestBody }) + async getFloorById(@Response() res: Res, @Param('floorId') floorId: string) { const floor = await this.floorsService.findOneById(floorId); if (!floor) { throw new NotFoundException(); } - return this.mapper.map(floor, Floor, FloorResponseBody); + const body = this.mapper.map(floor, Floor, FloorResponseBody); + return res.set({ 'Content-Range': `elements 0-1/1` }).json(body); } @Get() @@ -81,10 +92,6 @@ export class FloorsController { const body: FloorSearchResponseBody = { floors: this.mapper.mapArray(elements, Floor, FloorResponseBody), }; - return res - .set({ - 'Content-Range': `elements ${offset}-${offset + limit}/${count}`, - }) - .json(body); + return res.set({ 'Content-Range': `elements ${offset}-${offset + limit}/${count}` }).json(body); } } diff --git a/core/src/controllers/floors/models/floor.response.body.ts b/core/src/controllers/floors/models/floor.response.body.ts index a3d0e45..63e6038 100644 --- a/core/src/controllers/floors/models/floor.response.body.ts +++ b/core/src/controllers/floors/models/floor.response.body.ts @@ -1,4 +1,5 @@ import { ApiProperty } from '@nestjs/swagger'; +import { PublishedState } from '../../../entities/base.published.entity'; export class FloorResponseBody { @ApiProperty() @@ -10,6 +11,9 @@ export class FloorResponseBody { @ApiProperty() description: string; + @ApiProperty({ enum: PublishedState }) + status: PublishedState; + @ApiProperty() createdBy: string; diff --git a/core/src/controllers/floors/models/mapper.profiles.ts b/core/src/controllers/floors/models/mapper.profiles.ts index 865dd70..d4932c4 100644 --- a/core/src/controllers/floors/models/mapper.profiles.ts +++ b/core/src/controllers/floors/models/mapper.profiles.ts @@ -28,6 +28,10 @@ export class FloorMapperProfiles extends AutomapperProfile { (d) => d.description, mapFrom((s) => s.description), ), + forMember( + (d) => d.status, + mapFrom((s) => s.status), + ), forMember( (d) => d.createdBy, mapFrom((s) => s.createdBy.toString()), diff --git a/core/src/entities/floors/floors.service.ts b/core/src/entities/floors/floors.service.ts index f805c36..8febcdc 100644 --- a/core/src/entities/floors/floors.service.ts +++ b/core/src/entities/floors/floors.service.ts @@ -28,6 +28,14 @@ export class FloorsService extends BaseEntityService { } } + async deleteFloor(floorId: string): Promise { + try { + return await this.FloorModel.findByIdAndDelete(floorId); + } catch { + return null; + } + } + async findOneById(id: string): Promise { return await this.FloorModel.findById(id); } diff --git a/core/src/entities/floors/models/floor.entity.ts b/core/src/entities/floors/models/floor.entity.ts index dd46fcc..237c23c 100644 --- a/core/src/entities/floors/models/floor.entity.ts +++ b/core/src/entities/floors/models/floor.entity.ts @@ -1,11 +1,11 @@ import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'; import { Document } from 'mongoose'; -import { BaseEntity } from '../../base.entity'; +import { BasePublishedEntity } from '../../base.published.entity'; export type FloorDocument = Floor & Document; @Schema() -export class Floor extends BaseEntity { +export class Floor extends BasePublishedEntity { @Prop({ required: true, unique: true }) name: string;