mirror of
https://github.com/rzmk/learnhouse.git
synced 2025-12-19 04:19:25 +00:00
feat: edit tasks and general improvements
This commit is contained in:
parent
acfcea026b
commit
3c41e0ee73
13 changed files with 570 additions and 93 deletions
|
|
@ -127,11 +127,6 @@ class AssignmentTaskUpdate(SQLModel):
|
||||||
assignment_type: Optional[AssignmentTaskTypeEnum]
|
assignment_type: Optional[AssignmentTaskTypeEnum]
|
||||||
contents: Optional[Dict] = Field(default={}, sa_column=Column(JSON))
|
contents: Optional[Dict] = Field(default={}, sa_column=Column(JSON))
|
||||||
max_grade_value: Optional[int]
|
max_grade_value: Optional[int]
|
||||||
assignment_id: Optional[int]
|
|
||||||
org_id: Optional[int]
|
|
||||||
course_id: Optional[int]
|
|
||||||
chapter_id: Optional[int]
|
|
||||||
activity_id: Optional[int]
|
|
||||||
|
|
||||||
|
|
||||||
class AssignmentTask(AssignmentTaskBase, table=True):
|
class AssignmentTask(AssignmentTaskBase, table=True):
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
from fastapi import APIRouter, Depends, Request
|
from fastapi import APIRouter, Depends, Request, UploadFile
|
||||||
from src.db.courses.assignments import (
|
from src.db.courses.assignments import (
|
||||||
AssignmentCreate,
|
AssignmentCreate,
|
||||||
AssignmentRead,
|
AssignmentRead,
|
||||||
|
|
@ -21,6 +21,7 @@ from src.services.courses.activities.assignments import (
|
||||||
delete_assignment_submission,
|
delete_assignment_submission,
|
||||||
delete_assignment_task,
|
delete_assignment_task,
|
||||||
delete_assignment_task_submission,
|
delete_assignment_task_submission,
|
||||||
|
put_assignment_task_reference_file,
|
||||||
read_assignment,
|
read_assignment,
|
||||||
read_assignment_from_activity_uuid,
|
read_assignment_from_activity_uuid,
|
||||||
read_assignment_submissions,
|
read_assignment_submissions,
|
||||||
|
|
@ -64,6 +65,7 @@ async def api_read_assignment(
|
||||||
"""
|
"""
|
||||||
return await read_assignment(request, assignment_uuid, current_user, db_session)
|
return await read_assignment(request, assignment_uuid, current_user, db_session)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/activity/{activity_uuid}")
|
@router.get("/activity/{activity_uuid}")
|
||||||
async def api_read_assignment_from_activity(
|
async def api_read_assignment_from_activity(
|
||||||
request: Request,
|
request: Request,
|
||||||
|
|
@ -74,7 +76,9 @@ async def api_read_assignment_from_activity(
|
||||||
"""
|
"""
|
||||||
Read an assignment
|
Read an assignment
|
||||||
"""
|
"""
|
||||||
return await read_assignment_from_activity_uuid(request, activity_uuid, current_user, db_session)
|
return await read_assignment_from_activity_uuid(
|
||||||
|
request, activity_uuid, current_user, db_session
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.put("/{assignment_uuid}")
|
@router.put("/{assignment_uuid}")
|
||||||
|
|
@ -105,6 +109,7 @@ async def api_delete_assignment(
|
||||||
"""
|
"""
|
||||||
return await delete_assignment(request, assignment_uuid, current_user, db_session)
|
return await delete_assignment(request, assignment_uuid, current_user, db_session)
|
||||||
|
|
||||||
|
|
||||||
@router.delete("/activity/{activity_uuid}")
|
@router.delete("/activity/{activity_uuid}")
|
||||||
async def api_delete_assignment_from_activity(
|
async def api_delete_assignment_from_activity(
|
||||||
request: Request,
|
request: Request,
|
||||||
|
|
@ -115,7 +120,9 @@ async def api_delete_assignment_from_activity(
|
||||||
"""
|
"""
|
||||||
Delete an assignment
|
Delete an assignment
|
||||||
"""
|
"""
|
||||||
return await delete_assignment_from_activity_uuid(request, activity_uuid, current_user, db_session)
|
return await delete_assignment_from_activity_uuid(
|
||||||
|
request, activity_uuid, current_user, db_session
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
## ASSIGNMENTS Tasks ##
|
## ASSIGNMENTS Tasks ##
|
||||||
|
|
@ -166,7 +173,8 @@ async def api_read_assignment_task(
|
||||||
request, assignment_task_uuid, current_user, db_session
|
request, assignment_task_uuid, current_user, db_session
|
||||||
)
|
)
|
||||||
|
|
||||||
@router.put("/{assignment_uuid}/tasks/{task_uuid}")
|
|
||||||
|
@router.put("/{assignment_uuid}/tasks/{assignment_task_uuid}")
|
||||||
async def api_update_assignment_tasks(
|
async def api_update_assignment_tasks(
|
||||||
request: Request,
|
request: Request,
|
||||||
assignment_task_uuid: str,
|
assignment_task_uuid: str,
|
||||||
|
|
@ -182,6 +190,22 @@ async def api_update_assignment_tasks(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/{assignment_uuid}/tasks/{assignment_task_uuid}/ref_file")
|
||||||
|
async def api_put_assignment_task_ref_file(
|
||||||
|
request: Request,
|
||||||
|
assignment_task_uuid: str,
|
||||||
|
reference_file: UploadFile | None = None,
|
||||||
|
current_user: PublicUser = Depends(get_current_user),
|
||||||
|
db_session=Depends(get_db_session),
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Update tasks for an assignment
|
||||||
|
"""
|
||||||
|
return await put_assignment_task_reference_file(
|
||||||
|
request, db_session, assignment_task_uuid, current_user, reference_file
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.delete("/{assignment_uuid}/tasks/{task_uuid}")
|
@router.delete("/{assignment_uuid}/tasks/{task_uuid}")
|
||||||
async def api_delete_assignment_tasks(
|
async def api_delete_assignment_tasks(
|
||||||
request: Request,
|
request: Request,
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import Literal
|
from typing import Literal
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
from fastapi import HTTPException, Request
|
from fastapi import HTTPException, Request, UploadFile
|
||||||
from sqlmodel import Session, select
|
from sqlmodel import Session, select
|
||||||
|
|
||||||
from src.db.courses.activities import Activity
|
from src.db.courses.activities import Activity
|
||||||
|
|
@ -26,12 +26,16 @@ from src.db.courses.assignments import (
|
||||||
AssignmentUserSubmissionRead,
|
AssignmentUserSubmissionRead,
|
||||||
)
|
)
|
||||||
from src.db.courses.courses import Course
|
from src.db.courses.courses import Course
|
||||||
|
from src.db.organizations import Organization
|
||||||
from src.db.users import AnonymousUser, PublicUser
|
from src.db.users import AnonymousUser, PublicUser
|
||||||
from src.security.rbac.rbac import (
|
from src.security.rbac.rbac import (
|
||||||
authorization_verify_based_on_roles_and_authorship_and_usergroups,
|
authorization_verify_based_on_roles_and_authorship_and_usergroups,
|
||||||
authorization_verify_if_element_is_public,
|
authorization_verify_if_element_is_public,
|
||||||
authorization_verify_if_user_is_anon,
|
authorization_verify_if_user_is_anon,
|
||||||
)
|
)
|
||||||
|
from src.services.courses.activities.uploads.tasks_ref_files import (
|
||||||
|
upload_reference_file,
|
||||||
|
)
|
||||||
|
|
||||||
## > Assignments CRUD
|
## > Assignments CRUD
|
||||||
|
|
||||||
|
|
@ -104,6 +108,7 @@ async def read_assignment(
|
||||||
# return assignment read
|
# return assignment read
|
||||||
return AssignmentRead.model_validate(assignment)
|
return AssignmentRead.model_validate(assignment)
|
||||||
|
|
||||||
|
|
||||||
async def read_assignment_from_activity_uuid(
|
async def read_assignment_from_activity_uuid(
|
||||||
request: Request,
|
request: Request,
|
||||||
activity_uuid: str,
|
activity_uuid: str,
|
||||||
|
|
@ -227,6 +232,7 @@ async def delete_assignment(
|
||||||
|
|
||||||
return {"message": "Assignment deleted"}
|
return {"message": "Assignment deleted"}
|
||||||
|
|
||||||
|
|
||||||
async def delete_assignment_from_activity_uuid(
|
async def delete_assignment_from_activity_uuid(
|
||||||
request: Request,
|
request: Request,
|
||||||
activity_uuid: str,
|
activity_uuid: str,
|
||||||
|
|
@ -369,6 +375,7 @@ async def read_assignment_tasks(
|
||||||
for assignment_task in db_session.exec(statement).all()
|
for assignment_task in db_session.exec(statement).all()
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
async def read_assignment_task(
|
async def read_assignment_task(
|
||||||
request: Request,
|
request: Request,
|
||||||
assignment_task_uuid: str,
|
assignment_task_uuid: str,
|
||||||
|
|
@ -376,7 +383,9 @@ async def read_assignment_task(
|
||||||
db_session: Session,
|
db_session: Session,
|
||||||
):
|
):
|
||||||
# Find assignment
|
# Find assignment
|
||||||
statement = select(AssignmentTask).where(AssignmentTask.assignment_task_uuid == assignment_task_uuid)
|
statement = select(AssignmentTask).where(
|
||||||
|
AssignmentTask.assignment_task_uuid == assignment_task_uuid
|
||||||
|
)
|
||||||
assignmenttask = db_session.exec(statement).first()
|
assignmenttask = db_session.exec(statement).first()
|
||||||
|
|
||||||
if not assignmenttask:
|
if not assignmenttask:
|
||||||
|
|
@ -412,6 +421,79 @@ async def read_assignment_task(
|
||||||
return AssignmentTaskRead.model_validate(assignmenttask)
|
return AssignmentTaskRead.model_validate(assignmenttask)
|
||||||
|
|
||||||
|
|
||||||
|
async def put_assignment_task_reference_file(
|
||||||
|
request: Request,
|
||||||
|
db_session: Session,
|
||||||
|
assignment_task_uuid: str,
|
||||||
|
current_user: PublicUser | AnonymousUser,
|
||||||
|
reference_file: UploadFile | None = None,
|
||||||
|
):
|
||||||
|
# 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 exists
|
||||||
|
statement = select(Assignment).where(Assignment.id == assignment_task.assignment_id)
|
||||||
|
assignment = db_session.exec(statement).first()
|
||||||
|
|
||||||
|
if not assignment:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=404,
|
||||||
|
detail="Assignment not found",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check for activity
|
||||||
|
statement = select(Activity).where(Activity.id == assignment.activity_id)
|
||||||
|
activity = db_session.exec(statement).first()
|
||||||
|
|
||||||
|
# Check if course exists
|
||||||
|
statement = select(Course).where(Course.id == assignment.course_id)
|
||||||
|
course = db_session.exec(statement).first()
|
||||||
|
|
||||||
|
if not course:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=404,
|
||||||
|
detail="Course not found",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Get org uuid
|
||||||
|
org_statement = select(Organization).where(Organization.id == course.org_id)
|
||||||
|
org = db_session.exec(org_statement).first()
|
||||||
|
|
||||||
|
# RBAC check
|
||||||
|
await rbac_check(request, course.course_uuid, current_user, "update", db_session)
|
||||||
|
|
||||||
|
# Upload reference file
|
||||||
|
if reference_file and reference_file.filename and activity and org:
|
||||||
|
name_in_disk = (
|
||||||
|
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
|
||||||
|
)
|
||||||
|
course.thumbnail_image = name_in_disk
|
||||||
|
# Update reference file
|
||||||
|
assignment_task.reference_file = name_in_disk
|
||||||
|
|
||||||
|
assignment_task.update_date = str(datetime.now())
|
||||||
|
|
||||||
|
# Insert Assignment Task in DB
|
||||||
|
db_session.add(assignment_task)
|
||||||
|
db_session.commit()
|
||||||
|
db_session.refresh(assignment_task)
|
||||||
|
|
||||||
|
# return assignment task read
|
||||||
|
return AssignmentTaskRead.model_validate(assignment_task)
|
||||||
|
|
||||||
|
|
||||||
async def update_assignment_task(
|
async def update_assignment_task(
|
||||||
request: Request,
|
request: Request,
|
||||||
assignment_task_uuid: str,
|
assignment_task_uuid: str,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
from uuid import uuid4
|
||||||
|
from src.services.utils.upload_content import upload_content
|
||||||
|
|
||||||
|
|
||||||
|
async def upload_reference_file(
|
||||||
|
file,
|
||||||
|
name_in_disk,
|
||||||
|
activity_uuid,
|
||||||
|
org_uuid,
|
||||||
|
course_uuid,
|
||||||
|
assignment_uuid,
|
||||||
|
assignment_task_uuid,
|
||||||
|
):
|
||||||
|
contents = file.file.read()
|
||||||
|
file_format = file.filename.split(".")[-1]
|
||||||
|
|
||||||
|
await upload_content(
|
||||||
|
f"courses/{course_uuid}/activities/{activity_uuid}/assignments/{assignment_uuid}/tasks/{assignment_task_uuid}",
|
||||||
|
"orgs",
|
||||||
|
org_uuid,
|
||||||
|
contents,
|
||||||
|
f"{name_in_disk}",
|
||||||
|
["pdf", "docx", "mp4", "jpg", "jpeg", "png", "pptx"],
|
||||||
|
)
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
from typing import Literal
|
from typing import Literal, Optional
|
||||||
import boto3
|
import boto3
|
||||||
from botocore.exceptions import ClientError
|
from botocore.exceptions import ClientError
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
from fastapi import HTTPException
|
||||||
|
|
||||||
from config.config import get_learnhouse_config
|
from config.config import get_learnhouse_config
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -12,13 +14,24 @@ async def upload_content(
|
||||||
uuid: str, # org_uuid or user_uuid
|
uuid: str, # org_uuid or user_uuid
|
||||||
file_binary: bytes,
|
file_binary: bytes,
|
||||||
file_and_format: str,
|
file_and_format: str,
|
||||||
|
allowed_formats: Optional[list[str]] = None,
|
||||||
):
|
):
|
||||||
# Get Learnhouse Config
|
# Get Learnhouse Config
|
||||||
learnhouse_config = get_learnhouse_config()
|
learnhouse_config = get_learnhouse_config()
|
||||||
|
|
||||||
|
file_format = file_and_format.split(".")[-1].strip().lower()
|
||||||
|
|
||||||
# Get content delivery method
|
# Get content delivery method
|
||||||
content_delivery = learnhouse_config.hosting_config.content_delivery.type
|
content_delivery = learnhouse_config.hosting_config.content_delivery.type
|
||||||
|
|
||||||
|
# Check if format file is allowed
|
||||||
|
if allowed_formats:
|
||||||
|
if file_format not in allowed_formats:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=400,
|
||||||
|
detail=f"File format {file_format} not allowed",
|
||||||
|
)
|
||||||
|
|
||||||
if content_delivery == "filesystem":
|
if content_delivery == "filesystem":
|
||||||
# create folder for activity
|
# create folder for activity
|
||||||
if not os.path.exists(f"content/{type_of_dir}/{uuid}/{directory}"):
|
if not os.path.exists(f"content/{type_of_dir}/{uuid}/{directory}"):
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ function NewTaskModal({ closeModal, assignment_uuid }: any) {
|
||||||
<div className='px-5 py-5 rounded-full nice-shadow w-fit mx-auto bg-gray-100/50 text-gray-500 cursor-pointer hover:bg-gray-100 transition-all ease-linear'>
|
<div className='px-5 py-5 rounded-full nice-shadow w-fit mx-auto bg-gray-100/50 text-gray-500 cursor-pointer hover:bg-gray-100 transition-all ease-linear'>
|
||||||
<FileUp size={30} />
|
<FileUp size={30} />
|
||||||
</div>
|
</div>
|
||||||
<p className='text-xl text-gray-700 font-semibold'>File submissions</p>
|
<p className='text-xl text-gray-700 font-semibold'>File submission</p>
|
||||||
<p className='text-sm text-gray-500 w-40'>Students can submit files for this task</p>
|
<p className='text-sm text-gray-500 w-40'>Students can submit files for this task</p>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
|
@ -67,7 +67,7 @@ function NewTaskModal({ closeModal, assignment_uuid }: any) {
|
||||||
<div className='px-5 py-5 rounded-full nice-shadow w-fit mx-auto bg-gray-100/50 text-gray-500 cursor-pointer hover:bg-gray-100 transition-all ease-linear'>
|
<div className='px-5 py-5 rounded-full nice-shadow w-fit mx-auto bg-gray-100/50 text-gray-500 cursor-pointer hover:bg-gray-100 transition-all ease-linear'>
|
||||||
<AArrowUp size={30} />
|
<AArrowUp size={30} />
|
||||||
</div>
|
</div>
|
||||||
<p className='text-xl text-gray-700 font-semibold'>Forms</p>
|
<p className='text-xl text-gray-700 font-semibold'>Form</p>
|
||||||
<p className='text-sm text-gray-500 w-40'>Forms for students to fill out</p>
|
<p className='text-sm text-gray-500 w-40'>Forms for students to fill out</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,17 @@
|
||||||
'use client';
|
'use client';
|
||||||
import { useAssignmentsTask } from '@components/Contexts/Assignments/AssignmentsTaskContext';
|
import { useAssignments } from '@components/Contexts/Assignments/AssignmentContext';
|
||||||
import { Info, TentTree } from 'lucide-react'
|
import { useAssignmentsTask, useAssignmentsTaskDispatch } from '@components/Contexts/Assignments/AssignmentsTaskContext';
|
||||||
import React, { useEffect } from 'react'
|
import { useLHSession } from '@components/Contexts/LHSessionContext';
|
||||||
|
import FormLayout, { FormField, FormLabelAndMessage, Input, Textarea } from '@components/StyledElements/Form/Form';
|
||||||
|
import * as Form from '@radix-ui/react-form';
|
||||||
|
import { getActivity } from '@services/courses/activities';
|
||||||
|
import { updateAssignmentTask, updateReferenceFile } from '@services/courses/assignments';
|
||||||
|
import { getTaskRefFileDir } from '@services/media/media';
|
||||||
|
import { useFormik } from 'formik';
|
||||||
|
import { ArrowBigUpDash, Cloud, File, GalleryVerticalEnd, Info, Loader, TentTree, Upload, UploadCloud } from 'lucide-react'
|
||||||
|
import Link from 'next/link';
|
||||||
|
import React, { use, useEffect } from 'react'
|
||||||
|
import toast from 'react-hot-toast';
|
||||||
|
|
||||||
function AssignmentTaskEditor({ page }: any) {
|
function AssignmentTaskEditor({ page }: any) {
|
||||||
const [selectedSubPage, setSelectedSubPage] = React.useState(page)
|
const [selectedSubPage, setSelectedSubPage] = React.useState(page)
|
||||||
|
|
@ -15,22 +25,40 @@ function AssignmentTaskEditor({ page }: any) {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col font-black text-sm w-full z-20">
|
<div className="flex flex-col font-black text-sm w-full z-20">
|
||||||
{assignmentTaskState.assignmentTask && Object.keys(assignmentTaskState.assignmentTask).length > 0 && (
|
{assignmentTaskState.assignmentTask && Object.keys(assignmentTaskState.assignmentTask).length > 0 && (
|
||||||
<div className='flex flex-col bg-white pl-10 pr-10 text-sm tracking-tight z-10 shadow-[0px_4px_16px_rgba(0,0,0,0.06)] pt-5'>
|
<div className='flex flex-col space-y-3'>
|
||||||
|
<div className='flex flex-col bg-white pl-10 pr-10 text-sm tracking-tight z-10 shadow-[0px_4px_16px_rgba(0,0,0,0.06)] pt-5 mb-3 nice-shadow'>
|
||||||
<div className='font-semibold text-lg py-1'>
|
<div className='font-semibold text-lg py-1'>
|
||||||
Assignment Test #1
|
{assignmentTaskState?.assignmentTask.title}
|
||||||
</div>
|
</div>
|
||||||
<div className='flex space-x-2 '>
|
<div className='flex space-x-2 '>
|
||||||
<div
|
<div
|
||||||
className={`flex space-x-4 py-2 w-fit text-center border-black transition-all ease-linear ${selectedSubPage === 'overview'
|
onClick={() => setSelectedSubPage('general')}
|
||||||
|
className={`flex space-x-4 py-2 w-fit text-center border-black transition-all ease-linear ${selectedSubPage === 'general'
|
||||||
? 'border-b-4'
|
? 'border-b-4'
|
||||||
: 'opacity-50'
|
: 'opacity-50'
|
||||||
} cursor-pointer`}
|
} cursor-pointer`}
|
||||||
>
|
>
|
||||||
<div className="flex items-center space-x-2.5 mx-2">
|
<div className="flex items-center space-x-2.5 mx-2">
|
||||||
<Info size={16} />
|
<Info size={16} />
|
||||||
<div>Overview</div>
|
<div>General</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
onClick={() => setSelectedSubPage('content')}
|
||||||
|
className={`flex space-x-4 py-2 w-fit text-center border-black transition-all ease-linear ${selectedSubPage === 'content'
|
||||||
|
? 'border-b-4'
|
||||||
|
: 'opacity-50'
|
||||||
|
} cursor-pointer`}
|
||||||
|
>
|
||||||
|
<div className="flex items-center space-x-2.5 mx-2">
|
||||||
|
<GalleryVerticalEnd size={16} />
|
||||||
|
<div>Content</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className='ml-10 mr-10 mt-10 mx-auto bg-white rounded-xl shadow-sm px-6 py-5 nice-shadow'>
|
||||||
|
{selectedSubPage === 'general' && <AssignmentTaskGeneralEdit />}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
@ -51,4 +79,255 @@ function AssignmentTaskEditor({ page }: any) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function AssignmentTaskGeneralEdit() {
|
||||||
|
const session = useLHSession() as any;
|
||||||
|
const access_token = session?.data?.tokens?.access_token;
|
||||||
|
const assignmentTaskState = useAssignmentsTask() as any
|
||||||
|
const assignmentTaskStateHook = useAssignmentsTaskDispatch() as any
|
||||||
|
const assignment = useAssignments() as any
|
||||||
|
|
||||||
|
const validate = (values: any) => {
|
||||||
|
const errors: any = {};
|
||||||
|
if (values.max_grade_value < 20 || values.max_grade_value > 100) {
|
||||||
|
errors.max_grade_value = 'Value should be between 20 and 100';
|
||||||
|
}
|
||||||
|
return errors;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const formik = useFormik({
|
||||||
|
initialValues: {
|
||||||
|
title: assignmentTaskState.assignmentTask.title,
|
||||||
|
description: assignmentTaskState.assignmentTask.description,
|
||||||
|
hint: assignmentTaskState.assignmentTask.hint,
|
||||||
|
max_grade_value: assignmentTaskState.assignmentTask.max_grade_value,
|
||||||
|
},
|
||||||
|
validate,
|
||||||
|
onSubmit: async values => {
|
||||||
|
const res = await updateAssignmentTask(values, assignmentTaskState.assignmentTask.assignment_task_uuid, assignment.assignment_object.assignment_uuid, access_token)
|
||||||
|
if (res) {
|
||||||
|
assignmentTaskStateHook({ type: 'reload' })
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
toast.error('Error updating task, please retry later.')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
enableReinitialize: true,
|
||||||
|
}) as any;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FormLayout onSubmit={formik.handleSubmit}>
|
||||||
|
<FormField name="title">
|
||||||
|
<FormLabelAndMessage label="Title" message={formik.errors.title} />
|
||||||
|
<Form.Control asChild>
|
||||||
|
<Input
|
||||||
|
onChange={formik.handleChange}
|
||||||
|
value={formik.values.title}
|
||||||
|
type="text"
|
||||||
|
/>
|
||||||
|
</Form.Control>
|
||||||
|
</FormField>
|
||||||
|
|
||||||
|
<FormField name="description">
|
||||||
|
<FormLabelAndMessage label="Description" message={formik.errors.description} />
|
||||||
|
<Form.Control asChild>
|
||||||
|
<Input
|
||||||
|
onChange={formik.handleChange}
|
||||||
|
value={formik.values.description}
|
||||||
|
type="text"
|
||||||
|
/>
|
||||||
|
</Form.Control>
|
||||||
|
</FormField>
|
||||||
|
|
||||||
|
<FormField name="hint">
|
||||||
|
<FormLabelAndMessage label="Hint" message={formik.errors.hint} />
|
||||||
|
<Form.Control asChild>
|
||||||
|
<Textarea
|
||||||
|
onChange={formik.handleChange}
|
||||||
|
value={formik.values.hint}
|
||||||
|
/>
|
||||||
|
</Form.Control>
|
||||||
|
</FormField>
|
||||||
|
|
||||||
|
<FormField name="hint">
|
||||||
|
<div className='flex space-x-3 justify-between items-center'>
|
||||||
|
<FormLabelAndMessage label="Reference file" message={formik.errors.hint} />
|
||||||
|
<div className='flex 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>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<Form.Control asChild>
|
||||||
|
<UpdateTaskRef />
|
||||||
|
</Form.Control>
|
||||||
|
</FormField>
|
||||||
|
|
||||||
|
<FormField name="max_grade_value">
|
||||||
|
<FormLabelAndMessage label="Max Grade Value" message={formik.errors.max_grade_value} />
|
||||||
|
<Form.Control asChild>
|
||||||
|
<Input
|
||||||
|
onChange={formik.handleChange}
|
||||||
|
value={formik.values.max_grade_value}
|
||||||
|
type="number"
|
||||||
|
/>
|
||||||
|
</Form.Control>
|
||||||
|
</FormField>
|
||||||
|
|
||||||
|
{/* Submit button */}
|
||||||
|
<Form.Submit >
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="flex items-center justify-center w-full px-4 py-2 mt-4 font-semibold text-white bg-green-500 rounded-md hover:bg-green-600"
|
||||||
|
>
|
||||||
|
Submit
|
||||||
|
</button>
|
||||||
|
</Form.Submit>
|
||||||
|
|
||||||
|
|
||||||
|
</FormLayout>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function UpdateTaskRef() {
|
||||||
|
const session = useLHSession() as any;
|
||||||
|
const access_token = session?.data?.tokens?.access_token;
|
||||||
|
const assignmentTaskState = useAssignmentsTask() as any
|
||||||
|
const assignmentTaskStateHook = useAssignmentsTaskDispatch() as any
|
||||||
|
const assignment = useAssignments() as any
|
||||||
|
const [isLoading, setIsLoading] = React.useState(false)
|
||||||
|
const [error, setError] = React.useState('') as any
|
||||||
|
const [localRefFile, setLocalRefFile] = React.useState(null) as any
|
||||||
|
const [activity, setActivity] = React.useState('') as any
|
||||||
|
|
||||||
|
const handleFileChange = async (event: any) => {
|
||||||
|
const file = event.target.files[0]
|
||||||
|
setLocalRefFile(file)
|
||||||
|
setIsLoading(true)
|
||||||
|
const res = await updateReferenceFile(
|
||||||
|
file,
|
||||||
|
assignmentTaskState.assignmentTask.assignment_task_uuid,
|
||||||
|
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 {
|
||||||
|
setIsLoading(false)
|
||||||
|
setError('')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteReferenceFile = async () => {
|
||||||
|
setIsLoading(true)
|
||||||
|
const res = await updateReferenceFile(
|
||||||
|
'',
|
||||||
|
assignmentTaskState.assignmentTask.assignment_task_uuid,
|
||||||
|
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 {
|
||||||
|
setIsLoading(false)
|
||||||
|
setError('')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getActivityUI() {
|
||||||
|
const res = await getActivity(assignment.assignment_object.activity_id, null, access_token)
|
||||||
|
console.log(res)
|
||||||
|
setActivity(res.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
getActivityUI()
|
||||||
|
console.log(assignment.assignment_object.assignment_uuid)
|
||||||
|
console.log(assignmentTaskState.assignmentTask.assignment_task_uuid)
|
||||||
|
}
|
||||||
|
, [assignmentTaskState])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="w-auto bg-gray-50 rounded-xl outline outline-1 outline-gray-200 h-[200px] shadow">
|
||||||
|
<div className="flex flex-col justify-center items-center h-full">
|
||||||
|
<div className="flex flex-col justify-center items-center">
|
||||||
|
<div className="flex flex-col justify-center items-center">
|
||||||
|
{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="text-sm font-semibold">{error}</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
{assignmentTaskState.assignmentTask.reference_file && (
|
||||||
|
<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>
|
||||||
|
<File size={20} className='' />
|
||||||
|
<div className='font-semibold text-sm uppercase'>
|
||||||
|
{assignmentTaskState.assignmentTask.reference_file.split('.').pop()}
|
||||||
|
</div>
|
||||||
|
<div className='flex space-x-2 mt-2'>
|
||||||
|
<Link
|
||||||
|
href={''}
|
||||||
|
//href={getTaskRefFileDir(assignment.assignment_object.assignment_uuid, assignmentTaskState.assignmentTask.reference_file)}
|
||||||
|
className='bg-blue-500 text-white px-3 py-1 rounded-full text-xs font-semibold'>Download</Link>
|
||||||
|
{/** <button onClick={() => deleteReferenceFile()}
|
||||||
|
className='bg-red-500 text-white px-3 py-1 rounded-full text-xs font-semibold'>Delete</button> */}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{isLoading ? (
|
||||||
|
<div className="flex justify-center items-center">
|
||||||
|
<input
|
||||||
|
type="file"
|
||||||
|
id="fileInput"
|
||||||
|
style={{ display: 'none' }}
|
||||||
|
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">
|
||||||
|
<Loader size={16} className="mr-2" />
|
||||||
|
<span>Loading</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="flex justify-center items-center">
|
||||||
|
<input
|
||||||
|
type="file"
|
||||||
|
id="fileInput"
|
||||||
|
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()}
|
||||||
|
>
|
||||||
|
<UploadCloud size={16} className="mr-2" />
|
||||||
|
<span>Change Reference File</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export default AssignmentTaskEditor
|
export default AssignmentTaskEditor
|
||||||
|
|
@ -3,10 +3,11 @@ import Modal from '@components/StyledElements/Modal/Modal';
|
||||||
import { FileUp, ListTodo, PanelLeftOpen, Plus } from 'lucide-react';
|
import { FileUp, ListTodo, PanelLeftOpen, Plus } from 'lucide-react';
|
||||||
import React, { useEffect } from 'react'
|
import React, { useEffect } from 'react'
|
||||||
import NewTaskModal from './Modals/NewTaskModal';
|
import NewTaskModal from './Modals/NewTaskModal';
|
||||||
import { useAssignmentsTaskDispatch } from '@components/Contexts/Assignments/AssignmentsTaskContext';
|
import { useAssignmentsTask, useAssignmentsTaskDispatch } from '@components/Contexts/Assignments/AssignmentsTaskContext';
|
||||||
|
|
||||||
function AssignmentTasks({ assignment_uuid }: any) {
|
function AssignmentTasks({ assignment_uuid }: any) {
|
||||||
const assignments = useAssignments() as any;
|
const assignments = useAssignments() as any;
|
||||||
|
const assignmentTask = useAssignmentsTask() as any;
|
||||||
const assignmentTaskHook = useAssignmentsTaskDispatch() as any;
|
const assignmentTaskHook = useAssignmentsTaskDispatch() as any;
|
||||||
const [isNewTaskModalOpen, setIsNewTaskModalOpen] = React.useState(false)
|
const [isNewTaskModalOpen, setIsNewTaskModalOpen] = React.useState(false)
|
||||||
|
|
||||||
|
|
@ -21,30 +22,7 @@ function AssignmentTasks({ assignment_uuid }: any) {
|
||||||
return (
|
return (
|
||||||
<div className='flex w-full'>
|
<div className='flex w-full'>
|
||||||
<div className='flex flex-col space-y-3 mx-auto'>
|
<div className='flex flex-col space-y-3 mx-auto'>
|
||||||
{assignments && assignments?.assignment_tasks?.map((task: any) => {
|
{assignments && assignments?.assignment_tasks?.length < 10 && (<Modal
|
||||||
return (
|
|
||||||
<div
|
|
||||||
key={task.id}
|
|
||||||
className='flex flex-col w-[250px] nice-shadow bg-white shadow-[0px_4px_16px_rgba(0,0,0,0.06)] p-3 rounded-md'
|
|
||||||
onClick={() => setSelectTask(task.assignment_task_uuid)}
|
|
||||||
>
|
|
||||||
<div className='flex items-center px-2 justify-between'>
|
|
||||||
<div className="flex space-x-3 items-center">
|
|
||||||
<div className='text-gray-500'>
|
|
||||||
{task.assignment_type === 'QUIZ' && <ListTodo size={15} />}
|
|
||||||
{task.assignment_type === 'FILE_SUBMISSION' && <FileUp size={15} />}
|
|
||||||
</div>
|
|
||||||
<div className='font-semibold text-sm'>{task.title}</div>
|
|
||||||
</div>
|
|
||||||
<button className="outline outline-1 outline-gray-200 hover:bg-slate-100/50 rounded-md text-gray-500 font-bold py-2 px-3 focus:bg-slate-100 ease-linear transition-all">
|
|
||||||
<PanelLeftOpen size={16} />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
|
|
||||||
<Modal
|
|
||||||
isDialogOpen={isNewTaskModalOpen}
|
isDialogOpen={isNewTaskModalOpen}
|
||||||
onOpenChange={setIsNewTaskModalOpen}
|
onOpenChange={setIsNewTaskModalOpen}
|
||||||
minHeight="sm"
|
minHeight="sm"
|
||||||
|
|
@ -60,7 +38,31 @@ function AssignmentTasks({ assignment_uuid }: any) {
|
||||||
<p>Add Task</p>
|
<p>Add Task</p>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
/>
|
/>)}
|
||||||
|
{assignments && assignments?.assignment_tasks?.map((task: any) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={task.id}
|
||||||
|
className='flex flex-col w-[250px] nice-shadow bg-white shadow-[0px_4px_16px_rgba(0,0,0,0.06)] p-3 rounded-md'
|
||||||
|
onClick={() => setSelectTask(task.assignment_task_uuid)}
|
||||||
|
>
|
||||||
|
<div className='flex items-center px-2 justify-between'>
|
||||||
|
<div className="flex space-x-3 items-center">
|
||||||
|
<div className='text-gray-500'>
|
||||||
|
{task.assignment_type === 'QUIZ' && <ListTodo size={15} />}
|
||||||
|
{task.assignment_type === 'FILE_SUBMISSION' && <FileUp size={15} />}
|
||||||
|
</div>
|
||||||
|
<div className='font-semibold text-sm'>{task.title}</div>
|
||||||
|
</div>
|
||||||
|
<button className={`outline outline-1 outline-gray-200 ${task.assignment_task_uuid == assignmentTask.selectedAssignmentTaskUUID ? 'bg-slate-100' : ''} hover:bg-slate-100/50 rounded-md text-gray-500 font-bold py-2 px-3 ease-linear transition-all`}>
|
||||||
|
<PanelLeftOpen size={16} />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -50,22 +50,22 @@ function AssignmentEdit() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex h-full w-full">
|
<div className="flex h-full w-full">
|
||||||
|
<AssignmentProvider assignment_uuid={'assignment_' + params.assignmentuuid}>
|
||||||
<AssignmentsTaskProvider>
|
<AssignmentsTaskProvider>
|
||||||
<div className='flex w-[400px] flex-col h-full custom-dots-bg'>
|
<div className='flex w-[400px] flex-col h-full custom-dots-bg'>
|
||||||
<div className='flex mx-auto px-3.5 py-1 bg-neutral-600/80 space-x-2 my-5 items-center text-sm font-bold text-white rounded-full'>
|
<div className='flex mx-auto px-3.5 py-1 bg-neutral-600/80 space-x-2 my-5 items-center text-sm font-bold text-white rounded-full'>
|
||||||
<LayoutList size={18} />
|
<LayoutList size={18} />
|
||||||
<p>Tasks</p>
|
<p>Tasks</p>
|
||||||
</div>
|
</div>
|
||||||
<AssignmentProvider assignment_uuid={'assignment_' + params.assignmentuuid}>
|
|
||||||
<AssignmentTasks assignment_uuid={'assignment_' + params.assignmentuuid} />
|
<AssignmentTasks assignment_uuid={'assignment_' + params.assignmentuuid} />
|
||||||
</AssignmentProvider>
|
|
||||||
</div>
|
</div>
|
||||||
<div className='flex flex-grow bg-[#fefcfe] nice-shadow h-full w-full'>
|
<div className='flex flex-grow bg-[#fefcfe] nice-shadow h-full w-full'>
|
||||||
<AssignmentProvider assignment_uuid={'assignment_' + params.assignmentuuid}>
|
<AssignmentProvider assignment_uuid={'assignment_' + params.assignmentuuid}>
|
||||||
<AssignmentTaskEditor task_uuid='UUID' page='overview' />
|
<AssignmentTaskEditor page='general' />
|
||||||
</AssignmentProvider>
|
</AssignmentProvider>
|
||||||
</div>
|
</div>
|
||||||
</AssignmentsTaskProvider>
|
</AssignmentsTaskProvider>
|
||||||
|
</AssignmentProvider>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,14 @@
|
||||||
import React, { createContext, useContext, useEffect, useReducer } from 'react'
|
import React, { createContext, useContext, useEffect, useReducer } from 'react'
|
||||||
import { useLHSession } from '@components/Contexts/LHSessionContext'
|
import { useLHSession } from '@components/Contexts/LHSessionContext'
|
||||||
import { getAssignmentTask } from '@services/courses/assignments'
|
import { getAssignmentTask } from '@services/courses/assignments'
|
||||||
|
import { useAssignments } from './AssignmentContext';
|
||||||
|
import { mutate } from 'swr';
|
||||||
|
import { getAPIUrl } from '@services/config/config';
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
selectedAssignmentTaskUUID: string | null;
|
selectedAssignmentTaskUUID: string | null;
|
||||||
assignmentTask: Record<string, any>;
|
assignmentTask: Record<string, any>;
|
||||||
|
reloadTrigger: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Action {
|
interface Action {
|
||||||
|
|
@ -15,7 +19,8 @@ interface Action {
|
||||||
|
|
||||||
const initialState: State = {
|
const initialState: State = {
|
||||||
selectedAssignmentTaskUUID: null,
|
selectedAssignmentTaskUUID: null,
|
||||||
assignmentTask: {}
|
assignmentTask: {},
|
||||||
|
reloadTrigger: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const AssignmentsTaskContext = createContext<State | undefined>(undefined);
|
export const AssignmentsTaskContext = createContext<State | undefined>(undefined);
|
||||||
|
|
@ -24,21 +29,26 @@ export const AssignmentsTaskDispatchContext = createContext<React.Dispatch<Actio
|
||||||
export function AssignmentsTaskProvider({ children }: { children: React.ReactNode }) {
|
export function AssignmentsTaskProvider({ children }: { children: React.ReactNode }) {
|
||||||
const session = useLHSession() as any;
|
const session = useLHSession() as any;
|
||||||
const access_token = session?.data?.tokens?.access_token;
|
const access_token = session?.data?.tokens?.access_token;
|
||||||
|
const assignment = useAssignments() as any
|
||||||
|
|
||||||
const [state, dispatch] = useReducer(assignmentstaskReducer, initialState);
|
const [state, dispatch] = useReducer(assignmentstaskReducer, initialState);
|
||||||
|
|
||||||
async function fetchAssignmentTask(assignmentTaskUUID: string) {
|
async function fetchAssignmentTask(assignmentTaskUUID: string) {
|
||||||
const res = await getAssignmentTask(assignmentTaskUUID, access_token);
|
const res = await getAssignmentTask(assignmentTaskUUID, access_token);
|
||||||
|
|
||||||
|
|
||||||
if (res.success) {
|
if (res.success) {
|
||||||
dispatch({ type: 'setAssignmentTask', payload: res });
|
dispatch({ type: 'setAssignmentTask', payload: res.data });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
||||||
if (state.selectedAssignmentTaskUUID) {
|
if (state.selectedAssignmentTaskUUID) {
|
||||||
fetchAssignmentTask(state.selectedAssignmentTaskUUID);
|
fetchAssignmentTask(state.selectedAssignmentTaskUUID);
|
||||||
|
mutate(`${getAPIUrl()}assignments/${assignment.assignment_object?.assignment_uuid}/tasks`);
|
||||||
}
|
}
|
||||||
}, [state.selectedAssignmentTaskUUID]);
|
}, [state.selectedAssignmentTaskUUID, state.reloadTrigger,assignment]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AssignmentsTaskContext.Provider value={state}>
|
<AssignmentsTaskContext.Provider value={state}>
|
||||||
|
|
@ -72,9 +82,8 @@ function assignmentstaskReducer(state: State, action: Action): State {
|
||||||
case 'setAssignmentTask':
|
case 'setAssignmentTask':
|
||||||
return { ...state, assignmentTask: action.payload };
|
return { ...state, assignmentTask: action.payload };
|
||||||
case 'reload':
|
case 'reload':
|
||||||
return { ...state };
|
return { ...state, reloadTrigger: state.reloadTrigger + 1 };
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ export const inputStyles = {
|
||||||
borderRadius: 4,
|
borderRadius: 4,
|
||||||
fontSize: 15,
|
fontSize: 15,
|
||||||
color: '#7c7c7c',
|
color: '#7c7c7c',
|
||||||
background: '#F9FAFB',
|
background: '#fbfdff',
|
||||||
boxShadow: `0 0 0 1px #edeeef`,
|
boxShadow: `0 0 0 1px #edeeef`,
|
||||||
'&:hover': { boxShadow: `0 0 0 1px #edeeef` },
|
'&:hover': { boxShadow: `0 0 0 1px #edeeef` },
|
||||||
'&:focus': { boxShadow: `0 0 0 2px #edeeef` },
|
'&:focus': { boxShadow: `0 0 0 2px #edeeef` },
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { getAPIUrl } from '@services/config/config'
|
import { getAPIUrl } from '@services/config/config'
|
||||||
import {
|
import {
|
||||||
|
RequestBodyFormWithAuthHeader,
|
||||||
RequestBodyWithAuthHeader,
|
RequestBodyWithAuthHeader,
|
||||||
getResponseMetadata,
|
getResponseMetadata,
|
||||||
} from '@services/utils/ts/requests'
|
} from '@services/utils/ts/requests'
|
||||||
|
|
@ -76,3 +77,38 @@ export async function getAssignmentTask(
|
||||||
const res = await getResponseMetadata(result)
|
const res = await getResponseMetadata(result)
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function updateAssignmentTask(
|
||||||
|
body: any,
|
||||||
|
assignmentTaskUUID: string,
|
||||||
|
assignmentUUID: string,
|
||||||
|
access_token: string
|
||||||
|
) {
|
||||||
|
const result: any = await fetch(
|
||||||
|
`${getAPIUrl()}assignments/${assignmentUUID}/tasks/${assignmentTaskUUID}`,
|
||||||
|
RequestBodyWithAuthHeader('PUT', body, null, access_token)
|
||||||
|
)
|
||||||
|
const res = await getResponseMetadata(result)
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateReferenceFile(
|
||||||
|
file: any,
|
||||||
|
assignmentTaskUUID: string,
|
||||||
|
assignmentUUID: string,
|
||||||
|
access_token: string
|
||||||
|
) {
|
||||||
|
|
||||||
|
// Send file thumbnail as form data
|
||||||
|
const formData = new FormData()
|
||||||
|
|
||||||
|
if (file) {
|
||||||
|
formData.append('reference_file', file)
|
||||||
|
}
|
||||||
|
const result: any = await fetch(
|
||||||
|
`${getAPIUrl()}assignments/${assignmentUUID}/tasks/${assignmentTaskUUID}/ref_file`,
|
||||||
|
RequestBodyFormWithAuthHeader('POST', formData, null, access_token)
|
||||||
|
)
|
||||||
|
const res = await getResponseMetadata(result)
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,19 @@ export function getActivityBlockMediaDirectory(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getTaskRefFileDir(
|
||||||
|
orgUUID: string,
|
||||||
|
courseId: string,
|
||||||
|
activityId: string,
|
||||||
|
assignmentUUID: string,
|
||||||
|
assignmentTaskUUID: string,
|
||||||
|
fileID : string
|
||||||
|
|
||||||
|
) {
|
||||||
|
let uri = `${getMediaUrl()}content/orgs/${orgUUID}/courses/${courseId}/activities/${activityId}/assignments/${assignmentUUID}/tasks/${assignmentTaskUUID}/${fileID}`
|
||||||
|
return uri
|
||||||
|
}
|
||||||
|
|
||||||
export function getActivityMediaDirectory(
|
export function getActivityMediaDirectory(
|
||||||
orgUUID: string,
|
orgUUID: string,
|
||||||
courseId: string,
|
courseId: string,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue