feat: enable Tasks submissions

This commit is contained in:
swve 2024-07-19 19:56:49 +02:00
parent 6d7e521bba
commit bfb977ac5d
9 changed files with 330 additions and 110 deletions

View file

@ -1,5 +1,5 @@
from typing import Optional, Dict
from sqlalchemy import JSON, Column, ForeignKey
from sqlalchemy import JSON, Column, ForeignKey, null
from sqlmodel import Field, SQLModel
from enum import Enum
@ -125,7 +125,7 @@ class AssignmentTaskUpdate(SQLModel):
description: Optional[str]
hint: Optional[str]
assignment_type: Optional[AssignmentTaskTypeEnum]
contents: Optional[Dict] = Field(default={}, sa_column=Column(JSON))
contents: Optional[Dict] = Field(default=None, sa_column=Column(JSON))
max_grade_value: Optional[int]
@ -194,17 +194,12 @@ class AssignmentTaskSubmissionRead(AssignmentTaskSubmissionBase):
class AssignmentTaskSubmissionUpdate(SQLModel):
"""Model for updating an assignment task submission."""
assignment_task_id: Optional[int]
assignment_task_submission_uuid: Optional[str]
task_submission: Optional[Dict] = Field(default={}, sa_column=Column(JSON))
task_submission: Optional[Dict] = Field(default=None, sa_column=Column(JSON))
grade: Optional[int]
task_submission_grade_feedback: Optional[str]
assignment_type: Optional[AssignmentTaskTypeEnum]
user_id: Optional[int]
activity_id: Optional[int]
course_id: Optional[int]
chapter_id: Optional[int]
assignment_task_id: Optional[int]
class AssignmentTaskSubmission(AssignmentTaskSubmissionBase, table=True):

View file

