From 7c5008c49c8ea2411f659851a88cbdf60a4dd1ea Mon Sep 17 00:00:00 2001 From: Joe Slain Date: Fri, 13 Dec 2024 16:17:34 +0100 Subject: [PATCH] feat: evaluations call working --- src/api/useAddFeedback.tsx | 11 +- src/components/Feedbacks/Feedback.tsx | 6 +- src/components/Feedbacks/UserExperience.tsx | 38 ---- .../Feedbacks/UserFeedbackOptions.tsx | 14 +- .../Feedbacks/UserSatisfactionButtons.tsx | 4 +- src/pages/Evaluations.tsx | 198 +++++++++++++----- src/types.ts | 60 ++++-- 7 files changed, 218 insertions(+), 113 deletions(-) delete mode 100644 src/components/Feedbacks/UserExperience.tsx diff --git a/src/api/useAddFeedback.tsx b/src/api/useAddFeedback.tsx index 5f21bf2b..ad6a7bad 100644 --- a/src/api/useAddFeedback.tsx +++ b/src/api/useAddFeedback.tsx @@ -12,16 +12,19 @@ export function useAddFeedback() { interface AddFeedbackParams { feedback: Feedback streamId: number - reasons: string[] } -const addFeedback = async ({ feedback, streamId, reasons }: AddFeedbackParams) => { +const addFeedback = async ({ feedback, streamId }: AddFeedbackParams) => { + console.log('addFeedback', feedback, streamId) const authToken = localStorage.getItem('authToken') const data = { is_good: !feedback.isGood, message: feedback.message, - reason: - reasons[0] === 'other' ? (reasons[1]?.length ? reasons[1] : null) : reasons[0], + positives: feedback.positives ? feedback.positives : [], + negatives: feedback.negatives ? feedback.negatives : [], + note: feedback.note, + is_confirmed: feedback.isConfirmed, + type: feedback.type, } const res = await fetch(`${feedbackUrl}/${streamId}`, { method: 'POST', diff --git a/src/components/Feedbacks/Feedback.tsx b/src/components/Feedbacks/Feedback.tsx index a3632c56..d4515008 100644 --- a/src/components/Feedbacks/Feedback.tsx +++ b/src/components/Feedbacks/Feedback.tsx @@ -1,4 +1,4 @@ -import { InitialFeedback, type Feedback as FeedbackType } from '@types' +import { InitialChatFeedback, type Feedback as FeedbackType } from '@types' import { useEffect, useState } from 'react' import { GlobalColContainer } from '../Global/GlobalColContainer' import { UserFeedbackInput } from './UserFeedbackInput' @@ -11,10 +11,10 @@ import { UserSatisfactionButtons } from './UserSatisfactionButtons' * false when giving feedback on a question that has been regenerated */ export function Feedback() { - const [feedback, setFeedback] = useState(InitialFeedback) + const [feedback, setFeedback] = useState(InitialChatFeedback) useEffect(() => { - setFeedback(InitialFeedback) + setFeedback(InitialChatFeedback) }, []) return ( diff --git a/src/components/Feedbacks/UserExperience.tsx b/src/components/Feedbacks/UserExperience.tsx deleted file mode 100644 index 14f5a39e..00000000 --- a/src/components/Feedbacks/UserExperience.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import { askingQuality, redoAskingQuality } from '@constants/feedback' -import type { RootState } from '@types' -import { useSelector } from 'react-redux' -import { Avatar } from '../Chat/Avatar' -import { GlobalRowContainer } from '../Global/GlobalRowContainer' -import { Feedback } from './Feedback' -const AskingResponseQuality = ({ tabsLen }) => { - return ( -
-
{tabsLen > 1 ? redoAskingQuality : askingQuality}
-
- ) -} - -export function UserExperience() { - const stream = useSelector((state: RootState) => state.stream) - const tabsLen = stream.historyStream.length - - return ( - <> - {stream.activeTab === tabsLen && ( -
- -
- -
- -
-
-
- -
-
-
- )} - - ) -} diff --git a/src/components/Feedbacks/UserFeedbackOptions.tsx b/src/components/Feedbacks/UserFeedbackOptions.tsx index 902caafc..8cf42508 100644 --- a/src/components/Feedbacks/UserFeedbackOptions.tsx +++ b/src/components/Feedbacks/UserFeedbackOptions.tsx @@ -54,7 +54,17 @@ export function UserFeedbackOptions({ ) } -const ConfirmationButton = ({ reasons, otherReason, feedback, setFeedback }) => { +const ConfirmationButton = ({ + reasons, + otherReason, + feedback, + setFeedback, +}: { + reasons: string[] + otherReason: string + feedback: FeedbackType + setFeedback: (feedback: FeedbackType) => void +}) => { const streamId = useSelector((state: RootState) => state.user.lastStreamId) const addFeedback = useAddFeedback() @@ -65,7 +75,7 @@ const ConfirmationButton = ({ reasons, otherReason, feedback, setFeedback }) => ...feedback, message: otherReason, }) - addFeedback.mutate({ feedback, streamId, reasons }) + addFeedback.mutate({ feedback, streamId }) setFeedback({ ...feedback, isConfirmed: true, diff --git a/src/components/Feedbacks/UserSatisfactionButtons.tsx b/src/components/Feedbacks/UserSatisfactionButtons.tsx index 554436e2..e65b3b43 100644 --- a/src/components/Feedbacks/UserSatisfactionButtons.tsx +++ b/src/components/Feedbacks/UserSatisfactionButtons.tsx @@ -1,4 +1,4 @@ -import { InitialFeedback, type Feedback as FeedbackType } from '@types' +import { InitialChatFeedback, type Feedback as FeedbackType } from '@types' import { GlobalRowContainer } from '../Global/GlobalRowContainer' import { fr } from '@codegouvfr/react-dsfr' @@ -15,7 +15,7 @@ export function UserSatisfactionButtons({ setFeedback: (feedback: FeedbackType) => void }) { const handleClick = (isGood: number) => { - if (isGood === feedback.isGood) setFeedback(InitialFeedback) + if (isGood === feedback.isGood) setFeedback(InitialChatFeedback) else { setFeedback({ ...feedback, diff --git a/src/pages/Evaluations.tsx b/src/pages/Evaluations.tsx index ce753982..cd3198de 100644 --- a/src/pages/Evaluations.tsx +++ b/src/pages/Evaluations.tsx @@ -1,4 +1,4 @@ -import { chatUrl, streamUrl } from '@api' +import { chatUrl, streamUrl, useAddFeedback } from '@api' import { Notice } from '@codegouvfr/react-dsfr/Notice' import StarIcon from '@mui/icons-material/Star' import Box from '@mui/material/Box' @@ -9,6 +9,14 @@ import { TextWithSources } from 'components/Sources/TextWithSources' import { EventSourcePolyfill } from 'event-source-polyfill' import { useCallback, useEffect, useRef, useState } from 'react' import { onCloseStream } from '../utils/eventsEmitter' +import type { + NegativeFeedbackArray, + NegativeReason, + PositiveFeedbackArray, + PositiveReason, +} from '@types' +import { LoadingSpinner } from 'components/LoadingSpinner' +import { set } from 'valibot' //import ShowError from 'components/Error/ShowError' const questions = [ @@ -51,10 +59,10 @@ export default function Evaluations() { } return ( -
+
{selectedCardIndex === null ? ( <> -

Sélectionnez une question à évaluer

+

Sélectionnez une question

) : ( @@ -174,53 +182,91 @@ function QuestionDetail({ question, theme, operator, onBack, complexity }) { } function EvaluationPannel({ isStreamFinished, onBack }) { - const [positiveFeedback, setPositiveFeedback] = useState([]) - const [negativeFeedback, setNegativeFeedback] = useState([]) + const [positiveFeedback, setPositiveFeedback] = useState([]) + const [negativeFeedback, setNegativeFeedback] = useState([]) const [comments, setComments] = useState('') const [showNotice, setShowNotice] = useState(false) + const [showErrorNotice, setShowErrorNotice] = useState(false) const [rating, setRating] = useState(null) const [progress, setProgress] = useState(100) - - const positiveTags = ['Clair', 'Synthétique', 'Complet', 'Sources fiables'] - const negativeTags = ['Incorrect', 'Incohérent', 'Manque de sources'] + const addFeedback = useAddFeedback() + + const positiveTags: { + [index: string]: PositiveReason + } = { + Clair: 'clair', + Synthétique: 'synthetique', + Complet: 'complet', + 'Sources fiables': 'sources_fiables', + } + const negativeTags: { + [index: string]: NegativeReason + } = { + Incorrect: 'incorrect', + Incohérent: 'incoherent', + 'Manque de sources': 'manque_de_sources', + } const isSubmitDisabled = !( rating && (positiveFeedback.length > 0 || negativeFeedback.length > 0) && isStreamFinished ) - const handlePositiveTagClick = (tag: string) => { + const handlePositiveTagClick = (tag: PositiveReason) => { setPositiveFeedback((prev) => prev.includes(tag) ? prev.filter((t) => t !== tag) : [...prev, tag], ) } - const handleNegativeTagClick = (tag: string) => { + const handleNegativeTagClick = (tag: NegativeReason) => { setNegativeFeedback((prev) => prev.includes(tag) ? prev.filter((t) => t !== tag) : [...prev, tag], ) } const handleSubmit = () => { + setShowErrorNotice(false) // TODO: BACK END LOGIC - if (isSubmitDisabled) return - setShowNotice(true) - setProgress(100) - - const totalDuration = 3000 - const intervalTime = 100 - const decrement = 100 / (totalDuration / intervalTime) - - const interval = setInterval(() => { - setProgress((prevProgress) => { - const newProgress = prevProgress - decrement - if (newProgress <= 0) { - clearInterval(interval) - setShowNotice(false) - onBack() - return 0 - } - return newProgress - }) - }, intervalTime) + //if (isSubmitDisabled) return + + addFeedback.mutate( + { + feedback: { + isGood: null, + type: 'evaluations', + message: comments, + positives: positiveFeedback, + negatives: negativeFeedback, + note: rating, + isConfirmed: true, + }, + streamId: 1, + }, + { + onSuccess: () => { + setShowNotice(true) + setProgress(100) + + const totalDuration = 3000 + const intervalTime = 100 + const decrement = 100 / (totalDuration / intervalTime) + + const interval = setInterval(() => { + setProgress((prevProgress) => { + const newProgress = prevProgress - decrement + if (newProgress <= 0) { + clearInterval(interval) + setShowNotice(false) + onBack() + return 0 + } + return newProgress + }) + }, intervalTime) + }, + onError: (error) => { + setShowErrorNotice(true) + }, + }, + ) } return ( <> @@ -245,34 +291,34 @@ function EvaluationPannel({ isStreamFinished, onBack }) {
{/* Positive tags */}
- +
- {positiveTags.map((tag) => ( + {Object.entries(positiveTags).map(([tagKey, tagValue]) => ( ))}
{/* Negative tags */}
- +
- {negativeTags.map((tag) => ( + {Object.entries(negativeTags).map(([tagKey, tagValue]) => ( ))}
@@ -299,12 +345,12 @@ function EvaluationPannel({ isStreamFinished, onBack }) {
@@ -329,6 +375,25 @@ function EvaluationPannel({ isStreamFinished, onBack }) {
)} + {showErrorNotice && ( +
+
+ + {/* Progress bar over the Notice component */} +
+
+
+
+
+
+
+ )}
) @@ -497,7 +562,7 @@ export function HoverRating({ value, setValue }) { { setValue(newValue) @@ -516,3 +581,42 @@ export function HoverRating({ value, setValue }) { ) } + +function AlertNotice({ + setShowErrorNotice, +}: { setShowErrorNotice: (value: boolean) => void }) { + return ( +
+
+
+

+ + Erreur lors de l'envoi de votre évaluation + +

+ + Vous pouvez nous contacter pour nous signaler le problème. + + + Formulaire de contact + + +
+
+
+ ) +} diff --git a/src/types.ts b/src/types.ts index 8e1437ed..95b3874d 100644 --- a/src/types.ts +++ b/src/types.ts @@ -135,6 +135,49 @@ export type ChatCompletion = { } } +/**************************************************************** + * FEEDBACK * + ****************************************************************/ + +export type PositiveReason = 'clair' | 'synthetique' | 'complet' | 'sources_fiables' +export type NegativeReason = 'incorrect' | 'incoherent' | 'manque_de_sources' + +export type PositiveFeedbackArray = Array +export type NegativeFeedbackArray = Array + +/**œ + * Feedback form after a meeting response + */ +export interface Feedback { + type: 'chat' | 'evaluations' + note: number | undefined + positives: PositiveFeedbackArray + negatives: NegativeFeedbackArray + isConfirmed: boolean + isGood: number | undefined | null + message: string +} + +export const InitialChatFeedback: Feedback = { + note: undefined, + positives: [], + negatives: [], + type: 'chat', + isConfirmed: false, + isGood: undefined, + message: '', +} + +export const InitialEvaluationsFeedback: Feedback = { + note: 0.5, + positives: [], + negatives: [], + type: 'evaluations', + isConfirmed: false, + isGood: undefined, + message: '', +} + /**************************************************************** * OTHER * ****************************************************************/ @@ -187,20 +230,3 @@ type RelatedQuestion = { sid: string url: string } - -/** - * Feedback form after a meeting response - */ -export interface Feedback { - reasons: string[] - isConfirmed: boolean - isGood: number | undefined - message: string -} - -export const InitialFeedback: Feedback = { - reasons: [], - isConfirmed: false, - isGood: undefined, - message: '', -}