mirror of
https://github.com/rzmk/learnhouse.git
synced 2025-12-18 20:09:25 +00:00
feat: enhance assignment page with edit functionality and description display
This commit is contained in:
parent
67ac0b9d67
commit
0229380cba
3 changed files with 285 additions and 39 deletions
|
|
@ -1,6 +1,6 @@
|
|||
'use client';
|
||||
import BreadCrumbs from '@components/Dashboard/Misc/BreadCrumbs'
|
||||
import { BookOpen, BookX, EllipsisVertical, Eye, Layers2, Monitor, UserRoundPen } from 'lucide-react'
|
||||
import { ArrowRight, BookOpen, BookX, EllipsisVertical, Eye, Layers2, Monitor, Pencil, UserRoundPen } from 'lucide-react'
|
||||
import React, { useEffect } from 'react'
|
||||
import { AssignmentProvider, useAssignments } from '@components/Contexts/Assignments/AssignmentContext';
|
||||
import ToolTip from '@components/Objects/StyledElements/Tooltip/Tooltip';
|
||||
|
|
@ -16,6 +16,7 @@ import { updateActivity } from '@services/courses/activities';
|
|||
import dynamic from 'next/dynamic';
|
||||
import AssignmentEditorSubPage from './subpages/AssignmentEditorSubPage';
|
||||
import { useMediaQuery } from 'usehooks-ts';
|
||||
import EditAssignmentModal from '@components/Objects/Modals/Activities/Assignments/EditAssignmentModal';
|
||||
const AssignmentSubmissionsSubPage = dynamic(() => import('./subpages/AssignmentSubmissionsSubPage'))
|
||||
|
||||
function AssignmentEdit() {
|
||||
|
|
@ -46,7 +47,9 @@ function AssignmentEdit() {
|
|||
<div className="pl-10 mr-10 tracking-tighter">
|
||||
<BrdCmpx />
|
||||
<div className="w-100 flex justify-between">
|
||||
<div className="flex font-bold text-2xl">Assignment Tools </div>
|
||||
<div className="flex font-bold text-2xl">
|
||||
<AssignmentTitle />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex flex-col justify-center antialiased'>
|
||||
|
|
@ -106,6 +109,7 @@ function PublishingState() {
|
|||
const assignment = useAssignments() as any;
|
||||
const session = useLHSession() as any;
|
||||
const access_token = session?.data?.tokens?.access_token;
|
||||
const [isEditModalOpen, setIsEditModalOpen] = React.useState(false);
|
||||
|
||||
async function updateAssignmentPublishState(assignmentUUID: string) {
|
||||
const res = await updateAssignment({ published: !assignment?.assignment_object?.published }, assignmentUUID, access_token)
|
||||
|
|
@ -125,50 +129,83 @@ function PublishingState() {
|
|||
}, [assignment])
|
||||
|
||||
return (
|
||||
<div className='flex mx-auto mt-5 items-center space-x-4'>
|
||||
<div className={`flex text-xs rounded-full px-3.5 py-2 mx-auto font-bold outline outline-1 ${!assignment?.assignment_object?.published ? 'outline-gray-300 bg-gray-200/60' : 'outline-green-300 bg-green-200/60'}`}>
|
||||
{assignment?.assignment_object?.published ? 'Published' : 'Unpublished'}
|
||||
</div>
|
||||
<div><EllipsisVertical className='text-gray-500' size={13} /></div>
|
||||
|
||||
<ToolTip
|
||||
side='left'
|
||||
slateBlack
|
||||
sideOffset={10}
|
||||
content="Preview the Assignment as a student" >
|
||||
<Link
|
||||
target='_blank'
|
||||
href={`/course/${assignment?.course_object?.course_uuid.replace('course_', '')}/activity/${assignment?.activity_object?.activity_uuid.replace('activity_', '')}`}
|
||||
className='flex px-3 py-2 cursor-pointer rounded-md space-x-2 items-center bg-linear-to-bl text-cyan-800 font-medium from-sky-400/50 to-cyan-200/80 border border-cyan-600/10 shadow-cyan-900/10 shadow-lg'>
|
||||
<Eye size={18} />
|
||||
<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-linear-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 className='flex mx-auto mt-5 items-center space-x-4'>
|
||||
<div className={`flex text-xs rounded-full px-3.5 py-2 mx-auto font-bold outline outline-1 ${!assignment?.assignment_object?.published ? 'outline-gray-300 bg-gray-200/60' : 'outline-green-300 bg-green-200/60'}`}>
|
||||
{assignment?.assignment_object?.published ? 'Published' : 'Unpublished'}
|
||||
</div>
|
||||
</ToolTip>}
|
||||
{!assignment?.assignment_object?.published &&
|
||||
<div><EllipsisVertical className='text-gray-500' size={13} /></div>
|
||||
|
||||
<ToolTip
|
||||
side='left'
|
||||
slateBlack
|
||||
sideOffset={10}
|
||||
content="Make your Assignment public and available for students" >
|
||||
content="Edit Assignment Details">
|
||||
<div
|
||||
onClick={() => setIsEditModalOpen(true)}
|
||||
className='flex px-3 py-2 cursor-pointer rounded-md space-x-2 items-center bg-linear-to-bl text-blue-800 font-medium from-blue-400/50 to-blue-200/80 border border-blue-600/10 shadow-blue-900/10 shadow-lg'>
|
||||
<Pencil size={18} />
|
||||
<p className='text-sm font-bold'>Edit</p>
|
||||
</div>
|
||||
</ToolTip>
|
||||
|
||||
<ToolTip
|
||||
side='left'
|
||||
slateBlack
|
||||
sideOffset={10}
|
||||
content="Preview the Assignment as a student" >
|
||||
<Link
|
||||
target='_blank'
|
||||
href={`/course/${assignment?.course_object?.course_uuid.replace('course_', '')}/activity/${assignment?.activity_object?.activity_uuid.replace('activity_', '')}`}
|
||||
className='flex px-3 py-2 cursor-pointer rounded-md space-x-2 items-center bg-linear-to-bl text-cyan-800 font-medium from-sky-400/50 to-cyan-200/80 border border-cyan-600/10 shadow-cyan-900/10 shadow-lg'>
|
||||
<Eye size={18} />
|
||||
<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-linear-to-bl text-green-800 font-medium from-green-400/50 to-lime-200/80 border border-green-600/10 shadow-green-900/10 shadow-lg'>
|
||||
<BookOpen size={18} />
|
||||
<p className=' text-sm font-bold'>Publish</p>
|
||||
className='flex px-3 py-2 cursor-pointer rounded-md space-x-2 items-center bg-linear-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>}
|
||||
</div>
|
||||
{!assignment?.assignment_object?.published &&
|
||||
<ToolTip
|
||||
side='left'
|
||||
slateBlack
|
||||
sideOffset={10}
|
||||
content="Make your Assignment public and available 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-linear-to-bl text-green-800 font-medium from-green-400/50 to-lime-200/80 border border-green-600/10 shadow-green-900/10 shadow-lg'>
|
||||
<BookOpen size={18} />
|
||||
<p className=' text-sm font-bold'>Publish</p>
|
||||
</div>
|
||||
</ToolTip>}
|
||||
</div>
|
||||
{isEditModalOpen && (
|
||||
<EditAssignmentModal
|
||||
isOpen={isEditModalOpen}
|
||||
onClose={() => setIsEditModalOpen(false)}
|
||||
assignment={assignment?.assignment_object}
|
||||
accessToken={access_token}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function AssignmentTitle() {
|
||||
const assignment = useAssignments() as any;
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
Assignment Tools
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,7 +40,22 @@ function AssignmentStudentActivity() {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div className='w-full rounded-full bg-slate-500/5 nice-shadow h-[2px]'></div>
|
||||
|
||||
|
||||
{assignments?.assignment_object?.description && (
|
||||
<div className='flex flex-col space-y-2 p-4 md:p-6 bg-slate-100/30 rounded-md nice-shadow'>
|
||||
<div className='flex flex-col space-y-3'>
|
||||
<div className='flex items-center gap-2 text-slate-700'>
|
||||
<Info size={16} className="text-slate-500" />
|
||||
<h3 className='text-sm font-semibold'>Assignment Description</h3>
|
||||
</div>
|
||||
<div className='pl-6'>
|
||||
<p className='text-sm leading-relaxed text-slate-600'>{assignments.assignment_object.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
{assignments && assignments?.assignment_tasks?.sort((a: any, b: any) => a.id - b.id).map((task: any, index: number) => {
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -0,0 +1,194 @@
|
|||
import React from 'react';
|
||||
import { updateAssignment } from '@services/courses/assignments';
|
||||
import { mutate } from 'swr';
|
||||
import { getAPIUrl } from '@services/config/config';
|
||||
import toast from 'react-hot-toast';
|
||||
import FormLayout, {
|
||||
FormField,
|
||||
FormLabelAndMessage,
|
||||
Input,
|
||||
Textarea,
|
||||
Flex,
|
||||
FormLabel,
|
||||
FormMessage
|
||||
} from '@components/Objects/StyledElements/Form/Form';
|
||||
import * as Form from '@radix-ui/react-form';
|
||||
import { useFormik } from 'formik';
|
||||
import Modal from '@components/Objects/StyledElements/Modal/Modal';
|
||||
|
||||
interface Assignment {
|
||||
assignment_uuid: string;
|
||||
title: string;
|
||||
description: string;
|
||||
due_date?: string;
|
||||
grading_type?: 'ALPHABET' | 'NUMERIC' | 'PERCENTAGE';
|
||||
}
|
||||
|
||||
interface EditAssignmentFormProps {
|
||||
onClose: () => void;
|
||||
assignment: Assignment;
|
||||
accessToken: string;
|
||||
}
|
||||
|
||||
interface EditAssignmentModalProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
assignment: Assignment;
|
||||
accessToken: string;
|
||||
}
|
||||
|
||||
const EditAssignmentForm: React.FC<EditAssignmentFormProps> = ({
|
||||
onClose,
|
||||
assignment,
|
||||
accessToken
|
||||
}) => {
|
||||
const formik = useFormik({
|
||||
initialValues: {
|
||||
title: assignment.title || '',
|
||||
description: assignment.description || '',
|
||||
due_date: assignment.due_date || '',
|
||||
grading_type: assignment.grading_type || 'ALPHABET'
|
||||
},
|
||||
enableReinitialize: true,
|
||||
onSubmit: async (values, { setSubmitting }) => {
|
||||
const toast_loading = toast.loading('Updating assignment...');
|
||||
try {
|
||||
const res = await updateAssignment(values, assignment.assignment_uuid, accessToken);
|
||||
if (res.success) {
|
||||
mutate(`${getAPIUrl()}assignments/${assignment.assignment_uuid}`);
|
||||
toast.success('Assignment updated successfully');
|
||||
onClose();
|
||||
} else {
|
||||
toast.error('Failed to update assignment');
|
||||
}
|
||||
} catch (error) {
|
||||
toast.error('An error occurred while updating the assignment');
|
||||
} finally {
|
||||
toast.dismiss(toast_loading);
|
||||
setSubmitting(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<FormLayout onSubmit={formik.handleSubmit}>
|
||||
<FormField name="title">
|
||||
<Flex css={{ alignItems: 'baseline', justifyContent: 'space-between' }}>
|
||||
<FormLabel>Assignment Title</FormLabel>
|
||||
<FormMessage match="valueMissing">
|
||||
Please provide a name for your assignment
|
||||
</FormMessage>
|
||||
</Flex>
|
||||
<Form.Control asChild>
|
||||
<Input
|
||||
onChange={formik.handleChange}
|
||||
value={formik.values.title}
|
||||
type="text"
|
||||
required
|
||||
/>
|
||||
</Form.Control>
|
||||
</FormField>
|
||||
|
||||
<FormField name="description">
|
||||
<Flex css={{ alignItems: 'baseline', justifyContent: 'space-between' }}>
|
||||
<FormLabel>Assignment Description</FormLabel>
|
||||
<FormMessage match="valueMissing">
|
||||
Please provide a description for your assignment
|
||||
</FormMessage>
|
||||
</Flex>
|
||||
<Form.Control asChild>
|
||||
<Textarea
|
||||
onChange={formik.handleChange}
|
||||
value={formik.values.description}
|
||||
required
|
||||
/>
|
||||
</Form.Control>
|
||||
</FormField>
|
||||
|
||||
<FormField name="due_date">
|
||||
<Flex css={{ alignItems: 'baseline', justifyContent: 'space-between' }}>
|
||||
<FormLabel>Due Date</FormLabel>
|
||||
<FormMessage match="valueMissing">
|
||||
Please provide a due date for your assignment
|
||||
</FormMessage>
|
||||
</Flex>
|
||||
<Form.Control asChild>
|
||||
<Input
|
||||
type="date"
|
||||
onChange={formik.handleChange}
|
||||
value={formik.values.due_date}
|
||||
required
|
||||
/>
|
||||
</Form.Control>
|
||||
</FormField>
|
||||
|
||||
<FormField name="grading_type">
|
||||
<Flex css={{ alignItems: 'baseline', justifyContent: 'space-between' }}>
|
||||
<FormLabel>Grading Type</FormLabel>
|
||||
<FormMessage match="valueMissing">
|
||||
Please provide a grading type for your assignment
|
||||
</FormMessage>
|
||||
</Flex>
|
||||
<select
|
||||
id="grading_type"
|
||||
name="grading_type"
|
||||
className='w-full bg-gray-100/40 rounded-lg px-3 py-2 outline outline-1 outline-gray-100'
|
||||
onChange={(e) => formik.setFieldValue('grading_type', e.target.value, true)}
|
||||
value={formik.values.grading_type}
|
||||
required
|
||||
>
|
||||
<option value="ALPHABET">Alphabet</option>
|
||||
<option value="NUMERIC">Numeric</option>
|
||||
<option value="PERCENTAGE">Percentage</option>
|
||||
</select>
|
||||
</FormField>
|
||||
|
||||
<div className="flex justify-end space-x-3 mt-6">
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClose}
|
||||
className="px-4 py-2 text-gray-600 hover:bg-gray-100 rounded-md"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<Form.Submit asChild>
|
||||
<button
|
||||
type="submit"
|
||||
disabled={formik.isSubmitting}
|
||||
className="px-4 py-2 bg-black text-white font-bold rounded-md hover:bg-black/90"
|
||||
>
|
||||
{formik.isSubmitting ? 'Saving...' : 'Save Changes'}
|
||||
</button>
|
||||
</Form.Submit>
|
||||
</div>
|
||||
</FormLayout>
|
||||
);
|
||||
};
|
||||
|
||||
const EditAssignmentModal: React.FC<EditAssignmentModalProps> = ({
|
||||
isOpen,
|
||||
onClose,
|
||||
assignment,
|
||||
accessToken
|
||||
}) => {
|
||||
return (
|
||||
<Modal
|
||||
isDialogOpen={isOpen}
|
||||
onOpenChange={onClose}
|
||||
minHeight="md"
|
||||
minWidth="lg"
|
||||
dialogContent={
|
||||
<EditAssignmentForm
|
||||
onClose={onClose}
|
||||
assignment={assignment}
|
||||
accessToken={accessToken}
|
||||
/>
|
||||
}
|
||||
dialogTitle="Edit Assignment"
|
||||
dialogDescription="Update assignment details"
|
||||
dialogTrigger={null}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default EditAssignmentModal;
|
||||
Loading…
Add table
Add a link
Reference in a new issue