@ -4,6 +4,7 @@ from src.db.courses.assignments import (
AssignmentRead,
AssignmentTaskCreate,
AssignmentTaskSubmissionCreate,
AssignmentTaskSubmissionUpdate,
AssignmentTaskUpdate,
AssignmentUpdate,
AssignmentUserSubmissionCreate,
@ -15,12 +16,12 @@ from src.services.courses.activities.assignments import (
create_assignment,
create_assignment_submission,
create_assignment_task,
create_assignment_task_submission,
delete_assignment,
delete_assignment_from_activity_uuid,
delete_assignment_submission,
delete_assignment_task,
delete_assignment_task_submission,
handle_assignment_task_submission,
put_assignment_task_reference_file,
put_assignment_task_submission_file,
read_assignment,
@ -31,6 +32,7 @@ from src.services.courses.activities.assignments import (
read_assignment_tasks,
read_user_assignment_submissions,
read_user_assignment_task_submissions,
read_user_assignment_task_submissions_me,
update_assignment,
update_assignment_submission,
update_assignment_task,
@ -240,10 +242,10 @@ async def api_delete_assignment_tasks(
## ASSIGNMENTS Tasks Submissions ##
@router.post("/{assignment_uuid}/tasks/{assignment_task_uuid}/submissions")
async def api_create_assignment_task_submissions(
@router.put("/{assignment_uuid}/tasks/{assignment_task_uuid}/submissions")
async def api_handle_assignment_task_submissions(
request: Request,
assignment_task_submission_object: AssignmentTaskSubmissionCreate,
assignment_task_submission_object: AssignmentTaskSubmissionUpdate,
assignment_task_uuid: str,
current_user: PublicUser = Depends(get_current_user),
db_session=Depends(get_db_session),
@ -251,7 +253,7 @@ async def api_create_assignment_task_submissions(
"""
Create new task submissions for an assignment
"""
return await create_assignment_task_submission(
return await handle_assignment_task_submission(
request,
assignment_task_uuid,
assignment_task_submission_object,
@ -275,6 +277,20 @@ async def api_read_user_assignment_task_submissions(
request, assignment_task_uuid, user_id, current_user, db_session
)
@router.get("/{assignment_uuid}/tasks/{assignment_task_uuid}/submissions/user/me")
async def api_read_user_assignment_task_submissions_me(
request: Request,
assignment_task_uuid: str,
current_user: PublicUser = Depends(get_current_user),
db_session=Depends(get_db_session),
):
"""
Read task submissions for an assignment from a user
"""
return await read_user_assignment_task_submissions_me(
request, assignment_task_uuid, current_user, db_session
)
@router.get("/{assignment_uuid}/tasks/{assignment_task_uuid}/submissions")
async def api_read_assignment_task_submissions(

View file

@ -19,6 +19,7 @@ from src.db.courses.assignments import (
AssignmentTaskSubmission,
AssignmentTaskSubmissionCreate,
AssignmentTaskSubmissionRead,
AssignmentTaskSubmissionUpdate,
AssignmentTaskUpdate,
AssignmentUpdate,
AssignmentUserSubmission,
@ -478,7 +479,13 @@ async def put_assignment_task_reference_file(
f"{assignment_task_uuid}{uuid4()}.{reference_file.filename.split('.')[-1]}"
)
await upload_reference_file(
reference_file, name_in_disk, activity.activity_uuid, org.org_uuid, course.course_uuid, assignment.assignment_uuid, assignment_task_uuid
reference_file,
name_in_disk,
activity.activity_uuid,
org.org_uuid,
course.course_uuid,
assignment.assignment_uuid,
assignment_task_uuid,
)
course.thumbnail_image = name_in_disk
# Update reference file
@ -494,6 +501,7 @@ async def put_assignment_task_reference_file(
# return assignment task read
return AssignmentTaskRead.model_validate(assignment_task)
async def put_assignment_task_submission_file(
request: Request,
db_session: Session,
@ -546,15 +554,18 @@ async def put_assignment_task_submission_file(
# Upload reference file
if sub_file and sub_file.filename and activity and org:
name_in_disk = (
f"{assignment_task_uuid}_sub_{current_user.email}_{uuid4()}.{sub_file.filename.split('.')[-1]}"
)
name_in_disk = f"{assignment_task_uuid}_sub_{current_user.email}_{uuid4()}.{sub_file.filename.split('.')[-1]}"
await upload_submission_file(
sub_file, name_in_disk, activity.activity_uuid, org.org_uuid, course.course_uuid, assignment.assignment_uuid, assignment_task_uuid
sub_file,
name_in_disk,
activity.activity_uuid,
org.org_uuid,
course.course_uuid,
assignment.assignment_uuid,
assignment_task_uuid,
)
return {"message": "Assignment Task Submission File uploaded"}
return {"file_uuid": name_in_disk}
async def update_assignment_task(
@ -665,13 +676,14 @@ async def delete_assignment_task(
## > Assignments Tasks Submissions CRUD
async def create_assignment_task_submission(
async def handle_assignment_task_submission(
request: Request,
assignment_task_uuid: str,
assignment_task_submission_object: AssignmentTaskSubmissionCreate,
assignment_task_submission_object: AssignmentTaskSubmissionUpdate,
current_user: PublicUser | AnonymousUser,
db_session: Session,
):
# TODO: Improve terrible implementation of this function
# Check if assignment task exists
statement = select(AssignmentTask).where(
AssignmentTask.assignment_task_uuid == assignment_task_uuid
@ -694,51 +706,82 @@ async def create_assignment_task_submission(
detail="Assignment not found",
)
# Check if course exists
statement = select(Course).where(Course.id == assignment.course_id)
course = db_session.exec(statement).first()
# Check if user already submitted the assignment
statement = select(AssignmentTaskSubmission).where(
AssignmentTaskSubmission.assignment_task_id == assignment_task.id,
AssignmentTaskSubmission.user_id == current_user.id,
)
assignment_task_submission = db_session.exec(statement).first()
if not course:
raise HTTPException(
status_code=404,
detail="Course not found",
# Update Task submission if it exists
if assignment_task_submission:
# Update only the fields that were passed in
for var, value in vars(assignment_task_submission_object).items():
if value is not None:
setattr(assignment_task_submission, var, value)
assignment_task_submission.update_date = str(datetime.now())
# Insert Assignment Task Submission in DB
db_session.add(assignment_task_submission)
db_session.commit()
db_session.refresh(assignment_task_submission)
# return assignment task submission read
return AssignmentTaskSubmissionRead.model_validate(assignment_task_submission)
else:
# Create new Task submission
current_time = str(datetime.now())
# Assuming model_dump() returns a dictionary
model_data = assignment_task_submission_object.model_dump()
assignment_task_submission = AssignmentTaskSubmission(
assignment_task_submission_uuid=f"assignmenttasksubmission_{uuid4()}",
task_submission=model_data["task_submission"],
grade=model_data["grade"],
task_submission_grade_feedback=model_data["task_submission_grade_feedback"],
assignment_task_id=int(assignment_task.id), # type: ignore
assignment_type=assignment_task.assignment_type,
activity_id=assignment.activity_id,
course_id=assignment.course_id,
chapter_id=assignment.chapter_id,
user_id=current_user.id,
creation_date=current_time,
update_date=current_time,
)
# RBAC check
await rbac_check(request, course.course_uuid, current_user, "create", db_session)
# Insert Assignment Task Submission in DB
db_session.add(assignment_task_submission)
db_session.commit()
# Create Assignment Task Submission
assignment_task_submission = AssignmentTaskSubmission(
**assignment_task_submission_object.model_dump()
)
assignment_task_submission.assignment_task_submission_uuid = str(
f"assignmenttasksubmission_{uuid4()}"
)
assignment_task_submission.creation_date = str(datetime.now())
assignment_task_submission.update_date = str(datetime.now())
assignment_task_submission.org_id = course.org_id
# Insert Assignment Task Submission in DB
db_session.add(assignment_task_submission)
db_session.commit()
db_session.refresh(assignment_task_submission)
# return assignment task submission read
return AssignmentTaskSubmissionRead.model_validate(assignment_task_submission)
# return assignment task submission read
return AssignmentTaskSubmissionRead.model_validate(assignment_task_submission)
async def read_user_assignment_task_submissions(
request: Request,
assignment_task_submission_uuid: str,
assignment_task_uuid: str,
user_id: int,
current_user: PublicUser | AnonymousUser,
db_session: Session,
):
# Check if assignment task exists
statement = select(AssignmentTask).where(
AssignmentTask.assignment_task_uuid == assignment_task_uuid
)
assignment_task = db_session.exec(statement).first()
if not assignment_task:
raise HTTPException(
status_code=404,
detail="Assignment Task not found",
)
# Check if assignment task submission exists
statement = select(AssignmentTaskSubmission).where(
AssignmentTaskSubmission.assignment_task_submission_uuid
== assignment_task_submission_uuid,
AssignmentTaskSubmission.assignment_task_id == assignment_task.id,
AssignmentTaskSubmission.user_id == user_id,
)
assignment_task_submission = db_session.exec(statement).first()
@ -749,18 +792,6 @@ async def read_user_assignment_task_submissions(
detail="Assignment Task Submission not found",
)
# Check if assignment task exists
statement = select(AssignmentTask).where(
AssignmentTask.id == assignment_task_submission.assignment_task_id
)
assignment_task = db_session.exec(statement).first()
if not assignment_task:
raise HTTPException(
status_code=404,
detail="Assignment Task not found",
)
# Check if assignment exists
statement = select(Assignment).where(Assignment.id == assignment_task.assignment_id)
assignment = db_session.exec(statement).first()
@ -788,6 +819,21 @@ async def read_user_assignment_task_submissions(
return AssignmentTaskSubmissionRead.model_validate(assignment_task_submission)
async def read_user_assignment_task_submissions_me(
request: Request,
assignment_task_uuid: str,
current_user: PublicUser | AnonymousUser,
db_session: Session,
):
return await read_user_assignment_task_submissions(
request,
assignment_task_uuid,
current_user.id,
current_user,
db_session,
)
async def read_assignment_task_submissions(
request: Request,
assignment_task_submission_uuid: str,

View file

@ -2,13 +2,13 @@ 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, updateSubFile } from '@services/courses/assignments';
import { getAssignmentTask, getAssignmentTaskSubmissionsMe, handleAssignmentTaskSubmission, updateSubFile } from '@services/courses/assignments';
import { Cloud, File, Info, Loader, UploadCloud } from 'lucide-react'
import React, { useEffect } from 'react'
import React, { useEffect, useState } from 'react'
import toast from 'react-hot-toast';
type FileSchema = {
fileID: string;
fileUUID: string;
};
type TaskFileObjectProps = {
@ -26,6 +26,18 @@ export default function TaskFileObject({ view, assignmentTaskUUID }: TaskFileObj
const assignmentTaskStateHook = useAssignmentsTaskDispatch() as any;
const assignment = useAssignments() as any;
/* TEACHER VIEW CODE */
/* TEACHER VIEW CODE */
/* STUDENT VIEW CODE */
const [showSavingDisclaimer, setShowSavingDisclaimer] = useState<boolean>(false);
const [userSubmissions, setUserSubmissions] = useState<FileSchema>({
fileUUID: '',
});
const [initialUserSubmissions, setInitialUserSubmissions] = useState<FileSchema>({
fileUUID: '',
});
const handleFileChange = async (event: any) => {
const file = event.target.files[0]
@ -37,19 +49,56 @@ export default function TaskFileObject({ view, assignmentTaskUUID }: TaskFileObj
assignment.assignment_object.assignment_uuid,
access_token
)
assignmentTaskStateHook({ type: 'reload' })
// wait for 1 second to show loading animation
await new Promise((r) => setTimeout(r, 1500))
if (res.success === false) {
setError(res.data.detail)
setIsLoading(false)
} else {
assignmentTaskStateHook({ type: 'reload' })
setUserSubmissions({
fileUUID: res.data.file_uuid,
})
setIsLoading(false)
setError('')
}
}
async function getAssignmentTaskSubmissionFromUserUI() {
if (assignmentTaskUUID) {
const res = await getAssignmentTaskSubmissionsMe(assignmentTaskUUID, assignment.assignment_object.assignment_uuid, access_token);
if (res.success) {
setUserSubmissions(res.data.task_submission);
setInitialUserSubmissions(res.data.task_submission);
}
}
}
const submitFC = async () => {
// Save the quiz to the server
const values = {
task_submission: userSubmissions,
grade: 0,
task_submission_grade_feedback: '',
};
if (assignmentTaskUUID) {
const res = await handleAssignmentTaskSubmission(values, assignmentTaskUUID, assignment.assignment_object.assignment_uuid, access_token);
if (res) {
assignmentTaskStateHook({
type: 'reload',
});
toast.success('Task saved successfully');
setShowSavingDisclaimer(false);
} else {
toast.error('Error saving task, please retry later.');
}
}
};
async function getAssignmentTaskUI() {
if (assignmentTaskUUID) {
const res = await getAssignmentTask(assignmentTaskUUID, access_token);
@ -60,13 +109,27 @@ export default function TaskFileObject({ view, assignmentTaskUUID }: TaskFileObj
}
}
// Detect changes between initial and current submissions
useEffect(() => {
getAssignmentTaskUI()
if (userSubmissions.fileUUID !== initialUserSubmissions.fileUUID) {
setShowSavingDisclaimer(true);
} else {
setShowSavingDisclaimer(false);
}
}, [userSubmissions]);
/* STUDENT VIEW CODE */
useEffect(() => {
if (view === 'student') {
getAssignmentTaskUI()
getAssignmentTaskSubmissionFromUserUI()
}
}
, [assignmentTaskUUID])
return (
<AssignmentBoxUI view={view} type="file">
<AssignmentBoxUI submitFC={submitFC} showSavingDisclaimer={showSavingDisclaimer} view={view} type="file">
{view === 'teacher' && (
<div className='flex py-5 text-sm justify-center mx-auto space-x-2 text-slate-500'>
<Info size={20} />
@ -94,17 +157,32 @@ export default function TaskFileObject({ view, assignmentTaskUUID }: TaskFileObj
<div className='flex space-x-2 mt-2'>
<File size={20} className='' />
<div className='font-semibold text-sm uppercase'>
{localUploadFile.name}
<File size={20} className='' />
<div className='font-semibold text-sm uppercase'>
{localUploadFile.name}
</div>
</div>
</div>
)}
{userSubmissions.fileUUID && !isLoading && !localUploadFile && (
<div className='flex flex-col rounded-lg bg-white text-gray-400 shadow-lg nice-shadow px-5 py-3 space-y-1 items-center relative'>
<div className='absolute top-0 right-0 transform translate-x-1/2 -translate-y-1/2 bg-green-500 rounded-full px-1.5 py-1.5 text-white flex justify-center items-center'>
<Cloud size={15} />
</div>
<div className='flex space-x-2 mt-2'>
<File size={20} className='' />
<div className='font-semibold text-sm uppercase'>
{`${userSubmissions.fileUUID.slice(0, 8)}...${userSubmissions.fileUUID.slice(-4)}`}
</div>
</div>
</div>
)}
<div className='flex pt-4 font-semibold space-x-1.5 text-xs items-center text-gray-500 '>
<Info size={16} />
<p>Allowed formats : pdf, docx, mp4, jpg, jpeg, pptx</p>
</div>
<Info size={16} />
<p>Allowed formats : pdf, docx, mp4, jpg, jpeg, pptx</p>
</div>
{isLoading ? (
<div className="flex justify-center items-center">
<input
@ -122,13 +200,13 @@ export default function TaskFileObject({ view, assignmentTaskUUID }: TaskFileObj
<div className="flex justify-center items-center">
<input
type="file"
id="fileInput"
id={"fileInput_" + assignmentTaskUUID}
style={{ display: 'none' }}
onChange={handleFileChange}
/>
<button
className="font-bold antialiased items-center text-gray text-sm rounded-md px-4 mt-6 flex"
onClick={() => document.getElementById('fileInput')?.click()}
onClick={() => document.getElementById("fileInput_" + assignmentTaskUUID)?.click()}
>
<UploadCloud size={16} className="mr-2" />
<span>Submit File</span>

View file

@ -2,7 +2,7 @@ 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, updateAssignmentTask } from '@services/courses/assignments';
import { getAssignmentTask, getAssignmentTaskSubmissionsMe, handleAssignmentTaskSubmission, 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';
@ -113,6 +113,11 @@ function TaskQuizObject({ view, assignmentTaskUUID }: TaskQuizObjectProps) {
questions: [],
submissions: [],
});
const [initialUserSubmissions, setInitialUserSubmissions] = useState<QuizSubmitSchema>({
questions: [],
submissions: [],
});
const [showSavingDisclaimer, setShowSavingDisclaimer] = useState<boolean>(false);
async function chooseOption(qIndex: number, oIndex: number) {
const updatedSubmissions = [...userSubmissions.submissions];
@ -136,11 +141,8 @@ function TaskQuizObject({ view, assignmentTaskUUID }: TaskQuizObjectProps) {
...userSubmissions,
submissions: updatedSubmissions,
});
console.log(userSubmissions);
}
async function getAssignmentTaskUI() {
if (assignmentTaskUUID) {
const res = await getAssignmentTask(assignmentTaskUUID, access_token);
@ -151,6 +153,48 @@ function TaskQuizObject({ view, assignmentTaskUUID }: TaskQuizObjectProps) {
}
}
async function getAssignmentTaskSubmissionFromUserUI() {
if (assignmentTaskUUID) {
const res = await getAssignmentTaskSubmissionsMe(assignmentTaskUUID, assignment.assignment_object.assignment_uuid, access_token);
if (res.success) {
setUserSubmissions(res.data.task_submission);
setInitialUserSubmissions(res.data.task_submission);
}
}
}
// Detect changes between initial and current submissions
useEffect(() => {
const hasChanges = JSON.stringify(initialUserSubmissions.submissions) !== JSON.stringify(userSubmissions.submissions);
setShowSavingDisclaimer(hasChanges);
}, [userSubmissions, initialUserSubmissions.submissions]);
const submitFC = async () => {
// Save the quiz to the server
const values = {
task_submission: userSubmissions,
grade: 0,
task_submission_grade_feedback: '',
};
if (assignmentTaskUUID) {
const res = await handleAssignmentTaskSubmission(values, assignmentTaskUUID, assignment.assignment_object.assignment_uuid, access_token);
if (res) {
assignmentTaskStateHook({
type: 'reload',
});
toast.success('Task saved successfully');
setShowSavingDisclaimer(false);
} else {
toast.error('Error saving task, please retry later.');
}
}
};
/* STUDENT VIEW CODE */
useEffect(() => {
@ -164,12 +208,13 @@ function TaskQuizObject({ view, assignmentTaskUUID }: TaskQuizObjectProps) {
// Student area
else if (view == 'student') {
getAssignmentTaskUI();
getAssignmentTaskSubmissionFromUserUI();
}
}, [assignmentTaskState, assignment, assignmentTaskStateHook, access_token]);
return (
<AssignmentBoxUI saveFC={saveFC} view={view} type="quiz">
<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">
@ -245,8 +290,8 @@ function TaskQuizObject({ view, assignmentTaskUUID }: TaskQuizObjectProps) {
(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
? "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) =>

View file

@ -93,18 +93,7 @@ function PublishingState() {
{assignment?.assignment_object?.published ? 'Published' : 'Unpublished'}
</div>
<div><EllipsisVertical className='text-gray-500' size={13} /></div>
{assignment?.assignment_object?.published && <ToolTip
side='left'
slateBlack
sideOffset={10}
content="Make your Assignment unavailable for students" >
<div
onClick={() => updateAssignmentPublishState(assignment?.assignment_object?.assignment_uuid)}
className='flex px-3 py-2 cursor-pointer rounded-md space-x-2 items-center bg-gradient-to-bl text-gray-800 font-medium from-gray-400/50 to-gray-200/80 border border-gray-600/10 shadow-gray-900/10 shadow-lg'>
<BookX size={18} />
<p className='text-sm font-bold'>Unpublish</p>
</div>
</ToolTip>}
<ToolTip
side='left'
slateBlack
@ -118,6 +107,18 @@ function PublishingState() {
<p className=' text-sm font-bold'>Preview</p>
</Link>
</ToolTip>
{assignment?.assignment_object?.published && <ToolTip
side='left'
slateBlack
sideOffset={10}
content="Make your Assignment unavailable for students" >
<div
onClick={() => updateAssignmentPublishState(assignment?.assignment_object?.assignment_uuid)}
className='flex px-3 py-2 cursor-pointer rounded-md space-x-2 items-center bg-gradient-to-bl text-gray-800 font-medium from-gray-400/50 to-gray-200/80 border border-gray-600/10 shadow-gray-900/10 shadow-lg'>
<BookX size={18} />
<p className='text-sm font-bold'>Unpublish</p>
</div>
</ToolTip>}
{!assignment?.assignment_object?.published &&
<ToolTip
side='left'

View file

@ -1,4 +1,4 @@
import { BookUser, EllipsisVertical, FileUp, Forward, ListTodo, Save } from 'lucide-react'
import { BookUser, EllipsisVertical, FileUp, Forward, Info, InfoIcon, ListTodo, Save } from 'lucide-react'
import React from 'react'
type AssignmentBoxProps = {
@ -6,11 +6,12 @@ type AssignmentBoxProps = {
view?: 'teacher' | 'student'
saveFC?: () => void
submitFC?: () => void
showSavingDisclaimer?: boolean
children: React.ReactNode
}
function AssignmentBoxUI({ type, view, saveFC, submitFC, children }: AssignmentBoxProps) {
function AssignmentBoxUI({ type, view, saveFC, submitFC, showSavingDisclaimer, children }: AssignmentBoxProps) {
return (
<div className='flex flex-col px-6 py-4 nice-shadow rounded-md bg-slate-100/30'>
<div className='flex justify-between space-x-2 pb-2 text-slate-400 items-center'>
@ -40,12 +41,17 @@ function AssignmentBoxUI({ type, view, saveFC, submitFC, children }: AssignmentB
}
</div>
<div className='flex px-1 py-1 rounded-md items-center'>
{showSavingDisclaimer &&
<div className='flex space-x-2 items-center font-semibold px-3 py-1 outline-dashed outline-red-200 text-red-400 mr-5 rounded-full'>
<InfoIcon size={14} />
<p className='text-xs'>Don't forget to save your progress</p>
</div>
}
{/* Save button */}
{view === 'teacher' &&
<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 '>
className='flex px-2 py-1 cursor-pointer rounded-md space-x-2 items-center bg-gradient-to-bl text-emerald-700 bg-emerald-300/20 hover:bg-emerald-300/10 hover:outline-offset-4 active:outline-offset-1 linear transition-all outline-offset-2 outline-dashed outline-emerald-500/60'>
<Save size={14} />
<p className='text-xs font-semibold'>Save</p>
</div>
@ -53,9 +59,9 @@ function AssignmentBoxUI({ type, view, saveFC, submitFC, children }: AssignmentB
{view === 'student' &&
<div
onClick={() => submitFC && submitFC()}
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 '>
className='flex px-2 py-1 cursor-pointer rounded-md space-x-2 items-center bg-gradient-to-bl text-emerald-700 bg-emerald-300/20 hover:bg-emerald-300/10 hover:outline-offset-4 active:outline-offset-1 linear transition-all outline-offset-2 outline-dashed outline-emerald-500/60'>
<Forward size={14} />
<p className='text-xs font-semibold'>Save</p>
<p className='text-xs font-semibold'>Save your progress</p>
</div>
}

View file

@ -15,7 +15,6 @@ function AssignmentStudentActivity() {
const org = useOrg() as any;
useEffect(() => {
console.log(assignments)
}, [assignments, org])
@ -71,7 +70,14 @@ function AssignmentStudentActivity() {
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} />
<p className='text-xs font-semibold'>Reference file</p>
<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>

View file

@ -91,6 +91,33 @@ export async function getAssignmentTask(
return res
}
export async function getAssignmentTaskSubmissionsMe(
assignmentTaskUUID: string,
assignmentUUID: string,
access_token: string
) {
const result: any = await fetch(
`${getAPIUrl()}assignments/${assignmentUUID}/tasks/${assignmentTaskUUID}/submissions/user/me`,
RequestBodyWithAuthHeader('GET', null, null, access_token)
)
const res = await getResponseMetadata(result)
return res
}
export async function handleAssignmentTaskSubmission(
body: any,
assignmentTaskUUID: string,
assignmentUUID: string,
access_token: string
) {
const result: any = await fetch(
`${getAPIUrl()}assignments/${assignmentUUID}/tasks/${assignmentTaskUUID}/submissions`,
RequestBodyWithAuthHeader('PUT', body, null, access_token)
)
const res = await getResponseMetadata(result)
return res
}
export async function updateAssignmentTask(
body: any,
assignmentTaskUUID: string,