Skip to content

Commit

Permalink
feat: learningpath save step
Browse files Browse the repository at this point in the history
  • Loading branch information
Jonas-C committed Jan 7, 2025
1 parent 7122f5b commit 46b7f50
Show file tree
Hide file tree
Showing 7 changed files with 220 additions and 6 deletions.
13 changes: 7 additions & 6 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import { EditLearningpathTitlePage } from "./containers/MyNdla/Learningpath/Edit
import { LearningpathCheck } from "./containers/MyNdla/Learningpath/LearningpathCheck";
import LearningpathPage from "./containers/MyNdla/Learningpath/LearningpathPage";
import { NewLearningpathPage } from "./containers/MyNdla/Learningpath/NewLearningpathPage";
import { SaveLearningpathPage } from "./containers/MyNdla/Learningpath/SaveLearningpathPage";
import MyNdlaLayout from "./containers/MyNdla/MyNdlaLayout";
import MyNdlaPage from "./containers/MyNdla/MyNdlaPage";
import MyProfilePage from "./containers/MyNdla/MyProfile/MyProfilePage";
Expand Down Expand Up @@ -215,13 +216,13 @@ const AppRoutes = ({ base }: AppProps) => {
{!!config.learningpathEnabled && (
<Route path="learningpaths" element={<LearningpathCheck />}>
<Route path="new" element={<PrivateRoute element={<NewLearningpathPage />} />} />
<Route path=":learningpathId/edit">
<Route path="title" element={<PrivateRoute element={<EditLearningpathTitlePage />} />} />
<Route path="steps" element={<PrivateRoute element={<EditLearningpathStepsPage />} />} />
</Route>
<Route
path=":learningpathId/edit/steps"
element={<PrivateRoute element={<EditLearningpathStepsPage />} />}
/>
<Route
path=":learningpathId/edit/title"
element={<PrivateRoute element={<EditLearningpathTitlePage />} />}
path=":learningpathId/save"
element={<PrivateRoute element={<SaveLearningpathPage />} />}
/>
<Route index element={<PrivateRoute element={<LearningpathPage />} />} />
</Route>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* Copyright (c) 2025-present, NDLA.
*
* This source code is licensed under the GPLv3 license found in the
* LICENSE file in the root directory of this source tree.
*
*/

import { styled } from "@ndla/styled-system/jsx";

export const LearningpathFormButtonContainer = styled("div", {
base: {
display: "flex",
gap: "xsmall",
alignItems: "center",
justifyContent: "space-between",
marginBlockStart: "medium",
},
});
162 changes: 162 additions & 0 deletions src/containers/MyNdla/Learningpath/SaveLearningpathPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
/**
* Copyright (c) 2025-present, NDLA.
*
* This source code is licensed under the GPLv3 license found in the
* LICENSE file in the root directory of this source tree.
*
*/

import { MouseEvent, useContext, useEffect, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import { useParams } from "react-router-dom";
import { Button, DialogRoot, Heading, Text } from "@ndla/primitives";
import { SafeLinkButton } from "@ndla/safelink";
import { styled } from "@ndla/styled-system/jsx";
import { useTracker } from "@ndla/tracker";
import { LearningpathFormButtonContainer } from "./LearningpathFormButtonContainer";
import { useUpdateLearningpathStatus } from "./learningpathMutations";
import { useFetchLearningpath } from "./learningpathQueries";
import { copyLearningpathSharingLink, LEARNINGPATH_READY_FOR_SHARING, LEARNINGPATH_SHARED } from "./utils";
import { AuthContext } from "../../../components/AuthenticationContext";
import { DefaultErrorMessagePage } from "../../../components/DefaultErrorMessage";
import { PageSpinner } from "../../../components/PageSpinner";
import { useToast } from "../../../components/ToastContext";
import { SKIP_TO_CONTENT_ID } from "../../../constants";
import MyNdlaBreadcrumb from "../components/MyNdlaBreadcrumb";
import MyNdlaPageWrapper from "../components/MyNdlaPageWrapper";
import { LearningpathItem } from "./components/LearningpathItem";
import { LearningpathShareDialogContent } from "./components/LearningpathShareDialogContent";
import { LearningpathStepper } from "./components/LearningpathStepper";
import { routes } from "../../../routeHelpers";
import { getAllDimensions } from "../../../util/trackingUtil";

const TextWrapper = styled("div", {
base: {
display: "flex",
flexDirection: "column",
gap: "xsmall",
},
});

const ButtonWrapper = styled("div", {
base: {
display: "flex",
gap: "xsmall",
},
});

export const SaveLearningpathPage = () => {
const [open, setOpen] = useState(false);
const buttonRef = useRef<HTMLButtonElement>(null);
const { t } = useTranslation();
const { trackPageView } = useTracker();
const { learningpathId } = useParams();
const { user } = useContext(AuthContext);
const toast = useToast();
const learningpathQuery = useFetchLearningpath({
variables: { pathId: learningpathId ?? "" },
skip: !learningpathId,
});
const [updateLearningpathStatus] = useUpdateLearningpathStatus();

useEffect(() => {
trackPageView({
title: t("htmlTitles.learningpathSavePage", { name: learningpathQuery.data?.myNdlaLearningpath?.title }),
dimensions: getAllDimensions({ user }),
});
}, [learningpathQuery.data?.myNdlaLearningpath?.title, t, trackPageView, user]);

const onUnshare = async (e: MouseEvent<HTMLButtonElement>) => {
e.preventDefault();
const res = await updateLearningpathStatus({
variables: {
id: learningpath.id,
// TODO: Are we sure we want to set it to this status?
status: LEARNINGPATH_READY_FOR_SHARING,
},
});
if (!res.errors?.length) {
toast.create({
title: t("myNdla.learningpath.toast.unshared", { name: learningpath.title }),
});
} else {
toast.create({ title: t("myNdla.learningpath.toast.unshareFailed") });
}
};

const onShare = async () => {
const res = await updateLearningpathStatus({
variables: {
id: learningpath.id,
status: LEARNINGPATH_SHARED,
},
});
if (!res.errors?.length) {
toast.create({
title: t("myNdla.learningpath.toast.shared"),
});
setOpen(true);
} else {
toast.create({
title: t("myNdla.learningpath.toast.shareFailed"),
});
}
};

if (learningpathQuery.loading) {
return <PageSpinner />;
}

if (!learningpathQuery.data?.myNdlaLearningpath) {
return <DefaultErrorMessagePage />;
}

const learningpath = learningpathQuery.data.myNdlaLearningpath;
const isShared = learningpath.status === LEARNINGPATH_SHARED;

return (
<MyNdlaPageWrapper>
<title>{t("htmlTitles.learningpathSavePage", { name: learningpath?.title })}</title>
<MyNdlaBreadcrumb
breadcrumbs={[{ id: `save-${learningpath.id}`, name: t("myNdla.learningpath.form.steps.save") }]}
page="learningpath"
/>
<Heading id={SKIP_TO_CONTENT_ID} tabIndex={-1} textStyle="heading.medium">
{learningpath.title}
</Heading>
<LearningpathStepper step="save" learningpathId={learningpath.id} />
<TextWrapper>
<Heading textStyle="heading.small" asChild consumeCss>
<h2>{t("myNdla.learningpath.saveLearningpath.pageHeading")}</h2>
</Heading>
<Text>{t("myNdla.learningpath.saveLearningpath.pageDescription")}</Text>
</TextWrapper>
<LearningpathItem learningpath={learningpath} showMenu={false} context="standalone" />
<LearningpathFormButtonContainer>
<SafeLinkButton variant="secondary" to={routes.myNdla.learningpathPreview(learningpath.id)}>
{t("myNdla.learningpath.form.back")}
</SafeLinkButton>
<ButtonWrapper>
<SafeLinkButton to={routes.myNdla.learningpath}>
{t("myNdla.learningpath.saveLearningpath.saveAndClose")}
</SafeLinkButton>
<Button variant={isShared ? "danger" : "primary"} onClick={isShared ? onUnshare : onShare} ref={buttonRef}>
{/* TODO: Reconsider this translation. Do we want to tie this up to the menu translations? */}
{isShared ? t("myNdla.learningpath.menu.unShare") : t("myNdla.learningpath.menu.share")}
</Button>
</ButtonWrapper>
<DialogRoot
open={open}
onOpenChange={(details) => setOpen(details.open)}
finalFocusEl={() => buttonRef.current}
>
<LearningpathShareDialogContent
learningpath={learningpath}
onClose={() => setOpen(false)}
onCopyText={() => copyLearningpathSharingLink(learningpath.id)}
/>
</DialogRoot>
</LearningpathFormButtonContainer>
</MyNdlaPageWrapper>
);
};
8 changes: 8 additions & 0 deletions src/messages/messagesEN.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,9 @@ const messages = {
toast: {
deleted: 'The learning path "{{ name }}" has been deleted.',
unshared: 'The learning path "{{ name }}" is no longer shared.',
unshareFailed: "Failed to unshare the learning path.",
shared: "The learning path is shared.",
shareFailed: "Failed to share the learning path.",
copy: 'Copied the link to the learning path "{{ name }}"',
},
status: {
Expand Down Expand Up @@ -228,6 +230,12 @@ const messages = {
preview: "Preview learning path",
},
},
saveLearningpath: {
saveAndClose: "Save and close",
pageHeading: "Save and share",
pageDescription:
"Save and share your learning path. When you share the learning path, you create a link that can be shared with students or teachers.",
},
},
},
ndlaFilm: {
Expand Down
8 changes: 8 additions & 0 deletions src/messages/messagesNB.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,8 +200,10 @@ const messages = {
toast: {
deleted: 'Læringsstien "{{ name }}" er slettet.',
unshared: 'Læringsstien "{{ name }}" er ikke lenger delt.',
unshareFailed: "Kunne ikke avslutte deling av læringsstien.",
copy: 'Kopiert lenken til læringsstien "{{ name }}"',
shared: "Læringsstien er delt.",
shareFailed: "Kunne ikke dele læringsstien.",
},
status: {
shared: "Delt",
Expand Down Expand Up @@ -229,6 +231,12 @@ const messages = {
preview: "Forhåndsvis læringssti",
},
},
saveLearningpath: {
saveAndClose: "Lagre og lukk",
pageHeading: "Lagre og del",
pageDescription:
"Lagre og del læringsstien din. Når du deler oppretter du en delbar lenke som du kan sende til elever eller lærere.",
},
},
},
ndlaFilm: {
Expand Down
8 changes: 8 additions & 0 deletions src/messages/messagesNN.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,8 +200,10 @@ const messages = {
toast: {
deleted: 'Læringsstien "{{ name }}" er sletta.',
unshared: 'Læringsstien "{{ name }}" er ikkje lenger delt.',
unshareFailed: "Kunne ikkje avslutte delinga av læringsstien.",
copy: 'Kopierte lenka til læringsstien "{{ name }}"',
shared: "Læringsstien er delt.",
shareFailed: "Kunne ikkje dele læringsstien.",
},
status: {
shared: "Delt",
Expand Down Expand Up @@ -229,6 +231,12 @@ const messages = {
preview: "Førehandsvis læringssti",
},
},
saveLearningpath: {
saveAndClose: "Lagre og lukk",
pageHeading: "Lagre og del",
pageDescription:
"Lagre og del læringstien din. Når du deler opprettar du ein delbar lenke som du kan sende til elevar eller lærarar.",
},
},
},
ndlaFilm: {
Expand Down
8 changes: 8 additions & 0 deletions src/messages/messagesSE.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,8 +198,10 @@ const messages = {
toast: {
deleted: 'Læringsstien "{{ name }}" er slettet.',
unshared: 'Lærringsstien "{{ name }}" er ikke lenger delt.',
unshareFailed: "Kunne ikke avslutte deling av læringsstien.",
copy: 'Kopiert lenken til læringsstien "{{ name }}"',
shared: "Læringsstien er delt.",
shareFailed: "Kunne ikke dele læringsstien.",
},
status: {
shared: "Delt",
Expand All @@ -226,6 +228,12 @@ const messages = {
preview: "Forhåndsvis læringssti",
},
},
saveLearningpath: {
saveAndClose: "Lagre og lukk",
pageHeading: "Lagre og del",
pageDescription:
"Lagre og del læringsstien din. Når du deler oppretter du en delbar lenke som du kan sende til elever eller lærere.",
},
},
},
ndlaFilm: {
Expand Down

0 comments on commit 46b7f50

Please sign in to comment.