From a319da484f3d653970e9bcd93ade78ebc78182e1 Mon Sep 17 00:00:00 2001 From: Rudy Marchandise Date: Sat, 25 Jun 2022 01:21:18 +0200 Subject: [PATCH] feat(favorites): content-range, delete endpoint and admin page --- admin/src/App.js | 8 ++- admin/src/configs/Provider.js | 6 +- admin/src/views/favorites.varieties.js | 71 +++++++++++++++++++ admin/src/views/floors.js | 15 ++-- .../favorites.varieties.controller.ts | 45 ++++++------ .../entities/favorites/favorites.service.ts | 11 ++- 6 files changed, 122 insertions(+), 34 deletions(-) create mode 100644 admin/src/views/favorites.varieties.js diff --git a/admin/src/App.js b/admin/src/App.js index 2ab448a..a586634 100644 --- a/admin/src/App.js +++ b/admin/src/App.js @@ -7,11 +7,12 @@ import { BASE_URL } from "./configs/BaseUrl"; import { OpenGardenAdminLayout } from './themes/Layout'; -import { TbSeeding, TbTree, TbPlant2 } from 'react-icons/tb'; +import { TbSeeding, TbTree, TbPlant2, TbStar } 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"; +import { FavoritesVarietiesList, FavoritesVarietyCreate, FavoritesVarietyShow } from "./views/favorites.varieties"; const App = () => ( ( list={VarietiesList} show={VarietyShow} icon={TbSeeding} /> + ({ getList: (resource, params) => { + const resourceName = resource.split('/').slice(-1); const { page, perPage } = params.pagination; const query = { ...fetchUtils.flattenObject(params.filter), @@ -19,7 +20,7 @@ export default (apiUrl, httpClient = fetchUtils.fetchJson): DataProvider => ({ ); } return { - data: json[resource], + data: json[resourceName], total: parseInt( headers.get('content-range').split('/').pop(), 10 @@ -36,12 +37,13 @@ export default (apiUrl, httpClient = fetchUtils.fetchJson): DataProvider => ({ }, getMany: (resource, params) => { + const resourceName = resource.split('/').slice(-1); console.log('getMany', resource, params); const query = { id: params.ids, }; const url = `${apiUrl}/${resource}?${stringify(query)}`; - return httpClient(url).then(({ json }) => ({ data: json[resource] })) + return httpClient(url).then(({ json }) => ({ data: json[resourceName] })) }, getManyReference: (resource, params) => { diff --git a/admin/src/views/favorites.varieties.js b/admin/src/views/favorites.varieties.js new file mode 100644 index 0000000..f9cb511 --- /dev/null +++ b/admin/src/views/favorites.varieties.js @@ -0,0 +1,71 @@ +import * as React from "react"; +import { + List, + Datagrid, + TextField, + DateField, + Show, + ReferenceOneField, + ShowButton, + Create, + SingleFieldList, + ChipField, + ArrayField, + TabbedShowLayout, + Tab, + ReferenceInput, + SelectInput, + SimpleForm, + ReferenceField +} from "react-admin"; +import { StringToLabelObject } from '../helpers/StringToLabelObject'; + +export const FavoritesVarietyCreate = (props) => ( + + + + + + + +); + +export const FavoritesVarietiesList = (props) => { + return ( + + + + + + + + + + ); +}; + +export const FavoritesVarietyShow = (props) => ( + + + + + + + + + + + + + + + + + + + + + + + +); diff --git a/admin/src/views/floors.js b/admin/src/views/floors.js index 2077eb1..a5a0db1 100644 --- a/admin/src/views/floors.js +++ b/admin/src/views/floors.js @@ -16,7 +16,8 @@ import { ArrayField, Edit, TabbedShowLayout, - Tab + Tab, + SimpleForm } from "react-admin"; import { RichTextInput } from 'ra-input-rich-text'; import { StringToLabelObject } from '../helpers/StringToLabelObject'; @@ -24,15 +25,19 @@ import { GetPermissions } from "../helpers/GetPermissions"; export const FloorCreate = (props) => ( - - + + + + ); export const FloorEdit = (props) => ( - - + + + + ); diff --git a/core/src/controllers/favorites/favorites.varieties.controller.ts b/core/src/controllers/favorites/favorites.varieties.controller.ts index 640f30a..fef29f3 100644 --- a/core/src/controllers/favorites/favorites.varieties.controller.ts +++ b/core/src/controllers/favorites/favorites.varieties.controller.ts @@ -8,6 +8,7 @@ import { Delete, Param, NotFoundException, + Response, } from '@nestjs/common'; import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; import { Mapper } from '@automapper/core'; @@ -22,6 +23,7 @@ import { } from './models/favorite.response.body'; import { FavoriteVariety } from '../../entities/favorites/models/favorite.variety.entity'; import { FavoriteVarietiesSearchRequestQuery } from './models/favorite.variety.request.query'; +import { Response as Res } from 'express'; @ApiBearerAuth() @ApiTags('Favorites') @@ -33,22 +35,20 @@ export class FavoritesVarietiesController { @Delete(':varietyId') @ApiResponse({ status: 200 }) - @ApiResponse({ - status: 404, - description: 'Not Found', - type: ErrorsRequestBody, - }) - async deleteFavorite(@Request() req, @Param('varietyId') varietyId: string) { - await this.favoritesService.deleteVariety(varietyId, req.user); + @ApiResponse({ status: 404, description: 'Not Found', type: ErrorsRequestBody }) + async deleteFavorite(@Response() res: Res, @Request() req, @Param('varietyId') varietyId: string) { + const favorite = await this.favoritesService.deleteVariety(varietyId, req.user); + if (!favorite) { + throw new NotFoundException(); + } + + const body = this.mapper.map(favorite, FavoriteVariety, FavoriteVarietyResponseBody); + return res.set({ 'Content-Range': `elements 0-1/1` }).json(body); } @Post() @ApiResponse({ status: 201, type: FavoriteVarietyResponseBody }) - @ApiResponse({ - status: 400, - description: 'Bad Request', - type: ErrorsRequestBody, - }) + @ApiResponse({ status: 400, description: 'Bad Request', type: ErrorsRequestBody }) async createFavorite(@Request() req, @Body() createFavoriteRequestBody: CreateFavoriteVarietyRequestBody) { const favorite = await this.favoritesService.addVariety(createFavoriteRequestBody.variety, req.user); return this.mapper.map(favorite, FavoriteVariety, FavoriteVarietyResponseBody); @@ -57,11 +57,12 @@ export class FavoritesVarietiesController { @Get() @ApiResponse({ status: 200, type: FavoriteVarietySearchResponseBody }) async getFavoritesVarieties( + @Response() res: Res, @Query() favoritesSearchRequestQuery: FavoriteVarietiesSearchRequestQuery, @Request() req, ) { const { limit, offset, ...filters } = favoritesSearchRequestQuery; - const favorites = await this.favoritesService.getVarieties( + const [elements, count] = await this.favoritesService.getVarieties( { pagination: { limit, @@ -71,24 +72,22 @@ export class FavoritesVarietiesController { }, req.user, ); - const result: FavoriteVarietySearchResponseBody = { - varieties: this.mapper.mapArray(favorites, FavoriteVariety, FavoriteVarietyResponseBody), + const body: FavoriteVarietySearchResponseBody = { + varieties: this.mapper.mapArray(elements, FavoriteVariety, FavoriteVarietyResponseBody), }; - return result; + return res.set({ 'Content-Range': `elements ${offset}-${offset + limit}/${count}` }).json(body); } @Get(':varietyId') @ApiResponse({ status: 200, type: FavoriteVarietyResponseBody }) - @ApiResponse({ - status: 404, - description: 'Not Found', - type: ErrorsRequestBody, - }) - async getFavoriteVariety(@Request() req, @Param('varietyId') varietyId: string) { + @ApiResponse({ status: 404, description: 'Not Found', type: ErrorsRequestBody }) + async getFavoriteVariety(@Response() res: Res, @Request() req, @Param('varietyId') varietyId: string) { const favorite = await this.favoritesService.getVariety(varietyId, req.user); if (!favorite) { throw new NotFoundException(); } - return this.mapper.map(favorite, FavoriteVariety, FavoriteVarietyResponseBody); + + const body = this.mapper.map(favorite, FavoriteVariety, FavoriteVarietyResponseBody); + return res.set({ 'Content-Range': `elements 0-1/1` }).json(body); } } diff --git a/core/src/entities/favorites/favorites.service.ts b/core/src/entities/favorites/favorites.service.ts index fa3a09f..e98386c 100644 --- a/core/src/entities/favorites/favorites.service.ts +++ b/core/src/entities/favorites/favorites.service.ts @@ -41,16 +41,21 @@ export class FavoritesService extends BaseEntityService { filters = this._generateFilter(filters, 'createdBy', user._id); filters = this._generateFilter(filters, 'variety', new Types.ObjectId(varietyId)); filters = this._generateFilter(filters, 'compositeKey', `${user._id.toString()}${varietyId}`); - return await this.favoriteVarietyModel.remove(filters).limit(1); + return await this.favoriteVarietyModel.findOneAndRemove(filters); } - async getVarieties(params: FavoritesSearchParams, user: User): Promise { + async getVarieties(params: FavoritesSearchParams, user: User): Promise<[FavoriteVariety[], number]> { const { pagination } = params; const findParam = this._generateFilters({ createdBy: user._id, }); - return await this.favoriteVarietyModel.find(findParam).skip(pagination.offset).limit(pagination.limit); + const elements = await this.favoriteVarietyModel + .find(findParam) + .skip(pagination.offset) + .limit(pagination.limit); + const count = await this.favoriteVarietyModel.count(findParam); + return [elements, count]; } async getVariety(varietyId: string, user: User): Promise {