feat: add publish status change from the course edition page

This commit is contained in:
swve 2024-08-05 19:45:24 +02:00
parent 73e81830d3
commit d6aa071425
12 changed files with 929 additions and 581 deletions

View file

@ -2,8 +2,8 @@ import { useAssignments } from '@components/Contexts/Assignments/AssignmentConte
import { useAssignmentsTask, useAssignmentsTaskDispatch } from '@components/Contexts/Assignments/AssignmentsTaskContext';
import { useLHSession } from '@components/Contexts/LHSessionContext';
import AssignmentBoxUI from '@components/Objects/Activities/Assignment/AssignmentBoxUI';
import { getAssignmentTask, getAssignmentTaskSubmissionsMe, handleAssignmentTaskSubmission, updateAssignmentTask } from '@services/courses/assignments';
import { Check, Minus, Plus, PlusCircle, X } from 'lucide-react';
import { getAssignmentTask, getAssignmentTaskSubmissionsMe, getAssignmentTaskSubmissionsUser, handleAssignmentTaskSubmission, updateAssignmentTask } from '@services/courses/assignments';
import { Check, Info, 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';
@ -29,11 +29,12 @@ type QuizSubmitSchema = {
};
type TaskQuizObjectProps = {
view: 'teacher' | 'student';
view: 'teacher' | 'student' | 'grading';
user_id?: string; // Only for read-only view
assignmentTaskUUID?: string;
};
function TaskQuizObject({ view, assignmentTaskUUID }: TaskQuizObjectProps) {
function TaskQuizObject({ view, assignmentTaskUUID, user_id }: TaskQuizObjectProps) {
const session = useLHSession() as any;
const access_token = session?.data?.tokens?.access_token;
const assignmentTaskState = useAssignmentsTask() as any;
@ -118,6 +119,7 @@ function TaskQuizObject({ view, assignmentTaskUUID }: TaskQuizObjectProps) {
submissions: [],
});
const [showSavingDisclaimer, setShowSavingDisclaimer] = useState<boolean>(false);
const [assignmentTaskOutsideProvider, setAssignmentTaskOutsideProvider] = useState<any>(null);
async function chooseOption(qIndex: number, oIndex: number) {
const updatedSubmissions = [...userSubmissions.submissions];
@ -147,6 +149,7 @@ function TaskQuizObject({ view, assignmentTaskUUID }: TaskQuizObjectProps) {
if (assignmentTaskUUID) {
const res = await getAssignmentTask(assignmentTaskUUID, access_token);
if (res.success) {
setAssignmentTaskOutsideProvider(res.data);
setQuestions(res.data.contents.questions);
}
@ -164,11 +167,11 @@ function TaskQuizObject({ view, assignmentTaskUUID }: TaskQuizObjectProps) {
}
}
// Detect changes between initial and current submissions
useEffect(() => {
const hasChanges = JSON.stringify(initialUserSubmissions.submissions) !== JSON.stringify(userSubmissions.submissions);
setShowSavingDisclaimer(hasChanges);
}, [userSubmissions, initialUserSubmissions.submissions]);
// Detect changes between initial and current submissions
useEffect(() => {
const hasChanges = JSON.stringify(initialUserSubmissions.submissions) !== JSON.stringify(userSubmissions.submissions);
setShowSavingDisclaimer(hasChanges);
}, [userSubmissions, initialUserSubmissions.submissions]);
@ -193,10 +196,57 @@ function TaskQuizObject({ view, assignmentTaskUUID }: TaskQuizObjectProps) {
}
};
/* STUDENT VIEW CODE */
/* GRADING VIEW CODE */
const [userSubmissionObject, setUserSubmissionObject] = useState<any>(null);
async function getAssignmentTaskSubmissionFromIdentifiedUserUI() {
if (assignmentTaskUUID && user_id) {
const res = await getAssignmentTaskSubmissionsUser(assignmentTaskUUID, user_id, assignment.assignment_object.assignment_uuid, access_token);
if (res.success) {
setUserSubmissions(res.data.task_submission);
setUserSubmissionObject(res.data);
setInitialUserSubmissions(res.data.task_submission);
}
}
}
async function gradeFC() {
if (assignmentTaskUUID) {
// Ensure maxPoints is defined
const maxPoints = assignmentTaskOutsideProvider?.max_grade_value || 100; // Default to 100 if not defined
// Ensure userSubmissions.questions are set
const totalQuestions = questions.length;
const correctQuestions = userSubmissions.submissions.filter((submission) => {
const question = questions.find((q) => q.questionUUID === submission.questionUUID);
const option = question?.options.find((o) => o.optionUUID === submission.optionUUID);
return option?.correct;
}).length;
// Calculate grade based on correct questions
const grade = Math.floor((correctQuestions / totalQuestions) * maxPoints);
// Save the grade to the server
const values = {
task_submission: userSubmissions,
grade,
task_submission_grade_feedback: 'Auto graded by system',
};
const res = await handleAssignmentTaskSubmission(values, assignmentTaskUUID, assignment.assignment_object.assignment_uuid, access_token);
if (res) {
getAssignmentTaskSubmissionFromIdentifiedUserUI();
toast.success(`Task graded successfully with ${grade} points`);
} else {
toast.error('Error grading task, please retry later.');
}
}
}
/* GRADING VIEW CODE */
useEffect(() => {
assignmentTaskStateHook({
setSelectedAssignmentTaskUUID: assignmentTaskUUID,
@ -210,131 +260,180 @@ function TaskQuizObject({ view, assignmentTaskUUID }: TaskQuizObjectProps) {
getAssignmentTaskUI();
getAssignmentTaskSubmissionFromUserUI();
}
// Grading area
else if (view == 'grading') {
getAssignmentTaskUI();
//setQuestions(assignmentTaskState.assignmentTask.contents.questions);
getAssignmentTaskSubmissionFromIdentifiedUserUI();
}
}, [assignmentTaskState, assignment, assignmentTaskStateHook, access_token]);
return (
<AssignmentBoxUI submitFC={submitFC} saveFC={saveFC} view={view} showSavingDisclaimer={showSavingDisclaimer} type="quiz">
<div className="flex flex-col space-y-6">
{questions && questions.map((question, qIndex) => (
<div key={qIndex} className="flex flex-col space-y-1.5">
<div className="flex space-x-2 items-center">
{view === 'teacher' ? (
<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"
/>
) : (
<p className="w-full px-3 text-neutral-600 bg-[#00008b00] border-2 border-gray-200 rounded-md border-dotted text-sm font-bold">
{question.questionText}
</p>
)}
{view === 'teacher' && (
<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}>
if (questions && questions.length >= 0) {
return (
<AssignmentBoxUI submitFC={submitFC} saveFC={saveFC} gradeFC={gradeFC} view={view} currentPoints={userSubmissionObject?.grade} maxPoints={assignmentTaskOutsideProvider?.max_grade_value} showSavingDisclaimer={showSavingDisclaimer} type="quiz">
<div className="flex flex-col space-y-6">
{questions && questions.map((question, qIndex) => (
<div key={qIndex} className="flex flex-col space-y-1.5">
<div className="flex space-x-2 items-center">
{view === 'teacher' ? (
<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"
/>
) : (
<p className="w-full px-3 text-neutral-600 bg-[#00008b00] border-2 border-gray-200 rounded-md border-dotted text-sm font-bold">
{question.questionText}
</p>
)}
{view === 'teacher' && (
<div
onClick={() => view === 'student' && chooseOption(qIndex, 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 " + (view == 'student' ? 'active:scale-110' : '')}
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)}
>
<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>
{view === 'teacher' ? (
<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"
/>
) : (
<p className="w-full mx-2 px-3 pr-6 text-neutral-600 bg-[#00008b00] text-sm font-bold">
{option.text}
</p>
)}
{view === 'teacher' && (
<>
<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>
</>
)}
{view === 'student' && (
<div className={`w-[20px] flex-none flex items-center h-[20px] rounded-lg ${userSubmissions.submissions.find(
(submission) =>
submission.questionUUID === question.questionUUID && submission.optionUUID === option.optionUUID
)
? "bg-green-200/60 text-green-500 hover:bg-green-300" // Selected state colors
: "bg-slate-200/60 text-slate-500 hover:bg-slate-300" // Default state colors
} text-sm transition-all ease-linear cursor-pointer`}>
{userSubmissions.submissions.find(
<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
onClick={() => view === 'student' && chooseOption(qIndex, 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 " + (view == 'student' ? 'active:scale-110' : '')}
>
<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>
{view === 'teacher' ? (
<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"
/>
) : (
<p className="w-full mx-2 px-3 pr-6 text-neutral-600 bg-[#00008b00] text-sm font-bold">
{option.text}
</p>
)}
{view === 'teacher' && (
<>
<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>
</>
)}
{view === 'grading' && (
<>
<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`}
>
{option.correct ? <Check size={12} className="mx-auto" /> : <X size={12} className="mx-auto" />}
{option.correct ? (
<p className="mx-auto font-bold text-xs">Marked as Correct</p>
) : (
<p className="mx-auto font-bold text-xs">Marked as Incorrect</p>
)}
</div>
</>
)}
{view === 'student' && (
<div className={`w-[20px] flex-none flex items-center h-[20px] rounded-lg ${userSubmissions.submissions.find(
(submission) =>
submission.questionUUID === question.questionUUID && submission.optionUUID === option.optionUUID
) ? (
<Check size={12} className="mx-auto" />
) : (
<X size={12} className="mx-auto" />
)}
)
? "bg-green-200/60 text-green-500 hover:bg-green-300" // Selected state colors
: "bg-slate-200/60 text-slate-500 hover:bg-slate-300" // Default state colors
} text-sm transition-all ease-linear cursor-pointer`}>
{userSubmissions.submissions.find(
(submission) =>
submission.questionUUID === question.questionUUID && submission.optionUUID === option.optionUUID
) ? (
<Check size={12} className="mx-auto" />
) : (
<X size={12} className="mx-auto" />
)}
</div>
)}
{view === 'grading' && (
<div className={`w-[20px] flex-none flex items-center h-[20px] rounded-lg ${userSubmissions.submissions.find(
(submission) =>
submission.questionUUID === question.questionUUID && submission.optionUUID === option.optionUUID
)
? "bg-green-200/60 text-green-500 hover:bg-green-300" // Selected state colors
: "bg-slate-200/60 text-slate-500 hover:bg-slate-300" // Default state colors
} text-sm transition-all ease-linear cursor-pointer`}>
{userSubmissions.submissions.find(
(submission) =>
submission.questionUUID === question.questionUUID && submission.optionUUID === option.optionUUID
) ? (
<Check size={12} className="mx-auto" />
) : (
<X size={12} className="mx-auto" />
)}
</div>
)}
</div>
{view === 'teacher' && oIndex === question.options.length - 1 && questions[qIndex].options.length <= 4 && (
<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>
{view === 'teacher' && oIndex === question.options.length - 1 && questions[qIndex].options.length <= 4 && (
<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>
{view === 'teacher' && questions.length <= 5 && (
<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>
))}
</div>
{view === 'teacher' &&questions.length <= 5 && (
<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>
);
)}
</AssignmentBoxUI>
);
}
else {
return <div className='flex flex-row space-x-2 text-sm items-center'>
<Info size={12} />
<p>No questions found</p>
</div>;
}
}
export default TaskQuizObject;

View file

@ -1,8 +1,16 @@
import { useLHSession } from '@components/Contexts/LHSessionContext';
import { getAPIUrl } from '@services/config/config'
import UserAvatar from '@components/Objects/UserAvatar';
import Modal from '@components/StyledElements/Modal/Modal';
import { getAPIUrl } from '@services/config/config';
import { getUserAvatarMediaDirectory } from '@services/media/media';
import { swrFetcher } from '@services/utils/ts/requests';
import React from 'react'
import { Loader, SendHorizonal, UserCheck, X } from 'lucide-react';
import React, { useEffect } from 'react';
import useSWR from 'swr';
import EvaluateAssignment from './Modals/EvaluateAssignment';
import { AssignmentProvider } from '@components/Contexts/Assignments/AssignmentContext';
import { AssignmentsTaskProvider } from '@components/Contexts/Assignments/AssignmentsTaskContext';
import AssignmentSubmissionProvider from '@components/Contexts/Assignments/AssignmentSubmissionContext';
function AssignmentSubmissionsSubPage({ assignment_uuid }: { assignment_uuid: string }) {
const session = useLHSession() as any;
@ -11,15 +19,123 @@ function AssignmentSubmissionsSubPage({ assignment_uuid }: { assignment_uuid: st
const { data: assignmentSubmission, error: assignmentError } = useSWR(
`${getAPIUrl()}assignments/assignment_${assignment_uuid}/submissions`,
(url) => swrFetcher(url, access_token)
)
return (
<div className='pl-10 mr-10 flex'>
{assignmentSubmission && assignmentSubmission.length > 0 && (
<div>s</div>
)}
);
useEffect(() => {
console.log(assignmentSubmission);
}, [session, assignmentSubmission]);
const renderSubmissions = (status: string) => {
return assignmentSubmission
?.filter((submission: any) => submission.submission_status === status)
.map((submission: any) => (
<SubmissionBox key={submission.submission_uuid} submission={submission} assignment_uuid={assignment_uuid} user_id={submission.user_id} />
));
};
return (
<div className='pl-10 mr-10 flex flex-col pt-3 w-full'>
<div className='flex flex-row w-full'>
<div className='flex-1'>
<div className='flex w-fit mx-auto px-3.5 py-1 bg-rose-600/80 space-x-2 my-5 items-center text-sm font-bold text-white rounded-full'>
<X size={18} />
<h3>Late</h3>
</div>
{renderSubmissions('LATE')}
</div>
<div className='flex-1'>
<div className='flex w-fit mx-auto px-3.5 py-1 bg-amber-600/80 space-x-2 my-5 items-center text-sm font-bold text-white rounded-full'>
<SendHorizonal size={18} />
<h3>Submitted</h3>
</div>
{renderSubmissions('SUBMITTED')}
</div>
<div className='flex-1'>
<div className='flex w-fit mx-auto px-3.5 py-1 bg-emerald-600/80 space-x-2 my-5 items-center text-sm font-bold text-white rounded-full'>
<UserCheck size={18} />
<h3>Graded</h3>
</div>
{renderSubmissions('GRADED')}
</div>
</div>
</div>
)
);
}
export default AssignmentSubmissionsSubPage
function SubmissionBox({ assignment_uuid, user_id, submission }: any) {
const session = useLHSession() as any;
const access_token = session?.data?.tokens?.access_token;
const [gradeSudmissionModal, setGradeSubmissionModal] = React.useState({
open: false,
submission_id: '',
});
const { data: user, error: userError } = useSWR(
`${getAPIUrl()}users/id/${user_id}`,
(url) => swrFetcher(url, access_token)
);
useEffect(() => {
console.log(user);
}
, [session, user]);
return (
<div className='flex flex-row bg-white shadow-[0px_4px_16px_rgba(0,0,0,0.06)] nice-shadow rounded-lg p-4 w-[350px] mx-auto'>
<div className='flex flex-col space-y-2 w-full'>
<div className='flex justify-between w-full'>
<h2 className='uppercase text-slate-400 text-xs tracking-tight font-semibold'>Submission</h2>
<p className='uppercase text-xs tracking-tight font-semibold'>
{new Date(submission.creation_date).toLocaleDateString('en-UK', {
year: 'numeric',
month: 'long',
day: 'numeric',
})}
</p>
</div>
<div className='flex justify-between space-x-2'>
<div className='flex space-x-2'>
<UserAvatar
border="border-4"
avatar_url={getUserAvatarMediaDirectory(user?.user_uuid, user?.avatar_image)}
predefined_avatar={user?.avatar_image ? undefined : 'empty'}
width={40}
/>
<div className='flex flex-col'>
{user?.first_name && user?.last_name ? (<p className='text-sm font-semibold'>{user?.first_name} {user?.last_name}</p>) : (<p className='text-sm font-semibold'>@{user?.username}</p>)}
<p className='text-xs text-slate-400'>{user?.email}</p>
</div>
</div>
<div className='flex flex-col'>
<Modal
isDialogOpen={gradeSudmissionModal.open && gradeSudmissionModal.submission_id === submission.submission_uuid}
onOpenChange={(open: boolean) => setGradeSubmissionModal({ open, submission_id: submission.submission_uuid })}
minHeight="lg"
minWidth="lg"
dialogContent={
<AssignmentProvider assignment_uuid={"assignment_" + assignment_uuid}>
<AssignmentsTaskProvider>
<AssignmentSubmissionProvider assignment_uuid={"assignment_" + assignment_uuid}>
<EvaluateAssignment user_id={user_id} />
</AssignmentSubmissionProvider>
</AssignmentsTaskProvider>
</AssignmentProvider>
}
dialogTitle={`Evaluate @${user?.username}`}
dialogDescription="Evaluate the submission"
dialogTrigger={
<div className='bg-slate-800 hover:bg-slate-700 text-white font-bold py-2 px-4 rounded text-xs cursor-pointer'>
Evaluate
</div>
}
/>
</div>
</div>
</div>
</div>
);
}
export default AssignmentSubmissionsSubPage;

View file

@ -0,0 +1,73 @@
import { useAssignments } from '@components/Contexts/Assignments/AssignmentContext';
import { Download, Info, Medal } from 'lucide-react';
import Link from 'next/link';
import React from 'react'
import TaskQuizObject from '../../_components/TaskEditor/Subs/TaskTypes/TaskQuizObject';
import TaskFileObject from '../../_components/TaskEditor/Subs/TaskTypes/TaskFileObject';
import { useOrg } from '@components/Contexts/OrgContext';
import { getTaskRefFileDir } from '@services/media/media';
function EvaluateAssignment({ user_id }: any) {
const assignments = useAssignments() as any;
const org = useOrg() as any;
console.log(assignments);
return (
<div className='flex-col space-y-4 px-3 py-3 overflow-y-auto min-h-fit'>
{assignments && assignments?.assignment_tasks?.sort((a: any, b: any) => a.id - b.id).map((task: any, index: number) => {
return (
<div className='flex flex-col space-y-2' key={task.assignment_task_uuid}>
<div className='flex justify-between py-2'>
<div className='flex space-x-2 font-semibold text-slate-800'>
<p>Task {index + 1} : </p>
<p className='text-slate-500'>{task.description}</p>
</div>
<div className='flex space-x-2'>
<div
onClick={() => alert(task.hint)}
className='px-3 py-1 flex items-center nice-shadow bg-amber-50/40 text-amber-900 rounded-full space-x-2 cursor-pointer'>
<Info size={13} />
<p className='text-xs font-semibold'>View Hint</p>
</div>
<Link
href={getTaskRefFileDir(
org?.org_uuid,
assignments?.course_object.course_uuid,
assignments?.activity_object.activity_uuid,
assignments?.assignment_object.assignment_uuid,
task.assignment_task_uuid,
task.reference_file
)}
target='_blank'
download={true}
className='px-3 py-1 flex items-center nice-shadow bg-cyan-50/40 text-cyan-900 rounded-full space-x-2 cursor-pointer'>
<Download size={13} />
<div className='flex items-center space-x-2'>
{task.reference_file && (
<span className='relative'>
<span className='absolute right-0 top-0 block h-2 w-2 rounded-full ring-2 ring-white bg-green-400'></span>
</span>
)}
<p className='text-xs font-semibold'>Reference Document</p>
</div>
</Link>
</div>
</div>
<div>
{task.assignment_type === 'QUIZ' && <TaskQuizObject key={task.assignment_task_uuid} view='grading' user_id={user_id} assignmentTaskUUID={task.assignment_task_uuid} />}
{task.assignment_type === 'FILE_SUBMISSION' && <TaskFileObject key={task.assignment_task_uuid} view='student' assignmentTaskUUID={task.assignment_task_uuid} />}
</div>
</div>
)
})}
<div className='flex flex-row-reverse font-semibold items-center'>
<button className='flex space-x-2 px-4 py-2 bg-teal-600/80 text-white rounded-lg nice-shadow items-center'>
<Medal size={18} />
<span>Set final grade</span>
</button>
</div>
</div>
)
}
export default EvaluateAssignment