diff --git a/apps/api/migrations/orgconfigs/v0tov1.py b/apps/api/migrations/orgconfigs/v0tov1.py index fd7a537c..b010340f 100644 --- a/apps/api/migrations/orgconfigs/v0tov1.py +++ b/apps/api/migrations/orgconfigs/v0tov1.py @@ -30,7 +30,7 @@ def migrate_v0_to_v1(v0_config): ), }, "usergroups": { - "enabled": True, + "enabled": False, "limit": ( v0_config["GeneralConfig"]["limits"]["max_staff"] if v0_config["GeneralConfig"]["limits"]["limits_enabled"] @@ -46,7 +46,7 @@ def migrate_v0_to_v1(v0_config): ), }, "ai": { - "enabled": v0_config["AIConfig"]["enabled"], + "enabled": False, "limit": ( v0_config["AIConfig"]["limits"]["max_asks"] if v0_config["AIConfig"]["limits"]["limits_enabled"] @@ -54,12 +54,12 @@ def migrate_v0_to_v1(v0_config): ), "model": 'gpt-4o-mini', }, - "assignments": {"enabled": True, "limit": 10}, + "assignments": {"enabled": True, "limit": 5}, "payments": {"enabled": False, "stripe_key": ""}, "discussions": {"enabled": False, "limit": 10}, "analytics": {"enabled": False, "limit": 10}, "collaboration": { - "enabled": v0_config["GeneralConfig"]["collaboration"], + "enabled": False, "limit": 10, }, "api": {"enabled": False, "limit": 10}, diff --git a/apps/api/src/routers/courses/activities/activities.py b/apps/api/src/routers/courses/activities/activities.py index 8ca5877a..3b5bc84a 100644 --- a/apps/api/src/routers/courses/activities/activities.py +++ b/apps/api/src/routers/courses/activities/activities.py @@ -92,17 +92,17 @@ async def api_update_activity( ) -@router.delete("/{activity_id}") +@router.delete("/{activity_uuid}") async def api_delete_activity( request: Request, - activity_id: str, + activity_uuid: str, current_user: PublicUser = Depends(get_current_user), db_session=Depends(get_db_session), ): """ Delete activity by activity_id """ - return await delete_activity(request, activity_id, current_user, db_session) + return await delete_activity(request, activity_uuid, current_user, db_session) # Video activity diff --git a/apps/api/src/security/features_utils/usage.py b/apps/api/src/security/features_utils/usage.py new file mode 100644 index 00000000..3ac11403 --- /dev/null +++ b/apps/api/src/security/features_utils/usage.py @@ -0,0 +1,141 @@ +import redis +from src.db.organization_config import OrganizationConfig +from config.config import get_learnhouse_config +from typing import Literal, TypeAlias +from fastapi import HTTPException +from sqlmodel import Session, select + +FeatureSet: TypeAlias = Literal[ + "ai", + "analytics", + "api", + "assignments", + "collaboration", + "courses", + "discussions", + "members", + "payments", + "storage", + "usergroups", +] + + +def check_limits_with_usage( + feature: FeatureSet, + org_id: int, + db_session: Session, +): + + # Get the Organization Config + statement = select(OrganizationConfig).where(OrganizationConfig.org_id == org_id) + result = db_session.exec(statement) + + org_config = result.first() + + if org_config is None: + raise HTTPException( + status_code=404, + detail="Organization has no config", + ) + + # Check if the Organizations has AI enabled + if org_config.config["features"][feature]["enabled"] == False: + raise HTTPException( + status_code=403, + detail=f"{feature.capitalize()} is not enabled for this organization", + ) + + LH_CONFIG = get_learnhouse_config() + redis_conn_string = LH_CONFIG.redis_config.redis_connection_string + + if not redis_conn_string: + raise HTTPException( + status_code=500, + detail="Redis connection string not found", + ) + + # Connect to Redis + r = redis.Redis.from_url(redis_conn_string) + + # Check limits + feature_limit = org_config.config["features"][feature]["limit"] + + if feature_limit > 0: + # Get the number of feature usage + feature_usage = r.get(f"{feature}_usage:{org_id}") + + # Get a number of feature asks + if feature_usage is None: + feature_usage_count = 0 + else: + feature_usage_count = int(feature_usage) # type: ignore + + # Check if the Number of usage is less than the max_asks limit + if feature_limit <= feature_usage_count: + raise HTTPException( + status_code=403, + detail=f"Usage Limit has been reached for {feature.capitalize()}", + ) + return True + + +def increase_feature_usage( + feature: FeatureSet, + org_id: int, + db_session: Session, +): + LH_CONFIG = get_learnhouse_config() + redis_conn_string = LH_CONFIG.redis_config.redis_connection_string + + if not redis_conn_string: + raise HTTPException( + status_code=500, + detail="Redis connection string not found", + ) + + # Connect to Redis + r = redis.Redis.from_url(redis_conn_string) + + # Get the number of feature usage + feature_usage = r.get(f"{feature}_usage:{org_id}") + + # Get a number of feature asks + if feature_usage is None: + feature_usage_count = 0 + else: + feature_usage_count = int(feature_usage) # type: ignore + + # Increment the feature usage + r.set(f"{feature}_usage:{org_id}", feature_usage_count + 1) + return True + + +def decrease_feature_usage( + feature: FeatureSet, + org_id: int, + db_session: Session, +): + LH_CONFIG = get_learnhouse_config() + redis_conn_string = LH_CONFIG.redis_config.redis_connection_string + + if not redis_conn_string: + raise HTTPException( + status_code=500, + detail="Redis connection string not found", + ) + + # Connect to Redis + r = redis.Redis.from_url(redis_conn_string) + + # Get the number of feature usage + feature_usage = r.get(f"{feature}_usage:{org_id}") + + # Get a number of feature asks + if feature_usage is None: + feature_usage_count = 0 + else: + feature_usage_count = int(feature_usage) # type: ignore + + # Increment the feature usage + r.set(f"{feature}_usage:{org_id}", feature_usage_count - 1) + return True diff --git a/apps/api/src/services/ai/ai.py b/apps/api/src/services/ai/ai.py index 17edab1c..9eec0e84 100644 --- a/apps/api/src/services/ai/ai.py +++ b/apps/api/src/services/ai/ai.py @@ -2,7 +2,10 @@ from fastapi import Depends, HTTPException, Request from sqlmodel import Session, select from src.db.organization_config import OrganizationConfig from src.db.organizations import Organization -from src.services.ai.utils import check_limits_and_config, count_ai_ask +from src.security.features_utils.usage import ( + check_limits_with_usage, + increase_feature_usage, +) from src.db.courses.courses import Course, CourseRead from src.core.events.database import get_db_session from src.db.users import PublicUser @@ -52,9 +55,15 @@ def ai_start_activity_chat_session( statement = select(Organization).where(Organization.id == course.org_id) org = db_session.exec(statement).first() + if not org or org.id is None: + raise HTTPException( + status_code=404, + detail="Organization not found", + ) + # Check limits and usage - check_limits_and_config(db_session, org) # type: ignore - count_ai_ask(org, "increment") # type: ignore + check_limits_with_usage("ai", org.id, db_session) + increase_feature_usage("ai", org.id, db_session) if not activity: raise HTTPException( @@ -147,8 +156,8 @@ def ai_send_activity_chat_message( org = db_session.exec(statement).first() # Check limits and usage - check_limits_and_config(db_session, org) # type: ignore - count_ai_ask(org, "increment") # type: ignore + check_limits_with_usage("ai", course.org_id, db_session) + increase_feature_usage("ai", course.org_id, db_session) if not activity: raise HTTPException( diff --git a/apps/api/src/services/ai/utils.py b/apps/api/src/services/ai/utils.py deleted file mode 100644 index 9a853665..00000000 --- a/apps/api/src/services/ai/utils.py +++ /dev/null @@ -1,114 +0,0 @@ -from typing import Literal -import redis -from fastapi import HTTPException -from sqlmodel import Session, select -from config.config import get_learnhouse_config -from src.db.organization_config import OrganizationConfig -from src.db.organizations import Organization - - -def count_ai_ask( - organization: Organization, - operation: Literal["increment", "decrement"], -): - """ - Count the number of AI asks - """ - - LH_CONFIG = get_learnhouse_config() - redis_conn_string = LH_CONFIG.redis_config.redis_connection_string - - if not redis_conn_string: - raise HTTPException( - status_code=500, - detail="Redis connection string not found", - ) - - # Connect to Redis - r = redis.Redis.from_url(redis_conn_string) - - if not r: - raise HTTPException( - status_code=500, - detail="Could not connect to Redis", - ) - - # Get the number of AI asks - ai_asks = r.get(f"ai_asks:{organization.org_uuid}") - - if ai_asks is None: - ai_asks = 0 - - # Increment or decrement the number of AI asks - if operation == "increment": - ai_asks = int(ai_asks) + 1 - elif operation == "decrement": - ai_asks = int(ai_asks) - 1 - - # Update the number of AI asks - r.set(f"ai_asks:{organization.org_uuid}", ai_asks) - - # Set the expiration time to 30 days - r.expire(f"ai_asks:{organization.org_uuid}", 2592000) - - -def check_limits_and_config(db_session: Session, organization: Organization): - """ - Check the limits and config of an Organization - """ - - # Get the Organization Config - statement = select(OrganizationConfig).where( - OrganizationConfig.org_id == organization.id - ) - result = db_session.exec(statement) - org_config = result.first() - - if org_config is None: - raise HTTPException( - status_code=404, - detail="Organization has no config", - ) - - # Check if the Organizations has AI enabled - if org_config.config["features"]["ai"]["enabled"] == False: - raise HTTPException( - status_code=403, - detail="Organization has AI disabled", - ) - - # Check if the Organization has Limits enabled and if the max_asks limit has been reached - if org_config.config["features"]["ai"]["limit"] > 0: - LH_CONFIG = get_learnhouse_config() - redis_conn_string = LH_CONFIG.redis_config.redis_connection_string - - if not redis_conn_string: - raise HTTPException( - status_code=500, - detail="Redis connection string not found", - ) - - # Connect to Redis - r = redis.Redis.from_url(redis_conn_string) - - if not r: - raise HTTPException( - status_code=500, - detail="Could not connect to Redis", - ) - - # Get the number of AI asks - ai_asks = r.get(f"ai_asks:{organization.org_uuid}") - - # Get a number of AI asks - if ai_asks is None: - ai_asks = 0 - else: - ai_asks = int(ai_asks) - - # Check if the Number of asks is less than the max_asks limit - if org_config.config["features"]["ai"]["limit"] <= ai_asks: - raise HTTPException( - status_code=403, - detail="Organization has reached the max number of AI asks", - ) diff --git a/apps/api/src/services/courses/activities/assignments.py b/apps/api/src/services/courses/activities/assignments.py index a10d5f44..99b59b26 100644 --- a/apps/api/src/services/courses/activities/assignments.py +++ b/apps/api/src/services/courses/activities/assignments.py @@ -28,6 +28,11 @@ from src.db.organizations import Organization from src.db.trail_runs import TrailRun from src.db.trail_steps import TrailStep from src.db.users import AnonymousUser, PublicUser, User +from src.security.features_utils.usage import ( + check_limits_with_usage, + decrease_feature_usage, + increase_feature_usage, +) from src.security.rbac.rbac import ( authorization_verify_based_on_roles_and_authorship_and_usergroups, authorization_verify_if_element_is_public, @@ -61,6 +66,9 @@ async def create_assignment( # RBAC check await rbac_check(request, course.course_uuid, current_user, "create", db_session) + # Usage check + check_limits_with_usage("assignments", course.org_id, db_session) + # Create Assignment assignment = Assignment(**assignment_object.model_dump()) @@ -74,6 +82,9 @@ async def create_assignment( db_session.commit() db_session.refresh(assignment) + # Feature usage + increase_feature_usage("assignments", course.org_id, db_session) + # return assignment read return AssignmentRead.model_validate(assignment) @@ -228,6 +239,9 @@ async def delete_assignment( # RBAC check await rbac_check(request, course.course_uuid, current_user, "delete", db_session) + # Feature usage + decrease_feature_usage("assignments", course.org_id, db_session) + # Delete Assignment db_session.delete(assignment) db_session.commit() @@ -275,6 +289,9 @@ async def delete_assignment_from_activity_uuid( # RBAC check await rbac_check(request, course.course_uuid, current_user, "delete", db_session) + # Feature usage + decrease_feature_usage("assignments", course.org_id, db_session) + # Delete Assignment db_session.delete(assignment) @@ -1119,9 +1136,9 @@ async def create_assignment_submission( # Add TrailStep trail = await check_trail_presence( org_id=course.org_id, - user_id=user.id, + user_id=user.id, # type: ignore request=request, - user=user, + user=user, # type: ignore db_session=db_session, ) @@ -1137,7 +1154,7 @@ async def create_assignment_submission( trail_id=trail.id if trail.id is not None else 0, course_id=course.id if course.id is not None else 0, org_id=course.org_id, - user_id=user.id, + user_id=user.id, # type: ignore creation_date=str(datetime.now()), update_date=str(datetime.now()), ) @@ -1162,7 +1179,7 @@ async def create_assignment_submission( complete=True, teacher_verified=False, grade="", - user_id=user.id, + user_id=user.id, # type: ignore creation_date=str(datetime.now()), update_date=str(datetime.now()), ) @@ -1400,7 +1417,7 @@ async def grade_assignment_submission( status_code=404, detail="Assignment not found", ) - + statement = select(Course).where(Course.id == assignment.course_id) course = db_session.exec(statement).first() @@ -1409,7 +1426,6 @@ async def grade_assignment_submission( status_code=404, detail="Course not found", ) - await rbac_check(request, course.course_uuid, current_user, "update", db_session) @@ -1551,7 +1567,6 @@ async def mark_activity_as_done_for_user( status_code=404, detail="Course not found", ) - await rbac_check(request, course.course_uuid, current_user, "update", db_session) @@ -1596,6 +1611,7 @@ async def mark_activity_as_done_for_user( # return OK return {"message": "Activity marked as done for user"} + async def get_assignments_from_course( request: Request, course_uuid: str, diff --git a/apps/api/src/services/courses/courses.py b/apps/api/src/services/courses/courses.py index 744f77b4..fa140944 100644 --- a/apps/api/src/services/courses/courses.py +++ b/apps/api/src/services/courses/courses.py @@ -5,6 +5,11 @@ from sqlmodel import Session, select from src.db.usergroup_resources import UserGroupResource from src.db.usergroup_user import UserGroupUser from src.db.organizations import Organization +from src.security.features_utils.usage import ( + check_limits_with_usage, + decrease_feature_usage, + increase_feature_usage, +) from src.services.trail.trail import get_user_trail_with_orgid from src.db.resource_authors import ResourceAuthor, ResourceAuthorshipEnum from src.db.users import PublicUser, AnonymousUser, User, UserRead @@ -58,6 +63,7 @@ async def get_course( return course + async def get_course_by_id( request: Request, course_id: str, @@ -91,6 +97,7 @@ async def get_course_by_id( return course + async def get_course_meta( request: Request, course_uuid: str, @@ -158,6 +165,9 @@ async def create_course( # RBAC check await rbac_check(request, "course_x", current_user, "create", db_session) + # Usage check + check_limits_with_usage("courses", org_id, db_session) + # Complete course object course.org_id = course.org_id @@ -207,6 +217,9 @@ async def create_course( ) authors = db_session.exec(authors_statement).all() + # Feature usage + increase_feature_usage("courses", course.org_id, db_session) + # convert from User to UserRead authors = [UserRead.model_validate(author) for author in authors] @@ -344,6 +357,9 @@ async def delete_course( # RBAC check await rbac_check(request, course.course_uuid, current_user, "delete", db_session) + # Feature usage + decrease_feature_usage("courses", course.org_id, db_session) + db_session.delete(course) db_session.commit() @@ -358,7 +374,7 @@ async def get_courses_orgslug( page: int = 1, limit: int = 10, ): - + # TODO : This entire function is a mess. It needs to be rewritten. # Query for public courses @@ -372,7 +388,7 @@ async def get_courses_orgslug( statement_author = ( select(Course) .join(Organization) - .join(ResourceAuthor, ResourceAuthor.user_id == current_user.id) + .join(ResourceAuthor, ResourceAuthor.user_id == current_user.id) # type: ignore .where( Organization.slug == org_slug, ResourceAuthor.resource_uuid == Course.course_uuid, @@ -383,9 +399,9 @@ async def get_courses_orgslug( statement_usergroup = ( select(Course) .join(Organization) - .join(UserGroupResource, UserGroupResource.resource_uuid == Course.course_uuid) + .join(UserGroupResource, UserGroupResource.resource_uuid == Course.course_uuid) # type: ignore .join( - UserGroupUser, UserGroupUser.usergroup_id == UserGroupResource.usergroup_id + UserGroupUser, UserGroupUser.usergroup_id == UserGroupResource.usergroup_id # type: ignore ) .where(Organization.slug == org_slug, UserGroupUser.user_id == current_user.id) ) @@ -395,13 +411,12 @@ async def get_courses_orgslug( statement_public, statement_author, statement_usergroup ).subquery() - # TODO: migrate this to exec - courses = db_session.execute(select(statement_complete)).all() + # TODO: migrate this to exec + courses = db_session.execute(select(statement_complete)).all() # type: ignore # TODO: I have no idea why this is necessary, but it is courses = [CourseRead(**course._asdict(), authors=[]) for course in courses] - # for every course, get the authors for course in courses: authors_statement = ( diff --git a/apps/api/src/services/orgs/join.py b/apps/api/src/services/orgs/join.py index 177cc04e..a62f8fad 100644 --- a/apps/api/src/services/orgs/join.py +++ b/apps/api/src/services/orgs/join.py @@ -6,6 +6,10 @@ from sqlmodel import Session, select from src.db.organizations import Organization from src.db.user_organizations import UserOrganization from src.db.users import AnonymousUser, PublicUser, User +from src.security.features_utils.usage import ( + check_limits_with_usage, + increase_feature_usage, +) from src.services.orgs.invites import get_invite_code from src.services.orgs.orgs import get_org_join_mechanism @@ -27,12 +31,14 @@ async def join_org( org = result.first() - if not org: + if not org or org.id is None: raise HTTPException( status_code=404, detail="Organization not found", ) + check_limits_with_usage("members", org.id, db_session) + join_method = await get_org_join_mechanism( request, args.org_id, current_user, db_session ) @@ -104,6 +110,8 @@ async def join_org( db_session.add(user_organization) db_session.commit() + increase_feature_usage("members", org.id, db_session) + return "Great, You're part of the Organization" else: diff --git a/apps/api/src/services/orgs/users.py b/apps/api/src/services/orgs/users.py index 8cb0c4d6..2d7019a9 100644 --- a/apps/api/src/services/orgs/users.py +++ b/apps/api/src/services/orgs/users.py @@ -5,6 +5,7 @@ import logging import redis from fastapi import HTTPException, Request from sqlmodel import Session, select +from src.security.features_utils.usage import decrease_feature_usage from src.services.orgs.invites import send_invite_email from config.config import get_learnhouse_config from src.services.orgs.orgs import rbac_check @@ -147,6 +148,8 @@ async def remove_user_from_org( db_session.delete(user_org) db_session.commit() + decrease_feature_usage("members", org_id, db_session) + return {"detail": "User removed from org"} diff --git a/apps/api/src/services/users/usergroups.py b/apps/api/src/services/users/usergroups.py index 84b5de96..f443b6b2 100644 --- a/apps/api/src/services/users/usergroups.py +++ b/apps/api/src/services/users/usergroups.py @@ -4,6 +4,10 @@ from typing import Literal from uuid import uuid4 from fastapi import HTTPException, Request from sqlmodel import Session, select +from src.security.features_utils.usage import ( + check_limits_with_usage, + increase_feature_usage, +) from src.security.rbac.rbac import ( authorization_verify_based_on_roles_and_authorship_and_usergroups, authorization_verify_if_user_is_anon, @@ -35,14 +39,17 @@ async def create_usergroup( # Check if Organization exists statement = select(Organization).where(Organization.id == usergroup_create.org_id) - result = db_session.exec(statement) + org = db_session.exec(statement).first() - if not result.first(): + if not org or org.id is None: raise HTTPException( status_code=400, detail="Organization does not exist", ) + # Usage check + check_limits_with_usage("courses", org.id, db_session) + # Complete the object usergroup.usergroup_uuid = f"usergroup_{uuid4()}" usergroup.creation_date = str(datetime.now()) @@ -53,6 +60,9 @@ async def create_usergroup( db_session.commit() db_session.refresh(usergroup) + # Feature usage + increase_feature_usage("usergroups", org.id, db_session) + usergroup = UserGroupRead.model_validate(usergroup) return usergroup @@ -253,6 +263,9 @@ async def delete_usergroup_by_id( db_session=db_session, ) + # Feature usage + increase_feature_usage("usergroups", usergroup.org_id, db_session) + db_session.delete(usergroup) db_session.commit() @@ -275,8 +288,6 @@ async def add_users_to_usergroup( status_code=404, detail="UserGroup not found", ) - - # RBAC check await rbac_check( @@ -353,7 +364,9 @@ async def remove_users_from_usergroup( user_ids_array = user_ids.split(",") for user_id in user_ids_array: - statement = select(UserGroupUser).where(UserGroupUser.user_id == user_id, UserGroupUser.usergroup_id == usergroup_id) + statement = select(UserGroupUser).where( + UserGroupUser.user_id == user_id, UserGroupUser.usergroup_id == usergroup_id + ) usergroup_user = db_session.exec(statement).first() if usergroup_user: diff --git a/apps/api/src/services/users/users.py b/apps/api/src/services/users/users.py index a0a716f6..c7277122 100644 --- a/apps/api/src/services/users/users.py +++ b/apps/api/src/services/users/users.py @@ -3,6 +3,10 @@ from typing import Literal from uuid import uuid4 from fastapi import HTTPException, Request, UploadFile, status from sqlmodel import Session, select +from src.security.features_utils.usage import ( + check_limits_with_usage, + increase_feature_usage, +) from src.services.users.usergroups import add_users_to_usergroup from src.services.users.emails import ( send_account_creation_email, @@ -61,6 +65,9 @@ async def create_user( detail="Organization does not exist", ) + # Usage check + check_limits_with_usage("members", org_id, db_session) + # Username statement = select(User).where(User.username == user.username) result = db_session.exec(statement) @@ -106,6 +113,8 @@ async def create_user( user = UserRead.model_validate(user) + increase_feature_usage("members", org_id, db_session) + # Send Account creation email send_account_creation_email( user=user, @@ -135,6 +144,9 @@ async def create_user_with_invite( detail="Invite code is incorrect", ) + # Usage check + check_limits_with_usage("members", org_id, db_session) + # Check if invite code contains UserGroup if inviteCode.usergroup_id: # Add user to UserGroup @@ -148,6 +160,8 @@ async def create_user_with_invite( user = await create_user(request, db_session, current_user, user_object, org_id) + increase_feature_usage("members", org_id, db_session) + return user diff --git a/apps/web/components/Objects/Modals/Activities/Create/NewActivityModal/Assignment.tsx b/apps/web/components/Objects/Modals/Activities/Create/NewActivityModal/Assignment.tsx index f69d7cbc..cd304165 100644 --- a/apps/web/components/Objects/Modals/Activities/Create/NewActivityModal/Assignment.tsx +++ b/apps/web/components/Objects/Modals/Activities/Create/NewActivityModal/Assignment.tsx @@ -15,7 +15,8 @@ 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' +import { createActivity, deleteActivity } from '@services/courses/activities' +import toast from 'react-hot-toast' function NewAssignment({ submitActivity, chapterId, course, closeModal }: any) { const org = useOrg() as any; @@ -55,7 +56,7 @@ function NewAssignment({ submitActivity, chapterId, course, closeModal }: any) { } const activity_res = await createActivity(activity, chapterId, org?.id, session.data?.tokens?.access_token) - await createAssignment({ + const res = await createAssignment({ title: activityName, description: activityDescription, due_date: dueDate, @@ -66,6 +67,14 @@ function NewAssignment({ submitActivity, chapterId, course, closeModal }: any) { activity_id: activity_res?.id, }, session.data?.tokens?.access_token) + if (res.success) { + toast.success('Assignment created successfully') + } else { + toast.error(res.data.detail) + await deleteActivity(activity_res.activity_uuid, session.data?.tokens?.access_token) + + } + mutate(`${getAPIUrl()}courses/${course.courseStructure.course_uuid}/meta`) setIsSubmitting(false) closeModal() diff --git a/apps/web/components/Objects/Modals/Course/Create/CreateCourse.tsx b/apps/web/components/Objects/Modals/Course/Create/CreateCourse.tsx index 51bfe5f5..6f8ce885 100644 --- a/apps/web/components/Objects/Modals/Course/Create/CreateCourse.tsx +++ b/apps/web/components/Objects/Modals/Course/Create/CreateCourse.tsx @@ -16,6 +16,7 @@ import { BarLoader } from 'react-spinners' import { revalidateTags } from '@services/utils/ts/requests' import { useRouter } from 'next/navigation' import { useLHSession } from '@components/Contexts/LHSessionContext' +import toast from 'react-hot-toast' function CreateCourseModal({ closeModal, orgslug }: any) { const [isSubmitting, setIsSubmitting] = useState(false) @@ -69,21 +70,27 @@ function CreateCourseModal({ closeModal, orgslug }: any) { e.preventDefault() setIsSubmitting(true) - let status = await createNewCourse( + let res = await createNewCourse( orgId, { name, description, tags, visibility }, thumbnail, session.data?.tokens?.access_token ) - await revalidateTags(['courses'], orgslug) - setIsSubmitting(false) - - if (status.org_id == orgId) { - closeModal() - router.refresh() + if (res.success) { await revalidateTags(['courses'], orgslug) - } else { - alert('Error creating course, please see console logs') + setIsSubmitting(false) + toast.success('Course created successfully') + + if (res.data.org_id == orgId) { + closeModal() + router.refresh() + await revalidateTags(['courses'], orgslug) + } + + } + else { + setIsSubmitting(false) + toast.error(res.data.detail) } } diff --git a/apps/web/services/courses/activities.ts b/apps/web/services/courses/activities.ts index 162780f7..e2c41f2f 100644 --- a/apps/web/services/courses/activities.ts +++ b/apps/web/services/courses/activities.ts @@ -99,9 +99,9 @@ export async function getActivityByID( return res } -export async function deleteActivity(activity_id: any, access_token: string) { +export async function deleteActivity(activity_uuid: any, access_token: string) { const result = await fetch( - `${getAPIUrl()}activities/${activity_id}`, + `${getAPIUrl()}activities/${activity_uuid}`, RequestBodyWithAuthHeader('DELETE', null, null, access_token) ) const res = await result.json() diff --git a/apps/web/services/courses/courses.ts b/apps/web/services/courses/courses.ts index a8b687fd..e3f6bd1d 100644 --- a/apps/web/services/courses/courses.ts +++ b/apps/web/services/courses/courses.ts @@ -98,7 +98,7 @@ export async function createNewCourse( `${getAPIUrl()}courses/?org_id=${org_id}`, RequestBodyFormWithAuthHeader('POST', formData, null, access_token) ) - const res = await errorHandling(result) + const res = await getResponseMetadata(result) return res }