mirror of
https://github.com/rzmk/learnhouse.git
synced 2025-12-19 04:19:25 +00:00
feat: create and delete assignment activities from UI
This commit is contained in:
parent
04c05e4f9a
commit
10e9be1d33
14 changed files with 358 additions and 14 deletions
|
|
@ -1,7 +1,7 @@
|
||||||
import importlib
|
import importlib
|
||||||
from logging.config import fileConfig
|
from logging.config import fileConfig
|
||||||
import os
|
import os
|
||||||
|
import alembic_postgresql_enum
|
||||||
from sqlalchemy import engine_from_config
|
from sqlalchemy import engine_from_config
|
||||||
from sqlalchemy import pool
|
from sqlalchemy import pool
|
||||||
from sqlmodel import SQLModel
|
from sqlmodel import SQLModel
|
||||||
|
|
|
||||||
41
apps/api/migrations/versions/6295e05ff7d0_enum_updates.py
Normal file
41
apps/api/migrations/versions/6295e05ff7d0_enum_updates.py
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
"""Enum updates
|
||||||
|
|
||||||
|
Revision ID: 6295e05ff7d0
|
||||||
|
Revises: df2981bf24dd
|
||||||
|
Create Date: 2024-07-11 20:46:26.582170
|
||||||
|
|
||||||
|
"""
|
||||||
|
from typing import Sequence, Union
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
import sqlmodel
|
||||||
|
from alembic_postgresql_enum import TableReference # type: ignore
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision: str = '6295e05ff7d0'
|
||||||
|
down_revision: Union[str, None] = 'df2981bf24dd'
|
||||||
|
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.sync_enum_values('public', 'activitytypeenum', ['TYPE_VIDEO', 'TYPE_DOCUMENT', 'TYPE_DYNAMIC', 'TYPE_ASSIGNMENT', 'TYPE_CUSTOM'],
|
||||||
|
[TableReference(table_schema='public', table_name='activity', column_name='activity_type')],
|
||||||
|
enum_values_to_rename=[])
|
||||||
|
op.sync_enum_values('public', 'activitysubtypeenum', ['SUBTYPE_DYNAMIC_PAGE', 'SUBTYPE_VIDEO_YOUTUBE', 'SUBTYPE_VIDEO_HOSTED', 'SUBTYPE_DOCUMENT_PDF', 'SUBTYPE_DOCUMENT_DOC', 'SUBTYPE_ASSIGNMENT_ANY', 'SUBTYPE_CUSTOM'],
|
||||||
|
[TableReference(table_schema='public', table_name='activity', column_name='activity_sub_type')],
|
||||||
|
enum_values_to_rename=[])
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.sync_enum_values('public', 'activitysubtypeenum', ['SUBTYPE_DYNAMIC_PAGE', 'SUBTYPE_VIDEO_YOUTUBE', 'SUBTYPE_VIDEO_HOSTED', 'SUBTYPE_DOCUMENT_PDF', 'SUBTYPE_DOCUMENT_DOC', 'SUBTYPE_ASSESSMENT_QUIZ', 'SUBTYPE_CUSTOM'],
|
||||||
|
[TableReference(table_schema='public', table_name='activity', column_name='activity_sub_type')],
|
||||||
|
enum_values_to_rename=[])
|
||||||
|
op.sync_enum_values('public', 'activitytypeenum', ['TYPE_VIDEO', 'TYPE_DOCUMENT', 'TYPE_DYNAMIC', 'TYPE_ASSESSMENT', 'TYPE_CUSTOM'],
|
||||||
|
[TableReference(table_schema='public', table_name='activity', column_name='activity_type')],
|
||||||
|
enum_values_to_rename=[])
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
@ -23,6 +23,9 @@ depends_on: Union[str, Sequence[str], None] = None
|
||||||
def upgrade() -> None:
|
def upgrade() -> None:
|
||||||
# ### commands auto generated by Alembic - please adjust! ###
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
op.add_column('activity', sa.Column('published', sa.Boolean(), nullable=False, server_default=sa.true()))
|
op.add_column('activity', sa.Column('published', sa.Boolean(), nullable=False, server_default=sa.true()))
|
||||||
|
# If you need to rename columns instead of dropping them, use the rename_column command
|
||||||
|
# For example, if we are changing the name 'published_version' to 'published', we would use:
|
||||||
|
# op.alter_column('activity', 'published_version', new_column_name='published', existing_type=sa.Boolean())
|
||||||
op.drop_column('activity', 'published_version')
|
op.drop_column('activity', 'published_version')
|
||||||
op.drop_column('activity', 'version')
|
op.drop_column('activity', 'version')
|
||||||
|
|
||||||
|
|
@ -32,7 +35,6 @@ def upgrade() -> None:
|
||||||
op.create_foreign_key('trail_org_id_fkey', 'trail', 'organization', ['org_id'], ['id'], ondelete='CASCADE')
|
op.create_foreign_key('trail_org_id_fkey', 'trail', 'organization', ['org_id'], ['id'], ondelete='CASCADE')
|
||||||
op.drop_constraint('trail_user_id_fkey', 'trail', type_='foreignkey')
|
op.drop_constraint('trail_user_id_fkey', 'trail', type_='foreignkey')
|
||||||
op.create_foreign_key('trail_user_id_fkey', 'trail', 'user', ['user_id'], ['id'], ondelete='CASCADE')
|
op.create_foreign_key('trail_user_id_fkey', 'trail', 'user', ['user_id'], ['id'], ondelete='CASCADE')
|
||||||
|
|
||||||
# ### end Alembic commands ###
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -45,7 +47,8 @@ def downgrade() -> None:
|
||||||
|
|
||||||
op.add_column('assignmentusersubmission', sa.Column('assignment_user_uuid', sa.VARCHAR(), autoincrement=False, nullable=False))
|
op.add_column('assignmentusersubmission', sa.Column('assignment_user_uuid', sa.VARCHAR(), autoincrement=False, nullable=False))
|
||||||
|
|
||||||
op.add_column('activity', sa.Column('version', sa.INTEGER(), autoincrement=False, nullable=False , server_default=sa.text('1')))
|
op.add_column('activity', sa.Column('version', sa.INTEGER(), autoincrement=False, nullable=False, server_default=sa.text('1')))
|
||||||
op.add_column('activity', sa.Column('published_version', sa.INTEGER(), autoincrement=False, nullable=False , server_default=sa.text('1')) )
|
op.add_column('activity', sa.Column('published_version', sa.INTEGER(), autoincrement=False, nullable=False, server_default=sa.text('1')))
|
||||||
op.drop_column('activity', 'published')
|
op.drop_column('activity', 'published')
|
||||||
# ### end Alembic commands ###
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
|
||||||
45
apps/api/poetry.lock
generated
45
apps/api/poetry.lock
generated
|
|
@ -128,6 +128,21 @@ typing-extensions = ">=4"
|
||||||
[package.extras]
|
[package.extras]
|
||||||
tz = ["backports.zoneinfo"]
|
tz = ["backports.zoneinfo"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "alembic-postgresql-enum"
|
||||||
|
version = "1.2.0"
|
||||||
|
description = "Alembic autogenerate support for creation, alteration and deletion of enums"
|
||||||
|
optional = false
|
||||||
|
python-versions = "<4.0,>=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "alembic_postgresql_enum-1.2.0-py3-none-any.whl", hash = "sha256:bd156e882a10c680fc88ebad25cfe78ccf9f826dec89670f8aeb28e5359e502b"},
|
||||||
|
{file = "alembic_postgresql_enum-1.2.0.tar.gz", hash = "sha256:971bd3a4c35ea38869bb5e263ea79e5b4a9c4a02f174a3dd7ddcb29d41260cba"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
alembic = ">=1.7"
|
||||||
|
SQLAlchemy = ">=1.4"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyio"
|
name = "anyio"
|
||||||
version = "4.4.0"
|
version = "4.4.0"
|
||||||
|
|
@ -3018,6 +3033,34 @@ postgresql-psycopgbinary = ["psycopg[binary] (>=3.0.7)"]
|
||||||
pymysql = ["pymysql"]
|
pymysql = ["pymysql"]
|
||||||
sqlcipher = ["sqlcipher3_binary"]
|
sqlcipher = ["sqlcipher3_binary"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sqlalchemy-utils"
|
||||||
|
version = "0.41.2"
|
||||||
|
description = "Various utility functions for SQLAlchemy."
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "SQLAlchemy-Utils-0.41.2.tar.gz", hash = "sha256:bc599c8c3b3319e53ce6c5c3c471120bd325d0071fb6f38a10e924e3d07b9990"},
|
||||||
|
{file = "SQLAlchemy_Utils-0.41.2-py3-none-any.whl", hash = "sha256:85cf3842da2bf060760f955f8467b87983fb2e30f1764fd0e24a48307dc8ec6e"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
SQLAlchemy = ">=1.3"
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
arrow = ["arrow (>=0.3.4)"]
|
||||||
|
babel = ["Babel (>=1.3)"]
|
||||||
|
color = ["colour (>=0.0.4)"]
|
||||||
|
encrypted = ["cryptography (>=0.6)"]
|
||||||
|
intervals = ["intervals (>=0.7.1)"]
|
||||||
|
password = ["passlib (>=1.6,<2.0)"]
|
||||||
|
pendulum = ["pendulum (>=2.0.5)"]
|
||||||
|
phone = ["phonenumbers (>=5.9.2)"]
|
||||||
|
test = ["Jinja2 (>=2.3)", "Pygments (>=1.2)", "backports.zoneinfo", "docutils (>=0.10)", "flake8 (>=2.4.0)", "flexmock (>=0.9.7)", "isort (>=4.2.2)", "pg8000 (>=1.12.4)", "psycopg (>=3.1.8)", "psycopg2 (>=2.5.1)", "psycopg2cffi (>=2.8.1)", "pymysql", "pyodbc", "pytest (==7.4.4)", "python-dateutil (>=2.6)", "pytz (>=2014.2)"]
|
||||||
|
test-all = ["Babel (>=1.3)", "Jinja2 (>=2.3)", "Pygments (>=1.2)", "arrow (>=0.3.4)", "backports.zoneinfo", "colour (>=0.0.4)", "cryptography (>=0.6)", "docutils (>=0.10)", "flake8 (>=2.4.0)", "flexmock (>=0.9.7)", "furl (>=0.4.1)", "intervals (>=0.7.1)", "isort (>=4.2.2)", "passlib (>=1.6,<2.0)", "pendulum (>=2.0.5)", "pg8000 (>=1.12.4)", "phonenumbers (>=5.9.2)", "psycopg (>=3.1.8)", "psycopg2 (>=2.5.1)", "psycopg2cffi (>=2.8.1)", "pymysql", "pyodbc", "pytest (==7.4.4)", "python-dateutil", "python-dateutil (>=2.6)", "pytz (>=2014.2)"]
|
||||||
|
timezone = ["python-dateutil"]
|
||||||
|
url = ["furl (>=0.4.1)"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sqlmodel"
|
name = "sqlmodel"
|
||||||
version = "0.0.19"
|
version = "0.0.19"
|
||||||
|
|
@ -3871,4 +3914,4 @@ test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools",
|
||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "2.0"
|
lock-version = "2.0"
|
||||||
python-versions = "^3.12"
|
python-versions = "^3.12"
|
||||||
content-hash = "76c4defc807fe83375766ac085982a2edf16e57b2d092a4494021f00a0424a4c"
|
content-hash = "49d72c6871e3ecffae3b55ccad3a6b140f9a1ebbca84d7632dafd54e1d2b7f9d"
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,8 @@ uvicorn = "0.30.1"
|
||||||
typer = "^0.12.3"
|
typer = "^0.12.3"
|
||||||
chromadb = "^0.5.3"
|
chromadb = "^0.5.3"
|
||||||
alembic = "^1.13.2"
|
alembic = "^1.13.2"
|
||||||
|
alembic-postgresql-enum = "^1.2.0"
|
||||||
|
sqlalchemy-utils = "^0.41.2"
|
||||||
|
|
||||||
[build-system]
|
[build-system]
|
||||||
build-backend = "poetry.core.masonry.api"
|
build-backend = "poetry.core.masonry.api"
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,12 @@ from sqlmodel import Field, SQLModel
|
||||||
|
|
||||||
class CollectionCourse(SQLModel, table=True):
|
class CollectionCourse(SQLModel, table=True):
|
||||||
id: Optional[int] = Field(default=None, primary_key=True)
|
id: Optional[int] = Field(default=None, primary_key=True)
|
||||||
collection_id: int = Field(sa_column=Column(Integer, ForeignKey("collection.id", ondelete="CASCADE")))
|
collection_id: int = Field(
|
||||||
course_id: int = Field(sa_column=Column(Integer, ForeignKey("course.id", ondelete="CASCADE")))
|
sa_column=Column(Integer, ForeignKey("collection.id", ondelete="CASCADE"))
|
||||||
|
)
|
||||||
|
course_id: int = Field(
|
||||||
|
sa_column=Column(Integer, ForeignKey("course.id", ondelete="CASCADE"))
|
||||||
|
)
|
||||||
org_id: int = Field(default=None, foreign_key="organization.id")
|
org_id: int = Field(default=None, foreign_key="organization.id")
|
||||||
creation_date: str
|
creation_date: str
|
||||||
update_date: str
|
update_date: str
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,12 @@ class TrailBase(SQLModel):
|
||||||
|
|
||||||
class Trail(TrailBase, table=True):
|
class Trail(TrailBase, table=True):
|
||||||
id: Optional[int] = Field(default=None, primary_key=True)
|
id: Optional[int] = Field(default=None, primary_key=True)
|
||||||
|
org_id: int = Field(
|
||||||
|
sa_column=Column(Integer, ForeignKey("organization.id", ondelete="CASCADE"))
|
||||||
|
)
|
||||||
|
user_id: int = Field(
|
||||||
|
sa_column=Column(Integer, ForeignKey("user.id", ondelete="CASCADE"))
|
||||||
|
)
|
||||||
trail_uuid: str = ""
|
trail_uuid: str = ""
|
||||||
creation_date: str = ""
|
creation_date: str = ""
|
||||||
update_date: str = ""
|
update_date: str = ""
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ from src.services.courses.activities.assignments import (
|
||||||
create_assignment_task,
|
create_assignment_task,
|
||||||
create_assignment_task_submission,
|
create_assignment_task_submission,
|
||||||
delete_assignment,
|
delete_assignment,
|
||||||
|
delete_assignment_from_activity_uuid,
|
||||||
delete_assignment_submission,
|
delete_assignment_submission,
|
||||||
delete_assignment_task,
|
delete_assignment_task,
|
||||||
delete_assignment_task_submission,
|
delete_assignment_task_submission,
|
||||||
|
|
@ -90,6 +91,18 @@ 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}")
|
||||||
|
async def api_delete_assignment_from_activity(
|
||||||
|
request: Request,
|
||||||
|
activity_uuid: str,
|
||||||
|
current_user: PublicUser = Depends(get_current_user),
|
||||||
|
db_session=Depends(get_db_session),
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Delete an assignment
|
||||||
|
"""
|
||||||
|
return await delete_assignment_from_activity_uuid(request, activity_uuid, current_user, db_session)
|
||||||
|
|
||||||
|
|
||||||
## ASSIGNMENTS Tasks ##
|
## ASSIGNMENTS Tasks ##
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ from uuid import uuid4
|
||||||
from fastapi import HTTPException, Request
|
from fastapi import HTTPException, Request
|
||||||
from sqlmodel import Session, select
|
from sqlmodel import Session, select
|
||||||
|
|
||||||
|
from src.db.courses.activities import Activity
|
||||||
from src.db.courses.assignments import (
|
from src.db.courses.assignments import (
|
||||||
Assignment,
|
Assignment,
|
||||||
AssignmentCreate,
|
AssignmentCreate,
|
||||||
|
|
@ -184,6 +185,53 @@ async def delete_assignment(
|
||||||
|
|
||||||
return {"message": "Assignment deleted"}
|
return {"message": "Assignment deleted"}
|
||||||
|
|
||||||
|
async def delete_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, "delete", db_session)
|
||||||
|
|
||||||
|
# Delete Assignment
|
||||||
|
db_session.delete(assignment)
|
||||||
|
|
||||||
|
db_session.commit()
|
||||||
|
|
||||||
|
return {"message": "Assignment deleted"}
|
||||||
|
|
||||||
|
|
||||||
## > Assignments Tasks CRUD
|
## > Assignments Tasks CRUD
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ import { useRouter } from 'next/navigation'
|
||||||
import React from 'react'
|
import React 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'
|
||||||
|
|
||||||
type ActivitiyElementProps = {
|
type ActivitiyElementProps = {
|
||||||
orgslug: string
|
orgslug: string
|
||||||
|
|
@ -45,6 +46,11 @@ function ActivityElement(props: ActivitiyElementProps) {
|
||||||
const activityUUID = props.activity.activity_uuid
|
const activityUUID = props.activity.activity_uuid
|
||||||
|
|
||||||
async function deleteActivityUI() {
|
async function deleteActivityUI() {
|
||||||
|
// Assignments
|
||||||
|
if(props.activity.activity_type === 'TYPE_ASSIGNMENT') {
|
||||||
|
await deleteAssignmentUsingActivityUUID(props.activity.activity_uuid, access_token)
|
||||||
|
}
|
||||||
|
|
||||||
await deleteActivity(props.activity.activity_uuid, access_token)
|
await deleteActivity(props.activity.activity_uuid, access_token)
|
||||||
mutate(`${getAPIUrl()}courses/${props.course_uuid}/meta`)
|
mutate(`${getAPIUrl()}courses/${props.course_uuid}/meta`)
|
||||||
await revalidateTags(['courses'], props.orgslug)
|
await revalidateTags(['courses'], props.orgslug)
|
||||||
|
|
|
||||||
|
|
@ -102,9 +102,10 @@ function NewActivityModal({
|
||||||
|
|
||||||
{selectedView === 'assignments' && (
|
{selectedView === 'assignments' && (
|
||||||
<Assignment
|
<Assignment
|
||||||
submitFileActivity={submitFileActivity}
|
submitActivity={submitActivity}
|
||||||
chapterId={chapterId}
|
chapterId={chapterId}
|
||||||
course={course}
|
course={course}
|
||||||
|
closeModal={closeModal}
|
||||||
/>)
|
/>)
|
||||||
}
|
}
|
||||||
</>
|
</>
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,153 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
import FormLayout, {
|
||||||
|
ButtonBlack,
|
||||||
|
Flex,
|
||||||
|
FormField,
|
||||||
|
FormLabel,
|
||||||
|
FormMessage,
|
||||||
|
Input,
|
||||||
|
|
||||||
function Assignment() {
|
} from '@components/StyledElements/Form/Form'
|
||||||
return (
|
import * as Form from '@radix-ui/react-form'
|
||||||
<div>Assignment</div>
|
import { BarLoader } from 'react-spinners'
|
||||||
)
|
import { useOrg } from '@components/Contexts/OrgContext'
|
||||||
|
import { getAPIUrl } from '@services/config/config'
|
||||||
|
import { mutate } from 'swr'
|
||||||
|
import { createAssignment } from '@services/courses/assignments'
|
||||||
|
import { useLHSession } from '@components/Contexts/LHSessionContext'
|
||||||
|
import { createActivity } from '@services/courses/activities'
|
||||||
|
|
||||||
|
function NewAssignment({ submitActivity, chapterId, course, closeModal }: any) {
|
||||||
|
const org = useOrg() as any;
|
||||||
|
const session = useLHSession() as any
|
||||||
|
const [activityName, setActivityName] = React.useState('')
|
||||||
|
const [isSubmitting, setIsSubmitting] = React.useState(false)
|
||||||
|
const [activityDescription, setActivityDescription] = React.useState('')
|
||||||
|
const [dueDate, setDueDate] = React.useState('')
|
||||||
|
const [gradingType, setGradingType] = React.useState('ALPHABET')
|
||||||
|
|
||||||
|
const handleNameChange = (e: any) => {
|
||||||
|
setActivityName(e.target.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleDescriptionChange = (e: any) => {
|
||||||
|
setActivityDescription(e.target.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleDueDateChange = (e: any) => {
|
||||||
|
setDueDate(e.target.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleGradingTypeChange = (e: any) => {
|
||||||
|
setGradingType(e.target.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSubmit = async (e: any) => {
|
||||||
|
e.preventDefault()
|
||||||
|
setIsSubmitting(true)
|
||||||
|
const activity = {
|
||||||
|
name: activityName,
|
||||||
|
chapter_id: chapterId,
|
||||||
|
activity_type: 'TYPE_ASSIGNMENT',
|
||||||
|
activity_sub_type: 'SUBTYPE_ASSIGNMENT_ANY',
|
||||||
|
published: false,
|
||||||
|
course_id: course?.courseStructure.id,
|
||||||
|
}
|
||||||
|
|
||||||
|
const activity_res = await createActivity(activity, chapterId, org?.id, session.data?.tokens?.access_token)
|
||||||
|
console.log(course)
|
||||||
|
console.log(activity_res)
|
||||||
|
await createAssignment({
|
||||||
|
title: activityName,
|
||||||
|
description: activityDescription,
|
||||||
|
due_date: dueDate,
|
||||||
|
grading_type: gradingType,
|
||||||
|
course_id: course?.courseStructure.id,
|
||||||
|
org_id: org?.id,
|
||||||
|
chapter_id: chapterId,
|
||||||
|
activity_id: activity_res?.id,
|
||||||
|
}, session.data?.tokens?.access_token)
|
||||||
|
|
||||||
|
mutate(`${getAPIUrl()}courses/${course.courseStructure.course_uuid}/meta`)
|
||||||
|
setIsSubmitting(false)
|
||||||
|
closeModal()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FormLayout onSubmit={handleSubmit}>
|
||||||
|
<FormField name="assignment-activity-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={handleNameChange} type="text" required />
|
||||||
|
</Form.Control>
|
||||||
|
</FormField>
|
||||||
|
|
||||||
|
{/* Description */}
|
||||||
|
<FormField name="assignment-activity-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>
|
||||||
|
<Input onChange={handleDescriptionChange} type="text" required />
|
||||||
|
</Form.Control>
|
||||||
|
</FormField>
|
||||||
|
|
||||||
|
{/* Due date */}
|
||||||
|
<FormField name="assignment-activity-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 onChange={handleDueDateChange} type="date" required />
|
||||||
|
</Form.Control>
|
||||||
|
</FormField>
|
||||||
|
|
||||||
|
{/* Grading type */}
|
||||||
|
<FormField name="assignment-activity-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>
|
||||||
|
<Form.Control asChild>
|
||||||
|
<select className='bg-gray-100/40 rounded-lg px-1 py-2 outline outline-1 outline-gray-100' onChange={handleGradingTypeChange} required>
|
||||||
|
<option value="ALPHABET">Alphabet</option>
|
||||||
|
<option value="NUMERIC">Numeric</option>
|
||||||
|
<option value="PERCENTAGE">Percentage</option>
|
||||||
|
</select>
|
||||||
|
</Form.Control>
|
||||||
|
</FormField>
|
||||||
|
|
||||||
|
<Flex css={{ marginTop: 25, justifyContent: 'flex-end' }}>
|
||||||
|
<Form.Submit asChild>
|
||||||
|
<ButtonBlack type="submit" css={{ marginTop: 10 }}>
|
||||||
|
{isSubmitting ? (
|
||||||
|
<BarLoader
|
||||||
|
cssOverride={{ borderRadius: 60 }}
|
||||||
|
width={60}
|
||||||
|
color="#ffffff"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
'Create activity'
|
||||||
|
)}
|
||||||
|
</ButtonBlack>
|
||||||
|
</Form.Submit>
|
||||||
|
</Flex>
|
||||||
|
</FormLayout>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Assignment
|
export default NewAssignment
|
||||||
33
apps/web/services/courses/assignments.ts
Normal file
33
apps/web/services/courses/assignments.ts
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
import { getAPIUrl } from '@services/config/config'
|
||||||
|
import {
|
||||||
|
RequestBodyWithAuthHeader,
|
||||||
|
getResponseMetadata,
|
||||||
|
} from '@services/utils/ts/requests'
|
||||||
|
|
||||||
|
export async function createAssignment(body: any, access_token: string) {
|
||||||
|
const result: any = await fetch(
|
||||||
|
`${getAPIUrl()}assignments`,
|
||||||
|
RequestBodyWithAuthHeader('POST', body, null, access_token)
|
||||||
|
)
|
||||||
|
const res = await getResponseMetadata(result)
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete an assignment
|
||||||
|
export async function deleteAssignment(assignmentUUID: string, access_token: string) {
|
||||||
|
const result: any = await fetch(
|
||||||
|
`${getAPIUrl()}assignments/${assignmentUUID}`,
|
||||||
|
RequestBodyWithAuthHeader('DELETE', null, null, access_token)
|
||||||
|
)
|
||||||
|
const res = await getResponseMetadata(result)
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteAssignmentUsingActivityUUID(activityUUID: string, access_token: string) {
|
||||||
|
const result: any = await fetch(
|
||||||
|
`${getAPIUrl()}assignments/activity/${activityUUID}`,
|
||||||
|
RequestBodyWithAuthHeader('DELETE', null, null, access_token)
|
||||||
|
)
|
||||||
|
const res = await getResponseMetadata(result)
|
||||||
|
return res
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue