From f976669e0cf1f798cf69a6acb146d3b19fa1cb5c Mon Sep 17 00:00:00 2001
From: mantagen
Date: Wed, 28 Aug 2024 13:26:59 +0200
Subject: [PATCH 01/11] feat: updates to download resources page
---
.../app/aila/[id]/download/DownloadView.tsx | 178 ++++++++++++++
.../[id]/download/SurveyDialogLauncher.ts | 24 ++
.../src/app/aila/[id]/download/index.tsx | 232 ------------------
.../src/app/aila/[id]/download/page.tsx | 2 +-
.../app/aila/[id]/download/useDownloadView.ts | 55 +++++
.../Chat/hooks/useProgressForDownloads.ts | 88 +++++++
.../AppComponents/Chat/Chat/utils/index.ts | 28 ---
.../LessonPlanProgressDropdown.tsx | 60 ++---
.../findListOfUndefinedKeysFromZod.ts | 36 ---
packages/exports/src/schema/input.schema.ts | 1 +
10 files changed, 361 insertions(+), 343 deletions(-)
create mode 100644 apps/nextjs/src/app/aila/[id]/download/DownloadView.tsx
create mode 100644 apps/nextjs/src/app/aila/[id]/download/SurveyDialogLauncher.ts
delete mode 100644 apps/nextjs/src/app/aila/[id]/download/index.tsx
create mode 100644 apps/nextjs/src/app/aila/[id]/download/useDownloadView.ts
create mode 100644 apps/nextjs/src/components/AppComponents/Chat/Chat/hooks/useProgressForDownloads.ts
delete mode 100644 packages/exports/src/dataHelpers/findListOfUndefinedKeysFromZod.ts
diff --git a/apps/nextjs/src/app/aila/[id]/download/DownloadView.tsx b/apps/nextjs/src/app/aila/[id]/download/DownloadView.tsx
new file mode 100644
index 000000000..e06db24ef
--- /dev/null
+++ b/apps/nextjs/src/app/aila/[id]/download/DownloadView.tsx
@@ -0,0 +1,178 @@
+"use client";
+
+import { AilaPersistedChat } from "@oakai/aila/src/protocol/schema";
+import { Box, Flex, Grid } from "@radix-ui/themes";
+
+import Layout from "@/components/AppComponents/Layout";
+import Button from "@/components/Button";
+import DialogContents from "@/components/DialogControl/DialogContents";
+import { DialogRoot } from "@/components/DialogControl/DialogRoot";
+import { DownloadButton } from "@/components/DownloadButton";
+import { Icon } from "@/components/Icon";
+
+import { SurveyDialogLauncher } from "./SurveyDialogLauncher";
+import { useDownloadView } from "./useDownloadView";
+
+type DownloadViewProps = Readonly<{
+ chat: AilaPersistedChat;
+ featureFlag: boolean;
+}>;
+export function DownloadView({
+ chat,
+ featureFlag,
+}: Readonly) {
+ const { lessonPlan } = chat;
+ const {
+ lessonSlidesExport,
+ worksheetExport,
+ lessonPlanExport,
+ starterQuizExport,
+ exitQuizExport,
+ additionalMaterialsExport,
+ sections,
+ totalSections,
+ totalSectionsComplete,
+ } = useDownloadView(chat);
+
+ return (
+
+
+
+
+
+
+
+
+
+ {lessonPlan.title ? lessonPlan.title : "Back to lesson"}
+
+
+
+
+
Download resources
+
+ Complete all 12 sections of your lesson to unlock resources
+ below. If a section is missing, just ask Aila to help you
+ complete your lesson.
+
+
+
+
+ lessonPlanExport.start()}
+ title="Lesson plan"
+ subTitle="All sections"
+ downloadAvailable={!!lessonPlanExport.readyToExport}
+ downloadLoading={lessonPlanExport.status === "loading"}
+ data={lessonPlanExport.data}
+ exportsType="lessonPlanDoc"
+ data-testid="chat-download-lesson-plan"
+ lesson={lessonPlan}
+ />
+ starterQuizExport.start()}
+ title="Starter quiz"
+ subTitle="Questions and answers"
+ downloadAvailable={!!starterQuizExport.readyToExport}
+ downloadLoading={starterQuizExport.status === "loading"}
+ data={starterQuizExport.data}
+ exportsType="starterQuiz"
+ lesson={lessonPlan}
+ />
+ lessonSlidesExport.start()}
+ data-testid="chat-download-slides-btn"
+ title="Slide deck"
+ subTitle="Outcomes, key words, 1-3 learning cycles, summary"
+ downloadAvailable={lessonSlidesExport.readyToExport}
+ downloadLoading={lessonSlidesExport.status === "loading"}
+ data={lessonSlidesExport.data}
+ exportsType="lessonSlides"
+ lesson={lessonPlan}
+ />
+ worksheetExport.start()}
+ title="Worksheet"
+ subTitle="Interactive activities"
+ downloadAvailable={!!worksheetExport.readyToExport}
+ downloadLoading={worksheetExport.status === "loading"}
+ data={worksheetExport.data}
+ lesson={lessonPlan}
+ exportsType="worksheet"
+ />
+ exitQuizExport.start()}
+ title="Exit quiz"
+ subTitle="Questions and answers"
+ downloadAvailable={!!exitQuizExport.readyToExport}
+ downloadLoading={exitQuizExport.status === "loading"}
+ data={exitQuizExport.data}
+ exportsType="exitQuiz"
+ lesson={lessonPlan}
+ />
+ {lessonPlan.additionalMaterials &&
+ lessonPlan.additionalMaterials !== "None" && (
+ additionalMaterialsExport.start()}
+ title="Additional materials"
+ subTitle="Document containing any additional materials"
+ downloadAvailable={
+ !!additionalMaterialsExport.readyToExport
+ }
+ downloadLoading={
+ additionalMaterialsExport.status === "loading"
+ }
+ data={additionalMaterialsExport.data}
+ exportsType="additionalMaterials"
+ lesson={lessonPlan}
+ />
+ )}
+
+
+
+
+ {`${totalSectionsComplete} of ${totalSections} sections complete`}
+
+
+ {sections.map((section) => {
+ return (
+
+
+
+
+ {section.label}
+
+ );
+ })}
+
+
+
+
+
+
+
+ );
+}
diff --git a/apps/nextjs/src/app/aila/[id]/download/SurveyDialogLauncher.ts b/apps/nextjs/src/app/aila/[id]/download/SurveyDialogLauncher.ts
new file mode 100644
index 000000000..d672efb9b
--- /dev/null
+++ b/apps/nextjs/src/app/aila/[id]/download/SurveyDialogLauncher.ts
@@ -0,0 +1,24 @@
+import { useEffect } from "react";
+
+import { usePosthogFeedbackSurvey } from "hooks/surveys/usePosthogFeedbackSurvey";
+
+import { useDialog } from "@/components/AppComponents/DialogContext";
+
+export function SurveyDialogLauncher() {
+ const { survey } = usePosthogFeedbackSurvey({
+ closeDialog: () => null,
+ surveyName: "End of Aila generation survey launch aug24",
+ });
+ const { setDialogWindow } = useDialog();
+
+ useEffect(() => {
+ if (survey) {
+ const timer = setTimeout(() => {
+ setDialogWindow("feedback");
+ }, 3000);
+ return () => clearTimeout(timer);
+ }
+ }, [survey, setDialogWindow]);
+
+ return null;
+}
diff --git a/apps/nextjs/src/app/aila/[id]/download/index.tsx b/apps/nextjs/src/app/aila/[id]/download/index.tsx
deleted file mode 100644
index 49347ebfe..000000000
--- a/apps/nextjs/src/app/aila/[id]/download/index.tsx
+++ /dev/null
@@ -1,232 +0,0 @@
-"use client";
-
-import { useEffect, useState } from "react";
-
-import { AilaPersistedChat } from "@oakai/aila/src/protocol/schema";
-import { Box, Flex, Grid } from "@radix-ui/themes";
-import { lessonSections } from "ai-apps/lesson-planner/lessonSection";
-import { usePosthogFeedbackSurvey } from "hooks/surveys/usePosthogFeedbackSurvey";
-
-import { setLessonPlanProgress } from "@/components/AppComponents/Chat/Chat/utils";
-import { useDialog } from "@/components/AppComponents/DialogContext";
-import Layout from "@/components/AppComponents/Layout";
-import Button from "@/components/Button";
-import DialogContents from "@/components/DialogControl/DialogContents";
-import { DialogRoot } from "@/components/DialogControl/DialogRoot";
-import { DownloadButton } from "@/components/DownloadButton";
-import { useExportAdditionalMaterials } from "@/components/ExportsDialogs/useExportAdditionalMaterials";
-import { useExportLessonPlanDoc } from "@/components/ExportsDialogs/useExportLessonPlanDoc";
-import { useExportLessonSlides } from "@/components/ExportsDialogs/useExportLessonSlides";
-import { useExportQuizDoc } from "@/components/ExportsDialogs/useExportQuizDoc";
-import { useExportWorksheetSlides } from "@/components/ExportsDialogs/useExportWorksheetSlides";
-import { Icon } from "@/components/Icon";
-
-interface DownloadPageProps {
- chat: AilaPersistedChat;
- featureFlag: boolean;
-}
-export default function DownloadPage({
- chat,
- featureFlag,
-}: Readonly) {
- return (
-
-
-
- );
-}
-
-export function DownloadPageContents({ chat }: Readonly) {
- const { setDialogWindow } = useDialog();
-
- const [undefinedLessonPlanSections, setUndefinedLessonPlanSections] =
- useState([]);
- const lessonPlan = chat.lessonPlan;
- useEffect(() => {
- setLessonPlanProgress({
- lessonPlan: lessonPlan,
- setUndefinedItems: setUndefinedLessonPlanSections,
- });
- }, [lessonPlan, setUndefinedLessonPlanSections]);
-
- const exportProps = {
- onStart: () => null,
- lesson: lessonPlan,
- chatId: chat.id,
- messageId: chat.messages.length,
- active: true,
- };
-
- const lessonSlidesExport = useExportLessonSlides(exportProps);
-
- const worksheetExport = useExportWorksheetSlides(exportProps);
-
- const lessonPlanExport = useExportLessonPlanDoc(exportProps);
-
- const starterQuizExport = useExportQuizDoc({
- ...exportProps,
- quizType: "starter",
- });
-
- const exitQuizExport = useExportQuizDoc({
- ...exportProps,
- quizType: "exit",
- });
-
- const additionalMaterialsExport = useExportAdditionalMaterials(exportProps);
-
- const { survey } = usePosthogFeedbackSurvey({
- closeDialog: () => null,
- surveyName: "End of Aila generation survey launch aug24",
- });
-
- useEffect(() => {
- if (survey) {
- const timer = setTimeout(() => {
- setDialogWindow("feedback");
- }, 3000);
- return () => clearTimeout(timer);
- }
- }, [survey, setDialogWindow]);
-
- return (
-
-
-
-
-
-
-
- {lessonPlan.title ? lessonPlan.title : "Back to lesson"}
-
-
-
-
-
Download resources
-
- Complete all 12 sections of your lesson to unlock resources
- below. If a section is missing, just ask Aila to help you
- complete your lesson.
-
-
-
-
- lessonPlanExport.start()}
- title="Lesson plan"
- subTitle="All sections"
- downloadAvailable={!!lessonPlanExport.readyToExport}
- downloadLoading={lessonPlanExport.status === "loading"}
- data={lessonPlanExport.data}
- exportsType="lessonPlanDoc"
- data-testid="chat-download-lesson-plan"
- lesson={lessonPlan}
- />
- starterQuizExport.start()}
- title="Starter quiz"
- subTitle="Questions and answers"
- downloadAvailable={!!starterQuizExport.readyToExport}
- downloadLoading={starterQuizExport.status === "loading"}
- data={starterQuizExport.data}
- exportsType="starterQuiz"
- lesson={lessonPlan}
- />
- lessonSlidesExport.start()}
- data-testid="chat-download-slides-btn"
- title="Slide deck"
- subTitle="Outcomes, key words, 1-3 learning cycles, summary"
- downloadAvailable={lessonSlidesExport.readyToExport}
- downloadLoading={lessonSlidesExport.status === "loading"}
- data={lessonSlidesExport.data}
- exportsType="lessonSlides"
- lesson={lessonPlan}
- />
- worksheetExport.start()}
- title="Worksheet"
- subTitle="Interactive activities"
- downloadAvailable={!!worksheetExport.readyToExport}
- downloadLoading={worksheetExport.status === "loading"}
- data={worksheetExport.data}
- lesson={lessonPlan}
- exportsType="worksheet"
- />
- exitQuizExport.start()}
- title="Exit quiz"
- subTitle="Questions and answers"
- downloadAvailable={!!exitQuizExport.readyToExport}
- downloadLoading={exitQuizExport.status === "loading"}
- data={exitQuizExport.data}
- exportsType="exitQuiz"
- lesson={lessonPlan}
- />
- {lessonPlan.additionalMaterials &&
- lessonPlan.additionalMaterials !== "None" && (
- additionalMaterialsExport.start()}
- title="Additional materials"
- subTitle="Document containing any additional materials"
- downloadAvailable={
- !!additionalMaterialsExport.readyToExport
- }
- downloadLoading={
- additionalMaterialsExport.status === "loading"
- }
- data={additionalMaterialsExport.data}
- exportsType="additionalMaterials"
- lesson={lessonPlan}
- />
- )}
-
-
-
-
- {`${lessonSections.length - undefinedLessonPlanSections.length}
- of ${lessonSections.length} sections complete`}
-
-
- {lessonSections.map((section) => {
- return (
-
-
-
-
-
- {section.includes("Cycle 1") ? "Cycles 1-3" : section}
-
-
- );
- })}
-
-
-
-
-
-
- );
-}
diff --git a/apps/nextjs/src/app/aila/[id]/download/page.tsx b/apps/nextjs/src/app/aila/[id]/download/page.tsx
index a077481d9..64c13c5ac 100644
--- a/apps/nextjs/src/app/aila/[id]/download/page.tsx
+++ b/apps/nextjs/src/app/aila/[id]/download/page.tsx
@@ -5,7 +5,7 @@ import { type Metadata } from "next";
import { getChatById } from "@/app/actions";
import { serverSideFeatureFlag } from "@/utils/serverSideFeatureFlag";
-import DownloadView from ".";
+import { DownloadView } from "./DownloadView";
export interface DownloadPageProps {
params: {
diff --git a/apps/nextjs/src/app/aila/[id]/download/useDownloadView.ts b/apps/nextjs/src/app/aila/[id]/download/useDownloadView.ts
new file mode 100644
index 000000000..24860c182
--- /dev/null
+++ b/apps/nextjs/src/app/aila/[id]/download/useDownloadView.ts
@@ -0,0 +1,55 @@
+import { AilaPersistedChat } from "@oakai/aila/src/protocol/schema";
+
+import { useProgressForDownloads } from "@/components/AppComponents/Chat/Chat/hooks/useProgressForDownloads";
+import { useExportAdditionalMaterials } from "@/components/ExportsDialogs/useExportAdditionalMaterials";
+import { useExportLessonPlanDoc } from "@/components/ExportsDialogs/useExportLessonPlanDoc";
+import { useExportLessonSlides } from "@/components/ExportsDialogs/useExportLessonSlides";
+import { useExportQuizDoc } from "@/components/ExportsDialogs/useExportQuizDoc";
+import { useExportWorksheetSlides } from "@/components/ExportsDialogs/useExportWorksheetSlides";
+
+export function useDownloadView({
+ id,
+ lessonPlan,
+ messages,
+}: AilaPersistedChat) {
+ const exportProps = {
+ onStart: () => null,
+ lesson: lessonPlan,
+ chatId: id,
+ messageId: messages.length,
+ active: true,
+ };
+
+ const lessonSlidesExport = useExportLessonSlides(exportProps);
+
+ const worksheetExport = useExportWorksheetSlides(exportProps);
+
+ const lessonPlanExport = useExportLessonPlanDoc(exportProps);
+
+ const starterQuizExport = useExportQuizDoc({
+ ...exportProps,
+ quizType: "starter",
+ });
+
+ const exitQuizExport = useExportQuizDoc({
+ ...exportProps,
+ quizType: "exit",
+ });
+
+ const additionalMaterialsExport = useExportAdditionalMaterials(exportProps);
+
+ const { sections, totalSections, totalSectionsComplete } =
+ useProgressForDownloads(lessonPlan);
+
+ return {
+ lessonSlidesExport,
+ worksheetExport,
+ lessonPlanExport,
+ starterQuizExport,
+ exitQuizExport,
+ additionalMaterialsExport,
+ sections,
+ totalSections,
+ totalSectionsComplete,
+ };
+}
diff --git a/apps/nextjs/src/components/AppComponents/Chat/Chat/hooks/useProgressForDownloads.ts b/apps/nextjs/src/components/AppComponents/Chat/Chat/hooks/useProgressForDownloads.ts
new file mode 100644
index 000000000..8e4bf5389
--- /dev/null
+++ b/apps/nextjs/src/components/AppComponents/Chat/Chat/hooks/useProgressForDownloads.ts
@@ -0,0 +1,88 @@
+import { useMemo } from "react";
+
+import { LooseLessonPlan } from "@oakai/aila/src/protocol/schema";
+import { lessonPlanSectionsSchema } from "@oakai/exports/src/schema/input.schema";
+import { ZodIssue } from "zod";
+
+/**
+ * For a given list of Zod issues and lessonPlan fields, checks that none of
+ * the errors pertain to the fields.
+ */
+function getCompleteness(errors: ZodIssue[], fields: string[]) {
+ const hasErrorInSomeField = errors.reduce(
+ (acc, curr) => acc || fields.some((field) => curr.path[0] === field),
+ false,
+ );
+
+ return !hasErrorInSomeField;
+}
+
+export function useProgressForDownloads(lessonPlan: LooseLessonPlan) {
+ return useMemo(() => {
+ const parsedLessonPlan = lessonPlanSectionsSchema.safeParse(lessonPlan);
+ const errors = parsedLessonPlan.error?.errors || [];
+ const sections = [
+ {
+ label: "Lesson details",
+ key: "title",
+ complete: getCompleteness(errors, ["title", "subject", "keyStage"]),
+ },
+ {
+ label: "Lesson learning outcome",
+ key: "learningOutcome",
+ complete: getCompleteness(errors, ["learningOutcome"]),
+ },
+ {
+ label: "Learning cycle outcomes",
+ key: "learningCycles",
+ complete: getCompleteness(errors, ["learningCycles"]),
+ },
+ {
+ label: "Prior knowledge",
+ key: "priorKnowledge",
+ complete: getCompleteness(errors, ["priorKnowledge"]),
+ },
+ {
+ label: "Key learning points",
+ key: "keyLearningPoints",
+ complete: getCompleteness(errors, ["keyLearningPoints"]),
+ },
+ {
+ label: "Misconceptions",
+ key: "misconceptions",
+ complete: getCompleteness(errors, ["misconceptions"]),
+ },
+ {
+ label: "Key words",
+ key: "keywords",
+ complete: getCompleteness(errors, ["keywords"]),
+ },
+ {
+ label: "Starter quiz",
+ key: "starterQuiz",
+ complete: getCompleteness(errors, ["starterQuiz"]),
+ },
+ {
+ label: "Cycles 1 to 3",
+ key: "cycle1",
+ complete: getCompleteness(errors, ["cycle1", "cycle2", "cycle3"]),
+ },
+ {
+ label: "Exit Quiz",
+ key: "exitQuiz",
+ complete: getCompleteness(errors, ["exitQuiz"]),
+ },
+ ];
+
+ const totalSections = sections.length;
+ const totalSectionsComplete = sections.filter(
+ (section) => section.complete,
+ ).length;
+
+ return {
+ sections,
+ totalSections,
+ totalSectionsComplete,
+ };
+ }, [lessonPlan]);
+}
diff --git a/apps/nextjs/src/components/AppComponents/Chat/Chat/utils/index.ts b/apps/nextjs/src/components/AppComponents/Chat/Chat/utils/index.ts
index ae04d9409..8cbf375e4 100644
--- a/apps/nextjs/src/components/AppComponents/Chat/Chat/utils/index.ts
+++ b/apps/nextjs/src/components/AppComponents/Chat/Chat/utils/index.ts
@@ -1,34 +1,6 @@
import { type LooseLessonPlan } from "@oakai/aila/src/protocol/schema";
-import { findListOfUndefinedKeysFromZod } from "@oakai/exports/src/dataHelpers/findListOfUndefinedKeysFromZod";
-import { lessonPlanSectionsSchema } from "@oakai/exports/src/schema/input.schema";
import { type Message } from "ai/react";
-export function setLessonPlanProgress({
- lessonPlan,
- setUndefinedItems,
-}: {
- lessonPlan: LooseLessonPlan;
- setUndefinedItems: React.Dispatch>;
-}) {
- const parseForProgress = lessonPlanSectionsSchema.safeParse(lessonPlan);
-
- if (!parseForProgress.success) {
- const undefinedItems = findListOfUndefinedKeysFromZod(
- parseForProgress.error.errors as {
- expected: string;
- received: string;
- code: string;
- path: (string | number)[];
- message: string;
- }[],
- );
-
- setUndefinedItems(undefinedItems);
- } else {
- setUndefinedItems([]);
- }
-}
-
export function findLatestServerSideState(workingMessages: Message[]) {
console.log("Finding latest server-side state", { workingMessages });
const lastMessage = workingMessages[workingMessages.length - 1];
diff --git a/apps/nextjs/src/components/AppComponents/Chat/export-buttons/LessonPlanProgressDropdown.tsx b/apps/nextjs/src/components/AppComponents/Chat/export-buttons/LessonPlanProgressDropdown.tsx
index cb206f8d7..6121e3cd0 100644
--- a/apps/nextjs/src/components/AppComponents/Chat/export-buttons/LessonPlanProgressDropdown.tsx
+++ b/apps/nextjs/src/components/AppComponents/Chat/export-buttons/LessonPlanProgressDropdown.tsx
@@ -1,4 +1,4 @@
-import React, { useState, useMemo } from "react";
+import React, { useState } from "react";
import { LooseLessonPlan } from "@oakai/aila/src/protocol/schema";
import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
@@ -7,53 +7,21 @@ import { Flex } from "@radix-ui/themes";
import { Icon } from "@/components/Icon";
import { scrollToRef } from "@/utils/scrollToRef";
-export const LESSON_PLAN_SECTIONS = [
- { key: "title", title: "Title" },
- { key: "subject", title: "Subject" },
- { key: "keyStage", title: "Key Stage" },
- { key: "learningOutcome", title: "Learning Outcome" },
- { key: "learningCycles", title: "Learning Cycles" },
- { key: "priorKnowledge", title: "Prior Knowledge" },
- { key: "keyLearningPoints", title: "Key Learning Points" },
- { key: "misconceptions", title: "Misconceptions" },
- { key: "keywords", title: "Keywords" },
- { key: "starterQuiz", title: "Starter Quiz" },
- { key: "cycles", title: "Cycles 1-3" },
- { key: "exitQuiz", title: "Exit Quiz" },
- // { key: "additionalMaterials", title: "Additional Materials" }, Do not show the additional materials section
-] as const;
+import { useProgressForDownloads } from "../Chat/hooks/useProgressForDownloads";
-export type LessonPlanSectionKey = (typeof LESSON_PLAN_SECTIONS)[number]["key"];
-
-export type LessonPlanProgressDropdownProps = {
+type LessonPlanProgressDropdownProps = Readonly<{
lessonPlan: LooseLessonPlan;
sectionRefs: Record>;
documentContainerRef: React.MutableRefObject;
-};
-
-export const lessonPlanSectionIsComplete = (
- lessonPlan: LooseLessonPlan,
- key: LessonPlanSectionKey,
-) => {
- if (key === "cycles") {
- return lessonPlan.cycle1 && lessonPlan.cycle2 && lessonPlan.cycle3;
- }
- return lessonPlan[key] !== undefined && lessonPlan[key] !== null;
-};
+}>;
export const LessonPlanProgressDropdown: React.FC<
LessonPlanProgressDropdownProps
> = ({ lessonPlan, sectionRefs, documentContainerRef }) => {
+ const { sections, totalSections, totalSectionsComplete } =
+ useProgressForDownloads(lessonPlan);
const [openProgressDropDown, setOpenProgressDropDown] = useState(false);
- const completedSections = useMemo(() => {
- return LESSON_PLAN_SECTIONS.filter(({ key }) =>
- lessonPlanSectionIsComplete(lessonPlan, key),
- ).length;
- }, [lessonPlan]);
-
- const allCyclesComplete = lessonPlanSectionIsComplete(lessonPlan, "cycles");
-
return (
- {`${completedSections} of ${LESSON_PLAN_SECTIONS.length} sections complete`}
+ {`${totalSectionsComplete} of ${totalSections} sections complete`}
- {LESSON_PLAN_SECTIONS.map(({ key, title }) => (
-
+ {sections.map(({ label, complete, key }) => (
+
{
- if (key === "cycles" && allCyclesComplete) {
+ if (key === "cycles" && complete) {
if (sectionRefs["cycle-1"]) {
scrollToRef({
ref: sectionRefs["cycle-1"],
containerRef: documentContainerRef,
});
}
- } else if (lessonPlanSectionIsComplete(lessonPlan, key)) {
+ } else if (complete) {
if (sectionRefs[key]) {
scrollToRef({
ref: sectionRefs[key] as React.RefObject,
@@ -110,11 +78,11 @@ export const LessonPlanProgressDropdown: React.FC<
}}
>
- {lessonPlanSectionIsComplete(lessonPlan, key) && (
+ {complete && (
)}
- {title}
+ {label}
))}
diff --git a/packages/exports/src/dataHelpers/findListOfUndefinedKeysFromZod.ts b/packages/exports/src/dataHelpers/findListOfUndefinedKeysFromZod.ts
deleted file mode 100644
index 648fe23d0..000000000
--- a/packages/exports/src/dataHelpers/findListOfUndefinedKeysFromZod.ts
+++ /dev/null
@@ -1,36 +0,0 @@
-export function humanizeCamelCaseString(str: string) {
- return str.replace(/([A-Z])/g, " $1").replace(/^./, function (str) {
- return str.toUpperCase();
- });
-}
-
-export function findListOfUndefinedKeysFromZod(
- errors: {
- expected: string;
- received: string;
- code: string;
- path: (string | number)[];
- message: string;
- }[],
-) {
- const undefinedItems: (string | number)[] = [];
-
- // Map through errors and find undefined items
- errors.forEach((e) => {
- if (e.received === "undefined") {
- undefinedItems.push(...e.path);
- }
- });
-
- // Make array human readable
- const humanReadableArray = undefinedItems.map((item) => {
- if (typeof item === "number") {
- return `[${item}]`;
- }
- if (item.includes("cycle")) {
- return "Cycle " + item.split("cycle")[1];
- }
- return humanizeCamelCaseString(item);
- });
- return humanReadableArray;
-}
diff --git a/packages/exports/src/schema/input.schema.ts b/packages/exports/src/schema/input.schema.ts
index 838ed8338..0a5d9993e 100644
--- a/packages/exports/src/schema/input.schema.ts
+++ b/packages/exports/src/schema/input.schema.ts
@@ -78,6 +78,7 @@ export const lessonPlanSectionsSchema = z.object({
cycle3: cycleSchema.nullish(),
additionalMaterials: z.string().nullish(),
});
+export type LessonPlanSections = z.infer;
export type LessonPlanDocInputData = z.infer;
From 821bb856a573504b5cd953709b764caaa50413c3 Mon Sep 17 00:00:00 2001
From: mantagen
Date: Wed, 28 Aug 2024 13:33:31 +0200
Subject: [PATCH 02/11] copy changes
---
.../src/app/aila/[id]/download/DownloadView.tsx | 16 +++++++---------
1 file changed, 7 insertions(+), 9 deletions(-)
diff --git a/apps/nextjs/src/app/aila/[id]/download/DownloadView.tsx b/apps/nextjs/src/app/aila/[id]/download/DownloadView.tsx
index e06db24ef..eb777b66e 100644
--- a/apps/nextjs/src/app/aila/[id]/download/DownloadView.tsx
+++ b/apps/nextjs/src/app/aila/[id]/download/DownloadView.tsx
@@ -61,9 +61,7 @@ export function DownloadView({
Download resources
- Complete all 12 sections of your lesson to unlock resources
- below. If a section is missing, just ask Aila to help you
- complete your lesson.
+ Choose the resources you would like to generate and download.
lessonPlanExport.start()}
- title="Lesson plan"
- subTitle="All sections"
+ title="Lesson"
+ subTitle="Overview of the complete lesson"
downloadAvailable={!!lessonPlanExport.readyToExport}
downloadLoading={lessonPlanExport.status === "loading"}
data={lessonPlanExport.data}
@@ -88,7 +86,7 @@ export function DownloadView({
starterQuizExport.start()}
title="Starter quiz"
- subTitle="Questions and answers"
+ subTitle="Questions and answers to assess prior knowledge"
downloadAvailable={!!starterQuizExport.readyToExport}
downloadLoading={starterQuizExport.status === "loading"}
data={starterQuizExport.data}
@@ -99,7 +97,7 @@ export function DownloadView({
onClick={() => lessonSlidesExport.start()}
data-testid="chat-download-slides-btn"
title="Slide deck"
- subTitle="Outcomes, key words, 1-3 learning cycles, summary"
+ subTitle="Learning outcome, key words and learning cycles"
downloadAvailable={lessonSlidesExport.readyToExport}
downloadLoading={lessonSlidesExport.status === "loading"}
data={lessonSlidesExport.data}
@@ -109,7 +107,7 @@ export function DownloadView({
worksheetExport.start()}
title="Worksheet"
- subTitle="Interactive activities"
+ subTitle="Practice tasks"
downloadAvailable={!!worksheetExport.readyToExport}
downloadLoading={worksheetExport.status === "loading"}
data={worksheetExport.data}
@@ -119,7 +117,7 @@ export function DownloadView({
exitQuizExport.start()}
title="Exit quiz"
- subTitle="Questions and answers"
+ subTitle="Questions and answers to assess understanding"
downloadAvailable={!!exitQuizExport.readyToExport}
downloadLoading={exitQuizExport.status === "loading"}
data={exitQuizExport.data}
From 21eab934c48555b0d31db4f71a73e5b6db3a6d1f Mon Sep 17 00:00:00 2001
From: mantagen
Date: Wed, 28 Aug 2024 13:41:09 +0200
Subject: [PATCH 03/11] copy
---
.../AppComponents/Chat/Chat/hooks/useProgressForDownloads.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/apps/nextjs/src/components/AppComponents/Chat/Chat/hooks/useProgressForDownloads.ts b/apps/nextjs/src/components/AppComponents/Chat/Chat/hooks/useProgressForDownloads.ts
index 8e4bf5389..4007b9c00 100644
--- a/apps/nextjs/src/components/AppComponents/Chat/Chat/hooks/useProgressForDownloads.ts
+++ b/apps/nextjs/src/components/AppComponents/Chat/Chat/hooks/useProgressForDownloads.ts
@@ -63,7 +63,7 @@ export function useProgressForDownloads(lessonPlan: LooseLessonPlan) {
complete: getCompleteness(errors, ["starterQuiz"]),
},
{
- label: "Cycles 1 to 3",
+ label: "Learning cycles",
key: "cycle1",
complete: getCompleteness(errors, ["cycle1", "cycle2", "cycle3"]),
},
From 708796590a78cb02b6391d24bac2cbdac72b6f0f Mon Sep 17 00:00:00 2001
From: mantagen
Date: Thu, 29 Aug 2024 11:36:35 +0200
Subject: [PATCH 04/11] fix tests
---
.../LessonPlanProgressDropdown.test.tsx | 66 +++++++++----------
1 file changed, 31 insertions(+), 35 deletions(-)
diff --git a/apps/nextjs/src/components/AppComponents/Chat/export-buttons/LessonPlanProgressDropdown.test.tsx b/apps/nextjs/src/components/AppComponents/Chat/export-buttons/LessonPlanProgressDropdown.test.tsx
index 1ba24fc35..13753ae1c 100644
--- a/apps/nextjs/src/components/AppComponents/Chat/export-buttons/LessonPlanProgressDropdown.test.tsx
+++ b/apps/nextjs/src/components/AppComponents/Chat/export-buttons/LessonPlanProgressDropdown.test.tsx
@@ -21,23 +21,25 @@ const { Default, PartiallyCompleted, FullyCompleted, PartialCycles } =
describe("LessonPlanProgressDropdown", () => {
it("renders the Default story with correct closed state", () => {
render( );
- expect(screen.getByText("6 of 12 sections complete")).toBeInTheDocument();
+ expect(screen.getByText("4 of 10 sections complete")).toBeInTheDocument();
expect(screen.queryByText("Cycles")).not.toBeInTheDocument();
});
it("renders the PartiallyCompleted story with correct closed state", () => {
render( );
- expect(screen.getByText("8 of 12 sections complete")).toBeInTheDocument();
+ // expect(screen.getByText("4 of 10 sections complete")).toBeInTheDocument();
+ expect(screen.getByText("5 of 10 sections complete")).toBeInTheDocument();
+ expect(screen.getByText("6 of 10 sections complete")).toBeInTheDocument();
});
it("renders the FullyCompleted story with correct closed state", () => {
render( );
- expect(screen.getByText("12 of 12 sections complete")).toBeInTheDocument();
+ expect(screen.getByText("10 of 10 sections complete")).toBeInTheDocument();
});
it("renders the PartialCycles story with correct closed state", () => {
render( );
- expect(screen.getByText("6 of 12 sections complete")).toBeInTheDocument();
+ expect(screen.getByText("4 of 10 sections complete")).toBeInTheDocument();
});
it("displays the dropdown menu when clicked and shows correct completed sections", async () => {
@@ -59,23 +61,21 @@ describe("LessonPlanProgressDropdown", () => {
"lesson-plan-progress-dropdown-content",
);
expect(cyclesSection).toBeInTheDocument();
- expect(cyclesSection).toHaveTextContent("Cycles 1-3");
+ expect(cyclesSection).toHaveTextContent("Learning cycles");
const dropdownContent = screen.getByTestId(
"lesson-plan-progress-dropdown-content",
);
const sectionStates = [
- { name: "Title", completed: true },
- { name: "Subject", completed: true },
- { name: "Key Stage", completed: true },
- { name: "Learning Outcome", completed: true },
- { name: "Learning Cycles", completed: true },
- { name: "Prior Knowledge", completed: true },
- { name: "Key Learning Points", completed: true },
+ { name: "Lesson details", completed: true },
+ { name: "Lesson learning outcome", completed: true },
+ { name: "Learning cycle outcomes", completed: true },
+ { name: "Prior knowledge", completed: true },
+ { name: "Key learning points", completed: true },
{ name: "Misconceptions", completed: true },
{ name: "Keywords", completed: false },
- { name: "Starter Quiz", completed: false },
- { name: "Cycles 1-3", completed: false },
- { name: "Exit Quiz", completed: false },
+ { name: "Starter quiz", completed: false },
+ { name: "Learning cycles", completed: false },
+ { name: "Exit quiz", completed: false },
];
sectionStates.forEach(({ name, completed }) => {
@@ -108,18 +108,16 @@ describe("LessonPlanProgressDropdown", () => {
"lesson-plan-progress-dropdown-content",
);
const allSections = [
- "Title",
- "Subject",
- "Key Stage",
- "Learning Outcome",
- "Learning Cycles",
- "Prior Knowledge",
- "Key Learning Points",
+ "Lesson details",
+ "Lesson learning outcome",
+ "Learning cycle outcomes",
+ "Prior knowledge",
+ "Key learning points",
"Misconceptions",
"Keywords",
- "Starter Quiz",
- "Cycles 1-3",
- "Exit Quiz",
+ "Starter quiz",
+ "Learning cycles",
+ "Exit quiz",
];
allSections.forEach((name) => {
@@ -147,18 +145,16 @@ describe("LessonPlanProgressDropdown", () => {
"lesson-plan-progress-dropdown-content",
);
const sectionStates = [
- { name: "Title", completed: true },
- { name: "Subject", completed: true },
- { name: "Key Stage", completed: true },
- { name: "Learning Outcome", completed: true },
- { name: "Learning Cycles", completed: true },
- { name: "Prior Knowledge", completed: true },
- { name: "Key Learning Points", completed: false },
+ { name: "Lesson details", completed: true },
+ { name: "Lesson learning outcome", completed: true },
+ { name: "Learning cycle outcomes", completed: true },
+ { name: "Prior knowledge", completed: true },
+ { name: "Key learning points", completed: false },
{ name: "Misconceptions", completed: false },
{ name: "Keywords", completed: false },
- { name: "Starter Quiz", completed: false },
- { name: "Cycles 1-3", completed: false },
- { name: "Exit Quiz", completed: false },
+ { name: "Starter quiz", completed: false },
+ { name: "Learning cycles", completed: false },
+ { name: "Exit quiz", completed: false },
];
sectionStates.forEach(({ name, completed }) => {
From 4ca8fff63482922c3ccc81c9d60da4461593b10b Mon Sep 17 00:00:00 2001
From: mantagen
Date: Thu, 29 Aug 2024 12:23:43 +0200
Subject: [PATCH 05/11] changes test logic to reflect that only one learning
cycle is valie
---
.../Chat/hooks/useProgressForDownloads.ts | 2 +-
.../LessonPlanProgressDropdown.stories.tsx | 110 +++++++++++++++---
.../LessonPlanProgressDropdown.test.tsx | 16 ++-
3 files changed, 102 insertions(+), 26 deletions(-)
diff --git a/apps/nextjs/src/components/AppComponents/Chat/Chat/hooks/useProgressForDownloads.ts b/apps/nextjs/src/components/AppComponents/Chat/Chat/hooks/useProgressForDownloads.ts
index 4007b9c00..f50550b96 100644
--- a/apps/nextjs/src/components/AppComponents/Chat/Chat/hooks/useProgressForDownloads.ts
+++ b/apps/nextjs/src/components/AppComponents/Chat/Chat/hooks/useProgressForDownloads.ts
@@ -68,7 +68,7 @@ export function useProgressForDownloads(lessonPlan: LooseLessonPlan) {
complete: getCompleteness(errors, ["cycle1", "cycle2", "cycle3"]),
},
{
- label: "Exit Quiz",
+ label: "Exit quiz",
key: "exitQuiz",
complete: getCompleteness(errors, ["exitQuiz"]),
},
diff --git a/apps/nextjs/src/components/AppComponents/Chat/export-buttons/LessonPlanProgressDropdown.stories.tsx b/apps/nextjs/src/components/AppComponents/Chat/export-buttons/LessonPlanProgressDropdown.stories.tsx
index 413e72904..cbac20242 100644
--- a/apps/nextjs/src/components/AppComponents/Chat/export-buttons/LessonPlanProgressDropdown.stories.tsx
+++ b/apps/nextjs/src/components/AppComponents/Chat/export-buttons/LessonPlanProgressDropdown.stories.tsx
@@ -1,3 +1,9 @@
+import {
+ Cycle,
+ Keyword,
+ Misconception,
+ Quiz,
+} from "@oakai/aila/src/protocol/schema";
import type { Meta, StoryObj } from "@storybook/react";
import { LessonPlanProgressDropdown } from "./LessonPlanProgressDropdown";
@@ -14,11 +20,15 @@ type Story = StoryObj;
export const Default: Story = {
args: {
lessonPlan: {
+ // 1 (lesson details)
title: "Introduction to Glaciation",
keyStage: "key-stage-3",
subject: "geography",
+ // 2
learningOutcome: "Sample learning outcome",
+ // 3
learningCycles: ["Sample learning cycles"],
+ // 4
priorKnowledge: ["Sample prior knowledge"],
},
sectionRefs: {
@@ -40,11 +50,23 @@ export const PartiallyCompleted: Story = {
args: {
...Default.args,
lessonPlan: {
- ...(Default?.args?.lessonPlan ?? {}),
+ // 1 (lesson details)
+ title: "Introduction to Glaciation",
+ keyStage: "key-stage-3",
+ subject: "geography",
+ // 2
+ learningOutcome: "Sample learning outcome",
+ // 3
+ learningCycles: ["Sample learning cycles"],
+ // 4
+ priorKnowledge: ["Sample prior knowledge"],
+ // 5
keyLearningPoints: ["Sample key learning point"],
- misconceptions: [{ misconception: "Sample misconception" }],
- cycle1: { title: "Sample cycle 1" },
- // Only cycle1 is completed
+ // 6
+ misconceptions: [sampleMisconception()],
+ // 7
+ cycle1: sampleCycle(1),
+ // Only cycle1 is completed, but that is sufficient for downloads
},
},
};
@@ -53,22 +75,30 @@ export const FullyCompleted: Story = {
args: {
...Default.args,
lessonPlan: {
+ // 1 (lesson details)
title: "Introduction to Glaciation",
keyStage: "key-stage-3",
subject: "geography",
+ // 2
learningOutcome: "Sample learning outcome",
+ // 3
learningCycles: ["Sample learning cycles"],
+ // 4
priorKnowledge: ["Sample prior knowledge"],
+ // 5
keyLearningPoints: ["Sample key learning points"],
- misconceptions: [{ misconception: "Sample misconceptions" }],
- keywords: [{ keyword: "Sample keyword" }],
- starterQuiz: [
- { question: "Sample starter quiz", answers: ["Sample answer"] },
- ],
- cycle1: { title: "Sample cycle 1" },
- cycle2: { title: "Sample cycle 2" },
- cycle3: { title: "Sample cycle 3" },
- exitQuiz: [{ question: "Sample exit quiz", answers: ["Answer"] }],
+ // 6
+ misconceptions: [sampleMisconception()],
+ // 7
+ keywords: [sampleKeyword()],
+ // 8
+ starterQuiz: sampleQuiz(),
+ // 9
+ cycle1: sampleCycle(1),
+ cycle2: sampleCycle(2),
+ cycle3: sampleCycle(3),
+ // 10
+ exitQuiz: sampleQuiz(),
additionalMaterials: "Sample additional materials",
},
},
@@ -78,10 +108,58 @@ export const PartialCycles: Story = {
args: {
...Default.args,
lessonPlan: {
+ // 1 - 4
...(Default?.args?.lessonPlan ?? {}),
- cycle1: { title: "Sample cycle 1" },
- cycle2: { title: "Sample cycle 2" },
- // cycle3 is missing
+ // 5
+ cycle1: sampleCycle(1),
+ cycle2: sampleCycle(2),
+ // cycle3 is missing, but that is sufficient for downloads
},
},
};
+
+function sampleCycle(i: number): Cycle {
+ return {
+ title: `Sample cycle ${i}`,
+ durationInMinutes: 10,
+ explanation: {
+ spokenExplanation: "Sample spoken explanation",
+ accompanyingSlideDetails: "Sample slide details",
+ imagePrompt: "Sample image prompt",
+ slideText: "Sample slide text",
+ },
+ checkForUnderstanding: [
+ {
+ question: "Sample question",
+ answers: ["Sample answer"],
+ distractors: ["Sample distractor"],
+ },
+ ],
+ practice: "Sample practice",
+ feedback: "Sample feedback",
+ };
+}
+
+function sampleMisconception(): Misconception {
+ return {
+ misconception: "Sample misconception",
+ description: "Sample description",
+ };
+}
+
+function sampleKeyword(): Keyword {
+ return {
+ keyword: "Sample keyword",
+ definition: "Sample description",
+ };
+}
+
+function sampleQuiz(): Quiz {
+ return [
+ {
+ question: "Sample question",
+ answers: ["Sample answer"],
+ distractors: ["Sample distractor"],
+ },
+ ];
+}
diff --git a/apps/nextjs/src/components/AppComponents/Chat/export-buttons/LessonPlanProgressDropdown.test.tsx b/apps/nextjs/src/components/AppComponents/Chat/export-buttons/LessonPlanProgressDropdown.test.tsx
index 13753ae1c..d14f7d746 100644
--- a/apps/nextjs/src/components/AppComponents/Chat/export-buttons/LessonPlanProgressDropdown.test.tsx
+++ b/apps/nextjs/src/components/AppComponents/Chat/export-buttons/LessonPlanProgressDropdown.test.tsx
@@ -27,9 +27,7 @@ describe("LessonPlanProgressDropdown", () => {
it("renders the PartiallyCompleted story with correct closed state", () => {
render( );
- // expect(screen.getByText("4 of 10 sections complete")).toBeInTheDocument();
- expect(screen.getByText("5 of 10 sections complete")).toBeInTheDocument();
- expect(screen.getByText("6 of 10 sections complete")).toBeInTheDocument();
+ expect(screen.getByText("7 of 10 sections complete")).toBeInTheDocument();
});
it("renders the FullyCompleted story with correct closed state", () => {
@@ -39,7 +37,7 @@ describe("LessonPlanProgressDropdown", () => {
it("renders the PartialCycles story with correct closed state", () => {
render( );
- expect(screen.getByText("4 of 10 sections complete")).toBeInTheDocument();
+ expect(screen.getByText("5 of 10 sections complete")).toBeInTheDocument();
});
it("displays the dropdown menu when clicked and shows correct completed sections", async () => {
@@ -72,9 +70,9 @@ describe("LessonPlanProgressDropdown", () => {
{ name: "Prior knowledge", completed: true },
{ name: "Key learning points", completed: true },
{ name: "Misconceptions", completed: true },
- { name: "Keywords", completed: false },
+ { name: "Key words", completed: false },
{ name: "Starter quiz", completed: false },
- { name: "Learning cycles", completed: false },
+ { name: "Learning cycles", completed: true },
{ name: "Exit quiz", completed: false },
];
@@ -114,7 +112,7 @@ describe("LessonPlanProgressDropdown", () => {
"Prior knowledge",
"Key learning points",
"Misconceptions",
- "Keywords",
+ "Key words",
"Starter quiz",
"Learning cycles",
"Exit quiz",
@@ -151,9 +149,9 @@ describe("LessonPlanProgressDropdown", () => {
{ name: "Prior knowledge", completed: true },
{ name: "Key learning points", completed: false },
{ name: "Misconceptions", completed: false },
- { name: "Keywords", completed: false },
+ { name: "Key words", completed: false },
{ name: "Starter quiz", completed: false },
- { name: "Learning cycles", completed: false },
+ { name: "Learning cycles", completed: true },
{ name: "Exit quiz", completed: false },
];
From cc42587a145e157db15880a1c303bdcb281cc7c0 Mon Sep 17 00:00:00 2001
From: mantagen
Date: Thu, 29 Aug 2024 13:22:31 +0200
Subject: [PATCH 06/11] types on kew words and learning outcomes
---
.../src/app/aila/[id]/download/DownloadView.tsx | 2 +-
apps/nextjs/src/app/aila/help/index.tsx | 2 +-
.../Chat/Chat/hooks/useProgressForDownloads.ts | 4 ++--
.../LessonPlanProgressDropdown.test.tsx | 12 ++++++------
packages/core/src/prompts/lesson-assistant/old.ts | 2 +-
.../lesson-assistant/parts/yourInstructions.ts | 2 +-
6 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/apps/nextjs/src/app/aila/[id]/download/DownloadView.tsx b/apps/nextjs/src/app/aila/[id]/download/DownloadView.tsx
index eb777b66e..818d42298 100644
--- a/apps/nextjs/src/app/aila/[id]/download/DownloadView.tsx
+++ b/apps/nextjs/src/app/aila/[id]/download/DownloadView.tsx
@@ -97,7 +97,7 @@ export function DownloadView({
onClick={() => lessonSlidesExport.start()}
data-testid="chat-download-slides-btn"
title="Slide deck"
- subTitle="Learning outcome, key words and learning cycles"
+ subTitle="Learning outcome, keywords and learning cycles"
downloadAvailable={lessonSlidesExport.readyToExport}
downloadLoading={lessonSlidesExport.status === "loading"}
data={lessonSlidesExport.data}
diff --git a/apps/nextjs/src/app/aila/help/index.tsx b/apps/nextjs/src/app/aila/help/index.tsx
index 6b64a81be..169a73e15 100644
--- a/apps/nextjs/src/app/aila/help/index.tsx
+++ b/apps/nextjs/src/app/aila/help/index.tsx
@@ -115,7 +115,7 @@ const Help = () => {
include the following sections: