mirror of
https://github.com/rzmk/learnhouse.git
synced 2025-12-19 04:19:25 +00:00
feat: grading, mark as done and grade showing
This commit is contained in:
parent
9530ee6930
commit
40ef2d0cec
7 changed files with 572 additions and 94 deletions
|
|
@ -3,7 +3,7 @@ import Link from 'next/link'
|
|||
import { getAPIUrl, getUriWithOrg } from '@services/config/config'
|
||||
import Canva from '@components/Objects/Activities/DynamicCanva/DynamicCanva'
|
||||
import VideoActivity from '@components/Objects/Activities/Video/Video'
|
||||
import { BookOpenCheck, Check, MoreVertical, UserRoundPen } from 'lucide-react'
|
||||
import { BookOpenCheck, Check, CheckCircle, MoreVertical, UserRoundPen } from 'lucide-react'
|
||||
import { markActivityAsComplete } from '@services/courses/activity'
|
||||
import DocumentPdfActivity from '@components/Objects/Activities/DocumentPdf/DocumentPdf'
|
||||
import ActivityIndicators from '@components/Pages/Courses/ActivityIndicators'
|
||||
|
|
@ -17,7 +17,7 @@ import AIActivityAsk from '@components/Objects/Activities/AI/AIActivityAsk'
|
|||
import AIChatBotProvider from '@components/Contexts/AI/AIChatBotContext'
|
||||
import { useLHSession } from '@components/Contexts/LHSessionContext'
|
||||
import React, { useEffect } from 'react'
|
||||
import { getAssignmentFromActivityUUID, submitAssignmentForGrading } from '@services/courses/assignments'
|
||||
import { getAssignmentFromActivityUUID, getFinalGrade, submitAssignmentForGrading } from '@services/courses/assignments'
|
||||
import AssignmentStudentActivity from '@components/Objects/Activities/Assignment/AssignmentStudentActivity'
|
||||
import { AssignmentProvider } from '@components/Contexts/Assignments/AssignmentContext'
|
||||
import { AssignmentsTaskProvider } from '@components/Contexts/Assignments/AssignmentsTaskContext'
|
||||
|
|
@ -79,7 +79,7 @@ function ActivityClient(props: ActivityClientProps) {
|
|||
setBgColor('bg-zinc-950');
|
||||
}
|
||||
}
|
||||
, [activity,pathname ])
|
||||
, [activity, pathname])
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
@ -127,37 +127,37 @@ function ActivityClient(props: ActivityClientProps) {
|
|||
</h1>
|
||||
</div>
|
||||
<div className="flex space-x-1 items-center">
|
||||
{activity && activity.published == true && (
|
||||
<AuthenticatedClientElement checkMethod="authentication">
|
||||
{activity.activity_type != 'TYPE_ASSIGNMENT' &&
|
||||
<>
|
||||
<AIActivityAsk activity={activity} />
|
||||
<MoreVertical size={17} className="text-gray-300 " />
|
||||
<MarkStatus
|
||||
activity={activity}
|
||||
activityid={activityid}
|
||||
course={course}
|
||||
orgslug={orgslug}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
{activity.activity_type == 'TYPE_ASSIGNMENT' &&
|
||||
<>
|
||||
<MoreVertical size={17} className="text-gray-300 " />
|
||||
<AssignmentSubmissionProvider assignment_uuid={assignment?.assignment_uuid}>
|
||||
<AssignmentTools
|
||||
assignment={assignment}
|
||||
{activity && activity.published == true && (
|
||||
<AuthenticatedClientElement checkMethod="authentication">
|
||||
{activity.activity_type != 'TYPE_ASSIGNMENT' &&
|
||||
<>
|
||||
<AIActivityAsk activity={activity} />
|
||||
<MoreVertical size={17} className="text-gray-300 " />
|
||||
<MarkStatus
|
||||
activity={activity}
|
||||
activityid={activityid}
|
||||
course={course}
|
||||
orgslug={orgslug}
|
||||
/>
|
||||
</AssignmentSubmissionProvider>
|
||||
</>
|
||||
}
|
||||
</>
|
||||
}
|
||||
{activity.activity_type == 'TYPE_ASSIGNMENT' &&
|
||||
<>
|
||||
<MoreVertical size={17} className="text-gray-300 " />
|
||||
<AssignmentSubmissionProvider assignment_uuid={assignment?.assignment_uuid}>
|
||||
<AssignmentTools
|
||||
assignment={assignment}
|
||||
activity={activity}
|
||||
activityid={activityid}
|
||||
course={course}
|
||||
orgslug={orgslug}
|
||||
/>
|
||||
</AssignmentSubmissionProvider>
|
||||
</>
|
||||
}
|
||||
|
||||
</AuthenticatedClientElement>
|
||||
)}
|
||||
</AuthenticatedClientElement>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{activity && activity.published == false && (
|
||||
|
|
@ -240,7 +240,7 @@ export function MarkStatus(props: {
|
|||
)
|
||||
if (run) {
|
||||
return run.steps.find(
|
||||
(step: any) => step.activity_id == props.activity.id
|
||||
(step: any) => (step.activity_id == props.activity.id) && (step.complete == true)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -252,7 +252,7 @@ export function MarkStatus(props: {
|
|||
<i>
|
||||
<Check size={17}></Check>
|
||||
</i>{' '}
|
||||
<i className="not-italic text-xs font-bold">Already completed</i>
|
||||
<i className="not-italic text-xs font-bold">Complete</i>
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
|
|
@ -279,6 +279,7 @@ function AssignmentTools(props: {
|
|||
}) {
|
||||
const submission = useAssignmentSubmission() as any
|
||||
const session = useLHSession() as any;
|
||||
const [finalGrade, setFinalGrade] = React.useState(null) as any;
|
||||
|
||||
const submitForGradingUI = async () => {
|
||||
if (props.assignment) {
|
||||
|
|
@ -296,38 +297,91 @@ function AssignmentTools(props: {
|
|||
}
|
||||
}
|
||||
|
||||
const getGradingBasedOnMethod = async () => {
|
||||
const res = await getFinalGrade(
|
||||
session.data?.user?.id,
|
||||
props.assignment?.assignment_uuid,
|
||||
session.data?.tokens?.access_token
|
||||
);
|
||||
|
||||
if (res.success) {
|
||||
const { grade, max_grade, grading_type } = res.data;
|
||||
let displayGrade;
|
||||
|
||||
switch (grading_type) {
|
||||
case 'ALPHABET':
|
||||
displayGrade = convertNumericToAlphabet(grade, max_grade);
|
||||
break;
|
||||
case 'NUMERIC':
|
||||
displayGrade = `${grade}/${max_grade}`;
|
||||
break;
|
||||
case 'PERCENTAGE':
|
||||
const percentage = (grade / max_grade) * 100;
|
||||
displayGrade = `${percentage.toFixed(2)}%`;
|
||||
break;
|
||||
default:
|
||||
displayGrade = 'Unknown grading type';
|
||||
}
|
||||
|
||||
// Use displayGrade here, e.g., update state or display it
|
||||
setFinalGrade(displayGrade);
|
||||
} else {
|
||||
}
|
||||
};
|
||||
|
||||
// Helper function to convert numeric grade to alphabet grade
|
||||
function convertNumericToAlphabet(grade : any, maxGrade : any) {
|
||||
const percentage = (grade / maxGrade) * 100;
|
||||
if (percentage >= 90) return 'A';
|
||||
if (percentage >= 80) return 'B';
|
||||
if (percentage >= 70) return 'C';
|
||||
if (percentage >= 60) return 'D';
|
||||
return 'F';
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
getGradingBasedOnMethod();
|
||||
}
|
||||
, [submission, props.assignment])
|
||||
|
||||
return <>
|
||||
{submission && submission.length == 0 && (
|
||||
if (!submission || submission.length === 0) {
|
||||
return (
|
||||
<ConfirmationModal
|
||||
confirmationButtonText="Submit Assignment"
|
||||
confirmationMessage="Are you sure you want to submit your assignment for grading?, once submitted you will not be able to make any changes"
|
||||
confirmationMessage="Are you sure you want to submit your assignment for grading? Once submitted, you will not be able to make any changes."
|
||||
dialogTitle="Submit your assignment for grading"
|
||||
dialogTrigger={
|
||||
<div
|
||||
className="bg-cyan-800 rounded-full px-5 drop-shadow-md flex items-center space-x-2 p-2.5 text-white hover:cursor-pointer transition delay-150 duration-300 ease-in-out">
|
||||
<i>
|
||||
<BookOpenCheck size={17}></BookOpenCheck>
|
||||
</i>{' '}
|
||||
<i className="not-italic text-xs font-bold">Submit for grading</i>
|
||||
</div>}
|
||||
functionToExecute={() => submitForGradingUI()}
|
||||
<div className="bg-cyan-800 rounded-full px-5 drop-shadow-md flex items-center space-x-2 p-2.5 text-white hover:cursor-pointer transition delay-150 duration-300 ease-in-out">
|
||||
<BookOpenCheck size={17} />
|
||||
<span className="text-xs font-bold">Submit for grading</span>
|
||||
</div>
|
||||
}
|
||||
functionToExecute={submitForGradingUI}
|
||||
status="info"
|
||||
/>
|
||||
)}
|
||||
{submission && submission.length > 0 && (
|
||||
<div
|
||||
className="bg-amber-800 rounded-full px-5 drop-shadow-md flex items-center space-x-2 p-2.5 text-white transition delay-150 duration-300 ease-in-out">
|
||||
<i>
|
||||
<UserRoundPen size={17}></UserRoundPen>
|
||||
</i>{' '}
|
||||
<i className="not-italic text-xs font-bold">Grading in progress</i>
|
||||
</div>)
|
||||
}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
if (submission[0].submission_status === 'SUBMITTED') {
|
||||
return (
|
||||
<div className="bg-amber-800 rounded-full px-5 drop-shadow-md flex items-center space-x-2 p-2.5 text-white transition delay-150 duration-300 ease-in-out">
|
||||
<UserRoundPen size={17} />
|
||||
<span className="text-xs font-bold">Grading in progress</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (submission[0].submission_status === 'GRADED') {
|
||||
return (
|
||||
<div className="bg-teal-600 rounded-full px-5 drop-shadow-md flex items-center space-x-2 p-2.5 text-white transition delay-150 duration-300 ease-in-out">
|
||||
<CheckCircle size={17} />
|
||||
<span className="text-xs flex space-x-2 font-bold items-center"><span>Graded </span> <span className='bg-white text-teal-800 px-1 py-0.5 rounded-md'>{finalGrade}</span></span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// Default return in case none of the conditions are met
|
||||
return null
|
||||
}
|
||||
|
||||
export default ActivityClient
|
||||
|
|
|
|||
|
|
@ -195,7 +195,7 @@ export default function TaskFileObject({ view, user_id, assignmentTaskUUID }: Ta
|
|||
<div className='flex flex-col space-y-1'>
|
||||
<div className='flex py-5 text-sm justify-center mx-auto space-x-2 text-slate-500'>
|
||||
<Download size={20} />
|
||||
<p>Please download the file and grade it manually, then input the grade below</p>
|
||||
<p>Please download the file and grade it manually, then input the grade above</p>
|
||||
</div>
|
||||
{userSubmissions.fileUUID && !isLoading && assignmentTaskUUID && (
|
||||
<Link
|
||||
|
|
|
|||
|
|
@ -1,17 +1,48 @@
|
|||
import { useAssignments } from '@components/Contexts/Assignments/AssignmentContext';
|
||||
import { Download, Info, Medal } from 'lucide-react';
|
||||
import { Apple, ArrowRightFromLine, BookOpenCheck, Check, Download, Info, Medal, MoveRight, X } 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';
|
||||
import { deleteUserSubmission, markActivityAsDoneForUser, putFinalGrade } from '@services/courses/assignments';
|
||||
import { useLHSession } from '@components/Contexts/LHSessionContext';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import toast from 'react-hot-toast';
|
||||
|
||||
function EvaluateAssignment({ user_id }: any) {
|
||||
const assignments = useAssignments() as any;
|
||||
const session = useLHSession() as any;
|
||||
const org = useOrg() as any;
|
||||
const router = useRouter();
|
||||
|
||||
async function gradeAssignment() {
|
||||
const res = await putFinalGrade(user_id, assignments?.assignment_object.assignment_uuid, session.data?.tokens?.access_token);
|
||||
if (res.success) {
|
||||
toast.success(res.data.message)
|
||||
}
|
||||
else {
|
||||
toast.error(res.data.message)
|
||||
}
|
||||
}
|
||||
|
||||
async function markActivityAsDone() {
|
||||
const res = await markActivityAsDoneForUser(user_id, assignments?.assignment_object.assignment_uuid, session.data?.tokens?.access_token)
|
||||
if (res.success) {
|
||||
toast.success(res.data.message)
|
||||
}
|
||||
else {
|
||||
toast.error(res.data.message)
|
||||
}
|
||||
}
|
||||
|
||||
async function rejectAssignment() {
|
||||
const res = await deleteUserSubmission(user_id, assignments?.assignment_object.assignment_uuid, session.data?.tokens?.access_token)
|
||||
toast.success('Assignment rejected successfully')
|
||||
window.location.reload()
|
||||
}
|
||||
|
||||
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) => {
|
||||
|
|
@ -53,18 +84,29 @@ function EvaluateAssignment({ user_id }: any) {
|
|||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className='min-h-full'>
|
||||
{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='custom-grading' user_id={user_id} assignmentTaskUUID={task.assignment_task_uuid} />}
|
||||
{task.assignment_type === 'FILE_SUBMISSION' && <TaskFileObject key={task.assignment_task_uuid} view='custom-grading' user_id={user_id} 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>
|
||||
<div className='flex space-x-4 font-semibold items-center justify-between'>
|
||||
<button onClick={rejectAssignment} className='flex space-x-2 px-4 py-2 text-sm bg-rose-600/80 text-white rounded-lg nice-shadow items-center'>
|
||||
<X size={18} />
|
||||
<span>Reject Assignment</span>
|
||||
</button>
|
||||
<div className='flex space-x-3 items-center'>
|
||||
<button onClick={gradeAssignment} className='flex space-x-2 px-4 py-2 text-sm bg-violet-600/80 text-white rounded-lg nice-shadow items-center'>
|
||||
<BookOpenCheck size={18} />
|
||||
<span>Set final grade</span>
|
||||
</button>
|
||||
<MoveRight className='text-gray-400' size={18} />
|
||||
<button onClick={markActivityAsDone} className='flex space-x-2 px-4 py-2 text-sm bg-teal-600/80 text-white rounded-lg nice-shadow items-center'>
|
||||
<Check size={18} />
|
||||
<span>Mark Activity as Done for User</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue