From 7002fde2561dfe4ffcd0706064cb6440e7de10f0 Mon Sep 17 00:00:00 2001 From: Donald Wu Date: Sat, 7 Jan 2023 18:52:35 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20enable=20select=20dropdo?= =?UTF-8?q?wn=20to=20multiple?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../migration.sql | 2 + apps/api/prisma/schema.prisma | 1 + apps/api/src/incident/dto/getIncidents.dto.ts | 6 +- apps/api/src/incident/incident.repository.ts | 6 +- .../src/components/dashboard/Dashboard.tsx | 22 +++--- .../searchAndFilter/SearchAndFilter.tsx | 78 +++++++++++++++---- apps/web/src/services/incidentService.ts | 9 ++- package.json | 2 + yarn.lock | 5 ++ 9 files changed, 94 insertions(+), 37 deletions(-) create mode 100644 apps/api/prisma/migrations/20230107104915_add_incident_ref_index/migration.sql diff --git a/apps/api/prisma/migrations/20230107104915_add_incident_ref_index/migration.sql b/apps/api/prisma/migrations/20230107104915_add_incident_ref_index/migration.sql new file mode 100644 index 0000000..43be91f --- /dev/null +++ b/apps/api/prisma/migrations/20230107104915_add_incident_ref_index/migration.sql @@ -0,0 +1,2 @@ +-- CreateIndex +CREATE INDEX "index_incident_on_incidentRef" ON "Incident"("incidentRef"); diff --git a/apps/api/prisma/schema.prisma b/apps/api/prisma/schema.prisma index 4deeddb..811d28c 100644 --- a/apps/api/prisma/schema.prisma +++ b/apps/api/prisma/schema.prisma @@ -45,6 +45,7 @@ model Incident { @@index([creator_id], map: "index_incident_on_creator_id") @@index([assignee_id], map: "index_incident_on_assignee_id") @@index([status], map: "index_incident_on_status") + @@index([incidentRef], map: "index_incident_on_incidentRef") @@index([created_at], map: "index_incident_on_created_at") @@index([updated_at], map: "index_incident_on_updated_at") } diff --git a/apps/api/src/incident/dto/getIncidents.dto.ts b/apps/api/src/incident/dto/getIncidents.dto.ts index d2a8aac..35865fc 100644 --- a/apps/api/src/incident/dto/getIncidents.dto.ts +++ b/apps/api/src/incident/dto/getIncidents.dto.ts @@ -1,11 +1,11 @@ -import { UserRole, IncidentType, Status } from '@prisma/client'; +import { UserRole } from '@prisma/client'; export class GetIncidentsDto { userRole: UserRole; userId: string; searchText?: string; - incidentType?: IncidentType; - status?: Status; + incidentType?: string[]; + status?: string[]; page?: number; perPage?: number; sortByCreatedAt?: boolean; diff --git a/apps/api/src/incident/incident.repository.ts b/apps/api/src/incident/incident.repository.ts index 95a7d36..900b066 100644 --- a/apps/api/src/incident/incident.repository.ts +++ b/apps/api/src/incident/incident.repository.ts @@ -1,5 +1,5 @@ import { Injectable } from '@nestjs/common'; -import { Incident, UserRole, Status } from '@prisma/client'; +import { Incident, UserRole, Status, IncidentType } from '@prisma/client'; import { PrismaService } from '../prisma.service'; import { v4 as uuidv4 } from 'uuid'; import { AssignIncidentStatusDto } from './dto/assignIncidentStatus.dto'; @@ -108,12 +108,12 @@ export class IncidentRepository { }), ...(incidentType && { type: { - in: [incidentType], + in: incidentType as IncidentType[], }, }), ...(status && { status: { - in: [status], + in: status as Status[], }, }), }, diff --git a/apps/web/src/components/dashboard/Dashboard.tsx b/apps/web/src/components/dashboard/Dashboard.tsx index cb6e1bd..5e48e5b 100644 --- a/apps/web/src/components/dashboard/Dashboard.tsx +++ b/apps/web/src/components/dashboard/Dashboard.tsx @@ -19,7 +19,7 @@ import DialogTitle from '@mui/material/DialogTitle'; import CustomSnackBar from '../customSnackBar/CustomSnackBar'; import IncidentCardList from '../incidentCardList/IncidentCardList'; import CustomAppBar from '../customAppBar/CustomAppBar'; -import { IncidentType, Status, UserRole } from '@prisma/client'; +import { IncidentType, UserRole } from '@prisma/client'; import * as incidentService from '../../services/incidentService'; import CustomBreadcrumbs from '../customBreadcrumbs/CustomBreadcrumbs'; import SearchAndFilter from '../searchAndFilter/SearchAndFilter'; @@ -33,8 +33,8 @@ function Dashboard() { const [description, setDescription] = useState(''); const [createIncidentIncidentType, setCreateIncidentIncidentType] = useState(); - const [incidentType, setIncidentType] = useState(); - const [status, setStatus] = useState(); + const [incidentType, setIncidentType] = useState([]); + const [status, setStatus] = useState([]); const [incidents, setIncidents] = useState([]); const [totalPageCount, setTotalPageCount] = useState(0); @@ -70,8 +70,8 @@ function Dashboard() { const getIncidents = async ( searchText?: string, - incidentType?: IncidentType, - status?: Status, + incidentType?: string[], + status?: string[], page?: number, sortByCreatedAt?: boolean, sortByUpdatedAt?: boolean @@ -123,11 +123,13 @@ function Dashboard() { }; const handleIncidentTypeChange = (event: SelectChangeEvent) => { - setIncidentType(event.target.value as IncidentType); + const { value } = event.target; + setIncidentType(typeof value === 'string' ? value.split(',') : value); }; const handleStatusChange = (event: SelectChangeEvent) => { - setStatus(event.target.value as Status); + const { value } = event.target; + setStatus(typeof value === 'string' ? value.split(',') : value); }; const handleCreateIncidentButtonClick = () => { @@ -150,8 +152,8 @@ function Dashboard() { const handleClearFilterClick = () => { setSearchText(''); - setIncidentType(undefined); - setStatus(undefined); + setIncidentType([]); + setStatus([]); setPage(1); setSortByCreatedAt(false); setSortByUpdatedAt(false); @@ -186,7 +188,7 @@ function Dashboard() { setTitle(''); setDescription(''); - setIncidentType(undefined); + setIncidentType([]); await getIncidents(searchText); } diff --git a/apps/web/src/components/searchAndFilter/SearchAndFilter.tsx b/apps/web/src/components/searchAndFilter/SearchAndFilter.tsx index 8824e09..a7adc38 100644 --- a/apps/web/src/components/searchAndFilter/SearchAndFilter.tsx +++ b/apps/web/src/components/searchAndFilter/SearchAndFilter.tsx @@ -8,13 +8,16 @@ import FormControl from '@mui/material/FormControl'; import Grid from '@mui/material/Grid'; import Select, { SelectChangeEvent } from '@mui/material/Select'; import MenuItem from '@mui/material/MenuItem'; +import Box from '@mui/material/Box'; +import OutlinedInput from '@mui/material/OutlinedInput'; +import Chip from '@mui/material/Chip'; import InputLabel from '@mui/material/InputLabel'; import { IncidentType, Status } from '@prisma/client'; interface Props { searchText: string; - incidentType: IncidentType | undefined; - status: Status | undefined; + incidentType: string[]; + status: string[]; page: number; sortByCreatedAt: boolean; sortByUpdatedAt: boolean; @@ -30,6 +33,17 @@ interface Props { handleClearFilterClick: () => void; } +const ITEM_HEIGHT = 48; +const ITEM_PADDING_TOP = 8; +const MenuProps = { + PaperProps: { + style: { + maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP, + width: 250, + }, + }, +}; + function SearchAndFilter({ searchText, incidentType, @@ -46,6 +60,19 @@ function SearchAndFilter({ handleSearchTextChange, handleClearFilterClick, }: Props) { + const incidentTypesData = [ + IncidentType.HIGH, + IncidentType.MEDIUM, + IncidentType.LOW, + ]; + + const statusesData = [ + Status.UNASSIGNED, + Status.ASSIGNED, + Status.ACKNOWLEDGED, + Status.RESOLVED, + ]; + return (
@@ -71,15 +98,24 @@ function SearchAndFilter({ Incident Type @@ -87,16 +123,24 @@ function SearchAndFilter({ Status diff --git a/apps/web/src/services/incidentService.ts b/apps/web/src/services/incidentService.ts index c2e7b25..df89ccc 100644 --- a/apps/web/src/services/incidentService.ts +++ b/apps/web/src/services/incidentService.ts @@ -1,6 +1,7 @@ import axios from 'axios'; import { UserRole, IncidentType, Status } from '@prisma/client'; import { getRootUrl } from '../helper/helper'; +import _ from 'lodash'; const rootUrl = getRootUrl(); @@ -33,8 +34,8 @@ export const getIncidents = async ( userRole: UserRole, userId: string, searchText?: string, - incidentType?: IncidentType, - status?: Status, + incidentType?: string[], + status?: string[], page?: number, perPage?: number, sortByCreatedAt?: boolean, @@ -44,8 +45,8 @@ export const getIncidents = async ( userRole: userRole, userId: userId, ...(searchText && { searchText: searchText }), - ...(incidentType && { incidentType: incidentType }), - ...(status && { status: status }), + ...(!_.isEmpty(incidentType) && { incidentType: incidentType }), + ...(!_.isEmpty(status) && { status: status }), ...(page && { page: page, }), diff --git a/package.json b/package.json index e4bf2d5..ed8edc9 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "dayjs": "^1.11.7", "helmet": "^6.0.1", "jsonwebtoken": "^9.0.0", + "lodash": "^4.17.21", "react": "18.2.0", "react-dom": "18.2.0", "react-router-dom": "6.4.3", @@ -57,6 +58,7 @@ "@types/helmet": "^4.0.0", "@types/jest": "28.1.1", "@types/jsonwebtoken": "^9.0.0", + "@types/lodash": "^4.14.191", "@types/node": "18.11.9", "@types/react": "18.0.25", "@types/react-dom": "18.0.9", diff --git a/yarn.lock b/yarn.lock index f4092eb..8b1eb04 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1909,6 +1909,11 @@ dependencies: "@types/node" "*" +"@types/lodash@^4.14.191": + version "4.14.191" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.191.tgz#09511e7f7cba275acd8b419ddac8da9a6a79e2fa" + integrity sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ== + "@types/mime@*": version "3.0.1" resolved "https://registry.yarnpkg.com/@types/mime/-/mime-3.0.1.tgz#5f8f2bca0a5863cb69bc0b0acd88c96cb1d4ae10"