Skip to content

Commit

Permalink
feat: add components for question editor (#139)
Browse files Browse the repository at this point in the history
Adds most of the components for the question editor. Connections between
them are still missing.
  • Loading branch information
PupoSDC authored Feb 8, 2024
1 parent 12b0bf3 commit bffa7aa
Show file tree
Hide file tree
Showing 35 changed files with 1,602 additions and 884 deletions.
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
import { useRouter } from "next/router";
import { Box, Tab, TabList, TabPanel, Tabs, tabClasses } from "@mui/joy";
import { default as RightArrow } from "@mui/icons-material/ChevronRightOutlined";
import {
Divider,
Tab,
TabList,
TabPanel,
Tabs,
tabClasses,
tabPanelClasses,
} from "@mui/joy";
import { AppHead } from "@chair-flight/react/components";
import {
LayoutModule,
QuestionEditorAnnexes,
QuestionEditorExplanation,
QuestionEditorLearningObjectives,
QuestionEditorPreview,
QuestionEditorRelatedQuestions,
QuestionEditorVariant,
} from "@chair-flight/react/containers";
Expand All @@ -14,38 +23,17 @@ import type { QuestionBankName } from "@chair-flight/core/question-bank";
import type { Breadcrumbs } from "@chair-flight/react/containers";
import type { NextPage } from "next";

type QueryParams = {
tab?: string;
};

type PageParams = QueryParams & {
type PageParams = {
questionBank: QuestionBankName;
questionId: string;
};

type PageProps = {
questionBank: QuestionBankName;
questionId: string;
tab: string;
};

const Page: NextPage<PageProps> = ({
questionBank,
questionId,
tab: initialTab,
}) => {
const router = useRouter();
const query = router.query as PageParams;
const tab = query.tab ?? initialTab;

const updateQuery = (query: QueryParams) => {
router.push(
{ ...router, query: { ...router.query, ...query } },
undefined,
{ shallow: true },
);
};

const Page: NextPage<PageProps> = ({ questionBank, questionId }) => {
const crumbs = [
[questionBank.toUpperCase(), `/modules/${questionBank}`],
["Questions", `/modules/${questionBank}/questions`],
Expand All @@ -59,76 +47,87 @@ const Page: NextPage<PageProps> = ({
breadcrumbs={crumbs}
fixedHeight
noPadding
sx={{ flexDirection: "row" }}
>
<AppHead
linkTitle={`Chair Flight [${questionId}]`}
linkDescription={""}
/>

<Tabs
value={tab}
onChange={(_, v) => updateQuery({ tab: v as string })}
defaultValue={"question"}
sx={{
backgroundColor: "transparent",
display: "flex",
flexDirection: "column",
height: "100%",
pl: 1,
flex: 1,
background: "transparent",

[`& .${tabPanelClasses.root}`]: {
flex: 1,
overflow: "hidden",
my: 1,
py: 0,
},

[`& .${tabClasses.selected}`]: {
color: "primary.plainColor",
background: "transparent",
},
}}
>
<TabList
sx={{
position: "fixed",
bgcolor: "background.surface",
width: "100%",
height: (theme) => `calc(${theme.spacing(5)} + 2px)`,

[`& .${tabClasses.selected}`]: {
color: "primary.plainColor",
},
}}
>
<TabList sx={{ justifyContent: "center" }}>
<Tab value={"question"}>Question</Tab>
<Tab value={"explanation"}>Explanation</Tab>
<Tab value={"los"}>Learning Objectives</Tab>
<Tab value={"relatedQs"}>Related Questions</Tab>
<Tab value={"los"}>LOs</Tab>
<Tab value={"annexes"}>Annexes</Tab>
<Tab value={"relatedQs"}>Related</Tab>
</TabList>
<Box sx={{ height: (theme) => `calc(${theme.spacing(5)} + 2px)` }} />
<TabPanel value={"question"} sx={{ flex: 1, overflow: "hidden" }}>
<TabPanel value={"question"} sx={{ px: 0, borderRadius: 4 }}>
<QuestionEditorVariant
noSsr
questionBank={questionBank}
questionId={questionId}
/>
</TabPanel>
<TabPanel value={"explanation"} sx={{ flex: 1, overflow: "hidden" }}>
<TabPanel value={"explanation"} sx={{ px: 0, borderRadius: 4 }}>
<QuestionEditorExplanation
noSsr
questionBank={questionBank}
questionId={questionId}
/>
</TabPanel>
<TabPanel value={"los"} sx={{ flex: 1, overflow: "hidden" }}>
<QuestionEditorLearningObjectives
<TabPanel value={"annexes"} sx={{ px: 0 }}>
<QuestionEditorAnnexes
noSsr
questionBank={questionBank}
questionId={questionId}
sx={{ height: "100%" }}
/>
</TabPanel>
<TabPanel value={"relatedQs"} sx={{ flex: 1, overflow: "hidden" }}>
<QuestionEditorRelatedQuestions
<TabPanel value={"los"} sx={{ px: 0 }}>
<QuestionEditorLearningObjectives
noSsr
questionBank={questionBank}
questionId={questionId}
sx={{ height: "100%" }}
/>
</TabPanel>
<TabPanel value={"annexes"} sx={{ flex: 1, overflow: "hidden" }}>
<QuestionEditorAnnexes
<TabPanel value={"relatedQs"} sx={{ px: 0 }}>
<QuestionEditorRelatedQuestions
noSsr
questionBank={questionBank}
questionId={questionId}
sx={{ height: "100%" }}
/>
</TabPanel>
</Tabs>
<Divider orientation="vertical">
<RightArrow size="lg" />
</Divider>
<QuestionEditorPreview
questionBank={questionBank}
questionId={questionId}
sx={{ flex: 1, overflow: "hidden", height: "100%", pr: 1 }}
/>
</LayoutModule>
);
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import * as fs from "node:fs/promises";
import { Box, Tab, TabList, TabPanel, Tabs, tabClasses } from "@mui/joy";
import { AppHead } from "@chair-flight/react/components";
import { LayoutModule, QuestionManager } from "@chair-flight/react/containers";
import {
LayoutModule,
QuestionEditorDiffTool,
QuestionManager,
} from "@chair-flight/react/containers";
import { staticHandler } from "@chair-flight/trpc/server";
import type { QuestionBankName } from "@chair-flight/core/question-bank";
import type { Breadcrumbs } from "@chair-flight/react/containers";
Expand All @@ -22,9 +27,54 @@ const Page: NextPage<PageProps> = ({ questionBank }) => {
] as Breadcrumbs;

return (
<LayoutModule fixedHeight questionBank={questionBank} breadcrumbs={crumbs}>
<LayoutModule
questionBank={questionBank}
breadcrumbs={crumbs}
fixedHeight
noPadding
>
<AppHead />
<QuestionManager questionBank={questionBank} sx={{ height: "100%" }} />
<Tabs
sx={{
backgroundColor: "transparent",
display: "flex",
flexDirection: "column",
height: "100%",
}}
>
<TabList
sx={{
position: "fixed",
bgcolor: "background.surface",
width: "100%",
height: (theme) => `calc(${theme.spacing(5)} + 2px)`,

[`& .${tabClasses.selected}`]: {
color: "primary.plainColor",
},
}}
>
<Tab value={"Intro"}>Intro</Tab>
<Tab value={"Pick"}>Pick Questions</Tab>
<Tab value={"Edit"}>Edit Questions</Tab>
<Tab value={"Submit"}>Submit Changes</Tab>
</TabList>
<Box sx={{ height: (theme) => `calc(${theme.spacing(5)} + 2px)` }} />
<TabPanel value={"Pick"} sx={{ flex: 1, overflow: "hidden" }}>
<QuestionManager
noSsr
questionBank={questionBank}
sx={{ height: "100%" }}
/>
</TabPanel>
<TabPanel value={"Edit"} sx={{ flex: 1, overflow: "hidden" }}>
<QuestionEditorDiffTool
noSsr
questionBank={questionBank}
sx={{ height: "100%" }}
/>
</TabPanel>
</Tabs>
</LayoutModule>
);
};
Expand Down
1 change: 1 addition & 0 deletions libs/base/utils/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from "./assert/assert-type";
export * from "./js/deep-clone";
export * from "./js/make-map";
export * from "./js/noop";
export * from "./random/random";
3 changes: 3 additions & 0 deletions libs/base/utils/src/js/deep-clone.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const deepClone = <T>(obj: T): T => {
return JSON.parse(JSON.stringify(obj)) as T;
};
16 changes: 9 additions & 7 deletions libs/core/question-bank/src/entities/question-bank-question.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,14 @@ export type QuestionTemplate = {
variant: QuestionVariant;
};

export const questionVariantSchema = z.union([
questionVariantSimpleSchema,
questionVariantDefinitionSchema,
questionVariantTrueOrFalseSchema,
questionVariantMultipleCorrectSchema,
questionVariantOneTwoSchema,
]);

export const questionTemplateSchema = z.object({
id: z.string(),
doc: z.string(),
Expand All @@ -52,13 +60,7 @@ export const questionTemplateSchema = z.object({
learningObjectives: z.array(z.string()).min(1),
explanation: z.string(),
srcLocation: z.string().min(6),
variant: z.union([
questionVariantSimpleSchema,
questionVariantDefinitionSchema,
questionVariantTrueOrFalseSchema,
questionVariantMultipleCorrectSchema,
questionVariantOneTwoSchema,
]),
variant: questionVariantSchema,
});

type IQuestionTemplate = z.infer<typeof questionTemplateSchema>;
Expand Down
15 changes: 1 addition & 14 deletions libs/core/question-bank/src/questions/get-new-variant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,9 @@ import type {
} from "../entities/question-bank-question";

export const getNewVariant = (type: QuestionVariantType): QuestionVariant => {
const common = {
type,
id: getRandomId(),
annexes: [] as string[],
externalIds: [] as string[],
explanation: "",
};

switch (type) {
case "simple":
return {
...common,
type: "simple",
question: "",
options: [1, 2, 3, 4].map((i) => ({
Expand All @@ -28,7 +19,6 @@ export const getNewVariant = (type: QuestionVariantType): QuestionVariant => {
};
case "one-two":
return {
...common,
type: "one-two",
question: "",
firstCorrectStatements: [""],
Expand All @@ -38,14 +28,12 @@ export const getNewVariant = (type: QuestionVariantType): QuestionVariant => {
};
case "true-or-false":
return {
...common,
type: "true-or-false",
question: "",
answer: true,
};
case "definition":
return {
...common,
type: "definition",
question: "${term}...",
fakeOptions: [],
Expand All @@ -57,13 +45,12 @@ export const getNewVariant = (type: QuestionVariantType): QuestionVariant => {
};
case "multiple-correct":
return {
...common,
type: "multiple-correct",
options: [1, 2, 3, 4].map((i) => ({
text: "",
correct: i === 1,
why: "",
})),
type: "multiple-correct",
question: "",
};
}
Expand Down
16 changes: 8 additions & 8 deletions libs/core/question-bank/src/questions/get-question-preview.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { QuestionTemplate } from "../entities/question-bank-question";
import type { QuestionVariant } from "../entities/question-bank-question";
import type { QuestionVariantDefinition } from "../entities/question-bank-question-definition";
import type { QuestionVariantMultipleCorrect } from "../entities/question-bank-question-multiple-correct";
import type { QuestionVariantOneTwo } from "../entities/question-bank-question-one-two";
Expand Down Expand Up @@ -65,17 +65,17 @@ const getQuestionMultipleCorrectPreview = (
].join("\n");
};

export const getQuestionPreview = (question: QuestionTemplate) => {
switch (question.variant.type) {
export const getQuestionPreview = (variant: QuestionVariant) => {
switch (variant.type) {
case "simple":
return getQuestionVariantSimplePreview(question.variant);
return getQuestionVariantSimplePreview(variant);
case "one-two":
return getQuestionVariantOneTwoPreview(question.variant);
return getQuestionVariantOneTwoPreview(variant);
case "true-or-false":
return getQuestionVariantTrueOrFalsePreview(question.variant);
return getQuestionVariantTrueOrFalsePreview(variant);
case "definition":
return getQuestionVariantDefinitionPreview(question.variant);
return getQuestionVariantDefinitionPreview(variant);
case "multiple-correct":
return getQuestionMultipleCorrectPreview(question.variant);
return getQuestionMultipleCorrectPreview(variant);
}
};
4 changes: 2 additions & 2 deletions libs/providers/search/src/question-bank/question-search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export class QuestionSearch extends QuestionBankSearchProvider<
return {
id: q.id,
questionBank: bank.getName(),
text: getQuestionPreview(q),
text: getQuestionPreview(q.variant),
subjects: uniqueSubjects,
learningObjectives: q.learningObjectives.map((id) => ({
id,
Expand Down Expand Up @@ -97,7 +97,7 @@ export class QuestionSearch extends QuestionBankSearchProvider<
id: q.id,
questionBank: bank.getName(),
subjects: uniqueSubjects.join(", "),
text: getQuestionPreview(q),
text: getQuestionPreview(q.variant),
learningObjectives: los,
externalIds: q.externalIds.join(", "),
};
Expand Down
Loading

0 comments on commit bffa7aa

Please sign in to comment.