From 937b6486e5177543952d2c24f27ac9357a055342 Mon Sep 17 00:00:00 2001 From: Nebojsa Stojanovic Date: Thu, 23 Jun 2022 17:22:08 +0200 Subject: [PATCH 1/8] Added option to quantify multiple praises at once --- packages/api/src/praise/controllers.ts | 48 +++++++ packages/api/src/praise/routes.ts | 5 + packages/api/src/praise/types.ts | 5 + .../src/components/form/SearchInput.tsx | 8 +- packages/frontend/src/model/periods.ts | 2 + packages/frontend/src/model/praise.ts | 28 +++++ .../src/pages/EventLogs/EventLogsPage.tsx | 4 +- .../components/PraiseMultipleButton.tsx | 29 +++++ .../components/PraiseMultipleDialog.tsx | 63 ++++++++++ .../components/QuantifyTable.tsx | 119 ++++++++++++++++-- 10 files changed, 295 insertions(+), 16 deletions(-) create mode 100644 packages/frontend/src/pages/QuantifyPeriodReceiver/components/PraiseMultipleButton.tsx create mode 100644 packages/frontend/src/pages/QuantifyPeriodReceiver/components/PraiseMultipleDialog.tsx diff --git a/packages/api/src/praise/controllers.ts b/packages/api/src/praise/controllers.ts index ace1ff460..58f376026 100644 --- a/packages/api/src/praise/controllers.ts +++ b/packages/api/src/praise/controllers.ts @@ -31,6 +31,7 @@ import { PraiseDocument, PraiseDto, QuantificationCreateUpdateInput, + QuantifyMultiplePraiseInput, } from './types'; import { praiseWithScore, getPraisePeriod } from './utils/core'; @@ -201,3 +202,50 @@ export const quantify = async ( const response = await praiseDocumentListTransformer(affectedPraises); res.status(200).json(response); }; + +export const quantifyMultiple = async ( + req: TypedRequestBody, + res: TypedResponse +): Promise => { + const { score, praiseIds } = req.body; + + const affectedPraises = await Promise.all( + praiseIds.map(async (id) => { + const praise = await PraiseModel.findById(id).populate( + 'giver receiver forwarder' + ); + + if (!praise) throw new NotFoundError('Praise'); + + const period = await getPraisePeriod(praise); + + if (!period) + throw new BadRequestError('Praise does not have an associated period'); + + if (!res.locals.currentUser?._id) { + throw new InternalServerError('Current user not found.'); + } + + const quantification = praise.quantifications.find((q) => + q.quantifier.equals(res.locals.currentUser._id) + ); + + if (!quantification) + throw new BadRequestError( + 'User not assigned as quantifier for praise.' + ); + + quantification.score = score; + quantification.dismissed = false; + quantification.duplicatePraise = undefined; + + await praise.save(); + + return praise; + }) + ); + + const response = await praiseDocumentListTransformer(affectedPraises); + console.log('RESPONSE:', response); + res.status(200).json(response); +}; diff --git a/packages/api/src/praise/routes.ts b/packages/api/src/praise/routes.ts index 9280a2e60..c6eb71c55 100644 --- a/packages/api/src/praise/routes.ts +++ b/packages/api/src/praise/routes.ts @@ -12,5 +12,10 @@ praiseRouter.patchAsync( authMiddleware(UserRole.QUANTIFIER), controller.quantify ); +praiseRouter.patchAsync( + '/quantify', + authMiddleware(UserRole.QUANTIFIER), + controller.quantifyMultiple +); export { praiseRouter }; diff --git a/packages/api/src/praise/types.ts b/packages/api/src/praise/types.ts index 7c4e62afd..7c0aee6df 100644 --- a/packages/api/src/praise/types.ts +++ b/packages/api/src/praise/types.ts @@ -75,6 +75,11 @@ export interface QuantificationCreateUpdateInput { duplicatePraise: string; } +export interface QuantifyMultiplePraiseInput { + score: number; + praiseIds: string[]; +} + export interface Receiver { _id: string; praiseCount: number; diff --git a/packages/frontend/src/components/form/SearchInput.tsx b/packages/frontend/src/components/form/SearchInput.tsx index 5ce9b7ec3..9f61847e1 100644 --- a/packages/frontend/src/components/form/SearchInput.tsx +++ b/packages/frontend/src/components/form/SearchInput.tsx @@ -3,15 +3,17 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; interface SearchInputProps { handleChange: (element) => void; - value: string; + value?: string; + placeholder?: string; } const SearchInput = ({ handleChange, value, + placeholder = 'Search', }: SearchInputProps): JSX.Element => { return ( -
+
): void => handleChange(e.target.value) diff --git a/packages/frontend/src/model/periods.ts b/packages/frontend/src/model/periods.ts index b9a7bb9d4..73b548be6 100644 --- a/packages/frontend/src/model/periods.ts +++ b/packages/frontend/src/model/periods.ts @@ -702,7 +702,9 @@ export const PeriodQuantifierReceiverPraise = selectorFamily({ const userId = get(ActiveUserId); const listKey = periodQuantifierPraiseListKey(periodId); const praiseList = get(AllPraiseList(listKey)); + if (!praiseList) return undefined; + return praiseList.filter( (praise) => praise && diff --git a/packages/frontend/src/model/praise.ts b/packages/frontend/src/model/praise.ts index 9df6b22f1..93be48983 100644 --- a/packages/frontend/src/model/praise.ts +++ b/packages/frontend/src/model/praise.ts @@ -264,6 +264,7 @@ type useQuantifyPraiseReturn = { duplicatePraise: string | null ) => Promise; }; + /** * Hook that returns a function to use for closing a period */ @@ -296,3 +297,30 @@ export const useQuantifyPraise = (): useQuantifyPraiseReturn => { ); return { quantify }; }; + +type useQuantifyMultiplePraiseReturn = { + quantifyMultiple: (score: number, praiseIds: string[]) => Promise; +}; + +export const useQuantifyMultiplePraise = + (): useQuantifyMultiplePraiseReturn => { + const apiAuthClient = makeApiAuthClient(); + + const quantifyMultiple = useRecoilCallback( + ({ set }) => + async (score: number, praiseIds: string[]): Promise => { + const response: AxiosResponse = + await apiAuthClient.patch('/praise/quantify', { + score, + praiseIds, + }); + + const praises = response.data; + + praises.forEach((praise) => { + set(SinglePraise(praise._id), praise); + }); + } + ); + return { quantifyMultiple }; + }; diff --git a/packages/frontend/src/pages/EventLogs/EventLogsPage.tsx b/packages/frontend/src/pages/EventLogs/EventLogsPage.tsx index a3757045d..4d3a6919a 100644 --- a/packages/frontend/src/pages/EventLogs/EventLogsPage.tsx +++ b/packages/frontend/src/pages/EventLogs/EventLogsPage.tsx @@ -109,7 +109,7 @@ const EventLogsPage = (): JSX.Element => {
{/* Search */} -
+
{ setSearchValue(e); @@ -120,7 +120,7 @@ const EventLogsPage = (): JSX.Element => {
{/* Sort */} -
+
{ setSelectedSort(e); diff --git a/packages/frontend/src/pages/QuantifyPeriodReceiver/components/PraiseMultipleButton.tsx b/packages/frontend/src/pages/QuantifyPeriodReceiver/components/PraiseMultipleButton.tsx new file mode 100644 index 000000000..6e2bc81c2 --- /dev/null +++ b/packages/frontend/src/pages/QuantifyPeriodReceiver/components/PraiseMultipleButton.tsx @@ -0,0 +1,29 @@ +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faPrayingHands } from '@fortawesome/free-solid-svg-icons'; + +interface Props { + disabled?: boolean; + onClick(); +} + +const PraiseMultipleButton = ({ + disabled = false, + onClick, +}: Props): JSX.Element => { + return ( + + ); +}; + +export default PraiseMultipleButton; diff --git a/packages/frontend/src/pages/QuantifyPeriodReceiver/components/PraiseMultipleDialog.tsx b/packages/frontend/src/pages/QuantifyPeriodReceiver/components/PraiseMultipleDialog.tsx new file mode 100644 index 000000000..0e177b8fd --- /dev/null +++ b/packages/frontend/src/pages/QuantifyPeriodReceiver/components/PraiseMultipleDialog.tsx @@ -0,0 +1,63 @@ +import ScrollableDialog from '@/components/ScrollableDialog'; +import PraiseMultipleButton from '@/pages/QuantifyPeriodReceiver/components/PraiseMultipleButton'; +import QuantifySlider from '@/pages/QuantifyPeriodReceiver/components/QuantifySlider'; +import { faCalculator, faTimes } from '@fortawesome/free-solid-svg-icons'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { PraiseDto } from 'api/dist/praise/types'; +import { useState } from 'react'; + +interface PraiseMultipleDialogProps { + open: boolean; + onClose(): void; + selectedPraises: PraiseDto[]; + allowedValues: number[]; + onSetScore(newScore: number, selectedPraises: PraiseDto[]); +} + +const PraiseMultipleDialog = ({ + open = false, + onClose, + selectedPraises, + allowedValues, + onSetScore, +}: PraiseMultipleDialogProps): JSX.Element | null => { + const [score, setScore] = useState(0); + + return ( + +
+
+ +
+
+
+ +
+

+ Quantify selected ({selectedPraises.length}) praises +

+ +
+ setScore(newScore)} + /> +
+ +
+ { + onSetScore(score, selectedPraises); + onClose(); + }} + /> +
+
+
+
+ ); +}; + +export default PraiseMultipleDialog; diff --git a/packages/frontend/src/pages/QuantifyPeriodReceiver/components/QuantifyTable.tsx b/packages/frontend/src/pages/QuantifyPeriodReceiver/components/QuantifyTable.tsx index 9cd49296b..cf5f391ac 100644 --- a/packages/frontend/src/pages/QuantifyPeriodReceiver/components/QuantifyTable.tsx +++ b/packages/frontend/src/pages/QuantifyPeriodReceiver/components/QuantifyTable.tsx @@ -1,6 +1,6 @@ import { PraiseDto } from 'api/dist/praise/types'; import { PeriodQuantifierReceiverPraise } from '@/model/periods'; -import { useQuantifyPraise } from '@/model/praise'; +import { useQuantifyMultiplePraise, useQuantifyPraise } from '@/model/praise'; import { usePeriodSettingValueRealized } from '@/model/periodsettings'; import getWeek from 'date-fns/getWeek'; import parseISO from 'date-fns/parseISO'; @@ -15,6 +15,9 @@ import DuplicateSearchDialog from './DuplicateSearchDialog'; import MarkDuplicateButton from './MarkDuplicateButton'; import MarkDismissedButton from './MarkDismissedButton'; import QuantifyPraiseRow from './QuantifyPraiseRow'; +import SearchInput from '@/components/form/SearchInput'; +import PraiseMultipleDialog from '@/pages/QuantifyPeriodReceiver/components/PraiseMultipleDialog'; +import PraiseMultipleButton from '@/pages/QuantifyPeriodReceiver/components/PraiseMultipleButton'; interface Props { periodId: string; @@ -23,6 +26,10 @@ interface Props { } const QuantifyTable = ({ periodId, receiverId }: Props): JSX.Element | null => { + const [searchValue, setSearchValue] = React.useState( + undefined + ); + const data = useRecoilValue( PeriodQuantifierReceiverPraise({ periodId, receiverId }) ); @@ -31,21 +38,40 @@ const QuantifyTable = ({ periodId, receiverId }: Props): JSX.Element | null => { 'PRAISE_QUANTIFY_RECEIVER_PSEUDONYMS' ) as boolean; const { quantify } = useQuantifyPraise(); + const { quantifyMultiple } = useQuantifyMultiplePraise(); const [isDismissDialogOpen, setIsDismissDialogOpen] = React.useState(false); const [isDuplicateDialogOpen, setIsDuplicateDialogOpen] = React.useState(false); const [isDuplicateSearchDialogOpen, setIsDuplicateSearchDialogOpen] = React.useState(false); + const [isPraiseMultipleDialogOpen, setIsPraiseMultipleDialogOpen] = + React.useState(false); const [duplicateSearchDialogPraise, setDuplicateSearchDialogPraise] = React.useState(undefined); const [selectedPraises, setSelectedPraises] = React.useState([]); + const [selectAllChecked, setSelectAllChecked] = + React.useState(false); const allowedValues = usePeriodSettingValueRealized( periodId, 'PRAISE_QUANTIFY_ALLOWED_VALUES' ) as number[]; + const applyFilter = React.useCallback( + (data: PraiseDto[] | undefined): PraiseDto[] => { + if (!data) return []; + if (!searchValue) return data; + + const filteredData = data.filter((praise: PraiseDto) => { + return praise.reasonRealized.includes(searchValue); + }); + + return filteredData; + }, + [searchValue] + ); + if (!data) return null; const handleDismiss = (): void => { @@ -75,6 +101,16 @@ const QuantifyTable = ({ periodId, receiverId }: Props): JSX.Element | null => { void quantify(praise._id, score, false, null); }; + const handleSetMultipleScore = ( + score: number, + selectedPraises: PraiseDto[] + ): void => { + const praiseIds = selectedPraises.map((praise) => praise._id); + + void quantifyMultiple(score, praiseIds); + setSelectedPraises([]); + }; + const handleDuplicateSearchPraise = (originalPraiseId: string): void => { if (!duplicateSearchDialogPraise) return; @@ -83,6 +119,8 @@ const QuantifyTable = ({ periodId, receiverId }: Props): JSX.Element | null => { }; const handleToggleCheckbox = (praise: PraiseDto): void => { + setSelectAllChecked(false); + if (selectedPraises.includes(praise)) { const newSelectedPraiseIds = selectedPraises.filter( (p) => p._id !== praise._id @@ -96,8 +134,23 @@ const QuantifyTable = ({ periodId, receiverId }: Props): JSX.Element | null => { } }; + const handleToggleSelectAll = (): void => { + setSelectAllChecked(!selectAllChecked); + + if (selectAllChecked) { + setSelectedPraises([]); + } else { + setSelectedPraises(applyFilter(data)); + } + }; + + const handleSearchInput = (searchValue: string): void => { + setSearchValue(searchValue); + setSelectedPraises([]); + }; + const weeklyData = groupBy( - sortBy(data, (p) => p.createdAt), + sortBy(applyFilter(data), (p) => p.createdAt), (praise: PraiseDto) => { if (!praise) return 0; return getWeek(parseISO(praise.createdAt), { weekStartsOn: 1 }); @@ -110,20 +163,55 @@ const QuantifyTable = ({ periodId, receiverId }: Props): JSX.Element | null => { return (
-
- setIsDuplicateDialogOpen(true)} - /> - setIsDismissDialogOpen(true)} - /> +
+
+ setIsDuplicateDialogOpen(true)} + /> +
+ +
+ setIsDismissDialogOpen(true)} + /> +
+ +
+ setIsPraiseMultipleDialogOpen(true)} + /> +
+ +
+ handleSearchInput(e)} + placeholder="Filter" + /> +
+ + + + + + + {Object.keys(weeklyData).map((weekKey, index) => ( {index !== 0 && index !== data.length - 1 && ( @@ -178,6 +266,15 @@ const QuantifyTable = ({ periodId, receiverId }: Props): JSX.Element | null => { handleDuplicateSearchPraise(praiseId) } /> + setIsPraiseMultipleDialogOpen(false)} + selectedPraises={selectedPraises} + allowedValues={allowedValues} + onSetScore={(score, selectedPraises): void => + handleSetMultipleScore(score, selectedPraises) + } + /> ); }; From 086e226643f84d93b1fe5d6aa889b2cbd6aed4d8 Mon Sep 17 00:00:00 2001 From: Nebojsa Stojanovic Date: Thu, 23 Jun 2022 17:27:14 +0200 Subject: [PATCH 2/8] Change multiple quantify button & component name --- packages/api/src/praise/controllers.ts | 1 - ...ipleButton.tsx => QuantifyMultipleButton.tsx} | 6 +++--- ...ipleDialog.tsx => QuantifyMultipleDialog.tsx} | 12 ++++++------ .../components/QuantifyTable.tsx | 16 ++++++++-------- 4 files changed, 17 insertions(+), 18 deletions(-) rename packages/frontend/src/pages/QuantifyPeriodReceiver/components/{PraiseMultipleButton.tsx => QuantifyMultipleButton.tsx} (84%) rename packages/frontend/src/pages/QuantifyPeriodReceiver/components/{PraiseMultipleDialog.tsx => QuantifyMultipleDialog.tsx} (84%) diff --git a/packages/api/src/praise/controllers.ts b/packages/api/src/praise/controllers.ts index 58f376026..403fb49da 100644 --- a/packages/api/src/praise/controllers.ts +++ b/packages/api/src/praise/controllers.ts @@ -246,6 +246,5 @@ export const quantifyMultiple = async ( ); const response = await praiseDocumentListTransformer(affectedPraises); - console.log('RESPONSE:', response); res.status(200).json(response); }; diff --git a/packages/frontend/src/pages/QuantifyPeriodReceiver/components/PraiseMultipleButton.tsx b/packages/frontend/src/pages/QuantifyPeriodReceiver/components/QuantifyMultipleButton.tsx similarity index 84% rename from packages/frontend/src/pages/QuantifyPeriodReceiver/components/PraiseMultipleButton.tsx rename to packages/frontend/src/pages/QuantifyPeriodReceiver/components/QuantifyMultipleButton.tsx index 6e2bc81c2..18569dfcf 100644 --- a/packages/frontend/src/pages/QuantifyPeriodReceiver/components/PraiseMultipleButton.tsx +++ b/packages/frontend/src/pages/QuantifyPeriodReceiver/components/QuantifyMultipleButton.tsx @@ -6,7 +6,7 @@ interface Props { onClick(); } -const PraiseMultipleButton = ({ +const QuantifyMultipleButton = ({ disabled = false, onClick, }: Props): JSX.Element => { @@ -21,9 +21,9 @@ const PraiseMultipleButton = ({ onClick={onClick} > - Praise + Quantify ); }; -export default PraiseMultipleButton; +export default QuantifyMultipleButton; diff --git a/packages/frontend/src/pages/QuantifyPeriodReceiver/components/PraiseMultipleDialog.tsx b/packages/frontend/src/pages/QuantifyPeriodReceiver/components/QuantifyMultipleDialog.tsx similarity index 84% rename from packages/frontend/src/pages/QuantifyPeriodReceiver/components/PraiseMultipleDialog.tsx rename to packages/frontend/src/pages/QuantifyPeriodReceiver/components/QuantifyMultipleDialog.tsx index 0e177b8fd..16d7854a9 100644 --- a/packages/frontend/src/pages/QuantifyPeriodReceiver/components/PraiseMultipleDialog.tsx +++ b/packages/frontend/src/pages/QuantifyPeriodReceiver/components/QuantifyMultipleDialog.tsx @@ -1,12 +1,12 @@ import ScrollableDialog from '@/components/ScrollableDialog'; -import PraiseMultipleButton from '@/pages/QuantifyPeriodReceiver/components/PraiseMultipleButton'; +import QuantifyMultipleButton from '@/pages/QuantifyPeriodReceiver/components/QuantifyMultipleButton'; import QuantifySlider from '@/pages/QuantifyPeriodReceiver/components/QuantifySlider'; import { faCalculator, faTimes } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { PraiseDto } from 'api/dist/praise/types'; import { useState } from 'react'; -interface PraiseMultipleDialogProps { +interface QuantifyMultipleDialogProps { open: boolean; onClose(): void; selectedPraises: PraiseDto[]; @@ -14,13 +14,13 @@ interface PraiseMultipleDialogProps { onSetScore(newScore: number, selectedPraises: PraiseDto[]); } -const PraiseMultipleDialog = ({ +const QuantifyMultipleDialog = ({ open = false, onClose, selectedPraises, allowedValues, onSetScore, -}: PraiseMultipleDialogProps): JSX.Element | null => { +}: QuantifyMultipleDialogProps): JSX.Element | null => { const [score, setScore] = useState(0); return ( @@ -47,7 +47,7 @@ const PraiseMultipleDialog = ({
- { onSetScore(score, selectedPraises); onClose(); @@ -60,4 +60,4 @@ const PraiseMultipleDialog = ({ ); }; -export default PraiseMultipleDialog; +export default QuantifyMultipleDialog; diff --git a/packages/frontend/src/pages/QuantifyPeriodReceiver/components/QuantifyTable.tsx b/packages/frontend/src/pages/QuantifyPeriodReceiver/components/QuantifyTable.tsx index cf5f391ac..92fa7a9c8 100644 --- a/packages/frontend/src/pages/QuantifyPeriodReceiver/components/QuantifyTable.tsx +++ b/packages/frontend/src/pages/QuantifyPeriodReceiver/components/QuantifyTable.tsx @@ -16,8 +16,8 @@ import MarkDuplicateButton from './MarkDuplicateButton'; import MarkDismissedButton from './MarkDismissedButton'; import QuantifyPraiseRow from './QuantifyPraiseRow'; import SearchInput from '@/components/form/SearchInput'; -import PraiseMultipleDialog from '@/pages/QuantifyPeriodReceiver/components/PraiseMultipleDialog'; -import PraiseMultipleButton from '@/pages/QuantifyPeriodReceiver/components/PraiseMultipleButton'; +import QuantifyMultipleDialog from '@/pages/QuantifyPeriodReceiver/components/QuantifyMultipleDialog'; +import QuantifyMultipleButton from '@/pages/QuantifyPeriodReceiver/components/QuantifyMultipleButton'; interface Props { periodId: string; @@ -45,7 +45,7 @@ const QuantifyTable = ({ periodId, receiverId }: Props): JSX.Element | null => { React.useState(false); const [isDuplicateSearchDialogOpen, setIsDuplicateSearchDialogOpen] = React.useState(false); - const [isPraiseMultipleDialogOpen, setIsPraiseMultipleDialogOpen] = + const [isQuantifyMultipleDialogOpen, setIsQuantifyMultipleDialogOpen] = React.useState(false); const [duplicateSearchDialogPraise, setDuplicateSearchDialogPraise] = React.useState(undefined); @@ -179,9 +179,9 @@ const QuantifyTable = ({ periodId, receiverId }: Props): JSX.Element | null => {
- setIsPraiseMultipleDialogOpen(true)} + onClick={(): void => setIsQuantifyMultipleDialogOpen(true)} />
@@ -266,9 +266,9 @@ const QuantifyTable = ({ periodId, receiverId }: Props): JSX.Element | null => { handleDuplicateSearchPraise(praiseId) } /> - setIsPraiseMultipleDialogOpen(false)} + setIsQuantifyMultipleDialogOpen(false)} selectedPraises={selectedPraises} allowedValues={allowedValues} onSetScore={(score, selectedPraises): void => From ac0ff3f052a250c6567c697a33e3a6647db1635f Mon Sep 17 00:00:00 2001 From: Nebojsa Stojanovic Date: Tue, 28 Jun 2022 23:15:51 +0200 Subject: [PATCH 3/8] Moved select all checkbox, fixed issue with duplicate praise scores, modified inputs, changed buttons style --- packages/api/src/praise/controllers.ts | 39 ++++++- .../src/components/form/MultiselectInput.tsx | 4 +- .../src/components/form/SearchInput.tsx | 2 +- .../src/components/form/SelectInput.tsx | 4 +- .../src/pages/EventLogs/EventLogsPage.tsx | 4 +- .../components/MarkDismissedButton.tsx | 13 ++- .../components/MarkDuplicateButton.tsx | 13 ++- .../components/QuantifyMultipleButton.tsx | 17 +-- .../components/QuantifyMultipleDialog.tsx | 2 +- .../components/QuantifyTable.tsx | 108 ++++++++++-------- 10 files changed, 129 insertions(+), 77 deletions(-) diff --git a/packages/api/src/praise/controllers.ts b/packages/api/src/praise/controllers.ts index 403fb49da..dbfccf678 100644 --- a/packages/api/src/praise/controllers.ts +++ b/packages/api/src/praise/controllers.ts @@ -28,6 +28,7 @@ import { import { PraiseAllInput, PraiseDetailsDto, + Praise, PraiseDocument, PraiseDto, QuantificationCreateUpdateInput, @@ -203,13 +204,21 @@ export const quantify = async ( res.status(200).json(response); }; +/** + * Quantify multiple praise items + * @param req + * @param res + */ export const quantifyMultiple = async ( req: TypedRequestBody, res: TypedResponse ): Promise => { const { score, praiseIds } = req.body; - const affectedPraises = await Promise.all( + let eventLogMessage = ''; + const duplicatePraises: PraiseDocument[] = []; + + const praiseItems = await Promise.all( praiseIds.map(async (id) => { const praise = await PraiseModel.findById(id).populate( 'giver receiver forwarder' @@ -235,16 +244,42 @@ export const quantifyMultiple = async ( 'User not assigned as quantifier for praise.' ); + const praisesDuplicateOfThis = await PraiseModel.find({ + quantifications: { + $elemMatch: { + quantifier: res.locals.currentUser._id, + duplicatePraise: praise._id, + }, + }, + }).populate('giver receiver forwarder'); + + if (praisesDuplicateOfThis?.length > 0) + duplicatePraises.push(...praisesDuplicateOfThis); + quantification.score = score; quantification.dismissed = false; quantification.duplicatePraise = undefined; await praise.save(); + eventLogMessage = `Gave a score of ${ + quantification.score + } to the praise with id "${(praise._id as Types.ObjectId).toString()}"`; + + await logEvent( + EventLogTypeKey.QUANTIFICATION, + eventLogMessage, + { + userId: res.locals.currentUser._id, + }, + period._id + ); + return praise; }) ); - const response = await praiseDocumentListTransformer(affectedPraises); + const affectedPraiseItems = [...praiseItems, ...duplicatePraises]; + const response = await praiseDocumentListTransformer(affectedPraiseItems); res.status(200).json(response); }; diff --git a/packages/frontend/src/components/form/MultiselectInput.tsx b/packages/frontend/src/components/form/MultiselectInput.tsx index f74ba64dc..00db6b5d1 100644 --- a/packages/frontend/src/components/form/MultiselectInput.tsx +++ b/packages/frontend/src/components/form/MultiselectInput.tsx @@ -22,9 +22,9 @@ const MultiselectInput = ({ noSelectedMessage, }: MultiselectInputProps): JSX.Element => { return ( -
+
- + {(): JSX.Element => ( <> diff --git a/packages/frontend/src/components/form/SearchInput.tsx b/packages/frontend/src/components/form/SearchInput.tsx index 9f61847e1..b26020f7e 100644 --- a/packages/frontend/src/components/form/SearchInput.tsx +++ b/packages/frontend/src/components/form/SearchInput.tsx @@ -13,7 +13,7 @@ const SearchInput = ({ placeholder = 'Search', }: SearchInputProps): JSX.Element => { return ( -
+
{ return ( -
+
- + {selected.label}
diff --git a/packages/frontend/src/pages/EventLogs/EventLogsPage.tsx b/packages/frontend/src/pages/EventLogs/EventLogsPage.tsx index 4d3a6919a..a3757045d 100644 --- a/packages/frontend/src/pages/EventLogs/EventLogsPage.tsx +++ b/packages/frontend/src/pages/EventLogs/EventLogsPage.tsx @@ -109,7 +109,7 @@ const EventLogsPage = (): JSX.Element => {
{/* Search */} -
+
{ setSearchValue(e); @@ -120,7 +120,7 @@ const EventLogsPage = (): JSX.Element => {
{/* Sort */} -
+
{ setSelectedSort(e); diff --git a/packages/frontend/src/pages/QuantifyPeriodReceiver/components/MarkDismissedButton.tsx b/packages/frontend/src/pages/QuantifyPeriodReceiver/components/MarkDismissedButton.tsx index 9c47a3446..0b225b59c 100644 --- a/packages/frontend/src/pages/QuantifyPeriodReceiver/components/MarkDismissedButton.tsx +++ b/packages/frontend/src/pages/QuantifyPeriodReceiver/components/MarkDismissedButton.tsx @@ -3,25 +3,26 @@ import { faMinusCircle } from '@fortawesome/free-solid-svg-icons'; interface Props { disabled?: boolean; + small?: boolean; onClick(); } const MarkDismissedButton = ({ disabled = false, onClick, + small, }: Props): JSX.Element => { + const disabledClass = disabled ? 'praise-button-disabled' : 'praise-button'; + const smallClass = small ? 'praise-button-round' : ''; + return ( ); }; diff --git a/packages/frontend/src/pages/QuantifyPeriodReceiver/components/MarkDuplicateButton.tsx b/packages/frontend/src/pages/QuantifyPeriodReceiver/components/MarkDuplicateButton.tsx index 003837108..ca8615444 100644 --- a/packages/frontend/src/pages/QuantifyPeriodReceiver/components/MarkDuplicateButton.tsx +++ b/packages/frontend/src/pages/QuantifyPeriodReceiver/components/MarkDuplicateButton.tsx @@ -3,25 +3,26 @@ import { faCopy } from '@fortawesome/free-solid-svg-icons'; interface Props { disabled?: boolean; + small?: boolean; onClick(); } const MarkDuplicateButton = ({ disabled = false, onClick, + small, }: Props): JSX.Element => { + const disabledClass = disabled ? 'praise-button-disabled' : 'praise-button'; + const smallClass = small ? 'praise-button-round' : ''; + return ( ); }; diff --git a/packages/frontend/src/pages/QuantifyPeriodReceiver/components/QuantifyMultipleButton.tsx b/packages/frontend/src/pages/QuantifyPeriodReceiver/components/QuantifyMultipleButton.tsx index 18569dfcf..440799bda 100644 --- a/packages/frontend/src/pages/QuantifyPeriodReceiver/components/QuantifyMultipleButton.tsx +++ b/packages/frontend/src/pages/QuantifyPeriodReceiver/components/QuantifyMultipleButton.tsx @@ -1,27 +1,28 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { faPrayingHands } from '@fortawesome/free-solid-svg-icons'; +import { faScaleUnbalanced } from '@fortawesome/free-solid-svg-icons'; interface Props { disabled?: boolean; + small?: boolean; onClick(); } const QuantifyMultipleButton = ({ disabled = false, onClick, + small, }: Props): JSX.Element => { + const disabledClass = disabled ? 'praise-button-disabled' : 'praise-button'; + const smallClass = small ? 'praise-button-round' : ''; + return ( ); }; diff --git a/packages/frontend/src/pages/QuantifyPeriodReceiver/components/QuantifyMultipleDialog.tsx b/packages/frontend/src/pages/QuantifyPeriodReceiver/components/QuantifyMultipleDialog.tsx index 16d7854a9..3a8127565 100644 --- a/packages/frontend/src/pages/QuantifyPeriodReceiver/components/QuantifyMultipleDialog.tsx +++ b/packages/frontend/src/pages/QuantifyPeriodReceiver/components/QuantifyMultipleDialog.tsx @@ -36,7 +36,7 @@ const QuantifyMultipleDialog = ({

- Quantify selected ({selectedPraises.length}) praises + Quantify selected ({selectedPraises.length}) praise items.

diff --git a/packages/frontend/src/pages/QuantifyPeriodReceiver/components/QuantifyTable.tsx b/packages/frontend/src/pages/QuantifyPeriodReceiver/components/QuantifyTable.tsx index 92fa7a9c8..4da978039 100644 --- a/packages/frontend/src/pages/QuantifyPeriodReceiver/components/QuantifyTable.tsx +++ b/packages/frontend/src/pages/QuantifyPeriodReceiver/components/QuantifyTable.tsx @@ -18,6 +18,7 @@ import QuantifyPraiseRow from './QuantifyPraiseRow'; import SearchInput from '@/components/form/SearchInput'; import QuantifyMultipleDialog from '@/pages/QuantifyPeriodReceiver/components/QuantifyMultipleDialog'; import QuantifyMultipleButton from '@/pages/QuantifyPeriodReceiver/components/QuantifyMultipleButton'; +import { Tooltip } from '@mui/material'; interface Props { periodId: string; @@ -58,13 +59,17 @@ const QuantifyTable = ({ periodId, receiverId }: Props): JSX.Element | null => { 'PRAISE_QUANTIFY_ALLOWED_VALUES' ) as number[]; - const applyFilter = React.useCallback( + const filterBySearchValue = React.useCallback( (data: PraiseDto[] | undefined): PraiseDto[] => { if (!data) return []; if (!searchValue) return data; const filteredData = data.filter((praise: PraiseDto) => { - return praise.reasonRealized.includes(searchValue); + const searchString = searchValue.toLowerCase(); + const reason = praise.reasonRealized.toLowerCase(); + const giver = praise.giver.name.toLowerCase(); + + return reason.includes(searchString) || giver.includes(searchString); }); return filteredData; @@ -140,7 +145,7 @@ const QuantifyTable = ({ periodId, receiverId }: Props): JSX.Element | null => { if (selectAllChecked) { setSelectedPraises([]); } else { - setSelectedPraises(applyFilter(data)); + setSelectedPraises(filterBySearchValue(data)); } }; @@ -150,7 +155,7 @@ const QuantifyTable = ({ periodId, receiverId }: Props): JSX.Element | null => { }; const weeklyData = groupBy( - sortBy(applyFilter(data), (p) => p.createdAt), + sortBy(filterBySearchValue(data), (p) => p.createdAt), (praise: PraiseDto) => { if (!praise) return 0; return getWeek(parseISO(praise.createdAt), { weekStartsOn: 1 }); @@ -163,55 +168,64 @@ const QuantifyTable = ({ periodId, receiverId }: Props): JSX.Element | null => { return (
-
-
- setIsDuplicateDialogOpen(true)} - /> -
- -
- setIsDismissDialogOpen(true)} - /> -
- -
- setIsQuantifyMultipleDialogOpen(true)} - /> -
- -
- handleSearchInput(e)} - placeholder="Filter" - /> +
+
+
+ +
+ +
+ +
+ setIsDuplicateDialogOpen(true)} + small={true} + /> +
+
+
+ +
+ +
+ setIsDismissDialogOpen(true)} + small={true} + /> +
+
+
+ +
+ +
+ setIsQuantifyMultipleDialogOpen(true)} + small={true} + /> +
+
+
+ +
+ handleSearchInput(e)} + placeholder="Filter" + /> +
+ + Select All +
+
+
- - - - - - - {Object.keys(weeklyData).map((weekKey, index) => ( {index !== 0 && index !== data.length - 1 && ( From da5b6d6cc67d243fec93fbfd154f25a0c5a0d0df Mon Sep 17 00:00:00 2001 From: kristoferlund Date: Wed, 29 Jun 2022 13:32:32 +0200 Subject: [PATCH 4/8] Fix: Added InternalServerError import --- packages/api/src/praise/controllers.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/api/src/praise/controllers.ts b/packages/api/src/praise/controllers.ts index bff54e1e3..c82768b63 100644 --- a/packages/api/src/praise/controllers.ts +++ b/packages/api/src/praise/controllers.ts @@ -1,4 +1,8 @@ -import { BadRequestError, NotFoundError } from '@error/errors'; +import { + BadRequestError, + InternalServerError, + NotFoundError, +} from '@error/errors'; import { getPraiseAllInput, getQueryInput, @@ -24,7 +28,6 @@ import { import { PraiseAllInput, PraiseDetailsDto, - Praise, PraiseDocument, PraiseDto, QuantificationCreateUpdateInput, From 7acf2a1562f44aca1bdb2d61a7c99e842fef9b09 Mon Sep 17 00:00:00 2001 From: kristoferlund Date: Thu, 30 Jun 2022 17:40:51 +0200 Subject: [PATCH 5/8] Fix: Margins and colours --- .../components/BackNextLink.tsx | 2 +- .../components/QuantifyPraiseRow.tsx | 16 ++- .../components/QuantifyTable.tsx | 100 +++++++----------- 3 files changed, 51 insertions(+), 67 deletions(-) diff --git a/packages/frontend/src/pages/QuantifyPeriodReceiver/components/BackNextLink.tsx b/packages/frontend/src/pages/QuantifyPeriodReceiver/components/BackNextLink.tsx index a2e0ac858..0510498e5 100644 --- a/packages/frontend/src/pages/QuantifyPeriodReceiver/components/BackNextLink.tsx +++ b/packages/frontend/src/pages/QuantifyPeriodReceiver/components/BackNextLink.tsx @@ -34,7 +34,7 @@ export const QuantifyBackNextLink = ({ } return ( -
+
{backReceiver && ( -
+ - -
- - Select All -
-
-
+
+ )} +