From 8ae1953c71f3420887f7792bd4a3c8cf57f21a84 Mon Sep 17 00:00:00 2001 From: valentin-vrps Date: Fri, 5 Jul 2024 15:09:37 +0200 Subject: [PATCH] features improvement --- server/controllers/questions.ts | 5 +- server/entities/question.ts | 11 +++- server/translations/defaultLocales.ts | 6 +- src/components/collaboration/FormFeedback.tsx | 13 ++++- .../DiaporamaPlayer/DiaporamaPlayer.tsx | 2 +- src/components/layout/Form/input.module.scss | 1 - src/pages/create/3-storyboard/edit.tsx | 6 +- src/pages/create/3-storyboard/index.tsx | 6 +- src/pages/create/3-storyboard/title.tsx | 6 +- src/pages/create/4-pre-mounting/edit.tsx | 6 +- src/pages/create/4-pre-mounting/index.tsx | 4 +- src/pages/create/6-result/index.tsx | 55 ++++--------------- types/models/question.type.ts | 2 +- 13 files changed, 54 insertions(+), 69 deletions(-) diff --git a/server/controllers/questions.ts b/server/controllers/questions.ts index 307cd80a..09636dc8 100644 --- a/server/controllers/questions.ts +++ b/server/controllers/questions.ts @@ -257,7 +257,10 @@ questionController.put({ path: '/:id', userType: UserType.CLASS }, async (req, r logger.info(`dataStatus: ${dataStatus}`); if (dataStatus !== undefined && dataStatus !== null) { question.status = dataStatus; - question.feedback = [QuestionStatus.ONGOING, QuestionStatus.PREMOUNTING].includes(dataStatus) && data.feedback ? data.feedback : null; + question.feedbacks = + [QuestionStatus.ONGOING, QuestionStatus.PREMOUNTING].includes(dataStatus) && data.feedback + ? [...(question.feedbacks || []), data.feedback] + : question.feedbacks || []; } await getRepository(Question).save(question); res.sendJSON(question); diff --git a/server/entities/question.ts b/server/entities/question.ts index 70f717ed..33561f1b 100644 --- a/server/entities/question.ts +++ b/server/entities/question.ts @@ -49,6 +49,13 @@ export class Question implements QuestionInterface { }) status: QuestionStatus; - @Column({ type: 'varchar', length: 2000, nullable: true }) - public feedback: string | null; + @Column({ + type: 'text', + nullable: true, + transformer: { + to: (value: string[] | null) => (value ? JSON.stringify(value) : null), + from: (value: string | null) => (value ? JSON.parse(value) : null), + }, + }) + public feedbacks: string[] | null; } diff --git a/server/translations/defaultLocales.ts b/server/translations/defaultLocales.ts index c3d02689..ace92881 100644 --- a/server/translations/defaultLocales.ts +++ b/server/translations/defaultLocales.ts @@ -107,13 +107,8 @@ export const locales = { part6_subtitle1: 'À cette étape, vous pouvez pré-visualiser votre diaporama sonore achevé.', part6_pdf_button: 'Télécharger le storyboard', part6_mlt_button: 'Télécharger le fichier de montage', - part6_mp4_button: 'Générer votre vidéo', part6_mp4_download_button: 'Télécharger votre vidéo !', - part6_mp4_generate_button: 'Générer une nouvelle vidéo', part6_mp4_loading: 'Création de votre vidéo...', - part6_mp4_description_1: 'La génération de votre vidéo peut prendre du temps.', - part6_mp4_description_2: "Vous pouvez quitter et suivre à tout moment l'avancement du montage de votre vidéo sur cette page.", - part6_mp4_description_3: 'Votre vidéo sera disponible pendant 2 jours. Passé ce delai elle sera supprimée.', part6_mp4_user_disabled: 'Connectez-vous et créez un projet pour générer une vidéo.', part6_mp4_project_disabled: 'Créez un projet pour générer une vidéo.', part6_subtitle2: @@ -297,6 +292,7 @@ export const locales = { collaboration_form_feedback_btn_feedback: 'Envoyer le retour', collaboration_form_feedback_btn_ok: 'Valider le travail', collaboration_form_feedback_error: 'Veuillez renseigner un message de retour.', + collaboration_previous_feedbacks_label: 'Retours précédents', collaboration_form_feedback_label: 'Retours', collaboration_form_feedback_placeholder: 'Vos retours (Raccourcir la durée de la séquence, monter le son, ...)', collaboration_form_feedback_title: 'Travail à vérifier', diff --git a/src/components/collaboration/FormFeedback.tsx b/src/components/collaboration/FormFeedback.tsx index b3d9961d..651abab1 100644 --- a/src/components/collaboration/FormFeedback.tsx +++ b/src/components/collaboration/FormFeedback.tsx @@ -42,7 +42,7 @@ export const FormFeedback: React.FunctionComponent = ({ quest newQuestions[question.index] = { ...newQuestions[question.index], status, - feedback: feedbackData, + feedbacks: feedbackData ? [...(question.feedbacks ?? []), feedbackData] : question.feedbacks, }; const updatedProject = updateProject({ questions: newQuestions }); if (updatedProject) { @@ -87,6 +87,17 @@ export const FormFeedback: React.FunctionComponent = ({ quest {t('collaboration_form_feedback_title')} + {question.feedbacks && question.feedbacks.length > 0 && ( +
+
{t('collaboration_previous_feedbacks_label')} :
+
    + {question.feedbacks.map((feedbackItem, index) => ( +
  • {feedbackItem}
  • + ))} +
+
+ )} + diff --git a/src/components/layout/Form/input.module.scss b/src/components/layout/Form/input.module.scss index 4bc4fe17..55131e72 100644 --- a/src/components/layout/Form/input.module.scss +++ b/src/components/layout/Form/input.module.scss @@ -3,7 +3,6 @@ .inputContainer { display: block; position: relative; - width: 300px; &--is-full-width { width: 100%; diff --git a/src/pages/create/3-storyboard/edit.tsx b/src/pages/create/3-storyboard/edit.tsx index 95a9cd1e..bc00bd38 100644 --- a/src/pages/create/3-storyboard/edit.tsx +++ b/src/pages/create/3-storyboard/edit.tsx @@ -154,7 +154,7 @@ const EditPlan = () => { const { user } = React.useContext(userContext); const isStudent = user?.type === UserType.STUDENT; const [showButtonFeedback, setShowButtonFeedback] = React.useState( - (isStudent && sequence && sequence.feedback && QuestionStatus.ONGOING === sequence.status) as boolean, + (isStudent && sequence && sequence.feedbacks && QuestionStatus.ONGOING === sequence.status) as boolean, ); const [showFeedback, setShowFeedback] = React.useState(false); const imageUrl = React.useMemo(() => { @@ -165,7 +165,7 @@ const EditPlan = () => { }, [imageBlob, plan]); React.useEffect(() => { - if (isStudent && sequence && sequence.feedback && QuestionStatus.ONGOING === sequence.status) { + if (isStudent && sequence && sequence.feedbacks && QuestionStatus.ONGOING === sequence.status) { setShowButtonFeedback(true); } }, [isStudent, sequence]); @@ -531,7 +531,7 @@ const EditPlan = () => { setShowFeedback(false)} - feedback={sequence && sequence.feedback ? sequence.feedback : ''} + feedback={sequence && sequence.feedbacks && sequence.feedbacks.length > 0 ? sequence.feedbacks[sequence.feedbacks.length - 1] : ''} /> ); diff --git a/src/pages/create/3-storyboard/index.tsx b/src/pages/create/3-storyboard/index.tsx index ad00d682..a1604f02 100644 --- a/src/pages/create/3-storyboard/index.tsx +++ b/src/pages/create/3-storyboard/index.tsx @@ -75,7 +75,7 @@ const Scenario = ({ const plans = React.useMemo(() => sequence.plans || [], [sequence]); const [showFeedback, setShowFeedback] = React.useState(false); - const showButtonFeedback = isStudent && sequence.feedback && QuestionStatus.ONGOING === sequence.status; + const showButtonFeedback = isStudent && sequence.feedbacks && QuestionStatus.ONGOING === sequence.status; const studentColor = COLORS[sequenceIndex]; const { project, isLoading: isProjectLoading, questions, updateProject } = useCurrentProject(); @@ -267,7 +267,9 @@ const Scenario = ({ setShowFeedback(false)} - feedback={sequence && sequence.feedback ? sequence.feedback : ''} + feedback={ + sequence && sequence.feedbacks && sequence.feedbacks.length > 0 ? sequence.feedbacks[sequence.feedbacks.length - 1] : '' + } /> {isCollaborationActive && !isStudent && sequence.status === QuestionStatus.STORYBOARD && ( diff --git a/src/pages/create/3-storyboard/title.tsx b/src/pages/create/3-storyboard/title.tsx index a5d269d6..317dd2bb 100644 --- a/src/pages/create/3-storyboard/title.tsx +++ b/src/pages/create/3-storyboard/title.tsx @@ -71,12 +71,12 @@ const TitlePlan = () => { const isStudent = user?.type === UserType.STUDENT; const [showButtonFeedback, setShowButtonFeedback] = React.useState( - (isStudent && sequence && sequence.feedback && QuestionStatus.ONGOING === sequence.status) as boolean, + (isStudent && sequence && sequence.feedbacks && QuestionStatus.ONGOING === sequence.status) as boolean, ); const [showFeedback, setShowFeedback] = React.useState(false); React.useEffect(() => { - if (isStudent && sequence && sequence.feedback && QuestionStatus.ONGOING === sequence.status) { + if (isStudent && sequence && sequence.feedbacks && QuestionStatus.ONGOING === sequence.status) { setShowButtonFeedback(true); } }, [isStudent, sequence]); @@ -165,7 +165,7 @@ const TitlePlan = () => { setShowFeedback(false)} - feedback={sequence && sequence.feedback ? sequence.feedback : ''} + feedback={sequence && sequence.feedbacks && sequence.feedbacks.length > 0 ? sequence.feedbacks[sequence.feedbacks.length - 1] : ''} /> ); diff --git a/src/pages/create/4-pre-mounting/edit.tsx b/src/pages/create/4-pre-mounting/edit.tsx index f51c5a42..e5964f86 100644 --- a/src/pages/create/4-pre-mounting/edit.tsx +++ b/src/pages/create/4-pre-mounting/edit.tsx @@ -83,12 +83,12 @@ const PreMountSequence = () => { const { user } = React.useContext(userContext); const isStudent = user?.type === UserType.STUDENT; const [showButtonFeedback, setShowButtonFeedback] = React.useState( - (isStudent && sequence && sequence.feedback && QuestionStatus.PREMOUNTING === sequence.status) as boolean, + (isStudent && sequence && sequence.feedbacks && QuestionStatus.PREMOUNTING === sequence.status) as boolean, ); const [showFeedback, setShowFeedback] = React.useState(false); React.useEffect(() => { - if (isStudent && sequence && sequence.feedback && QuestionStatus.PREMOUNTING === sequence.status) { + if (isStudent && sequence && sequence.feedbacks && QuestionStatus.PREMOUNTING === sequence.status) { setShowButtonFeedback(true); } }, [isStudent, sequence]); @@ -337,7 +337,7 @@ const PreMountSequence = () => { setShowFeedback(false)} - feedback={sequence && sequence.feedback ? sequence.feedback : ''} + feedback={sequence && sequence.feedbacks && sequence.feedbacks.length > 0 ? sequence.feedbacks[sequence.feedbacks.length - 1] : ''} /> ); diff --git a/src/pages/create/4-pre-mounting/index.tsx b/src/pages/create/4-pre-mounting/index.tsx index 453466f1..179eac2d 100644 --- a/src/pages/create/4-pre-mounting/index.tsx +++ b/src/pages/create/4-pre-mounting/index.tsx @@ -85,7 +85,7 @@ const PreMountingPage = () => { {questions.map((q, index) => { if (isStudent && sequencyId !== q.id) return null; const hasBeenEdited = q.title !== null || (q.plans || []).some((plan) => plan.description || plan.imageUrl); - const showButtonFeedback = isStudent && q.id === sequencyId && q.feedback && QuestionStatus.PREMOUNTING === q.status; + const showButtonFeedback = isStudent && q.id === sequencyId && q.feedbacks && QuestionStatus.PREMOUNTING === q.status; return (
@@ -110,7 +110,7 @@ const PreMountingPage = () => { <FeedbackModal isOpen={showFeedback} onClose={() => setShowFeedback(false)} - feedback={q && q.feedback ? q.feedback : ''} + feedback={q && q.feedbacks && q.feedbacks.length > 0 ? q.feedbacks[q.feedbacks.length - 1] : ''} /> </div> ); diff --git a/src/pages/create/6-result/index.tsx b/src/pages/create/6-result/index.tsx index 90e623ad..777f4342 100644 --- a/src/pages/create/6-result/index.tsx +++ b/src/pages/create/6-result/index.tsx @@ -12,7 +12,6 @@ import { Button } from 'src/components/layout/Button'; import { Container } from 'src/components/layout/Container'; import { Flex } from 'src/components/layout/Flex'; import { LinearProgress } from 'src/components/layout/LinearProgress'; -import { Modal } from 'src/components/layout/Modal'; import { Tooltip } from 'src/components/layout/Tooltip'; import { Title, Text } from 'src/components/layout/Typography'; import { Steps } from 'src/components/navigation/Steps'; @@ -81,7 +80,6 @@ const ResultPage = () => { const { isCollaborationActive } = useCollaboration(); const { socket, connectTeacher } = useSocket(); const [isLoading, setIsLoading] = React.useState(false); - const [isVideoModalOpen, setIsVideoModalOpen] = React.useState(false); const { projectVideo, isLoading: isLoadingProjectVideo, @@ -164,9 +162,16 @@ const ResultPage = () => { data, }, { - onSettled: () => { - setIsVideoModalOpen(false); + onSuccess: (data) => { setIsDownloading(true); + if (data && data.url) { + const a = document.createElement('a'); + a.href = data.url; + a.download = 'project_video.mp4'; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + } }, onError: () => { sendToast({ message: t('unknown_error'), type: 'error' }); @@ -175,7 +180,6 @@ const ResultPage = () => { ); }; - const videoUrl = projectVideo?.url; const videoProgress = projectVideo?.progress; const hasProject = project !== undefined && project.id !== 0; @@ -221,22 +225,6 @@ const ResultPage = () => { <div>loading</div> ) : ( <> - {videoUrl && ( - <> - <Button - label={t('part6_mp4_download_button')} - as="a" - href={videoUrl} - className="full-width" - variant="contained" - color="secondary" - style={{ width: '100%' }} - leftIcon={<VideoIcon style={{ marginRight: '10px', width: '24px', height: '24px' }} />} - download - ></Button> - <Or /> - </> - )} {videoProgress && videoProgress !== 100 ? ( <div style={{ @@ -261,13 +249,11 @@ const ResultPage = () => { hasArrow > <Button - label={t(videoUrl ? 'part6_mp4_generate_button' : 'part6_mp4_button')} + label={t('part6_mp4_download_button')} className="full-width" variant="contained" color="secondary" - onClick={() => { - setIsVideoModalOpen(true); - }} + onClick={generateMP4} disabled={user === null || !hasProject} style={{ width: '100%' }} leftIcon={<VideoIcon style={{ marginRight: '10px', width: '24px', height: '24px' }} />} @@ -287,25 +273,6 @@ const ResultPage = () => { style={{ width: '100%' }} ></Button> </Flex> - <Modal - isOpen={isVideoModalOpen} - onClose={() => { - setIsVideoModalOpen(false); - }} - isLoading={createProjectVideoMutation.isLoading} - title={t('part6_mp4_button')} - confirmLabel={t('generate')} - onConfirm={generateMP4} - width="md" - isFullWidth - confirmLevel="secondary" - > - <ul style={{ margin: 0 }}> - <li style={{ marginBottom: '0.5rem' }}>{t('part6_mp4_description_1')}</li> - <li style={{ marginBottom: '0.5rem' }}>{t('part6_mp4_description_2')}</li> - <li style={{ marginBottom: '0.5rem' }}>{t('part6_mp4_description_3')}</li> - </ul> - </Modal> </div> <Loader isLoading={isLoading} /> </Container> diff --git a/types/models/question.type.ts b/types/models/question.type.ts index e6029f19..77e2aa41 100644 --- a/types/models/question.type.ts +++ b/types/models/question.type.ts @@ -22,7 +22,7 @@ export interface Question { soundUrl: string | null; soundVolume: number | null; status?: QuestionStatus; - feedback?: string | null; + feedbacks?: string[] | null; } export interface QuestionTemplate {