option to link feedbackResponses to additional comments

Signed-off-by: nikolar <reyna.nikolayev@autodesk.com>
This commit is contained in:
nikolar
2024-01-22 21:59:04 -08:00
parent 48b81a80f0
commit 510a94d89f
5 changed files with 64 additions and 10 deletions
@@ -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": "*",
@@ -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({
@@ -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 (
<Dialog open={open} onClose={() => !saving && onClose()}>
{saving && <Progress />}
<DialogTitle>{feedbackDialogTitle}</DialogTitle>
<DialogContent>
<FormControl component="fieldset">
<FormLabel component="legend">Choose all that applies</FormLabel>
<FormLabel component="legend">Choose all that apply</FormLabel>
<FormGroup>
{feedbackDialogResponses.map(response => (
<FormControlLabel
@@ -138,7 +188,7 @@ export const FeedbackResponseDialog = (props: FeedbackResponseDialogProps) => {
label="Additional comments"
multiline
minRows={2}
onChange={e => setComments(e.target.value)}
onChange={e => verifyComments(e)}
variant="outlined"
value={comments}
/>
@@ -80,9 +80,12 @@ export const FeedbackResponseTable = (props: FeedbackResponseTableProps) => {
width: '35%',
render: (response: ResponseRow) => (
<>
{response.response?.split(',').map(res => (
<Chip key={res} size="small" label={res} />
))}
{response.response?.length !== undefined &&
response.response?.length > 0
? response.response
?.split(',')
.map(res => <Chip key={res} size="small" label={res} />)
: ''}
</>
),
},
+1
View File
@@ -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": "*"