diff --git a/apps/api/src/services/courses/activities/assignments.py b/apps/api/src/services/courses/activities/assignments.py
index e5e3ef85..4d58e196 100644
--- a/apps/api/src/services/courses/activities/assignments.py
+++ b/apps/api/src/services/courses/activities/assignments.py
@@ -363,7 +363,7 @@ async def read_assignment_tasks(
# Find assignments tasks for an assignment
statement = select(AssignmentTask).where(
- assignment.assignment_uuid == assignment_uuid
+ AssignmentTask.assignment_id == assignment.id
)
# RBAC check
diff --git a/apps/web/app/orgs/[orgslug]/dash/assignments/[assignmentuuid]/_components/Modals/NewTaskModal.tsx b/apps/web/app/orgs/[orgslug]/dash/assignments/[assignmentuuid]/_components/Modals/NewTaskModal.tsx
index fea5b9f3..73a74beb 100644
--- a/apps/web/app/orgs/[orgslug]/dash/assignments/[assignmentuuid]/_components/Modals/NewTaskModal.tsx
+++ b/apps/web/app/orgs/[orgslug]/dash/assignments/[assignmentuuid]/_components/Modals/NewTaskModal.tsx
@@ -1,3 +1,4 @@
+import { useAssignmentsTaskDispatch } from '@components/Contexts/Assignments/AssignmentsTaskContext';
import { useLHSession } from '@components/Contexts/LHSessionContext';
import { getAPIUrl } from '@services/config/config';
import { createAssignmentTask } from '@services/courses/assignments'
@@ -10,6 +11,7 @@ function NewTaskModal({ closeModal, assignment_uuid }: any) {
const session = useLHSession() as any;
const access_token = session?.data?.tokens?.access_token;
const reminderShownRef = React.useRef(false);
+ const assignmentTaskStateHook = useAssignmentsTaskDispatch() as any
function showReminderToast() {
// Check if the reminder has already been shown using sessionStorage
@@ -33,10 +35,11 @@ function NewTaskModal({ closeModal, assignment_uuid }: any) {
contents: {},
max_grade_value: 100,
}
- await createAssignmentTask(task_object, assignment_uuid, access_token)
+ const res = await createAssignmentTask(task_object, assignment_uuid, access_token)
toast.success('Task created successfully')
showReminderToast()
mutate(`${getAPIUrl()}assignments/${assignment_uuid}/tasks`)
+ assignmentTaskStateHook({ type: 'setSelectedAssignmentTaskUUID', payload: res.data.assignment_task_uuid })
closeModal(false)
}
diff --git a/apps/web/app/orgs/[orgslug]/dash/assignments/[assignmentuuid]/_components/TaskEditor/Subs/AssignmentTaskContentEdit.tsx b/apps/web/app/orgs/[orgslug]/dash/assignments/[assignmentuuid]/_components/TaskEditor/Subs/AssignmentTaskContentEdit.tsx
new file mode 100644
index 00000000..0485d125
--- /dev/null
+++ b/apps/web/app/orgs/[orgslug]/dash/assignments/[assignmentuuid]/_components/TaskEditor/Subs/AssignmentTaskContentEdit.tsx
@@ -0,0 +1,24 @@
+import { useAssignments } from '@components/Contexts/Assignments/AssignmentContext';
+import { useAssignmentsTask, useAssignmentsTaskDispatch } from '@components/Contexts/Assignments/AssignmentsTaskContext';
+import { useLHSession } from '@components/Contexts/LHSessionContext';
+import React, { useEffect } from 'react'
+import TaskQuizObject from './TaskTypes/TaskQuizObject';
+
+function AssignmentTaskContentEdit() {
+ const session = useLHSession() as any;
+ const access_token = session?.data?.tokens?.access_token;
+ const assignmentTaskStateHook = useAssignmentsTaskDispatch() as any
+ const assignment = useAssignments() as any
+
+ useEffect(() => {
+ }
+ , [assignment, assignmentTaskStateHook])
+
+ return (
+
+
+
+ )
+}
+
+export default AssignmentTaskContentEdit
\ No newline at end of file
diff --git a/apps/web/app/orgs/[orgslug]/dash/assignments/[assignmentuuid]/_components/TaskEditor.tsx b/apps/web/app/orgs/[orgslug]/dash/assignments/[assignmentuuid]/_components/TaskEditor/Subs/AssignmentTaskGeneralEdit.tsx
similarity index 69%
rename from apps/web/app/orgs/[orgslug]/dash/assignments/[assignmentuuid]/_components/TaskEditor.tsx
rename to apps/web/app/orgs/[orgslug]/dash/assignments/[assignmentuuid]/_components/TaskEditor/Subs/AssignmentTaskGeneralEdit.tsx
index 85557ec4..271bd7e8 100644
--- a/apps/web/app/orgs/[orgslug]/dash/assignments/[assignmentuuid]/_components/TaskEditor.tsx
+++ b/apps/web/app/orgs/[orgslug]/dash/assignments/[assignmentuuid]/_components/TaskEditor/Subs/AssignmentTaskGeneralEdit.tsx
@@ -16,103 +16,7 @@ import React, { use, useEffect } from 'react'
import toast from 'react-hot-toast';
import { mutate } from 'swr';
-function AssignmentTaskEditor({ page }: any) {
- const [selectedSubPage, setSelectedSubPage] = React.useState(page)
- const assignment = useAssignments() as any
- const assignmentTaskState = useAssignmentsTask() as any
- const assignmentTaskStateHook = useAssignmentsTaskDispatch() as any
- const session = useLHSession() as any;
- const access_token = session?.data?.tokens?.access_token;
-
- async function deleteTaskUI() {
- const res = await deleteAssignmentTask(assignmentTaskState.assignmentTask.assignment_task_uuid, assignment.assignment_object.assignment_uuid, access_token)
- if (res) {
- assignmentTaskStateHook({
- type: 'SET_MULTIPLE_STATES',
- payload: {
- selectedAssignmentTaskUUID: null,
- assignmentTask: {},
- },
- });
- mutate(`${getAPIUrl()}assignments/${assignment.assignment_object.assignment_uuid}/tasks`)
- toast.success('Task deleted successfully')
- } else {
- toast.error('Error deleting task, please retry later.')
- }
- }
-
- useEffect(() => {
- }
- , [assignmentTaskState,assignmentTaskStateHook])
-
- return (
-
- {assignmentTaskState.assignmentTask && Object.keys(assignmentTaskState.assignmentTask).length > 0 && (
-
-
-
-
- {assignmentTaskState?.assignmentTask.title}
-
-
-
deleteTaskUI()}
- className='flex px-2 py-1.5 cursor-pointer rounded-md space-x-2 items-center bg-gradient-to-bl text-red-800 bg-rose-100 border border-rose-600/10 shadow-rose-900/10 shadow-lg'>
-
-
Delete Task
-
-
-
-
-
setSelectedSubPage('general')}
- className={`flex space-x-4 py-2 w-fit text-center border-black transition-all ease-linear ${selectedSubPage === 'general'
- ? 'border-b-4'
- : 'opacity-50'
- } cursor-pointer`}
- >
-
-
-
setSelectedSubPage('content')}
- className={`flex space-x-4 py-2 w-fit text-center border-black transition-all ease-linear ${selectedSubPage === 'content'
- ? 'border-b-4'
- : 'opacity-50'
- } cursor-pointer`}
- >
-
-
-
-
-
- {selectedSubPage === 'general' &&
}
-
-
- )}
- {Object.keys(assignmentTaskState.assignmentTask).length == 0 && (
-
-
-
-
-
- No Task Selected
-
-
-
-
- )}
-
-
- )
-}
-
-function AssignmentTaskGeneralEdit() {
+export function AssignmentTaskGeneralEdit() {
const session = useLHSession() as any;
const access_token = session?.data?.tokens?.access_token;
const assignmentTaskState = useAssignmentsTask() as any
@@ -127,8 +31,6 @@ function AssignmentTaskGeneralEdit() {
return errors;
};
-
-
const formik = useFormik({
initialValues: {
title: assignmentTaskState.assignmentTask.title,
@@ -373,6 +275,4 @@ function UpdateTaskRef() {
)
-}
-
-export default AssignmentTaskEditor
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/apps/web/app/orgs/[orgslug]/dash/assignments/[assignmentuuid]/_components/TaskEditor/Subs/TaskTypes/TaskQuizObject.tsx b/apps/web/app/orgs/[orgslug]/dash/assignments/[assignmentuuid]/_components/TaskEditor/Subs/TaskTypes/TaskQuizObject.tsx
new file mode 100644
index 00000000..1b53c085
--- /dev/null
+++ b/apps/web/app/orgs/[orgslug]/dash/assignments/[assignmentuuid]/_components/TaskEditor/Subs/TaskTypes/TaskQuizObject.tsx
@@ -0,0 +1,192 @@
+import { useAssignments } from '@components/Contexts/Assignments/AssignmentContext';
+import { useAssignmentsTask, useAssignmentsTaskDispatch } from '@components/Contexts/Assignments/AssignmentsTaskContext';
+import { useLHSession } from '@components/Contexts/LHSessionContext';
+import AssignmentBoxUI from '@components/Objects/Assignments/AssignmentBoxUI';
+import { updateAssignmentTask } from '@services/courses/assignments';
+import { Check, Minus, Plus, PlusCircle, X } from 'lucide-react';
+import React, { useEffect, useState } from 'react';
+import toast from 'react-hot-toast';
+import { v4 as uuidv4 } from 'uuid';
+
+type QuizSchema = {
+ questionText: string;
+ questionUUID?: string;
+ options: {
+ optionUUID?: string;
+ text: string;
+ fileID: string;
+ type: 'text' | 'image' | 'audio' | 'video';
+ correct: boolean;
+ }[];
+};
+
+function TaskQuizObject() {
+ const session = useLHSession() as any;
+ const access_token = session?.data?.tokens?.access_token;
+ const assignmentTaskState = useAssignmentsTask() as any;
+ const assignmentTaskStateHook = useAssignmentsTaskDispatch() as any;
+ const assignment = useAssignments() as any;
+
+ // Teacher area
+ const [questions, setQuestions] = useState([
+ { questionText: '', questionUUID: 'question_' + uuidv4(), options: [{ text: '', fileID: '', type: 'text', correct: false, optionUUID: 'option_' + uuidv4() }] },
+ ]);
+
+ const handleQuestionChange = (index: number, value: string) => {
+ const updatedQuestions = [...questions];
+ updatedQuestions[index].questionText = value;
+ setQuestions(updatedQuestions);
+ };
+
+ const handleOptionChange = (qIndex: number, oIndex: number, value: string) => {
+ const updatedQuestions = [...questions];
+ updatedQuestions[qIndex].options[oIndex].text = value;
+ setQuestions(updatedQuestions);
+ };
+
+ const addOption = (qIndex: number) => {
+ const updatedQuestions = [...questions];
+ updatedQuestions[qIndex].options.push({ text: '', fileID: '', type: 'text', correct: false, optionUUID: 'option_' + uuidv4() });
+ setQuestions(updatedQuestions);
+ };
+
+ const removeOption = (qIndex: number, oIndex: number) => {
+ const updatedQuestions = [...questions];
+ updatedQuestions[qIndex].options.splice(oIndex, 1);
+ setQuestions(updatedQuestions);
+ };
+
+ const addQuestion = () => {
+ setQuestions([...questions, { questionText: '', questionUUID: 'question_' + uuidv4(), options: [{ text: '', fileID: '', type: 'text', correct: false, optionUUID: 'option_' + uuidv4() }] }]);
+ };
+
+ const removeQuestion = (qIndex: number) => {
+ const updatedQuestions = [...questions];
+ updatedQuestions.splice(qIndex, 1);
+ setQuestions(updatedQuestions);
+ };
+
+ const toggleCorrectOption = (qIndex: number, oIndex: number) => {
+ const updatedQuestions = [...questions];
+ // Find the option to toggle
+ const optionToToggle = updatedQuestions[qIndex].options[oIndex];
+ // Toggle the 'correct' property of the option
+ optionToToggle.correct = !optionToToggle.correct;
+ setQuestions(updatedQuestions);
+ };
+
+ const saveFC = async () => {
+ // Save the quiz to the server
+ const values = {
+ contents: {
+ questions,
+ },
+ };
+ const res = await updateAssignmentTask(values, assignmentTaskState.assignmentTask.assignment_task_uuid, assignment.assignment_object.assignment_uuid, access_token);
+ if (res) {
+ assignmentTaskStateHook({
+ type: 'reload',
+ });
+ toast.success('Task saved successfully');
+ } else {
+ toast.error('Error saving task, please retry later.');
+ }
+ };
+
+ useEffect(() => {
+ if (assignmentTaskState.assignmentTask.contents?.questions) {
+ setQuestions(assignmentTaskState.assignmentTask.contents.questions);
+ }
+ }, [assignmentTaskState,assignment,assignmentTaskStateHook,access_token]);
+
+ // Teacher area end
+
+ return (
+
+
+ {questions.map((question, qIndex) => (
+
+
+
handleQuestionChange(qIndex, e.target.value)}
+ placeholder="Question"
+ className="w-full px-3 text-neutral-600 bg-[#00008b00] border-2 border-gray-200 rounded-md border-dotted text-sm font-bold"
+ />
+
removeQuestion(qIndex)}
+ >
+
+
+
+
+ {question.options.map((option, oIndex) => (
+
+
+
+
{String.fromCharCode(65 + oIndex)}
+
+
handleOptionChange(qIndex, oIndex, e.target.value)}
+ placeholder="Option"
+ className="w-full mx-2 px-3 pr-6 text-neutral-600 bg-[#00008b00] border-2 border-gray-200 rounded-md border-dotted text-sm font-bold"
+ />
+
toggleCorrectOption(qIndex, oIndex)}
+ >
+ {option.correct ?
:
}
+ {option.correct ? (
+
Correct
+ ) : (
+
Incorrect
+ )}
+
+
removeOption(qIndex, oIndex)}
+ >
+
+
+
+
+ {/* Show this at the last option */}
+ {oIndex === question.options.length - 1 && (
+
+
addOption(qIndex)}
+ >
+
+
+
+
+ )}
+
+
+ ))}
+
+
+ ))}
+
+
+
+ );
+}
+
+export default TaskQuizObject;
diff --git a/apps/web/app/orgs/[orgslug]/dash/assignments/[assignmentuuid]/_components/TaskEditor/TaskEditor.tsx b/apps/web/app/orgs/[orgslug]/dash/assignments/[assignmentuuid]/_components/TaskEditor/TaskEditor.tsx
new file mode 100644
index 00000000..6d14990b
--- /dev/null
+++ b/apps/web/app/orgs/[orgslug]/dash/assignments/[assignmentuuid]/_components/TaskEditor/TaskEditor.tsx
@@ -0,0 +1,125 @@
+'use client';
+import { useAssignments } from '@components/Contexts/Assignments/AssignmentContext';
+import { useAssignmentsTask, useAssignmentsTaskDispatch } from '@components/Contexts/Assignments/AssignmentsTaskContext';
+import { useLHSession } from '@components/Contexts/LHSessionContext';
+import { useOrg } from '@components/Contexts/OrgContext';
+import FormLayout, { FormField, FormLabelAndMessage, Input, Textarea } from '@components/StyledElements/Form/Form';
+import * as Form from '@radix-ui/react-form';
+import { getAPIUrl } from '@services/config/config';
+import { getActivity, getActivityByID } from '@services/courses/activities';
+import { deleteAssignmentTask, updateAssignmentTask, updateReferenceFile } from '@services/courses/assignments';
+import { getTaskRefFileDir } from '@services/media/media';
+import { useFormik } from 'formik';
+import { ArrowBigUpDash, Cloud, File, GalleryVerticalEnd, Info, Loader, TentTree, Trash, Upload, UploadCloud } from 'lucide-react'
+import Link from 'next/link';
+import React, { use, useEffect } from 'react'
+import toast from 'react-hot-toast';
+import { mutate } from 'swr';
+import { AssignmentTaskGeneralEdit } from './Subs/AssignmentTaskGeneralEdit';
+import AssignmentTaskContentEdit from './Subs/AssignmentTaskContentEdit';
+
+function AssignmentTaskEditor({ page }: any) {
+ const [selectedSubPage, setSelectedSubPage] = React.useState(page)
+ const assignment = useAssignments() as any
+ const assignmentTaskState = useAssignmentsTask() as any
+ const assignmentTaskStateHook = useAssignmentsTaskDispatch() as any
+ const session = useLHSession() as any;
+ const access_token = session?.data?.tokens?.access_token;
+
+ async function deleteTaskUI() {
+ const res = await deleteAssignmentTask(assignmentTaskState.assignmentTask.assignment_task_uuid, assignment.assignment_object.assignment_uuid, access_token)
+ if (res) {
+ assignmentTaskStateHook({
+ type: 'SET_MULTIPLE_STATES',
+ payload: {
+ selectedAssignmentTaskUUID: null,
+ assignmentTask: {},
+ },
+ });
+ mutate(`${getAPIUrl()}assignments/${assignment.assignment_object.assignment_uuid}/tasks`)
+ mutate(`${getAPIUrl()}assignments/${assignment.assignment_object.assignment_uuid}`)
+ toast.success('Task deleted successfully')
+ } else {
+ toast.error('Error deleting task, please retry later.')
+ }
+ }
+
+ useEffect(() => {
+ // Switch back to general page if the selectedAssignmentTaskUUID is changed
+ if (assignmentTaskState.selectedAssignmentTaskUUID !== assignmentTaskState.assignmentTask.assignment_task_uuid) {
+ setSelectedSubPage('general')
+ }
+ }
+ , [assignmentTaskState, assignmentTaskStateHook, selectedSubPage, assignment])
+
+ return (
+
+ {assignmentTaskState.assignmentTask && Object.keys(assignmentTaskState.assignmentTask).length > 0 && (
+
+
+
+
+ {assignmentTaskState?.assignmentTask.title}
+
+
+
deleteTaskUI()}
+ className='flex px-2 py-1.5 cursor-pointer rounded-md space-x-2 items-center bg-gradient-to-bl text-red-800 bg-rose-100 border border-rose-600/10 shadow-rose-900/10 shadow-lg'>
+
+
Delete Task
+
+
+
+
+
setSelectedSubPage('general')}
+ className={`flex space-x-4 py-2 w-fit text-center border-black transition-all ease-linear ${selectedSubPage === 'general'
+ ? 'border-b-4'
+ : 'opacity-50'
+ } cursor-pointer`}
+ >
+
+
+
setSelectedSubPage('content')}
+ className={`flex space-x-4 py-2 w-fit text-center border-black transition-all ease-linear ${selectedSubPage === 'content'
+ ? 'border-b-4'
+ : 'opacity-50'
+ } cursor-pointer`}
+ >
+
+
+
+
+
+ {selectedSubPage === 'general' &&
}
+ {selectedSubPage === 'content' &&
}
+
+
+ )}
+ {Object.keys(assignmentTaskState.assignmentTask).length == 0 && (
+
+
+
+
+
+ No Task Selected
+
+
+
+
+ )}
+
+
+ )
+}
+
+
+
+export default AssignmentTaskEditor
\ No newline at end of file
diff --git a/apps/web/app/orgs/[orgslug]/dash/assignments/[assignmentuuid]/page.tsx b/apps/web/app/orgs/[orgslug]/dash/assignments/[assignmentuuid]/page.tsx
index 76cb6eef..66664d94 100644
--- a/apps/web/app/orgs/[orgslug]/dash/assignments/[assignmentuuid]/page.tsx
+++ b/apps/web/app/orgs/[orgslug]/dash/assignments/[assignmentuuid]/page.tsx
@@ -1,56 +1,57 @@
'use client';
import BreadCrumbs from '@components/Dashboard/UI/BreadCrumbs'
import { BookOpen, BookX, EllipsisVertical, LayoutList } from 'lucide-react'
-import React from 'react'
-import AssignmentTaskEditor from './_components/TaskEditor';
-import { AssignmentProvider } from '@components/Contexts/Assignments/AssignmentContext';
+import React, { useEffect } from 'react'
+import { AssignmentProvider, useAssignments } from '@components/Contexts/Assignments/AssignmentContext';
import AssignmentTasks from './_components/Tasks';
import { useParams } from 'next/navigation';
import { AssignmentsTaskProvider } from '@components/Contexts/Assignments/AssignmentsTaskContext';
import ToolTip from '@components/StyledElements/Tooltip/Tooltip';
+import AssignmentTaskEditor from './_components/TaskEditor/TaskEditor';
function AssignmentEdit() {
const params = useParams<{ assignmentuuid: string; }>()
return (
-
-
-
-
-
-
Assignment Editor
+
+
+
-
-
-
Published
-
-
-
-
-
-
-
+
+
+
Published
+
+
+
+
+
+
+
+
-
-
-
+
+
@@ -65,10 +66,21 @@ function AssignmentEdit() {
-
-
+
+
)
}
-export default AssignmentEdit
\ No newline at end of file
+export default AssignmentEdit
+
+function BrdCmpx() {
+ const assignment = useAssignments() as any
+
+ useEffect(() => {
+ }, [assignment])
+
+ return (
+
+ )
+}
\ No newline at end of file
diff --git a/apps/web/components/Objects/Assignments/AssignmentBoxUI.tsx b/apps/web/components/Objects/Assignments/AssignmentBoxUI.tsx
new file mode 100644
index 00000000..d27769e7
--- /dev/null
+++ b/apps/web/components/Objects/Assignments/AssignmentBoxUI.tsx
@@ -0,0 +1,53 @@
+import { BookUser, EllipsisVertical, ListTodo, Save } from 'lucide-react'
+import React from 'react'
+
+type AssignmentBoxProps = {
+ type: 'quiz' | 'task'
+ view?: 'teacher' | 'student'
+ saveFC?: () => void
+ children: React.ReactNode
+
+}
+
+function AssignmentBoxUI({ type, view, saveFC, children }: AssignmentBoxProps) {
+ return (
+
+
+
+
+ {type === 'quiz' &&
+
}
+
+
+
+
+
+
+ {view === 'teacher' &&
+
+ }
+
+
+
+ {/* Save button */}
+
saveFC && saveFC()}
+ className='flex px-2 py-1 cursor-pointer rounded-md space-x-2 items-center bg-gradient-to-bl text-slate-500 bg-white/60 hover:bg-white/80 linear transition-all nice-shadow '>
+
+
Save
+
+
+
+
+ {children}
+
+ )
+}
+
+export default AssignmentBoxUI
\ No newline at end of file