From 510a94d89f9b4a886610f4171dfdd5416335ed18 Mon Sep 17 00:00:00 2001 From: nikolar Date: Mon, 22 Jan 2024 21:59:04 -0800 Subject: [PATCH] option to link feedbackResponses to additional comments Signed-off-by: nikolar --- plugins/entity-feedback-backend/package.json | 1 + .../src/service/router.ts | 5 +- .../FeedbackResponseDialog.tsx | 58 +++++++++++++++++-- .../FeedbackResponseTable.tsx | 9 ++- yarn.lock | 1 + 5 files changed, 64 insertions(+), 10 deletions(-) diff --git a/plugins/entity-feedback-backend/package.json b/plugins/entity-feedback-backend/package.json index 896b2c9c14..14bfabd98b 100644 --- a/plugins/entity-feedback-backend/package.json +++ b/plugins/entity-feedback-backend/package.json @@ -33,6 +33,7 @@ "@backstage/catalog-client": "workspace:^", "@backstage/catalog-model": "workspace:^", "@backstage/config": "workspace:^", + "@backstage/errors": "workspace:^", "@backstage/plugin-auth-node": "workspace:^", "@backstage/plugin-entity-feedback-common": "workspace:^", "@types/express": "*", diff --git a/plugins/entity-feedback-backend/src/service/router.ts b/plugins/entity-feedback-backend/src/service/router.ts index 7cf89bfd5c..8d8cb0d600 100644 --- a/plugins/entity-feedback-backend/src/service/router.ts +++ b/plugins/entity-feedback-backend/src/service/router.ts @@ -28,6 +28,7 @@ import { EntityRatingsData, Ratings, } from '@backstage/plugin-entity-feedback-common'; +import { InputError } from '@backstage/errors'; import express from 'express'; import Router from 'express-promise-router'; import { Logger } from 'winston'; @@ -138,11 +139,9 @@ export async function createRouter( const rating = req.body.rating; if (!rating) { - logger.warn( + throw new InputError( `Can't save rating because there is not enough info: user=${credentials.principal.userEntityRef}, rating=${rating}`, ); - res.status(400).end(); - return; } await dbHandler.recordRating({ diff --git a/plugins/entity-feedback/src/components/FeedbackResponseDialog/FeedbackResponseDialog.tsx b/plugins/entity-feedback/src/components/FeedbackResponseDialog/FeedbackResponseDialog.tsx index c3f935e432..4e98f3b8d8 100644 --- a/plugins/entity-feedback/src/components/FeedbackResponseDialog/FeedbackResponseDialog.tsx +++ b/plugins/entity-feedback/src/components/FeedbackResponseDialog/FeedbackResponseDialog.tsx @@ -16,7 +16,12 @@ import { Entity, stringifyEntityRef } from '@backstage/catalog-model'; import { Progress } from '@backstage/core-components'; -import { ErrorApiError, errorApiRef, useApi } from '@backstage/core-plugin-api'; +import { + ErrorApiError, + errorApiRef, + useApi, + alertApiRef, +} from '@backstage/core-plugin-api'; import { Button, Checkbox, @@ -45,12 +50,13 @@ import { entityFeedbackApiRef } from '../../api'; export interface EntityFeedbackResponse { id: string; label: string; + mustComment?: boolean; } const defaultFeedbackResponses: EntityFeedbackResponse[] = [ { id: 'incorrect', label: 'Incorrect info' }, { id: 'missing', label: 'Missing info' }, - { id: 'other', label: 'Other (please specify below)' }, + { id: 'other', label: 'Other (please specify below)', mustComment: true }, ]; /** @@ -81,13 +87,41 @@ export const FeedbackResponseDialog = (props: FeedbackResponseDialogProps) => { const classes = useStyles(); const errorApi = useApi(errorApiRef); const feedbackApi = useApi(entityFeedbackApiRef); + const alertApi = useApi(alertApiRef); const [responseSelections, setResponseSelections] = useState( Object.fromEntries(feedbackDialogResponses.map(r => [r.id, false])), ); const [comments, setComments] = useState(''); const [consent, setConsent] = useState(true); + const requireComments = feedbackDialogResponses + .filter(r => r.mustComment) + .map(r => r.id); + + const isMandatedBoxChecked = () => { + const checkedBoxes = Object.keys(responseSelections).filter( + id => responseSelections[id], + ); + return checkedBoxes.some(id => requireComments.includes(id)); + }; const [{ loading: saving }, saveResponse] = useAsyncFn(async () => { + if (requireComments.length > 0) { + if (comments.length === 0 && isMandatedBoxChecked()) { + alertApi.post({ + message: + 'The selected option(s) require a comment. Please provide a comment.', + severity: 'info', + }); + return; + } + if (comments.length > 0 && !isMandatedBoxChecked()) { + alertApi.post({ + message: 'Please select the option(s) that require a comment.', + severity: 'info', + }); + return; + } + } try { await feedbackApi.recordResponse(stringifyEntityRef(entity), { comments, @@ -102,13 +136,29 @@ export const FeedbackResponseDialog = (props: FeedbackResponseDialogProps) => { } }, [comments, consent, entity, feedbackApi, onClose, responseSelections]); + const selectMandatedBox = (res: boolean) => { + const newResponseSelections = { ...responseSelections }; + requireComments.forEach(id => newResponseSelections[id] === res); + setResponseSelections(newResponseSelections); + }; + + const verifyComments = (e: any) => { + setComments(e.target.value); + if (requireComments.length > 0) { + selectMandatedBox(true); + if (e.target.value.length === 0) { + selectMandatedBox(false); + } + } + }; + return ( !saving && onClose()}> {saving && } {feedbackDialogTitle} - Choose all that applies + Choose all that apply {feedbackDialogResponses.map(response => ( { label="Additional comments" multiline minRows={2} - onChange={e => setComments(e.target.value)} + onChange={e => verifyComments(e)} variant="outlined" value={comments} /> diff --git a/plugins/entity-feedback/src/components/FeedbackResponseTable/FeedbackResponseTable.tsx b/plugins/entity-feedback/src/components/FeedbackResponseTable/FeedbackResponseTable.tsx index 863cdcc6b6..22155f90ce 100644 --- a/plugins/entity-feedback/src/components/FeedbackResponseTable/FeedbackResponseTable.tsx +++ b/plugins/entity-feedback/src/components/FeedbackResponseTable/FeedbackResponseTable.tsx @@ -80,9 +80,12 @@ export const FeedbackResponseTable = (props: FeedbackResponseTableProps) => { width: '35%', render: (response: ResponseRow) => ( <> - {response.response?.split(',').map(res => ( - - ))} + {response.response?.length !== undefined && + response.response?.length > 0 + ? response.response + ?.split(',') + .map(res => ) + : ''} ), }, diff --git a/yarn.lock b/yarn.lock index 1d2fe96275..13d7effd5c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6288,6 +6288,7 @@ __metadata: "@backstage/catalog-model": "workspace:^" "@backstage/cli": "workspace:^" "@backstage/config": "workspace:^" + "@backstage/errors": "workspace:^" "@backstage/plugin-auth-node": "workspace:^" "@backstage/plugin-entity-feedback-common": "workspace:^" "@types/express": "*"