mirror of
https://github.com/rzmk/learnhouse.git
synced 2025-12-19 04:19:25 +00:00
feat: init assignments UI and fix bugs
This commit is contained in:
parent
10e9be1d33
commit
6a4e16ec29
16 changed files with 436 additions and 47 deletions
|
|
@ -0,0 +1,31 @@
|
||||||
|
"""Add reference for AssignmentTasks
|
||||||
|
|
||||||
|
Revision ID: d8bc71595932
|
||||||
|
Revises: 6295e05ff7d0
|
||||||
|
Create Date: 2024-07-12 18:59:50.242716
|
||||||
|
|
||||||
|
"""
|
||||||
|
from typing import Sequence, Union
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
import sqlmodel
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision: str = 'd8bc71595932'
|
||||||
|
down_revision: Union[str, None] = '6295e05ff7d0'
|
||||||
|
branch_labels: Union[str, Sequence[str], None] = None
|
||||||
|
depends_on: Union[str, Sequence[str], None] = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.add_column('assignmenttask', sa.Column('reference_file', sa.VARCHAR(), nullable=True))
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.drop_column('assignmenttask', 'reference_file')
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
@ -29,8 +29,8 @@ class ActivitySubTypeEnum(str, Enum):
|
||||||
|
|
||||||
class ActivityBase(SQLModel):
|
class ActivityBase(SQLModel):
|
||||||
name: str
|
name: str
|
||||||
activity_type: ActivityTypeEnum = ActivityTypeEnum.TYPE_CUSTOM
|
activity_type: ActivityTypeEnum
|
||||||
activity_sub_type: ActivitySubTypeEnum = ActivitySubTypeEnum.SUBTYPE_CUSTOM
|
activity_sub_type: ActivitySubTypeEnum
|
||||||
content: dict = Field(default={}, sa_column=Column(JSON))
|
content: dict = Field(default={}, sa_column=Column(JSON))
|
||||||
published: bool = False
|
published: bool = False
|
||||||
|
|
||||||
|
|
@ -51,12 +51,16 @@ class Activity(ActivityBase, table=True):
|
||||||
|
|
||||||
class ActivityCreate(ActivityBase):
|
class ActivityCreate(ActivityBase):
|
||||||
chapter_id: int
|
chapter_id: int
|
||||||
|
activity_type: ActivityTypeEnum = ActivityTypeEnum.TYPE_CUSTOM
|
||||||
|
activity_sub_type: ActivitySubTypeEnum = ActivitySubTypeEnum.SUBTYPE_CUSTOM
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class ActivityUpdate(ActivityBase):
|
class ActivityUpdate(ActivityBase):
|
||||||
name: Optional[str]
|
name: Optional[str]
|
||||||
content: dict = Field(default={}, sa_column=Column(JSON))
|
content: dict = Field(default={}, sa_column=Column(JSON))
|
||||||
|
activity_type: Optional[ActivityTypeEnum]
|
||||||
|
activity_sub_type: Optional[ActivitySubTypeEnum]
|
||||||
published_version: Optional[int]
|
published_version: Optional[int]
|
||||||
version: Optional[int]
|
version: Optional[int]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -97,6 +97,7 @@ class AssignmentTaskBase(SQLModel):
|
||||||
title: str
|
title: str
|
||||||
description: str
|
description: str
|
||||||
hint: str
|
hint: str
|
||||||
|
reference_file: Optional[str]
|
||||||
assignment_type: AssignmentTaskTypeEnum
|
assignment_type: AssignmentTaskTypeEnum
|
||||||
contents: Dict = Field(default={}, sa_column=Column(JSON))
|
contents: Dict = Field(default={}, sa_column=Column(JSON))
|
||||||
max_grade_value: int = 0 # Value is always between 0-100
|
max_grade_value: int = 0 # Value is always between 0-100
|
||||||
|
|
@ -108,7 +109,7 @@ class AssignmentTaskBase(SQLModel):
|
||||||
activity_id: int
|
activity_id: int
|
||||||
|
|
||||||
|
|
||||||
class AssignmentTaskCreate(AssignmentTaskBase ):
|
class AssignmentTaskCreate(AssignmentTaskBase):
|
||||||
"""Model for creating a new assignment task."""
|
"""Model for creating a new assignment task."""
|
||||||
|
|
||||||
pass # Inherits all fields from AssignmentTaskBase
|
pass # Inherits all fields from AssignmentTaskBase
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ from src.services.courses.activities.assignments import (
|
||||||
delete_assignment_task,
|
delete_assignment_task,
|
||||||
delete_assignment_task_submission,
|
delete_assignment_task_submission,
|
||||||
read_assignment,
|
read_assignment,
|
||||||
|
read_assignment_from_activity_uuid,
|
||||||
read_assignment_submissions,
|
read_assignment_submissions,
|
||||||
read_assignment_task_submissions,
|
read_assignment_task_submissions,
|
||||||
read_assignment_tasks,
|
read_assignment_tasks,
|
||||||
|
|
@ -62,6 +63,18 @@ 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}")
|
||||||
|
async def api_read_assignment_from_activity(
|
||||||
|
request: Request,
|
||||||
|
activity_uuid: str,
|
||||||
|
current_user: PublicUser = Depends(get_current_user),
|
||||||
|
db_session=Depends(get_db_session),
|
||||||
|
) -> AssignmentRead:
|
||||||
|
"""
|
||||||
|
Read an assignment
|
||||||
|
"""
|
||||||
|
return await read_assignment_from_activity_uuid(request, activity_uuid, current_user, db_session)
|
||||||
|
|
||||||
|
|
||||||
@router.put("/{assignment_uuid}")
|
@router.put("/{assignment_uuid}")
|
||||||
async def api_update_assignment(
|
async def api_update_assignment(
|
||||||
|
|
|
||||||
|
|
@ -104,6 +104,48 @@ 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(
|
||||||
|
request: Request,
|
||||||
|
activity_uuid: str,
|
||||||
|
current_user: PublicUser | AnonymousUser,
|
||||||
|
db_session: Session,
|
||||||
|
):
|
||||||
|
# Check if activity exists
|
||||||
|
statement = select(Activity).where(Activity.activity_uuid == activity_uuid)
|
||||||
|
activity = db_session.exec(statement).first()
|
||||||
|
|
||||||
|
if not activity:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=404,
|
||||||
|
detail="Activity not found",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check if course exists
|
||||||
|
statement = select(Course).where(Course.id == activity.course_id)
|
||||||
|
course = db_session.exec(statement).first()
|
||||||
|
|
||||||
|
if not course:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=404,
|
||||||
|
detail="Course not found",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check if assignment exists
|
||||||
|
statement = select(Assignment).where(Assignment.activity_id == activity.id)
|
||||||
|
assignment = db_session.exec(statement).first()
|
||||||
|
|
||||||
|
if not assignment:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=404,
|
||||||
|
detail="Assignment not found",
|
||||||
|
)
|
||||||
|
|
||||||
|
# RBAC check
|
||||||
|
await rbac_check(request, course.course_uuid, current_user, "read", db_session)
|
||||||
|
|
||||||
|
# return assignment read
|
||||||
|
return AssignmentRead.model_validate(assignment)
|
||||||
|
|
||||||
|
|
||||||
async def update_assignment(
|
async def update_assignment(
|
||||||
request: Request,
|
request: Request,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
'use client';
|
||||||
|
import { Info, Link } from 'lucide-react'
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
function AssignmentTaskEditor({ task_uuid, page }: any) {
|
||||||
|
const [selectedSubPage, setSelectedSubPage] = React.useState(page)
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col font-black text-sm w-full z-20">
|
||||||
|
|
||||||
|
<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='font-semibold text-lg py-1'>
|
||||||
|
Assignment Test #1
|
||||||
|
</div>
|
||||||
|
<div className='flex space-x-2 '>
|
||||||
|
<div
|
||||||
|
className={`flex space-x-4 py-2 w-fit text-center border-black transition-all ease-linear ${selectedSubPage === 'overview'
|
||||||
|
? 'border-b-4'
|
||||||
|
: 'opacity-50'
|
||||||
|
} cursor-pointer`}
|
||||||
|
>
|
||||||
|
<div className="flex items-center space-x-2.5 mx-2">
|
||||||
|
<Info size={16} />
|
||||||
|
<div>Overview</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AssignmentTaskEditor
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
import { useAssignments } from '@components/Contexts/Assignments/AssignmentContext'
|
||||||
|
import { Plus } from 'lucide-react';
|
||||||
|
import React, { useEffect } from 'react'
|
||||||
|
|
||||||
|
function AssignmentTasks() {
|
||||||
|
const assignments = useAssignments() as any;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
console.log(assignments)
|
||||||
|
}, [assignments])
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='flex w-full'>
|
||||||
|
<div className='flex flex-col space-y-3 mx-auto'>
|
||||||
|
{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'>
|
||||||
|
<div className='flex justify-between px-2'>
|
||||||
|
<div className='font-semibold text-sm'>{task.title}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
<div className='flex space-x-1.5 px-2 py-2 bg-black text-white text-xs rounded-md antialiased items-center font-semibold cursor-pointer'>
|
||||||
|
<Plus size={17} />
|
||||||
|
<p>Add Task</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AssignmentTasks
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
'use client';
|
||||||
|
import BreadCrumbs from '@components/Dashboard/UI/BreadCrumbs'
|
||||||
|
import AuthenticatedClientElement from '@components/Security/AuthenticatedClientElement'
|
||||||
|
import { BookOpen, BookOpenCheck, BookX, Check, Ellipsis, EllipsisVertical, GalleryVerticalEnd, Info, LayoutList, UserRoundCog } from 'lucide-react'
|
||||||
|
import React from 'react'
|
||||||
|
import AssignmentTaskEditor from './_components/TaskEditor';
|
||||||
|
import { AssignmentProvider } from '@components/Contexts/Assignments/AssignmentContext';
|
||||||
|
import AssignmentTasks from './_components/Tasks';
|
||||||
|
import { useParams } from 'next/navigation';
|
||||||
|
|
||||||
|
function AssignmentEdit() {
|
||||||
|
const params = useParams<{ assignmentuuid: string; }>()
|
||||||
|
return (
|
||||||
|
<div className='flex w-full flex-col'>
|
||||||
|
<div className='pb-5 bg-white z-50 shadow-[0px_4px_16px_rgba(0,0,0,0.06)] nice-shadow'>
|
||||||
|
<div className='flex justify-between mr-10 h-full'>
|
||||||
|
<div className="pl-10 mr-10 tracking-tighter">
|
||||||
|
<BreadCrumbs type="assignments" last_breadcrumb='UUID' />
|
||||||
|
<div className="w-100 flex justify-between">
|
||||||
|
<div className="flex font-bold text-2xl">Assignment Editor</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className='flex flex-col justify-center antialiased'>
|
||||||
|
<div className='flex mx-auto mt-5 items-center space-x-4'>
|
||||||
|
<div className='flex bg-green-200/60 text-xs rounded-full px-3.5 py-2 mx-auto font-bold outline outline-1 outline-green-300'>Published</div>
|
||||||
|
<div><EllipsisVertical className='text-gray-500' size={13} /></div>
|
||||||
|
<div className='flex px-3 py-2 cursor-pointer rounded-md space-x-2 items-center bg-gradient-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>
|
||||||
|
<div className='flex px-3 py-2 cursor-pointer rounded-md space-x-2 items-center bg-gradient-to-bl text-gray-800 font-medium from-gray-400/50 to-gray-200/80 border border-gray-600/10 shadow-gray-900/10 shadow-lg'>
|
||||||
|
<BookX size={18} />
|
||||||
|
<p className='text-sm font-bold'>Unpublish</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex w-full">
|
||||||
|
<div className='flex w-[400px] flex-col h-screen 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'>
|
||||||
|
<LayoutList size={18} />
|
||||||
|
<p>Tasks</p>
|
||||||
|
</div>
|
||||||
|
<AssignmentProvider assignment_uuid='assignment_a35fdbb9-11bd-40cf-a781-f6bdd5d87165'>
|
||||||
|
<AssignmentTasks />
|
||||||
|
</AssignmentProvider>
|
||||||
|
</div>
|
||||||
|
<div className='flex flex-grow bg-[#fefcfe] nice-shadow h-screen w-full'>
|
||||||
|
<AssignmentProvider assignment_uuid={'assignment_' + params.assignmentuuid}>
|
||||||
|
<AssignmentTaskEditor task_uuid='UUID' page='overview' />
|
||||||
|
</AssignmentProvider>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AssignmentEdit
|
||||||
9
apps/web/app/orgs/[orgslug]/dash/assignments/page.tsx
Normal file
9
apps/web/app/orgs/[orgslug]/dash/assignments/page.tsx
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
function AssignmentsHome() {
|
||||||
|
return (
|
||||||
|
<div>AssignmentsHome</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AssignmentsHome
|
||||||
|
|
@ -82,7 +82,6 @@ function CourseOverviewPage({ params }: { params: CourseOverviewParams }) {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
'use client'
|
||||||
|
import { getAPIUrl } from '@services/config/config'
|
||||||
|
import { swrFetcher } from '@services/utils/ts/requests'
|
||||||
|
import React, { createContext, useContext, useEffect } from 'react'
|
||||||
|
import useSWR from 'swr'
|
||||||
|
import { useLHSession } from '@components/Contexts/LHSessionContext'
|
||||||
|
|
||||||
|
export const AssignmentContext = createContext({})
|
||||||
|
|
||||||
|
export function AssignmentProvider({ children, assignment_uuid }: { children: React.ReactNode, assignment_uuid: string }) {
|
||||||
|
const session = useLHSession() as any
|
||||||
|
const accessToken = session?.data?.tokens?.access_token
|
||||||
|
const [assignmentsFull, setAssignmentsFull] = React.useState({ assignment_object: null, assignment_tasks: null })
|
||||||
|
|
||||||
|
const { data: assignment, error: assignmentError } = useSWR(
|
||||||
|
`${getAPIUrl()}assignments/${assignment_uuid}`,
|
||||||
|
(url) => swrFetcher(url, accessToken)
|
||||||
|
)
|
||||||
|
|
||||||
|
const { data: assignment_tasks, error: assignmentTasksError } = useSWR(
|
||||||
|
`${getAPIUrl()}assignments/${assignment_uuid}/tasks`,
|
||||||
|
(url) => swrFetcher(url, accessToken)
|
||||||
|
)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setAssignmentsFull({ assignment_object: assignment, assignment_tasks: assignment_tasks })
|
||||||
|
}
|
||||||
|
, [assignment, assignment_tasks])
|
||||||
|
|
||||||
|
if (assignmentError || assignmentTasksError) return <div></div>
|
||||||
|
|
||||||
|
if (!assignment || !assignment_tasks) return <div></div>
|
||||||
|
|
||||||
|
|
||||||
|
return <AssignmentContext.Provider value={assignmentsFull}>{children}</AssignmentContext.Provider>
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useAssignments() {
|
||||||
|
return useContext(AssignmentContext)
|
||||||
|
}
|
||||||
|
|
@ -3,6 +3,7 @@ import { getAPIUrl, getUriWithOrg } from '@services/config/config'
|
||||||
import { deleteActivity, updateActivity } from '@services/courses/activities'
|
import { deleteActivity, updateActivity } from '@services/courses/activities'
|
||||||
import { revalidateTags } from '@services/utils/ts/requests'
|
import { revalidateTags } from '@services/utils/ts/requests'
|
||||||
import {
|
import {
|
||||||
|
Backpack,
|
||||||
Eye,
|
Eye,
|
||||||
File,
|
File,
|
||||||
FilePenLine,
|
FilePenLine,
|
||||||
|
|
@ -16,10 +17,12 @@ import {
|
||||||
import { useLHSession } from '@components/Contexts/LHSessionContext'
|
import { useLHSession } from '@components/Contexts/LHSessionContext'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { useRouter } from 'next/navigation'
|
import { useRouter } from 'next/navigation'
|
||||||
import React from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
import { Draggable } from 'react-beautiful-dnd'
|
import { Draggable } from 'react-beautiful-dnd'
|
||||||
import { mutate } from 'swr'
|
import { mutate } from 'swr'
|
||||||
import { deleteAssignment, deleteAssignmentUsingActivityUUID } from '@services/courses/assignments'
|
import { deleteAssignment, deleteAssignmentUsingActivityUUID, getAssignmentFromActivityUUID } from '@services/courses/assignments'
|
||||||
|
import { useOrg } from '@components/Contexts/OrgContext'
|
||||||
|
import { useCourse } from '@components/Contexts/CourseContext'
|
||||||
|
|
||||||
type ActivitiyElementProps = {
|
type ActivitiyElementProps = {
|
||||||
orgslug: string
|
orgslug: string
|
||||||
|
|
@ -47,7 +50,7 @@ function ActivityElement(props: ActivitiyElementProps) {
|
||||||
|
|
||||||
async function deleteActivityUI() {
|
async function deleteActivityUI() {
|
||||||
// Assignments
|
// Assignments
|
||||||
if(props.activity.activity_type === 'TYPE_ASSIGNMENT') {
|
if (props.activity.activity_type === 'TYPE_ASSIGNMENT') {
|
||||||
await deleteAssignmentUsingActivityUUID(props.activity.activity_uuid, access_token)
|
await deleteAssignmentUsingActivityUUID(props.activity.activity_uuid, access_token)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -66,8 +69,6 @@ function ActivityElement(props: ActivitiyElementProps) {
|
||||||
let modifiedActivityCopy = {
|
let modifiedActivityCopy = {
|
||||||
name: modifiedActivity.activityName,
|
name: modifiedActivity.activityName,
|
||||||
description: '',
|
description: '',
|
||||||
type: props.activity.type,
|
|
||||||
content: props.activity.content,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await updateActivity(modifiedActivityCopy, activityUUID, access_token)
|
await updateActivity(modifiedActivityCopy, activityUUID, access_token)
|
||||||
|
|
@ -135,29 +136,7 @@ function ActivityElement(props: ActivitiyElementProps) {
|
||||||
</div>
|
</div>
|
||||||
{/* Edit and View Button */}
|
{/* Edit and View Button */}
|
||||||
<div className="flex flex-row space-x-2">
|
<div className="flex flex-row space-x-2">
|
||||||
{props.activity.activity_type === 'TYPE_DYNAMIC' && (
|
<ActivityElementOptions activity={props.activity} />
|
||||||
<>
|
|
||||||
<Link
|
|
||||||
href={
|
|
||||||
getUriWithOrg(props.orgslug, '') +
|
|
||||||
`/course/${props.course_uuid.replace(
|
|
||||||
'course_',
|
|
||||||
''
|
|
||||||
)}/activity/${props.activity.activity_uuid.replace(
|
|
||||||
'activity_',
|
|
||||||
''
|
|
||||||
)}/edit`
|
|
||||||
}
|
|
||||||
prefetch
|
|
||||||
className=" hover:cursor-pointer p-1 px-3 bg-sky-700 rounded-md items-center"
|
|
||||||
target='_blank' // hotfix for an editor prosemirror bug
|
|
||||||
>
|
|
||||||
<div className="text-sky-100 font-bold text-xs flex items-center space-x-1">
|
|
||||||
<FilePenLine size={12} /> <span>Edit Page</span>
|
|
||||||
</div>
|
|
||||||
</Link>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
<Link
|
<Link
|
||||||
href={
|
href={
|
||||||
getUriWithOrg(props.orgslug, '') +
|
getUriWithOrg(props.orgslug, '') +
|
||||||
|
|
@ -227,6 +206,18 @@ const ActivityTypeIndicator = (props: { activityType: string }) => {
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
{props.activityType === 'TYPE_ASSIGNMENT' && (
|
||||||
|
<>
|
||||||
|
<div className="flex space-x-2 items-center">
|
||||||
|
<div className="w-[30px]">
|
||||||
|
<Backpack size={16} />{' '}
|
||||||
|
</div>
|
||||||
|
<div className="text-xs bg-gray-200 text-gray-400 font-bold px-2 py-1 rounded-full">
|
||||||
|
Assignment
|
||||||
|
</div>{' '}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
{props.activityType === 'TYPE_DYNAMIC' && (
|
{props.activityType === 'TYPE_DYNAMIC' && (
|
||||||
<>
|
<>
|
||||||
<div className="flex space-x-2 items-center">
|
<div className="flex space-x-2 items-center">
|
||||||
|
|
@ -240,4 +231,78 @@ const ActivityTypeIndicator = (props: { activityType: string }) => {
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ActivityElementOptions = ({ activity }: any) => {
|
||||||
|
const [assignmentUUID, setAssignmentUUID] = useState('');
|
||||||
|
const org = useOrg() as any;
|
||||||
|
const course = useCourse() as any;
|
||||||
|
const session = useLHSession() as any;
|
||||||
|
const access_token = session?.data?.tokens?.access_token;
|
||||||
|
|
||||||
|
async function getAssignmentUUIDFromActivityUUID(activityUUID: string) {
|
||||||
|
const activity = await getAssignmentFromActivityUUID(activityUUID, access_token);
|
||||||
|
if (activity) {
|
||||||
|
return activity.data.assignment_uuid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchAssignmentUUID = async () => {
|
||||||
|
if (activity.activity_type === 'TYPE_ASSIGNMENT') {
|
||||||
|
const assignment_uuid = await getAssignmentUUIDFromActivityUUID(activity.activity_uuid);
|
||||||
|
setAssignmentUUID(assignment_uuid.replace('assignment_', ''));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
|
||||||
|
console.log(activity)
|
||||||
|
|
||||||
|
fetchAssignmentUUID();
|
||||||
|
}, [activity, course]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{activity.activity_type === 'TYPE_DYNAMIC' && (
|
||||||
|
<>
|
||||||
|
<Link
|
||||||
|
href={
|
||||||
|
getUriWithOrg(org.slug, '') +
|
||||||
|
`/course/${course?.courseStructure.course_uuid.replace(
|
||||||
|
'course_',
|
||||||
|
''
|
||||||
|
)}/activity/${activity.activity_uuid.replace(
|
||||||
|
'activity_',
|
||||||
|
''
|
||||||
|
)}/edit`
|
||||||
|
}
|
||||||
|
prefetch
|
||||||
|
className=" hover:cursor-pointer p-1 px-3 bg-sky-700 rounded-md items-center"
|
||||||
|
target='_blank' // hotfix for an editor prosemirror bug
|
||||||
|
>
|
||||||
|
<div className="text-sky-100 font-bold text-xs flex items-center space-x-1">
|
||||||
|
<FilePenLine size={12} /> <span>Edit Page</span>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{activity.activity_type === 'TYPE_ASSIGNMENT' && assignmentUUID && (
|
||||||
|
<>
|
||||||
|
<Link
|
||||||
|
href={
|
||||||
|
getUriWithOrg(org.slug, '') +
|
||||||
|
`/dash/assignments/${assignmentUUID}`
|
||||||
|
}
|
||||||
|
prefetch
|
||||||
|
className=" hover:cursor-pointer p-1 px-3 bg-teal-700 rounded-md items-center"
|
||||||
|
>
|
||||||
|
<div className="text-sky-100 font-bold text-xs flex items-center space-x-1">
|
||||||
|
<FilePenLine size={12} /> <span>Edit Assignment</span>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default ActivityElement
|
export default ActivityElement
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,16 @@
|
||||||
import { useCourse } from '@components/Contexts/CourseContext'
|
'use client';
|
||||||
import { Book, ChevronRight, School, User, Users } from 'lucide-react'
|
import { useOrg } from '@components/Contexts/OrgContext';
|
||||||
|
import { Backpack, Book, ChevronRight, School, User, Users } from 'lucide-react'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
type BreadCrumbsProps = {
|
type BreadCrumbsProps = {
|
||||||
type: 'courses' | 'user' | 'users' | 'org' | 'orgusers'
|
type: 'courses' | 'user' | 'users' | 'org' | 'orgusers' | 'assignments'
|
||||||
last_breadcrumb?: string
|
last_breadcrumb?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
function BreadCrumbs(props: BreadCrumbsProps) {
|
function BreadCrumbs(props: BreadCrumbsProps) {
|
||||||
const course = useCourse() as any
|
const org = useOrg() as any
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -25,6 +26,15 @@ function BreadCrumbs(props: BreadCrumbsProps) {
|
||||||
) : (
|
) : (
|
||||||
''
|
''
|
||||||
)}
|
)}
|
||||||
|
{props.type == 'assignments' ? (
|
||||||
|
<div className="flex space-x-2 items-center">
|
||||||
|
{' '}
|
||||||
|
<Backpack className="text-gray" size={14}></Backpack>
|
||||||
|
<Link href="/dash/assignments">Assignments</Link>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
''
|
||||||
|
)}
|
||||||
{props.type == 'user' ? (
|
{props.type == 'user' ? (
|
||||||
<div className="flex space-x-2 items-center">
|
<div className="flex space-x-2 items-center">
|
||||||
{' '}
|
{' '}
|
||||||
|
|
@ -64,7 +74,6 @@ function BreadCrumbs(props: BreadCrumbsProps) {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="h-2"></div>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import { useOrg } from '@components/Contexts/OrgContext'
|
||||||
import { signOut } from 'next-auth/react'
|
import { signOut } from 'next-auth/react'
|
||||||
import ToolTip from '@components/StyledElements/Tooltip/Tooltip'
|
import ToolTip from '@components/StyledElements/Tooltip/Tooltip'
|
||||||
import LearnHouseDashboardLogo from '@public/dashLogo.png'
|
import LearnHouseDashboardLogo from '@public/dashLogo.png'
|
||||||
import { BookCopy, Home, LogOut, School, Settings, Users } from 'lucide-react'
|
import { Backpack, BookCopy, Home, LogOut, School, Settings, Users } from 'lucide-react'
|
||||||
import Image from 'next/image'
|
import Image from 'next/image'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import React, { useEffect } from 'react'
|
import React, { useEffect } from 'react'
|
||||||
|
|
@ -96,6 +96,14 @@ function LeftMenu() {
|
||||||
<BookCopy size={18} />
|
<BookCopy size={18} />
|
||||||
</Link>
|
</Link>
|
||||||
</ToolTip>
|
</ToolTip>
|
||||||
|
<ToolTip content={'Assignments'} slateBlack sideOffset={8} side="right">
|
||||||
|
<Link
|
||||||
|
className="bg-white/5 rounded-lg p-2 hover:bg-white/10 transition-all ease-linear"
|
||||||
|
href={`/dash/assignments`}
|
||||||
|
>
|
||||||
|
<Backpack size={18} />
|
||||||
|
</Link>
|
||||||
|
</ToolTip>
|
||||||
<ToolTip content={'Users'} slateBlack sideOffset={8} side="right">
|
<ToolTip content={'Users'} slateBlack sideOffset={8} side="right">
|
||||||
<Link
|
<Link
|
||||||
className="bg-white/5 rounded-lg p-2 hover:bg-white/10 transition-all ease-linear"
|
className="bg-white/5 rounded-lg p-2 hover:bg-white/10 transition-all ease-linear"
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,23 @@ export async function createAssignment(body: any, access_token: string) {
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getAssignmentFromActivityUUID(
|
||||||
|
activityUUID: string,
|
||||||
|
access_token: string
|
||||||
|
) {
|
||||||
|
const result: any = await fetch(
|
||||||
|
`${getAPIUrl()}assignments/activity/${activityUUID}`,
|
||||||
|
RequestBodyWithAuthHeader('GET', null, null, access_token)
|
||||||
|
)
|
||||||
|
const res = await getResponseMetadata(result)
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
// Delete an assignment
|
// Delete an assignment
|
||||||
export async function deleteAssignment(assignmentUUID: string, access_token: string) {
|
export async function deleteAssignment(
|
||||||
|
assignmentUUID: string,
|
||||||
|
access_token: string
|
||||||
|
) {
|
||||||
const result: any = await fetch(
|
const result: any = await fetch(
|
||||||
`${getAPIUrl()}assignments/${assignmentUUID}`,
|
`${getAPIUrl()}assignments/${assignmentUUID}`,
|
||||||
RequestBodyWithAuthHeader('DELETE', null, null, access_token)
|
RequestBodyWithAuthHeader('DELETE', null, null, access_token)
|
||||||
|
|
@ -23,11 +38,29 @@ export async function deleteAssignment(assignmentUUID: string, access_token: str
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function deleteAssignmentUsingActivityUUID(activityUUID: string, access_token: string) {
|
export async function deleteAssignmentUsingActivityUUID(
|
||||||
|
activityUUID: string,
|
||||||
|
access_token: string
|
||||||
|
) {
|
||||||
const result: any = await fetch(
|
const result: any = await fetch(
|
||||||
`${getAPIUrl()}assignments/activity/${activityUUID}`,
|
`${getAPIUrl()}assignments/activity/${activityUUID}`,
|
||||||
RequestBodyWithAuthHeader('DELETE', null, null, access_token)
|
RequestBodyWithAuthHeader('DELETE', null, null, access_token)
|
||||||
)
|
)
|
||||||
const res = await getResponseMetadata(result)
|
const res = await getResponseMetadata(result)
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// tasks
|
||||||
|
|
||||||
|
export async function createAssignmentTask(
|
||||||
|
body: any,
|
||||||
|
assignmentUUID: string,
|
||||||
|
access_token: string
|
||||||
|
) {
|
||||||
|
const result: any = await fetch(
|
||||||
|
`${getAPIUrl()}assignments/${assignmentUUID}/tasks`,
|
||||||
|
RequestBodyWithAuthHeader('POST', body, null, access_token)
|
||||||
|
)
|
||||||
|
const res = await getResponseMetadata(result)
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,15 @@
|
||||||
@apply shadow-md shadow-gray-300/25 outline outline-1 outline-neutral-200/40
|
@apply shadow-md shadow-gray-300/25 outline outline-1 outline-neutral-200/40
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.custom-dots-bg {
|
||||||
|
@apply bg-fixed;
|
||||||
|
background-image: radial-gradient(#4744446b 1px, transparent 1px),
|
||||||
|
radial-gradient(#4744446b 1px, transparent 1px);
|
||||||
|
background-position: 0 0, 25px 25px;
|
||||||
|
background-size: 50px 50px;
|
||||||
|
background-repeat: repeat;
|
||||||
|
}
|
||||||
|
|
||||||
html,
|
html,
|
||||||
body {
|
body {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue