diff --git a/client/Makefile b/client/Makefile index 062e7d51..e387858d 100644 --- a/client/Makefile +++ b/client/Makefile @@ -4,7 +4,7 @@ clean: .PHONY: develop develop: node_modules/gatsby - npx gatsby develop -p 8000 + npx gatsby develop -p 80 .PHONY: pretty pretty: diff --git a/client/src/api.ts b/client/src/api.ts index 33f6b55f..9b482bbf 100644 --- a/client/src/api.ts +++ b/client/src/api.ts @@ -473,13 +473,16 @@ export async function fetchSubject(id: string): Promise { id name description + defaultTopics } topics { id name description + categoryParent } questions { + useDefaultTopics question { _id question @@ -494,11 +497,13 @@ export async function fetchSubject(id: string): Promise { id name description + defaultTopics } topics { id name description + categoryParent } } } @@ -538,13 +543,16 @@ export async function fetchSubjects( id name description + defaultTopics } topics { id name description + categoryParent } questions { + useDefaultTopics question { _id question @@ -559,12 +567,14 @@ export async function fetchSubjects( id name description + defaultTopics } topics { id name description - } + categoryParent + } } } } @@ -609,13 +619,16 @@ export async function updateSubject( id name description + defaultTopics } topics { id name - description + categoryParent + description } questions { + useDefaultTopics question { _id question @@ -630,11 +643,13 @@ export async function updateSubject( id name description + defaultTopics } topics { id name description + categoryParent } } } @@ -674,6 +689,7 @@ export async function addOrUpdateSubjectQuestions( question category topics + useDefaultTopics } } } @@ -1104,13 +1120,16 @@ export async function fetchMentorById( id name description + defaultTopics } topics { id name description + categoryParent } questions(mentor:$mentor) { + useDefaultTopics question { _id clientId @@ -1119,10 +1138,12 @@ export async function fetchMentorById( id name description + defaultTopics } topics { id name + categoryParent description } } @@ -2222,14 +2243,17 @@ export async function exportMentor( topics { id name + categoryParent description } categories { id name description + defaultTopics } questions { + useDefaultTopics question { _id question @@ -2244,11 +2268,13 @@ export async function exportMentor( id name description + defaultTopics } topics { id name - description + categoryParent + description } } } @@ -2358,14 +2384,17 @@ export async function importMentorPreview( topics { id name - description + categoryParent + description } categories { id name description + defaultTopics } questions { + useDefaultTopics question { _id question @@ -2380,11 +2409,13 @@ export async function importMentorPreview( id name description + defaultTopics } topics { id name - description + categoryParent + description } } } @@ -2396,7 +2427,8 @@ export async function importMentorPreview( isRequired isArchived topics { - id + categoryParent + id name description } @@ -2404,8 +2436,10 @@ export async function importMentorPreview( id name description + defaultTopics } questions { + useDefaultTopics question { _id question @@ -2420,11 +2454,13 @@ export async function importMentorPreview( id name description + defaultTopics } topics { id name - description + categoryParent + description } } } diff --git a/client/src/components/author/category-list-item.tsx b/client/src/components/author/category-list-item.tsx index 5eb6756a..4b2a7474 100644 --- a/client/src/components/author/category-list-item.tsx +++ b/client/src/components/author/category-list-item.tsx @@ -16,16 +16,20 @@ import { IconButton, Collapse, ListSubheader, + Autocomplete, + ListItemText, + ListItemSecondaryAction, } from "@mui/material"; import DeleteIcon from "@mui/icons-material/Delete"; import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; -import { Category } from "types"; +import { Category, Topic } from "types"; import QuestionListItem from "./question-list-item"; import { SubjectQuestionGQL } from "types-gql"; import { onTextInputChanged } from "helpers"; export function CategoryListItem(props: { category: Category; + topics: Topic[]; questions: SubjectQuestionGQL[]; selectedQuestion?: string; selectedCategory?: string; @@ -38,6 +42,7 @@ export function CategoryListItem(props: { }): JSX.Element { const { category, + topics, questions, selectedQuestion, selectedCategory, @@ -121,6 +126,73 @@ export function CategoryListItem(props: { shrink: true, }} /> + Default Topics + + !category.defaultTopics.find((id) => id === t.id) && + !t.categoryParent + )} + getOptionLabel={(option: Topic) => option.name} + onChange={(e, v) => { + if (!v?.id) { + return; + } + updateCategory({ + ...category, + defaultTopics: [...category.defaultTopics, v.id], + }); + }} + style={{ width: "100%" }} + renderInput={(params) => ( + + )} + /> + + {category.defaultTopics.map((t, i) => { + const topic = topics.find((topic) => topic.id === t); + if (!topic) { + return null; + } + let topicName = + topic.categoryParent === category.id + ? `${category.name}` + : topic.name || ""; + return ( + +
+ + + { + updateCategory({ + ...category, + defaultTopics: category.defaultTopics.filter( + (topicId) => topicId !== topic.id + ), + }); + }} + > + + + +
+
+ ); + })} +
{(provided) => ( + props.updateQuestion({ + ...question, + useDefaultTopics: val, + }) + } + useDefaultTopics={Boolean(question.useDefaultTopics)} questionTopics={question.topics.filter((t) => props.topics.map((t) => t.id).includes(t.id) )} + questionCategory={question.category} updateTopics={(t: Topic[]) => props.updateQuestion({ ...question, topics: t }) } diff --git a/client/src/components/author/question-topics-list.tsx b/client/src/components/author/question-topics-list.tsx index cc53ac7d..75b5e027 100644 --- a/client/src/components/author/question-topics-list.tsx +++ b/client/src/components/author/question-topics-list.tsx @@ -15,20 +15,34 @@ import { ListItemText, ListItemSecondaryAction, ListSubheader, + FormControlLabel, + Checkbox, } from "@mui/material"; import Autocomplete from "@mui/material/Autocomplete"; import AddIcon from "@mui/icons-material/Add"; import DeleteIcon from "@mui/icons-material/Delete"; -import { Topic } from "types"; +import { Category, Topic } from "types"; +import { RowDiv } from "components/styled-components"; export function TopicsList(props: { classes: Record; + useDefaultTopics: boolean; + updateUseDefaultTopics: (val: boolean) => void; allTopics: Topic[]; questionTopics: Topic[]; + questionCategory?: Category; updateTopics: (val: Topic[]) => void; }): JSX.Element { - const { classes, questionTopics, allTopics } = props; + const { + classes, + questionTopics, + allTopics, + useDefaultTopics, + updateUseDefaultTopics, + questionCategory, + } = props; const [topicSearch, setTopicSearch] = useState(); + console.log(questionCategory); function addTopic(val: Topic) { props.updateTopics([...questionTopics, val]); @@ -54,6 +68,40 @@ export function TopicsList(props: { > Topics + {questionCategory ? ( + + { + updateUseDefaultTopics(e.target.checked); + }} + /> + } + /> + + ) : undefined} + {useDefaultTopics && + questionCategory?.defaultTopics?.map((t, i) => { + const topicName = + allTopics.find((topic) => topic.id === t)?.name || + questionCategory.name; + return ( + +
+ +
+
+ ); + })} {questionTopics.map((t, i) => { const topicName = allTopics.find((topic) => topic.id === t.id)?.name || ""; @@ -83,7 +131,12 @@ export function TopicsList(props: {
+ !questionCategory?.defaultTopics?.find((qt) => qt === t.id) && + !questionTopics.find((qt) => qt.id === t.id) && + !t.categoryParent // category specific topic should not be added + )} getOptionLabel={(option: Topic) => option.name} onChange={(e, v) => { setTopicSearch(v || undefined); diff --git a/client/src/components/author/questions-list.tsx b/client/src/components/author/questions-list.tsx index 4c708e90..c6d70497 100644 --- a/client/src/components/author/questions-list.tsx +++ b/client/src/components/author/questions-list.tsx @@ -139,6 +139,7 @@ export function QuestionsList(props: { > q.category?.id === category.id )} diff --git a/client/src/components/author/topics-list.tsx b/client/src/components/author/topics-list.tsx index 87c4b468..2ed22449 100644 --- a/client/src/components/author/topics-list.tsx +++ b/client/src/components/author/topics-list.tsx @@ -48,7 +48,12 @@ export function TopicCard(props: { data-test={topic.name} label="Topic" variant="outlined" - value={topic.name} + value={ + Boolean(topic.categoryParent) + ? `(Category Default) ${topic.name}` + : topic.name + } + disabled={Boolean(topic.categoryParent)} onChange={(e) => onTextInputChanged(e, () => { props.editTopic({ ...topic, name: e.target.value }); @@ -111,8 +116,9 @@ export function TopicsList(props: { removeTopic: (val: Topic) => void; moveTopic: (toMove: number, moveTo: number) => void; }): JSX.Element { - const { classes } = props; + const { classes, topics } = props; const { height: windowHeight } = useWithWindowSize(); + const topicsToDisplay = topics.filter((t) => !t.categoryParent); function onDragEnd(result: DropResult) { if (!result.destination) { @@ -133,7 +139,7 @@ export function TopicsList(props: { style={{ height: windowHeight - 300, overflow: "auto" }} {...provided.droppableProps} > - {props.topics.map((t, i) => ( + {topics.map((t, i) => ( c.id == args.categoryId) : undefined, topics: [], + useDefaultTopics: true, }, ], }); diff --git a/client/src/types-gql.ts b/client/src/types-gql.ts index f1d7169e..aada965f 100644 --- a/client/src/types-gql.ts +++ b/client/src/types-gql.ts @@ -123,12 +123,14 @@ export interface SubjectQuestionGQL { question: Question; category?: Category; topics: Topic[]; + useDefaultTopics?: boolean; } export interface AddOrUpdateQuestionGQL { question: string; category?: string; topics: string[]; + useDefaultTopics?: boolean; } export interface ExternalVideoIdsGQL { diff --git a/client/src/types.ts b/client/src/types.ts index 1910ff65..0145003b 100644 --- a/client/src/types.ts +++ b/client/src/types.ts @@ -265,12 +265,14 @@ export interface Category { id: string; name: string; description: string; + defaultTopics: string[]; } export interface Topic { id: string; name: string; description: string; + categoryParent?: string; } export interface Question {