mirror of
https://github.com/rzmk/learnhouse.git
synced 2025-12-19 04:19:25 +00:00
feat: Improve Assignment UI and File Upload Responsiveness
This commit is contained in:
parent
df4cc587ba
commit
85cbb100fe
3 changed files with 133 additions and 94 deletions
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -18,38 +18,39 @@ 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>
|
||||||
|
</div>
|
||||||
</div>
|
</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>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue