mirror of
https://github.com/rzmk/learnhouse.git
synced 2025-12-19 04:19:25 +00:00
feat: full quiz tasks edition and creation
This commit is contained in:
parent
aa55c51b48
commit
175a5a97fa
8 changed files with 453 additions and 144 deletions
|
|
@ -363,7 +363,7 @@ async def read_assignment_tasks(
|
||||||
|
|
||||||
# Find assignments tasks for an assignment
|
# Find assignments tasks for an assignment
|
||||||
statement = select(AssignmentTask).where(
|
statement = select(AssignmentTask).where(
|
||||||
assignment.assignment_uuid == assignment_uuid
|
AssignmentTask.assignment_id == assignment.id
|
||||||
)
|
)
|
||||||
|
|
||||||
# RBAC check
|
# RBAC check
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { useAssignmentsTaskDispatch } from '@components/Contexts/Assignments/AssignmentsTaskContext';
|
||||||
import { useLHSession } from '@components/Contexts/LHSessionContext';
|
import { useLHSession } from '@components/Contexts/LHSessionContext';
|
||||||
import { getAPIUrl } from '@services/config/config';
|
import { getAPIUrl } from '@services/config/config';
|
||||||
import { createAssignmentTask } from '@services/courses/assignments'
|
import { createAssignmentTask } from '@services/courses/assignments'
|
||||||
|
|
@ -10,6 +11,7 @@ function NewTaskModal({ closeModal, assignment_uuid }: any) {
|
||||||
const session = useLHSession() as any;
|
const session = useLHSession() as any;
|
||||||
const access_token = session?.data?.tokens?.access_token;
|
const access_token = session?.data?.tokens?.access_token;
|
||||||
const reminderShownRef = React.useRef(false);
|
const reminderShownRef = React.useRef(false);
|
||||||
|
const assignmentTaskStateHook = useAssignmentsTaskDispatch() as any
|
||||||
|
|
||||||
function showReminderToast() {
|
function showReminderToast() {
|
||||||
// Check if the reminder has already been shown using sessionStorage
|
// Check if the reminder has already been shown using sessionStorage
|
||||||
|
|
@ -33,10 +35,11 @@ function NewTaskModal({ closeModal, assignment_uuid }: any) {
|
||||||
contents: {},
|
contents: {},
|
||||||
max_grade_value: 100,
|
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')
|
toast.success('Task created successfully')
|
||||||
showReminderToast()
|
showReminderToast()
|
||||||
mutate(`${getAPIUrl()}assignments/${assignment_uuid}/tasks`)
|
mutate(`${getAPIUrl()}assignments/${assignment_uuid}/tasks`)
|
||||||
|
assignmentTaskStateHook({ type: 'setSelectedAssignmentTaskUUID', payload: res.data.assignment_task_uuid })
|
||||||
closeModal(false)
|
closeModal(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 (
|
||||||
|
<div>
|
||||||
|
<TaskQuizObject />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AssignmentTaskContentEdit
|
||||||
|
|
@ -16,103 +16,7 @@ import React, { use, useEffect } from 'react'
|
||||||
import toast from 'react-hot-toast';
|
import toast from 'react-hot-toast';
|
||||||
import { mutate } from 'swr';
|
import { mutate } from 'swr';
|
||||||
|
|
||||||
function AssignmentTaskEditor({ page }: any) {
|
export function AssignmentTaskGeneralEdit() {
|
||||||
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 (
|
|
||||||
<div className="flex flex-col font-black text-sm w-full z-20">
|
|
||||||
{assignmentTaskState.assignmentTask && Object.keys(assignmentTaskState.assignmentTask).length > 0 && (
|
|
||||||
<div className='flex flex-col space-y-3'>
|
|
||||||
<div className='flex flex-col bg-white pl-10 pr-10 text-sm tracking-tight z-10 shadow-[0px_4px_16px_rgba(0,0,0,0.06)] pt-5 mb-3 nice-shadow'>
|
|
||||||
<div className='flex py-1 justify-between items-center'>
|
|
||||||
<div className='font-semibold text-lg '>
|
|
||||||
{assignmentTaskState?.assignmentTask.title}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div
|
|
||||||
onClick={() => 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'>
|
|
||||||
<Trash size={18} />
|
|
||||||
<p className='text-xs font-semibold'>Delete Task</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className='flex space-x-2 '>
|
|
||||||
<div
|
|
||||||
onClick={() => 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`}
|
|
||||||
>
|
|
||||||
<div className="flex items-center space-x-2.5 mx-2">
|
|
||||||
<Info size={16} />
|
|
||||||
<div>General</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
onClick={() => 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`}
|
|
||||||
>
|
|
||||||
<div className="flex items-center space-x-2.5 mx-2">
|
|
||||||
<GalleryVerticalEnd size={16} />
|
|
||||||
<div>Content</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className='ml-10 mr-10 mt-10 mx-auto bg-white rounded-xl shadow-sm px-6 py-5 nice-shadow'>
|
|
||||||
{selectedSubPage === 'general' && <AssignmentTaskGeneralEdit />}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{Object.keys(assignmentTaskState.assignmentTask).length == 0 && (
|
|
||||||
<div className='flex flex-col h-full bg-white pl-10 pr-10 text-sm tracking-tight z-10 shadow-[0px_4px_16px_rgba(0,0,0,0.06)] pt-5'>
|
|
||||||
<div className='flex justify-center items-center h-full text-gray-300 antialiased'>
|
|
||||||
<div className='flex flex-col space-y-2 items-center'>
|
|
||||||
<TentTree size={60} />
|
|
||||||
<div className='font-semibold text-2xl py-1'>
|
|
||||||
No Task Selected
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function AssignmentTaskGeneralEdit() {
|
|
||||||
const session = useLHSession() as any;
|
const session = useLHSession() as any;
|
||||||
const access_token = session?.data?.tokens?.access_token;
|
const access_token = session?.data?.tokens?.access_token;
|
||||||
const assignmentTaskState = useAssignmentsTask() as any
|
const assignmentTaskState = useAssignmentsTask() as any
|
||||||
|
|
@ -127,8 +31,6 @@ function AssignmentTaskGeneralEdit() {
|
||||||
return errors;
|
return errors;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const formik = useFormik({
|
const formik = useFormik({
|
||||||
initialValues: {
|
initialValues: {
|
||||||
title: assignmentTaskState.assignmentTask.title,
|
title: assignmentTaskState.assignmentTask.title,
|
||||||
|
|
@ -374,5 +276,3 @@ function UpdateTaskRef() {
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default AssignmentTaskEditor
|
|
||||||
|
|
@ -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<QuizSchema[]>([
|
||||||
|
{ 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 (
|
||||||
|
<AssignmentBoxUI saveFC={saveFC} view='teacher' type="quiz">
|
||||||
|
<div className="flex flex-col space-y-6">
|
||||||
|
{questions.map((question, qIndex) => (
|
||||||
|
<div key={qIndex} className="flex flex-col space-y-1.5">
|
||||||
|
<div className="flex space-x-2 items-center">
|
||||||
|
<input
|
||||||
|
value={question.questionText}
|
||||||
|
onChange={(e) => 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"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
className="w-[20px] flex-none flex items-center h-[20px] rounded-lg bg-slate-200/60 text-slate-500 hover:bg-slate-300 text-sm transition-all ease-linear cursor-pointer"
|
||||||
|
onClick={() => removeQuestion(qIndex)}
|
||||||
|
>
|
||||||
|
<Minus size={12} className="mx-auto" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col space-y-2">
|
||||||
|
{question.options.map((option, oIndex) => (
|
||||||
|
<div className="flex" key={oIndex}>
|
||||||
|
<div
|
||||||
|
key={oIndex}
|
||||||
|
className="answer outline outline-3 outline-white pr-2 shadow w-full flex items-center space-x-2 h-[30px] hover:bg-opacity-100 hover:shadow-md rounded-lg bg-white text-sm duration-150 cursor-pointer ease-linear nice-shadow"
|
||||||
|
>
|
||||||
|
<div className="font-bold text-base flex items-center h-full w-[40px] rounded-l-md text-slate-800 bg-slate-100/80">
|
||||||
|
<p className="mx-auto font-bold text-sm">{String.fromCharCode(65 + oIndex)}</p>
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={option.text}
|
||||||
|
onChange={(e) => 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"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
className={`w-fit flex-none flex text-xs px-2 py-0.5 space-x-1 items-center h-fit rounded-lg ${option.correct ? 'bg-lime-200 text-lime-600' : 'bg-rose-200/60 text-rose-500'
|
||||||
|
} hover:bg-lime-300 text-sm transition-all ease-linear cursor-pointer`}
|
||||||
|
onClick={() => toggleCorrectOption(qIndex, oIndex)}
|
||||||
|
>
|
||||||
|
{option.correct ? <Check size={12} className="mx-auto" /> : <X size={12} className="mx-auto" />}
|
||||||
|
{option.correct ? (
|
||||||
|
<p className="mx-auto font-bold text-xs">Correct</p>
|
||||||
|
) : (
|
||||||
|
<p className="mx-auto font-bold text-xs">Incorrect</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="w-[20px] flex-none flex items-center h-[20px] rounded-lg bg-slate-200/60 text-slate-500 hover:bg-slate-300 text-sm transition-all ease-linear cursor-pointer"
|
||||||
|
onClick={() => removeOption(qIndex, oIndex)}
|
||||||
|
>
|
||||||
|
<Minus size={12} className="mx-auto" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{/* Show this at the last option */}
|
||||||
|
{oIndex === question.options.length - 1 && (
|
||||||
|
<div className="flex justify-center mx-auto px-2">
|
||||||
|
<div
|
||||||
|
className="outline text-xs outline-3 outline-white px-2 shadow w-full flex items-center h-[30px] hover:bg-opacity-100 hover:shadow-md rounded-lg bg-white duration-150 cursor-pointer ease-linear nice-shadow"
|
||||||
|
onClick={() => addOption(qIndex)}
|
||||||
|
>
|
||||||
|
<Plus size={14} className="inline-block" />
|
||||||
|
<span></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-center mx-auto px-2">
|
||||||
|
<div
|
||||||
|
className="flex w-full my-2 py-2 px-4 bg-white text-slate text-xs rounded-md nice-shadow hover:shadow-sm cursor-pointer space-x-3 items-center transition duration-150 ease-linear"
|
||||||
|
onClick={addQuestion}
|
||||||
|
>
|
||||||
|
<PlusCircle size={14} className="inline-block" />
|
||||||
|
<span>Add Question</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</AssignmentBoxUI>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TaskQuizObject;
|
||||||
|
|
@ -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 (
|
||||||
|
<div className="flex flex-col font-black text-sm w-full z-20">
|
||||||
|
{assignmentTaskState.assignmentTask && Object.keys(assignmentTaskState.assignmentTask).length > 0 && (
|
||||||
|
<div className='flex flex-col space-y-3'>
|
||||||
|
<div className='flex flex-col bg-white pl-10 pr-10 text-sm tracking-tight z-10 shadow-[0px_4px_16px_rgba(0,0,0,0.06)] pt-5 mb-3 nice-shadow'>
|
||||||
|
<div className='flex py-1 justify-between items-center'>
|
||||||
|
<div className='font-semibold text-lg '>
|
||||||
|
{assignmentTaskState?.assignmentTask.title}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
onClick={() => 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'>
|
||||||
|
<Trash size={18} />
|
||||||
|
<p className='text-xs font-semibold'>Delete Task</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className='flex space-x-2 '>
|
||||||
|
<div
|
||||||
|
onClick={() => 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`}
|
||||||
|
>
|
||||||
|
<div className="flex items-center space-x-2.5 mx-2">
|
||||||
|
<Info size={16} />
|
||||||
|
<div>General</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
onClick={() => 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`}
|
||||||
|
>
|
||||||
|
<div className="flex items-center space-x-2.5 mx-2">
|
||||||
|
<GalleryVerticalEnd size={16} />
|
||||||
|
<div>Content</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className='ml-10 mr-10 mt-10 mx-auto bg-white rounded-xl shadow-sm px-6 py-5 nice-shadow'>
|
||||||
|
{selectedSubPage === 'general' && <AssignmentTaskGeneralEdit />}
|
||||||
|
{selectedSubPage === 'content' && <AssignmentTaskContentEdit />}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{Object.keys(assignmentTaskState.assignmentTask).length == 0 && (
|
||||||
|
<div className='flex flex-col h-full bg-white pl-10 pr-10 text-sm tracking-tight z-10 shadow-[0px_4px_16px_rgba(0,0,0,0.06)] pt-5'>
|
||||||
|
<div className='flex justify-center items-center h-full text-gray-300 antialiased'>
|
||||||
|
<div className='flex flex-col space-y-2 items-center'>
|
||||||
|
<TentTree size={60} />
|
||||||
|
<div className='font-semibold text-2xl py-1'>
|
||||||
|
No Task Selected
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export default AssignmentTaskEditor
|
||||||
|
|
@ -1,22 +1,23 @@
|
||||||
'use client';
|
'use client';
|
||||||
import BreadCrumbs from '@components/Dashboard/UI/BreadCrumbs'
|
import BreadCrumbs from '@components/Dashboard/UI/BreadCrumbs'
|
||||||
import { BookOpen, BookX, EllipsisVertical, LayoutList } from 'lucide-react'
|
import { BookOpen, BookX, EllipsisVertical, LayoutList } from 'lucide-react'
|
||||||
import React from 'react'
|
import React, { useEffect } from 'react'
|
||||||
import AssignmentTaskEditor from './_components/TaskEditor';
|
import { AssignmentProvider, useAssignments } from '@components/Contexts/Assignments/AssignmentContext';
|
||||||
import { AssignmentProvider } from '@components/Contexts/Assignments/AssignmentContext';
|
|
||||||
import AssignmentTasks from './_components/Tasks';
|
import AssignmentTasks from './_components/Tasks';
|
||||||
import { useParams } from 'next/navigation';
|
import { useParams } from 'next/navigation';
|
||||||
import { AssignmentsTaskProvider } from '@components/Contexts/Assignments/AssignmentsTaskContext';
|
import { AssignmentsTaskProvider } from '@components/Contexts/Assignments/AssignmentsTaskContext';
|
||||||
import ToolTip from '@components/StyledElements/Tooltip/Tooltip';
|
import ToolTip from '@components/StyledElements/Tooltip/Tooltip';
|
||||||
|
import AssignmentTaskEditor from './_components/TaskEditor/TaskEditor';
|
||||||
|
|
||||||
function AssignmentEdit() {
|
function AssignmentEdit() {
|
||||||
const params = useParams<{ assignmentuuid: string; }>()
|
const params = useParams<{ assignmentuuid: string; }>()
|
||||||
return (
|
return (
|
||||||
<div className='flex w-full flex-col'>
|
<div className='flex w-full flex-col'>
|
||||||
|
<AssignmentProvider assignment_uuid={'assignment_' + params.assignmentuuid}>
|
||||||
<div className='pb-5 bg-white z-50 shadow-[0px_4px_16px_rgba(0,0,0,0.06)] nice-shadow'>
|
<div className='pb-5 bg-white z-50 shadow-[0px_4px_16px_rgba(0,0,0,0.06)] nice-shadow'>
|
||||||
<div className='flex justify-between mr-10 h-full'>
|
<div className='flex justify-between mr-10 h-full'>
|
||||||
<div className="pl-10 mr-10 tracking-tighter">
|
<div className="pl-10 mr-10 tracking-tighter">
|
||||||
<BreadCrumbs type="assignments" last_breadcrumb='UUID' />
|
<BrdCmpx />
|
||||||
<div className="w-100 flex justify-between">
|
<div className="w-100 flex justify-between">
|
||||||
<div className="flex font-bold text-2xl">Assignment Editor</div>
|
<div className="flex font-bold text-2xl">Assignment Editor</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -50,7 +51,7 @@ function AssignmentEdit() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex h-full w-full">
|
<div className="flex h-full w-full">
|
||||||
<AssignmentProvider assignment_uuid={'assignment_' + params.assignmentuuid}>
|
|
||||||
<AssignmentsTaskProvider>
|
<AssignmentsTaskProvider>
|
||||||
<div className='flex w-[400px] flex-col h-full custom-dots-bg'>
|
<div className='flex w-[400px] flex-col h-full custom-dots-bg'>
|
||||||
<div className='flex mx-auto px-3.5 py-1 bg-neutral-600/80 space-x-2 my-5 items-center text-sm font-bold text-white rounded-full'>
|
<div className='flex mx-auto px-3.5 py-1 bg-neutral-600/80 space-x-2 my-5 items-center text-sm font-bold text-white rounded-full'>
|
||||||
|
|
@ -65,10 +66,21 @@ function AssignmentEdit() {
|
||||||
</AssignmentProvider>
|
</AssignmentProvider>
|
||||||
</div>
|
</div>
|
||||||
</AssignmentsTaskProvider>
|
</AssignmentsTaskProvider>
|
||||||
</AssignmentProvider>
|
|
||||||
</div>
|
</div>
|
||||||
|
</AssignmentProvider>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default AssignmentEdit
|
export default AssignmentEdit
|
||||||
|
|
||||||
|
function BrdCmpx() {
|
||||||
|
const assignment = useAssignments() as any
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
}, [assignment])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<BreadCrumbs type="assignments" last_breadcrumb={assignment?.assignment_object?.title} />
|
||||||
|
)
|
||||||
|
}
|
||||||
53
apps/web/components/Objects/Assignments/AssignmentBoxUI.tsx
Normal file
53
apps/web/components/Objects/Assignments/AssignmentBoxUI.tsx
Normal file
|
|
@ -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 (
|
||||||
|
<div className='flex flex-col px-4 py-2 nice-shadow rounded-md bg-slate-100/30'>
|
||||||
|
<div className='flex justify-between space-x-2 pb-2 text-slate-400 items-center'>
|
||||||
|
<div className='flex space-x-1 items-center'>
|
||||||
|
<div className='text-lg font-semibold'>
|
||||||
|
{type === 'quiz' &&
|
||||||
|
<div className='flex space-x-1.5 items-center'>
|
||||||
|
<ListTodo size={17} />
|
||||||
|
<p>Quiz</p>
|
||||||
|
</div>}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div className='flex items-center space-x-1'>
|
||||||
|
<EllipsisVertical size={15} />
|
||||||
|
</div>
|
||||||
|
{view === 'teacher' &&
|
||||||
|
<div className='flex bg-amber-200/20 text-xs rounded-full space-x-1 px-2 py-0.5 mx-auto font-bold outline items-center text-amber-600 outline-1 outline-amber-300/40'>
|
||||||
|
<BookUser size={12} />
|
||||||
|
<p>Teacher view</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<div className='flex px-1 py-1 rounded-md items-center'>
|
||||||
|
|
||||||
|
{/* Save button */}
|
||||||
|
<div
|
||||||
|
onClick={() => 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 size={14} />
|
||||||
|
<p className='text-xs font-semibold'>Save</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AssignmentBoxUI
|
||||||
Loading…
Add table
Add a link
Reference in a new issue