From 57c1115f28fcc82d09f4f7a23fdaf15234693b0f Mon Sep 17 00:00:00 2001 From: joyce-shi Date: Tue, 24 Oct 2023 16:34:25 +0200 Subject: [PATCH 1/5] feat: initial implementation --- .../assessment-creation/AssessmentPreview.tsx | 12 +++- .../EditStatusButtons/PreviewButton.tsx | 46 +++++++++++++++ .../assessment-status/EditStatusPopover.tsx | 2 + .../components/pages/admin/AdminRouting.tsx | 8 +++ .../AssessmentEditorPage/AssessmentEditor.tsx | 1 + .../pages/admin/AssessmentPreviewPage.tsx | 56 +++++++++++++++++++ frontend/src/constants/Routes.ts | 6 ++ 7 files changed, 128 insertions(+), 3 deletions(-) create mode 100644 frontend/src/components/admin/assessment-status/EditStatusButtons/PreviewButton.tsx create mode 100644 frontend/src/components/pages/admin/AssessmentPreviewPage.tsx diff --git a/frontend/src/components/admin/assessment-creation/AssessmentPreview.tsx b/frontend/src/components/admin/assessment-creation/AssessmentPreview.tsx index 24063ab5e..4dca67ddb 100644 --- a/frontend/src/components/admin/assessment-creation/AssessmentPreview.tsx +++ b/frontend/src/components/admin/assessment-creation/AssessmentPreview.tsx @@ -2,18 +2,20 @@ import type { ReactNode } from "react"; import React, { useEffect } from "react"; import { Button } from "@chakra-ui/react"; -import { EditOutlineIcon } from "../../../assets/icons"; +import { ArrowBackOutlineIcon } from "../../../assets/icons"; import type { Question } from "../../../types/QuestionTypes"; import AssessmentExperience from "../../student/AssessmentExperience"; type AssessmentPreviewProps = { questions: Question[]; goBack: () => void; + backButtonText: string; }; const AssessmentPreview = ({ questions, goBack, + backButtonText, }: AssessmentPreviewProps): ReactNode => { useEffect(() => { if (!questions.length) { @@ -26,8 +28,12 @@ const AssessmentPreview = ({ } const closeAssessmentPreviewButton = ( - ); diff --git a/frontend/src/components/admin/assessment-status/EditStatusButtons/PreviewButton.tsx b/frontend/src/components/admin/assessment-status/EditStatusButtons/PreviewButton.tsx new file mode 100644 index 000000000..41a767990 --- /dev/null +++ b/frontend/src/components/admin/assessment-status/EditStatusButtons/PreviewButton.tsx @@ -0,0 +1,46 @@ +import React from "react"; +import { useHistory } from "react-router-dom"; +import { useQuery } from "@apollo/client"; + +import { GET_TEST } from "../../../../APIClients/queries/TestQueries"; +import type { TestResponse } from "../../../../APIClients/types/TestClientTypes"; +import * as Routes from "../../../../constants/Routes"; +import useToast from "../../../common/info/useToast"; +import PopoverButton from "../../../common/popover/PopoverButton"; + +interface PreviewButtonProps { + assessmentId: string; +} + +const PreviewButton = ({ + assessmentId, +}: PreviewButtonProps): React.ReactElement => { + const history = useHistory(); + const { data } = useQuery<{ test: TestResponse }>(GET_TEST, { + fetchPolicy: "cache-and-network", + variables: { id: assessmentId }, + }); + const { showToast } = useToast(); + + return ( + { + if (data) { + history.push({ + pathname: Routes.ASESESSMENT_PREVIEW_PAGE({ assessmentId }), + state: data.test, + }); + } else { + showToast({ + message: + "This assessment cannot be previewed at this time. Please try again.", + status: "error", + }); + } + }} + /> + ); +}; + +export default PreviewButton; diff --git a/frontend/src/components/admin/assessment-status/EditStatusPopover.tsx b/frontend/src/components/admin/assessment-status/EditStatusPopover.tsx index c54a5d0a7..bf8466579 100644 --- a/frontend/src/components/admin/assessment-status/EditStatusPopover.tsx +++ b/frontend/src/components/admin/assessment-status/EditStatusPopover.tsx @@ -8,6 +8,7 @@ import ArchiveButton from "./EditStatusButtons/ArchiveButton"; import DeleteButton from "./EditStatusButtons/DeleteButton"; import DuplicateButton from "./EditStatusButtons/DuplicateButton"; import EditButton from "./EditStatusButtons/EditButton"; +import PreviewButton from "./EditStatusButtons/PreviewButton"; import PublishButton from "./EditStatusButtons/PublishButton"; import UnarchiveButton from "./EditStatusButtons/UnarchiveButton"; @@ -38,6 +39,7 @@ const EditStatusPopover = ({ )} + diff --git a/frontend/src/components/pages/admin/AdminRouting.tsx b/frontend/src/components/pages/admin/AdminRouting.tsx index 9d1a7d84d..97cbdb7ac 100644 --- a/frontend/src/components/pages/admin/AdminRouting.tsx +++ b/frontend/src/components/pages/admin/AdminRouting.tsx @@ -10,6 +10,7 @@ import Navbar from "../../common/navigation/Navbar"; import NotFound from "../NotFound"; import AssessmentEditorPage from "./AssessmentEditorPage"; +import AssessmentPreviewPage from "./AssessmentPreviewPage"; import DisplayAssessmentsPage from "./DisplayAssessmentsPage"; import UsersPage from "./UsersPage"; @@ -33,6 +34,13 @@ const AdminRouting = (): React.ReactElement => { path={Routes.ASSESSMENT_EDITOR_BASE({ assessmentId: ":assessmentId" })} roles={["Admin"]} /> + diff --git a/frontend/src/components/pages/admin/AssessmentEditorPage/AssessmentEditor.tsx b/frontend/src/components/pages/admin/AssessmentEditorPage/AssessmentEditor.tsx index b9cfda60a..88cd720f6 100644 --- a/frontend/src/components/pages/admin/AssessmentEditorPage/AssessmentEditor.tsx +++ b/frontend/src/components/pages/admin/AssessmentEditorPage/AssessmentEditor.tsx @@ -278,6 +278,7 @@ const AssessmentEditor = ({ state }: AssessmentEditorProps): ReactElement => { })} > disableEditorPrompt(history.push)( Routes.ASSESSMENT_EDITOR_PAGE({ diff --git a/frontend/src/components/pages/admin/AssessmentPreviewPage.tsx b/frontend/src/components/pages/admin/AssessmentPreviewPage.tsx new file mode 100644 index 000000000..c92e8ddd7 --- /dev/null +++ b/frontend/src/components/pages/admin/AssessmentPreviewPage.tsx @@ -0,0 +1,56 @@ +import React, { useMemo } from "react"; +import { useLocation, useParams } from "react-router-dom"; +import { useHistory } from "react-router-dom"; +import { useQuery } from "@apollo/client"; +import { Box } from "@chakra-ui/react"; + +import { GET_TEST } from "../../../APIClients/queries/TestQueries"; +import type { TestResponse } from "../../../APIClients/types/TestClientTypes"; +import { ASSESSMENTS_PAGE } from "../../../constants/Routes"; +import { formatQuestionsResponse } from "../../../utils/QuestionUtils"; +import AssessmentPreview from "../../admin/assessment-creation/AssessmentPreview"; +import QueryStateHandler from "../../common/QueryStateHandler"; + +const AssessmentPreviewPage = () => { + const history = useHistory(); + + // Data could come from the previous page. + const { state: locationState } = useLocation(); + + // If data is not available from the previous page, then we have to fetch it. + const { assessmentId } = useParams<{ assessmentId?: string }>(); + const { + data: testData, + loading, + error, + } = useQuery<{ + test: TestResponse; + }>(GET_TEST, { + variables: { id: assessmentId }, + skip: !!locationState || !assessmentId, + }); + + const test = locationState || testData?.test; + const state = useMemo( + () => + test && { + ...test, + questions: formatQuestionsResponse(test.questions), + }, + [test], + ); + + return ( + + + history.push(ASSESSMENTS_PAGE)} + questions={state?.questions || []} + /> + + + ); +}; + +export default AssessmentPreviewPage; diff --git a/frontend/src/constants/Routes.ts b/frontend/src/constants/Routes.ts index 3250f5d6a..b45abe20a 100644 --- a/frontend/src/constants/Routes.ts +++ b/frontend/src/constants/Routes.ts @@ -55,6 +55,12 @@ export const ASSESSMENT_EDITOR_QUESTION_PREVIEW_PAGE = ({ ASSESSMENT_EDITOR_QUESTION_EDITOR_BASE({ assessmentId, questionIndex }) + "/preview"; +export const ASESESSMENT_PREVIEW_PAGE = ({ + assessmentId, +}: { + assessmentId: string; +}) => "/admin/assessment/" + assessmentId; + // Private Teacher Routes export const TEACHER_LANDING_PAGE = "/teacher"; export const TEACHER_DASHBOARD_PAGE = "/teacher/dashboard"; From ca9bad622a95561ab0a255d78f919bb91ec61959 Mon Sep 17 00:00:00 2001 From: joyce-shi Date: Tue, 24 Oct 2023 16:57:10 +0200 Subject: [PATCH 2/5] refactor: to reduce redundant code --- .../AssessmentEditorHeader.tsx | 6 +++--- .../assessment-creation/AssessmentPreview.tsx | 1 + .../EditStatusButtons/PreviewButton.tsx | 2 +- .../components/pages/admin/AdminRouting.tsx | 2 +- .../AssessmentEditorPage/AssessmentEditor.tsx | 18 ------------------ .../pages/admin/AssessmentPreviewPage.tsx | 5 ++--- frontend/src/constants/Routes.ts | 11 +++-------- 7 files changed, 11 insertions(+), 34 deletions(-) diff --git a/frontend/src/components/admin/assessment-creation/AssessmentEditorHeader.tsx b/frontend/src/components/admin/assessment-creation/AssessmentEditorHeader.tsx index df6d20216..a34d522c3 100644 --- a/frontend/src/components/admin/assessment-creation/AssessmentEditorHeader.tsx +++ b/frontend/src/components/admin/assessment-creation/AssessmentEditorHeader.tsx @@ -75,11 +75,11 @@ const AssessmentEditorHeader = ({ const onPreview = () => { validateForm(); - disableEditorPrompt(history.push)( - Routes.ASSESSMENT_EDITOR_PREVIEW_PAGE({ + disableEditorPrompt(history.push)({ + pathname: Routes.ASSESSMENT_PREVIEW_PAGE({ assessmentId, }), - ); + }); }; const onPublish = () => { diff --git a/frontend/src/components/admin/assessment-creation/AssessmentPreview.tsx b/frontend/src/components/admin/assessment-creation/AssessmentPreview.tsx index 4dca67ddb..225132963 100644 --- a/frontend/src/components/admin/assessment-creation/AssessmentPreview.tsx +++ b/frontend/src/components/admin/assessment-creation/AssessmentPreview.tsx @@ -37,6 +37,7 @@ const AssessmentPreview = ({ ); + // TODO: combine with AssessmentPreviewPage component return ( { if (data) { history.push({ - pathname: Routes.ASESESSMENT_PREVIEW_PAGE({ assessmentId }), + pathname: Routes.ASSESSMENT_PREVIEW_PAGE({ assessmentId }), state: data.test, }); } else { diff --git a/frontend/src/components/pages/admin/AdminRouting.tsx b/frontend/src/components/pages/admin/AdminRouting.tsx index 97cbdb7ac..eccf87479 100644 --- a/frontend/src/components/pages/admin/AdminRouting.tsx +++ b/frontend/src/components/pages/admin/AdminRouting.tsx @@ -36,7 +36,7 @@ const AdminRouting = (): React.ReactElement => { /> { - - - disableEditorPrompt(history.push)( - Routes.ASSESSMENT_EDITOR_PAGE({ - assessmentId: state?.id, - }), - ) - } - questions={questions} - /> - diff --git a/frontend/src/components/pages/admin/AssessmentPreviewPage.tsx b/frontend/src/components/pages/admin/AssessmentPreviewPage.tsx index c92e8ddd7..b818eaf84 100644 --- a/frontend/src/components/pages/admin/AssessmentPreviewPage.tsx +++ b/frontend/src/components/pages/admin/AssessmentPreviewPage.tsx @@ -6,7 +6,6 @@ import { Box } from "@chakra-ui/react"; import { GET_TEST } from "../../../APIClients/queries/TestQueries"; import type { TestResponse } from "../../../APIClients/types/TestClientTypes"; -import { ASSESSMENTS_PAGE } from "../../../constants/Routes"; import { formatQuestionsResponse } from "../../../utils/QuestionUtils"; import AssessmentPreview from "../../admin/assessment-creation/AssessmentPreview"; import QueryStateHandler from "../../common/QueryStateHandler"; @@ -44,8 +43,8 @@ const AssessmentPreviewPage = () => { history.push(ASSESSMENTS_PAGE)} + backButtonText="Back" + goBack={() => history.goBack()} questions={state?.questions || []} /> diff --git a/frontend/src/constants/Routes.ts b/frontend/src/constants/Routes.ts index b45abe20a..80482f471 100644 --- a/frontend/src/constants/Routes.ts +++ b/frontend/src/constants/Routes.ts @@ -21,11 +21,6 @@ export const ASSESSMENT_EDITOR_PAGE = ({ }: { assessmentId?: string; }) => ASSESSMENT_EDITOR_BASE({ assessmentId }) + (assessmentId ? "/edit" : ""); -export const ASSESSMENT_EDITOR_PREVIEW_PAGE = ({ - assessmentId, -}: { - assessmentId?: string; -}) => ASSESSMENT_EDITOR_BASE({ assessmentId }) + "/preview"; export const ASSESSMENT_EDITOR_QUESTION_EDITOR_BASE = ({ assessmentId, questionIndex, @@ -55,11 +50,11 @@ export const ASSESSMENT_EDITOR_QUESTION_PREVIEW_PAGE = ({ ASSESSMENT_EDITOR_QUESTION_EDITOR_BASE({ assessmentId, questionIndex }) + "/preview"; -export const ASESESSMENT_PREVIEW_PAGE = ({ +export const ASSESSMENT_PREVIEW_PAGE = ({ assessmentId, }: { - assessmentId: string; -}) => "/admin/assessment/" + assessmentId; + assessmentId?: string; +}) => ASSESSMENT_EDITOR_BASE({ assessmentId }) + "/preview"; // Private Teacher Routes export const TEACHER_LANDING_PAGE = "/teacher"; From 76e990b3c84137aa07ec2ade5ae91f185174390d Mon Sep 17 00:00:00 2001 From: joyce-shi Date: Tue, 24 Oct 2023 17:08:01 +0200 Subject: [PATCH 3/5] refactor: combine components --- .../assessment-creation/AssessmentPreview.tsx | 51 ------------------- .../pages/admin/AssessmentPreviewPage.tsx | 26 ++++++++-- frontend/src/constants/Routes.ts | 2 +- 3 files changed, 22 insertions(+), 57 deletions(-) delete mode 100644 frontend/src/components/admin/assessment-creation/AssessmentPreview.tsx diff --git a/frontend/src/components/admin/assessment-creation/AssessmentPreview.tsx b/frontend/src/components/admin/assessment-creation/AssessmentPreview.tsx deleted file mode 100644 index 225132963..000000000 --- a/frontend/src/components/admin/assessment-creation/AssessmentPreview.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import type { ReactNode } from "react"; -import React, { useEffect } from "react"; -import { Button } from "@chakra-ui/react"; - -import { ArrowBackOutlineIcon } from "../../../assets/icons"; -import type { Question } from "../../../types/QuestionTypes"; -import AssessmentExperience from "../../student/AssessmentExperience"; - -type AssessmentPreviewProps = { - questions: Question[]; - goBack: () => void; - backButtonText: string; -}; - -const AssessmentPreview = ({ - questions, - goBack, - backButtonText, -}: AssessmentPreviewProps): ReactNode => { - useEffect(() => { - if (!questions.length) { - goBack(); - } - }, [goBack, questions]); - - if (!questions.length) { - return null; - } - - const closeAssessmentPreviewButton = ( - - ); - - // TODO: combine with AssessmentPreviewPage component - return ( - - ); -}; - -export default AssessmentPreview; diff --git a/frontend/src/components/pages/admin/AssessmentPreviewPage.tsx b/frontend/src/components/pages/admin/AssessmentPreviewPage.tsx index b818eaf84..c2fb2344c 100644 --- a/frontend/src/components/pages/admin/AssessmentPreviewPage.tsx +++ b/frontend/src/components/pages/admin/AssessmentPreviewPage.tsx @@ -2,13 +2,15 @@ import React, { useMemo } from "react"; import { useLocation, useParams } from "react-router-dom"; import { useHistory } from "react-router-dom"; import { useQuery } from "@apollo/client"; -import { Box } from "@chakra-ui/react"; +import { Box, Button } from "@chakra-ui/react"; import { GET_TEST } from "../../../APIClients/queries/TestQueries"; import type { TestResponse } from "../../../APIClients/types/TestClientTypes"; +import { ArrowBackOutlineIcon } from "../../../assets/icons"; import { formatQuestionsResponse } from "../../../utils/QuestionUtils"; -import AssessmentPreview from "../../admin/assessment-creation/AssessmentPreview"; +import usePageTitle from "../../auth/usePageTitle"; import QueryStateHandler from "../../common/QueryStateHandler"; +import AssessmentExperience from "../../student/AssessmentExperience"; const AssessmentPreviewPage = () => { const history = useHistory(); @@ -39,13 +41,27 @@ const AssessmentPreviewPage = () => { [test], ); + const assessmentName = state?.name; + usePageTitle(`Previewing "${assessmentName}`); + + const closeAssessmentPreviewButton = ( + + ); + return ( - history.goBack()} + diff --git a/frontend/src/constants/Routes.ts b/frontend/src/constants/Routes.ts index 80482f471..29d067312 100644 --- a/frontend/src/constants/Routes.ts +++ b/frontend/src/constants/Routes.ts @@ -54,7 +54,7 @@ export const ASSESSMENT_PREVIEW_PAGE = ({ assessmentId, }: { assessmentId?: string; -}) => ASSESSMENT_EDITOR_BASE({ assessmentId }) + "/preview"; +}) => "/admin/assessment/" + assessmentId + "/preview"; // Private Teacher Routes export const TEACHER_LANDING_PAGE = "/teacher"; From 620c8c9c357ad24d654d8d327f9a4cfe7eb2cbc7 Mon Sep 17 00:00:00 2001 From: joyce-shi Date: Tue, 24 Oct 2023 17:10:44 +0200 Subject: [PATCH 4/5] cleanup: minor --- .../admin/assessment-creation/AssessmentEditorHeader.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/admin/assessment-creation/AssessmentEditorHeader.tsx b/frontend/src/components/admin/assessment-creation/AssessmentEditorHeader.tsx index a34d522c3..b994940a7 100644 --- a/frontend/src/components/admin/assessment-creation/AssessmentEditorHeader.tsx +++ b/frontend/src/components/admin/assessment-creation/AssessmentEditorHeader.tsx @@ -75,11 +75,11 @@ const AssessmentEditorHeader = ({ const onPreview = () => { validateForm(); - disableEditorPrompt(history.push)({ - pathname: Routes.ASSESSMENT_PREVIEW_PAGE({ + disableEditorPrompt(history.push)( + Routes.ASSESSMENT_PREVIEW_PAGE({ assessmentId, }), - }); + ); }; const onPublish = () => { From dc9b93fa5023336c723f13c6fa13e993948beb88 Mon Sep 17 00:00:00 2001 From: joyce-shi Date: Sun, 10 Dec 2023 18:25:42 +0100 Subject: [PATCH 5/5] pr comments: default tab name + remove async --- .../admin/assessment-status/EditStatusButtons/PreviewButton.tsx | 2 +- frontend/src/components/pages/admin/AssessmentPreviewPage.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/admin/assessment-status/EditStatusButtons/PreviewButton.tsx b/frontend/src/components/admin/assessment-status/EditStatusButtons/PreviewButton.tsx index 4e07bc683..45dd805d4 100644 --- a/frontend/src/components/admin/assessment-status/EditStatusButtons/PreviewButton.tsx +++ b/frontend/src/components/admin/assessment-status/EditStatusButtons/PreviewButton.tsx @@ -25,7 +25,7 @@ const PreviewButton = ({ return ( { + onClick={() => { if (data) { history.push({ pathname: Routes.ASSESSMENT_PREVIEW_PAGE({ assessmentId }), diff --git a/frontend/src/components/pages/admin/AssessmentPreviewPage.tsx b/frontend/src/components/pages/admin/AssessmentPreviewPage.tsx index c2fb2344c..29b81d6d2 100644 --- a/frontend/src/components/pages/admin/AssessmentPreviewPage.tsx +++ b/frontend/src/components/pages/admin/AssessmentPreviewPage.tsx @@ -42,7 +42,7 @@ const AssessmentPreviewPage = () => { ); const assessmentName = state?.name; - usePageTitle(`Previewing "${assessmentName}`); + usePageTitle(`Previewing "${assessmentName || "assesssment"}`); const closeAssessmentPreviewButton = (