feat: Improve Assignment UI and File Upload Responsiveness

This commit is contained in:
swve 2025-02-26 17:39:37 +01:00
parent df4cc587ba
commit 85cbb100fe
3 changed files with 133 additions and 94 deletions

View file

@ -44,6 +44,12 @@ export default function TaskFileObject({ view, user_id, assignmentTaskUUID }: Ta
}); });
const handleFileChange = async (event: any) => { const handleFileChange = async (event: any) => {
// Check if user is authenticated
if (!access_token) {
setError('Authentication required. Please sign in to upload files.');
return;
}
const file = event.target.files[0] const file = event.target.files[0]
setLocalUploadFile(file) setLocalUploadFile(file)
@ -69,21 +75,30 @@ export default function TaskFileObject({ view, user_id, assignmentTaskUUID }: Ta
setIsLoading(false) setIsLoading(false)
setError('') setError('')
} }
} }
async function getAssignmentTaskSubmissionFromUserUI() { async function getAssignmentTaskSubmissionFromUserUI() {
if (!access_token) {
// Silently fail if not authenticated
return;
}
if (assignmentTaskUUID) { if (assignmentTaskUUID) {
const res = await getAssignmentTaskSubmissionsMe(assignmentTaskUUID, assignment.assignment_object.assignment_uuid, access_token); const res = await getAssignmentTaskSubmissionsMe(assignmentTaskUUID, assignment.assignment_object.assignment_uuid, access_token);
if (res.success) { if (res.success) {
setUserSubmissions(res.data.task_submission); setUserSubmissions(res.data.task_submission);
setInitialUserSubmissions(res.data.task_submission); setInitialUserSubmissions(res.data.task_submission);
} }
} }
} }
const submitFC = async () => { const submitFC = async () => {
// Check if user is authenticated
if (!access_token) {
toast.error('Authentication required. Please sign in to submit your task.');
return;
}
// Save the quiz to the server // Save the quiz to the server
const values = { const values = {
task_submission: userSubmissions, task_submission: userSubmissions,
@ -105,13 +120,17 @@ export default function TaskFileObject({ view, user_id, assignmentTaskUUID }: Ta
}; };
async function getAssignmentTaskUI() { async function getAssignmentTaskUI() {
if (!access_token) {
// Silently fail if not authenticated
return;
}
if (assignmentTaskUUID) { if (assignmentTaskUUID) {
const res = await getAssignmentTask(assignmentTaskUUID, access_token); const res = await getAssignmentTask(assignmentTaskUUID, access_token);
if (res.success) { if (res.success) {
setAssignmentTask(res.data); setAssignmentTask(res.data);
setAssignmentTaskOutsideProvider(res.data); setAssignmentTaskOutsideProvider(res.data);
} }
} }
} }
@ -129,6 +148,11 @@ export default function TaskFileObject({ view, user_id, assignmentTaskUUID }: Ta
/* GRADING VIEW CODE */ /* GRADING VIEW CODE */
const [userSubmissionObject, setUserSubmissionObject] = useState<any>(null); const [userSubmissionObject, setUserSubmissionObject] = useState<any>(null);
async function getAssignmentTaskSubmissionFromIdentifiedUserUI() { async function getAssignmentTaskSubmissionFromIdentifiedUserUI() {
if (!access_token) {
// Silently fail if not authenticated
return;
}
if (assignmentTaskUUID && user_id) { if (assignmentTaskUUID && user_id) {
const res = await getAssignmentTaskSubmissionsUser(assignmentTaskUUID, user_id, assignment.assignment_object.assignment_uuid, access_token); const res = await getAssignmentTaskSubmissionsUser(assignmentTaskUUID, user_id, assignment.assignment_object.assignment_uuid, access_token);
if (res.success) { if (res.success) {
@ -136,7 +160,6 @@ export default function TaskFileObject({ view, user_id, assignmentTaskUUID }: Ta
setUserSubmissionObject(res.data); setUserSubmissionObject(res.data);
setInitialUserSubmissions(res.data.task_submission); setInitialUserSubmissions(res.data.task_submission);
} }
} }
} }
@ -186,31 +209,29 @@ export default function TaskFileObject({ view, user_id, assignmentTaskUUID }: Ta
return ( return (
<AssignmentBoxUI submitFC={submitFC} showSavingDisclaimer={showSavingDisclaimer} view={view} gradeCustomFC={gradeCustomFC} currentPoints={userSubmissionObject?.grade} maxPoints={assignmentTaskOutsideProvider?.max_grade_value} type="file"> <AssignmentBoxUI submitFC={submitFC} showSavingDisclaimer={showSavingDisclaimer} view={view} gradeCustomFC={gradeCustomFC} currentPoints={userSubmissionObject?.grade} maxPoints={assignmentTaskOutsideProvider?.max_grade_value} type="file">
{view === 'teacher' && ( {view === 'teacher' && (
<div className='flex py-5 text-sm justify-center mx-auto space-x-2 text-slate-500'> <div className='flex flex-col sm:flex-row py-5 sm:py-6 text-xs sm:text-sm justify-center mx-auto space-y-2 sm:space-y-0 sm:space-x-3 text-slate-600 px-4 sm:px-2 text-center sm:text-left bg-slate-50 rounded-lg border border-slate-100'>
<Info size={20} /> <Info size={18} className="mx-auto sm:mx-0 text-slate-500" />
<p>User will be able to submit a file for this task, you'll be able to review it in the Submissions Tab</p> <p>User will be able to submit a file for this task, you'll be able to review it in the Submissions Tab</p>
</div> </div>
)} )}
{view === 'custom-grading' && ( {view === 'custom-grading' && (
<div className='flex flex-col space-y-1'> <div className='flex flex-col space-y-4 w-full px-2 sm:px-0'>
<div className='flex py-5 text-sm justify-center mx-auto space-x-2 text-slate-500'> <div className='flex flex-col sm:flex-row py-5 sm:py-6 text-xs sm:text-sm justify-center mx-auto space-y-2 sm:space-y-0 sm:space-x-3 text-slate-600 px-4 sm:px-2 text-center sm:text-left bg-slate-50 rounded-lg border border-slate-100'>
<Download size={20} /> <Download size={18} className="mx-auto sm:mx-0 text-slate-500" />
<p>Please download the file and grade it manually, then input the grade above</p> <p>Please download the file and grade it manually, then input the grade above</p>
</div> </div>
{userSubmissions.fileUUID && !isLoading && assignmentTaskUUID && ( {userSubmissions.fileUUID && !isLoading && assignmentTaskUUID && (
<Link <Link
href={getTaskFileSubmissionDir(org?.org_uuid, assignment.course_object.course_uuid, assignment.activity_object.activity_uuid, assignment.assignment_object.assignment_uuid, assignmentTaskUUID, userSubmissions.fileUUID)} href={getTaskFileSubmissionDir(org?.org_uuid, assignment.course_object.course_uuid, assignment.activity_object.activity_uuid, assignment.assignment_object.assignment_uuid, assignmentTaskUUID, userSubmissions.fileUUID)}
target='_blank' target='_blank'
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'> className='flex flex-col rounded-lg bg-white text-gray-500 shadow-sm hover:shadow-md transition-shadow border border-gray-100 px-4 sm:px-5 py-4 space-y-1 items-center relative w-full sm:w-auto mx-auto'>
<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'> <div className='absolute top-0 right-0 transform translate-x-1/2 -translate-y-1/2 bg-emerald-500 rounded-full p-1.5 text-white flex justify-center items-center shadow-sm'>
<Cloud size={15} /> <Cloud size={14} />
</div> </div>
<div <div className='flex space-x-2 mt-2 items-center'>
<File size={18} className="text-emerald-500" />
className='flex space-x-2 mt-2'> <div className='font-medium text-xs sm:text-sm uppercase break-all'>
<File size={20} className='' />
<div className='font-semibold text-sm uppercase'>
{`${userSubmissions.fileUUID.slice(0, 8)}...${userSubmissions.fileUUID.slice(-4)}`} {`${userSubmissions.fileUUID.slice(0, 8)}...${userSubmissions.fileUUID.slice(-4)}`}
</div> </div>
</div> </div>
@ -220,66 +241,72 @@ export default function TaskFileObject({ view, user_id, assignmentTaskUUID }: Ta
)} )}
{view === 'student' && ( {view === 'student' && (
<> <>
<div className="w-auto bg-white rounded-xl outline outline-1 outline-gray-200 h-[200px] shadow"> <div className="w-full bg-white rounded-lg border border-gray-100 min-h-[200px] shadow-sm px-4 sm:px-6 py-5 sm:py-6">
<div className="flex flex-col justify-center items-center h-full"> <div className="flex flex-col justify-center items-center h-full w-full">
<div className="flex flex-col justify-center items-center"> <div className="flex flex-col justify-center items-center w-full max-w-full">
<div className="flex flex-col justify-center items-center"> <div className="flex flex-col justify-center items-center w-full">
{error && ( {error && (
<div className="flex justify-center bg-red-200 rounded-md text-red-950 space-x-2 items-center p-2 transition-all shadow-sm"> <div className="flex justify-center bg-red-50 border border-red-100 rounded-md text-red-600 space-x-2 items-center p-3 transition-all shadow-sm w-full sm:w-auto mb-4">
<div className="text-sm font-semibold">{error}</div> <div className="text-xs sm:text-sm font-medium">{error}</div>
</div> </div>
)} )}
</div> </div>
{localUploadFile && !isLoading && ( {localUploadFile && !isLoading && (
<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='flex flex-col rounded-lg bg-white text-gray-500 shadow-sm border border-gray-100 px-4 sm:px-5 py-4 space-y-1 items-center relative w-full sm:w-auto mt-3'>
<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'> <div className='absolute top-0 right-0 transform translate-x-1/2 -translate-y-1/2 bg-emerald-500 rounded-full p-1.5 text-white flex justify-center items-center shadow-sm'>
<Cloud size={15} /> <Cloud size={14} />
</div> </div>
<div className='flex space-x-2 mt-2'> <div className='flex space-x-2 mt-2 items-center'>
<File size={18} className="text-emerald-500" />
<File size={20} className='' /> <div className='font-medium text-xs sm:text-sm uppercase break-all'>
<div className='font-semibold text-sm uppercase'> {localUploadFile.name.length > 20
{localUploadFile.name} ? `${localUploadFile.name.slice(0, 10)}...${localUploadFile.name.slice(-10)}`
: localUploadFile.name}
</div> </div>
</div> </div>
</div> </div>
)} )}
{userSubmissions.fileUUID && !isLoading && !localUploadFile && ( {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='flex flex-col rounded-lg bg-white text-gray-500 shadow-sm border border-gray-100 px-4 sm:px-5 py-4 space-y-1 items-center relative w-full sm:w-auto mt-3'>
<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'> <div className='absolute top-0 right-0 transform translate-x-1/2 -translate-y-1/2 bg-emerald-500 rounded-full p-1.5 text-white flex justify-center items-center shadow-sm'>
<Cloud size={15} /> <Cloud size={14} />
</div> </div>
<div className='flex space-x-2 mt-2'> <div className='flex space-x-2 mt-2 items-center'>
<File size={18} className="text-emerald-500" />
<File size={20} className='' /> <div className='font-medium text-xs sm:text-sm uppercase break-all'>
<div className='font-semibold text-sm uppercase'>
{`${userSubmissions.fileUUID.slice(0, 8)}...${userSubmissions.fileUUID.slice(-4)}`} {`${userSubmissions.fileUUID.slice(0, 8)}...${userSubmissions.fileUUID.slice(-4)}`}
</div> </div>
</div> </div>
</div> </div>
)} )}
<div className='flex pt-4 font-semibold space-x-1.5 text-xs items-center text-gray-500 '> <div className='flex flex-col sm:flex-row pt-5 font-medium space-y-1 sm:space-y-0 sm:space-x-2 text-xs items-center text-slate-500 text-center sm:text-left bg-slate-50 rounded-lg px-3 py-2 mt-5 border border-slate-100 w-full sm:w-auto'>
<Info size={16} /> <Info size={15} className="mx-auto sm:mx-0 text-slate-400" />
<p>Allowed formats: pdf, docx, mp4, jpg, jpeg, png, pptx, zip</p> <p>Allowed formats: pdf, docx, mp4, jpg, jpeg, png, pptx, zip</p>
</div> </div>
{isLoading ? ( {!access_token ? (
<div className="flex justify-center items-center"> <div className="flex justify-center items-center w-full mt-5">
<div className="flex justify-center bg-amber-50 border border-amber-100 rounded-md text-amber-600 space-x-2 items-center p-3 transition-all shadow-sm w-full sm:w-auto">
<Info size={15} className="text-amber-500" />
<div className="text-xs sm:text-sm font-medium">Please sign in to upload files</div>
</div>
</div>
) : isLoading ? (
<div className="flex justify-center items-center w-full mt-5">
<input <input
type="file" type="file"
id="fileInput" id="fileInput"
style={{ display: 'none' }} style={{ display: 'none' }}
onChange={handleFileChange} onChange={handleFileChange}
/> />
<div className="font-bold animate-pulse antialiased items-center bg-slate-200 text-gray text-sm rounded-md px-4 py-2 mt-4 flex"> <div className="font-medium animate-pulse antialiased items-center bg-slate-100 text-slate-600 text-xs sm:text-sm rounded-md px-4 sm:px-5 py-2.5 flex">
<Loader size={16} className="mr-2" /> <Loader size={15} className="mr-2" />
<span>Loading</span> <span>Loading</span>
</div> </div>
</div> </div>
) : ( ) : (
<div className="flex justify-center items-center"> <div className="flex justify-center items-center w-full mt-5">
<input <input
type="file" type="file"
id={"fileInput_" + assignmentTaskUUID} id={"fileInput_" + assignmentTaskUUID}
@ -287,10 +314,10 @@ export default function TaskFileObject({ view, user_id, assignmentTaskUUID }: Ta
onChange={handleFileChange} onChange={handleFileChange}
/> />
<button <button
className="font-bold antialiased items-center text-gray text-sm rounded-md px-4 mt-6 flex" className="font-medium antialiased items-center text-white text-xs sm:text-sm rounded-md px-4 sm:px-5 py-2.5 flex bg-emerald-500 hover:bg-emerald-600 transition-colors shadow-sm"
onClick={() => document.getElementById("fileInput_" + assignmentTaskUUID)?.click()} onClick={() => document.getElementById("fileInput_" + assignmentTaskUUID)?.click()}
> >
<UploadCloud size={16} className="mr-2" /> <UploadCloud size={15} className="mr-2" />
<span>Submit File</span> <span>Submit File</span>
</button> </button>
</div> </div>

View file

@ -1,6 +1,7 @@
import { useAssignmentSubmission } from '@components/Contexts/Assignments/AssignmentSubmissionContext' import { useAssignmentSubmission } from '@components/Contexts/Assignments/AssignmentSubmissionContext'
import { BookPlus, BookUser, EllipsisVertical, FileUp, Forward, InfoIcon, ListTodo, Save } from 'lucide-react' import { BookPlus, BookUser, EllipsisVertical, FileUp, Forward, InfoIcon, ListTodo, Save } from 'lucide-react'
import React, { useEffect } from 'react' import React, { useEffect } from 'react'
import { useLHSession } from '@components/Contexts/LHSessionContext'
type AssignmentBoxProps = { type AssignmentBoxProps = {
type: 'quiz' | 'file' type: 'quiz' | 'file'
@ -13,21 +14,25 @@ type AssignmentBoxProps = {
gradeCustomFC?: (grade: number) => void gradeCustomFC?: (grade: number) => void
showSavingDisclaimer?: boolean showSavingDisclaimer?: boolean
children: React.ReactNode children: React.ReactNode
} }
function AssignmentBoxUI({ type, view, currentPoints, maxPoints, saveFC, submitFC, gradeFC, gradeCustomFC, showSavingDisclaimer, children }: AssignmentBoxProps) { function AssignmentBoxUI({ type, view, currentPoints, maxPoints, saveFC, submitFC, gradeFC, gradeCustomFC, showSavingDisclaimer, children }: AssignmentBoxProps) {
const [customGrade, setCustomGrade] = React.useState<number>(0) const [customGrade, setCustomGrade] = React.useState<number>(0)
const submission = useAssignmentSubmission() as any const submission = useAssignmentSubmission() as any
const session = useLHSession() as any
useEffect(() => { useEffect(() => {
console.log(submission) console.log(submission)
} }, [submission])
, [submission])
// Check if user is authenticated
const isAuthenticated = session?.status === 'authenticated'
return ( return (
<div className='flex flex-col px-6 py-4 nice-shadow rounded-md bg-slate-100/30'> <div className='flex flex-col px-3 sm: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'> <div className='flex flex-col sm:flex-row sm:justify-between sm:space-x-2 pb-2 text-slate-400 sm:items-center'>
<div className='flex space-x-1 items-center'> {/* Left side with type and badges */}
<div className='flex flex-wrap gap-2 items-center mb-2 sm:mb-0'>
<div className='text-lg font-semibold'> <div className='text-lg font-semibold'>
{type === 'quiz' && {type === 'quiz' &&
<div className='flex space-x-1.5 items-center'> <div className='flex space-x-1.5 items-center'>
@ -41,26 +46,27 @@ function AssignmentBoxUI({ type, view, currentPoints, maxPoints, saveFC, submitF
</div>} </div>}
</div> </div>
<div className='flex items-center space-x-1'> <div className='flex items-center space-x-1'>
<EllipsisVertical size={15} /> <EllipsisVertical size={15} />
</div> </div>
{view === 'teacher' && {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'> <div className='flex bg-amber-200/20 text-xs rounded-full space-x-1 px-2 py-0.5 font-bold outline items-center text-amber-600 outline-1 outline-amber-300/40'>
<BookUser size={12} /> <BookUser size={12} />
<p>Teacher view</p> <p>Teacher view</p>
</div> </div>
} }
{maxPoints && {maxPoints &&
<div className='flex bg-emerald-200/20 text-xs rounded-full space-x-1 px-2 py-0.5 mx-auto font-bold outline items-center text-emerald-600 outline-1 outline-emerald-300/40'> <div className='flex bg-emerald-200/20 text-xs rounded-full space-x-1 px-2 py-0.5 font-bold outline items-center text-emerald-600 outline-1 outline-emerald-300/40'>
<BookPlus size={12} /> <BookPlus size={12} />
<p>{maxPoints} points</p> <p>{maxPoints} points</p>
</div> </div>
} }
</div> </div>
<div className='flex px-1 py-1 rounded-md items-center'>
{/* Right side with buttons and actions */}
<div className='flex flex-wrap gap-2 items-center'>
{showSavingDisclaimer && {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'> <div className='flex space-x-2 items-center font-semibold px-3 py-1 outline-dashed outline-red-200 text-red-400 sm:mr-5 rounded-full w-full sm:w-auto mb-2 sm:mb-0'>
<InfoIcon size={14} /> <InfoIcon size={14} />
<p className='text-xs'>Don't forget to save your progress</p> <p className='text-xs'>Don't forget to save your progress</p>
</div> </div>
@ -76,11 +82,11 @@ function AssignmentBoxUI({ type, view, currentPoints, maxPoints, saveFC, submitF
</div> </div>
} }
{/* Student button */} {/* Student button - only show if authenticated */}
{view === 'student' && submission && submission.length <= 0 && {view === 'student' && isAuthenticated && submission && submission.length <= 0 &&
<div <div
onClick={() => submitFC && submitFC()} onClick={() => submitFC && submitFC()}
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'> className='flex px-2 py-1 cursor-pointer rounded-md space-x-2 items-center justify-center mx-auto w-full sm:w-auto 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} /> <Forward size={14} />
<p className='text-xs font-semibold'>Save your progress</p> <p className='text-xs font-semibold'>Save your progress</p>
</div> </div>
@ -89,11 +95,11 @@ function AssignmentBoxUI({ type, view, currentPoints, maxPoints, saveFC, submitF
{/* Grading button */} {/* Grading button */}
{view === 'grading' && {view === 'grading' &&
<div <div
className='flex px-0.5 py-0.5 cursor-pointer rounded-md space-x-2 items-center bg-gradient-to-bl hover:outline-offset-4 active:outline-offset-1 linear transition-all outline-offset-2 outline-dashed outline-orange-500/60'> className='flex flex-wrap sm:flex-nowrap w-full sm:w-auto px-0.5 py-0.5 cursor-pointer rounded-md gap-2 sm:space-x-2 items-center bg-gradient-to-bl hover:outline-offset-4 active:outline-offset-1 linear transition-all outline-offset-2 outline-dashed outline-orange-500/60'>
<p className='font-semibold px-2 text-xs text-orange-700'>Current points: {currentPoints}</p> <p className='font-semibold px-2 text-xs text-orange-700'>Current points: {currentPoints}</p>
<div <div
onClick={() => gradeFC && gradeFC()} onClick={() => gradeFC && gradeFC()}
className='bg-gradient-to-bl text-orange-700 bg-orange-300/20 hover:bg-orange-300/10 items-center flex rounded-md px-2 py-1 space-x-2'> className='bg-gradient-to-bl text-orange-700 bg-orange-300/20 hover:bg-orange-300/10 items-center flex rounded-md px-2 py-1 space-x-2 ml-auto'>
<BookPlus size={14} /> <BookPlus size={14} />
<p className='text-xs font-semibold'>Grade</p> <p className='text-xs font-semibold'>Grade</p>
</div> </div>
@ -103,18 +109,23 @@ function AssignmentBoxUI({ type, view, currentPoints, maxPoints, saveFC, submitF
{/* CustomGrading button */} {/* CustomGrading button */}
{view === 'custom-grading' && maxPoints && {view === 'custom-grading' && maxPoints &&
<div <div
className='flex px-0.5 py-0.5 cursor-pointer rounded-md space-x-2 items-center bg-gradient-to-bl hover:outline-offset-4 active:outline-offset-1 linear transition-all outline-offset-2 outline-dashed outline-orange-500/60'> className='flex flex-wrap sm:flex-nowrap w-full sm:w-auto px-0.5 py-0.5 cursor-pointer rounded-md gap-2 sm:space-x-2 items-center bg-gradient-to-bl hover:outline-offset-4 active:outline-offset-1 linear transition-all outline-offset-2 outline-dashed outline-orange-500/60'>
<p className='font-semibold px-2 text-xs text-orange-700'>Current points : {currentPoints}</p> <p className='font-semibold px-2 text-xs text-orange-700 w-full sm:w-auto'>Current points: {currentPoints}</p>
<div className='flex items-center gap-2 w-full sm:w-auto'>
<input <input
onChange={(e) => setCustomGrade(parseInt(e.target.value))} onChange={(e) => setCustomGrade(parseInt(e.target.value))}
placeholder={maxPoints.toString()} className='w-[100px] light-shadow text-sm py-0.5 outline outline-gray-200 rounded-lg px-2' type="number" /> placeholder={maxPoints.toString()}
className='w-full sm:w-[100px] light-shadow text-sm py-0.5 outline outline-gray-200 rounded-lg px-2'
type="number"
/>
<div <div
onClick={() => gradeCustomFC && gradeCustomFC(customGrade)} onClick={() => gradeCustomFC && gradeCustomFC(customGrade)}
className='bg-gradient-to-bl text-orange-700 bg-orange-300/20 hover:bg-orange-300/10 items-center flex rounded-md px-2 py-1 space-x-2'> className='bg-gradient-to-bl text-orange-700 bg-orange-300/20 hover:bg-orange-300/10 items-center flex rounded-md px-2 py-1 space-x-2 whitespace-nowrap'>
<BookPlus size={14} /> <BookPlus size={14} />
<p className='text-xs font-semibold'>Grade</p> <p className='text-xs font-semibold'>Grade</p>
</div> </div>
</div> </div>
</div>
} }
</div> </div>
</div> </div>

View file

@ -18,19 +18,19 @@ function AssignmentStudentActivity() {
return ( return (
<div className='flex flex-col space-y-6'> <div className='flex flex-col space-y-4 md:space-y-6'>
<div className='flex flex-row justify-center space-x-3 items-center '> <div className='flex flex-col md:flex-row justify-center md:space-x-3 space-y-3 md:space-y-0 items-center'>
<div className='text-xs h-fit flex space-x-3 items-center'> <div className='text-xs h-fit flex space-x-3 items-center'>
<div className='flex space-x-2 py-2 px-5 h-fit text-sm text-slate-700 bg-slate-100/5 rounded-full nice-shadow'> <div className='flex space-x-2 py-2 px-4 md:px-5 h-fit text-sm text-slate-700 bg-slate-100/5 rounded-full nice-shadow'>
<Backpack size={18} /> <Backpack size={16} className="md:size-18" />
<p className='font-semibold'>Assignment</p> <p className='font-semibold'>Assignment</p>
</div> </div>
</div> </div>
<div> <div>
<div className='flex space-x-2 items-center'> <div className='flex space-x-2 items-center'>
<EllipsisVertical className='text-slate-400' size={18} /> <EllipsisVertical className='text-slate-400 hidden md:block' size={18} />
<div className='flex space-x-2 items-center'> <div className='flex space-x-2 items-center'>
<div className='flex space-x-2 text-xs items-center text-slate-400'> <div className='flex space-x-1 md:space-x-2 text-xs items-center text-slate-400'>
<Calendar size={14} /> <Calendar size={14} />
<p className='font-semibold'>Due Date</p> <p className='font-semibold'>Due Date</p>
<p className='font-semibold'>{assignments?.assignment_object?.due_date}</p> <p className='font-semibold'>{assignments?.assignment_object?.due_date}</p>
@ -38,18 +38,19 @@ function AssignmentStudentActivity() {
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div className='w-full rounded-full bg-slate-500/5 nice-shadow h-[2px]'></div> <div className='w-full rounded-full bg-slate-500/5 nice-shadow h-[2px]'></div>
{assignments && assignments?.assignment_tasks?.sort((a: any, b: any) => a.id - b.id).map((task: any, index: number) => { {assignments && assignments?.assignment_tasks?.sort((a: any, b: any) => a.id - b.id).map((task: any, index: number) => {
return ( return (
<div className='flex flex-col space-y-2' key={task.assignment_task_uuid}> <div className='flex flex-col space-y-2' key={task.assignment_task_uuid}>
<div className='flex justify-between py-2'> <div className='flex flex-col md:flex-row md:justify-between py-2 space-y-2 md:space-y-0'>
<div className='flex space-x-2 font-semibold text-slate-800'> <div className='flex flex-wrap space-x-2 font-semibold text-slate-800'>
<p>Task {index + 1} : </p> <p>Task {index + 1} : </p>
<p className='text-slate-500'>{task.description}</p> <p className='text-slate-500 break-words'>{task.description}</p>
</div> </div>
<div className='flex space-x-2'> <div className='flex flex-wrap gap-2'>
<div <div
onClick={() => alert(task.hint)} 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'> className='px-3 py-1 flex items-center nice-shadow bg-amber-50/40 text-amber-900 rounded-full space-x-2 cursor-pointer'>
@ -67,9 +68,9 @@ function AssignmentStudentActivity() {
)} )}
target='_blank' target='_blank'
download={true} 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'> className='px-3 py-1 flex items-center nice-shadow bg-cyan-50/40 text-cyan-900 rounded-full space-x-1 md:space-x-2 cursor-pointer'>
<Download size={13} /> <Download size={13} />
<div className='flex items-center space-x-2'> <div className='flex items-center space-x-1 md:space-x-2'>
{task.reference_file && ( {task.reference_file && (
<span className='relative'> <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 className='absolute right-0 top-0 block h-2 w-2 rounded-full ring-2 ring-white bg-green-400'></span>
@ -80,7 +81,7 @@ function AssignmentStudentActivity() {
</Link> </Link>
</div> </div>
</div> </div>
<div> <div className='w-full'>
{task.assignment_type === 'QUIZ' && <TaskQuizObject key={task.assignment_task_uuid} view='student' assignmentTaskUUID={task.assignment_task_uuid} />} {task.assignment_type === 'QUIZ' && <TaskQuizObject key={task.assignment_task_uuid} view='student' assignmentTaskUUID={task.assignment_task_uuid} />}
{task.assignment_type === 'FILE_SUBMISSION' && <TaskFileObject key={task.assignment_task_uuid} view='student' 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>