diff --git a/apps/api/src/services/users/users.py b/apps/api/src/services/users/users.py
index da5620db..70d6ddc4 100644
--- a/apps/api/src/services/users/users.py
+++ b/apps/api/src/services/users/users.py
@@ -3,6 +3,7 @@ from typing import Literal
from uuid import uuid4
from fastapi import HTTPException, Request, UploadFile, status
from sqlmodel import Session, select
+from src.services.users.usergroups import add_users_to_usergroup
from src.services.users.emails import (
send_account_creation_email,
)
@@ -124,20 +125,27 @@ async def create_user_with_invite(
):
# Check if invite code exists
- inviteCOde = await get_invite_code(
+ inviteCode = await get_invite_code(
request, org_id, invite_code, current_user, db_session
)
- # Check if invite code contains UserGroup
- #TODO
-
-
- if not inviteCOde:
+ if not inviteCode:
raise HTTPException(
status_code=400,
detail="Invite code is incorrect",
)
+ # Check if invite code contains UserGroup
+ if inviteCode.usergroup_id:
+ # Add user to UserGroup
+ await add_users_to_usergroup(
+ request,
+ db_session,
+ current_user,
+ inviteCode.usergroup_id,
+ user_object.username,
+ )
+
user = await create_user(request, db_session, current_user, user_object, org_id)
return user
@@ -350,6 +358,7 @@ async def update_user_password(
return user
+
async def read_user_by_id(
request: Request,
db_session: Session,
@@ -467,8 +476,10 @@ async def authorize_user_action(
)
# RBAC check
- authorized = await authorization_verify_based_on_roles_and_authorship_and_usergroups(
- request, current_user.id, action, resource_uuid, db_session
+ authorized = (
+ await authorization_verify_based_on_roles_and_authorship_and_usergroups(
+ request, current_user.id, action, resource_uuid, db_session
+ )
)
if authorized:
diff --git a/apps/web/app/orgs/[orgslug]/dash/users/settings/[subpage]/page.tsx b/apps/web/app/orgs/[orgslug]/dash/users/settings/[subpage]/page.tsx
index 9a04c539..8d364a22 100644
--- a/apps/web/app/orgs/[orgslug]/dash/users/settings/[subpage]/page.tsx
+++ b/apps/web/app/orgs/[orgslug]/dash/users/settings/[subpage]/page.tsx
@@ -29,12 +29,12 @@ function UsersSettingsPage({ params }: { params: SettingsParams }) {
setH2Label('Manage your organization users, assign roles and permissions')
}
if (params.subpage == 'signups') {
- setH1Label('Signup Access')
+ setH1Label('Signups & Invite Codes')
setH2Label('Choose from where users can join your organization')
}
if (params.subpage == 'add') {
- setH1Label('Invite users')
- setH2Label('Invite users to join your organization')
+ setH1Label('Invite Members')
+ setH2Label('Invite members to join your organization')
}
if (params.subpage == 'usergroups') {
setH1Label('UserGroups')
@@ -95,23 +95,6 @@ function UsersSettingsPage({ params }: { params: SettingsParams }) {
-
-
-
-
Signup Access
+
Signups & Invite Codes
+
+
+
+
{
onClick={validateCode}
className="flex w-fit space-x-2 bg-black px-6 py-2 text-md rounded-lg font-semibold h-fit text-white items-center shadow-md"
>
-
+
Submit
diff --git a/apps/web/components/Dashboard/Course/EditCourseAccess/EditCourseAccess.tsx b/apps/web/components/Dashboard/Course/EditCourseAccess/EditCourseAccess.tsx
index fb274c41..9fa1abc3 100644
--- a/apps/web/components/Dashboard/Course/EditCourseAccess/EditCourseAccess.tsx
+++ b/apps/web/components/Dashboard/Course/EditCourseAccess/EditCourseAccess.tsx
@@ -109,7 +109,7 @@ function EditCourseAccess(props: EditCourseAccessProps) {
status="info"
>
-
+ {!isPublic ? ( ) : null}
)
@@ -137,7 +137,7 @@ function UserGroupsSection({ usergroups }: { usergroups: any[] }) {
UserGroups
{' '}
- Choose which UserGroups can access this course{' '}
+ You can choose to give access to this course to specific groups of users only by linking it to a UserGroup{' '}
@@ -186,13 +186,14 @@ function UserGroupsSection({ usergroups }: { usergroups: any[] }) {
setUserGroupModal(!userGroupModal)
}
minHeight="no-min"
+ minWidth='md'
dialogContent={
}
dialogTitle="Link Course to a UserGroup"
dialogDescription={
- 'Choose which UserGroups can access this course'
+ 'Choose a UserGroup to link this course to, Users from this UserGroup will have access to this course.'
}
dialogTrigger={
) : null}
-
+
Closed
@@ -161,6 +157,7 @@ function OrgAccess() {
Code
Signup link
+ Type
Expiration date
Actions
@@ -188,6 +185,19 @@ function OrgAccess() {
)}
+
+ {invite.usergroup_id ? (
+
+
+ Linked to a UserGroup
+
+ ) : (
+
+
+ Normal
+
+ )}
+
{dayjs(invite.expiration_date)
.add(1, 'year')
@@ -215,13 +225,36 @@ function OrgAccess() {
>
- createInvite()}
- className=" flex space-x-2 hover:cursor-pointer p-1 px-3 bg-green-700 rounded-md font-bold items-center text-sm text-green-100"
- >
-
- Generate invite code
-
+
+
+ setInvitesModal(!invitesModal)
+ }
+ minHeight="no-min"
+ minWidth='lg'
+ dialogContent={
+
+ }
+ dialogTitle="Generate Invite Code"
+ dialogDescription={
+ 'Generate a new invite code for your organization'
+ }
+ dialogTrigger={
+
+
+ Generate invite code
+
+ }
+ />
+
+
diff --git a/apps/web/components/Objects/Modals/Dash/EditCourseAccess/LinkToUserGroup.tsx b/apps/web/components/Objects/Modals/Dash/EditCourseAccess/LinkToUserGroup.tsx
index 81e1716c..56976fba 100644
--- a/apps/web/components/Objects/Modals/Dash/EditCourseAccess/LinkToUserGroup.tsx
+++ b/apps/web/components/Objects/Modals/Dash/EditCourseAccess/LinkToUserGroup.tsx
@@ -4,6 +4,7 @@ import { useOrg } from '@components/Contexts/OrgContext';
import { getAPIUrl } from '@services/config/config';
import { linkResourcesToUserGroup } from '@services/usergroups/usergroups';
import { swrFetcher } from '@services/utils/ts/requests';
+import { AlertTriangle, Info } from 'lucide-react';
import React, { useEffect } from 'react'
import toast from 'react-hot-toast';
import useSWR, { mutate } from 'swr'
@@ -47,9 +48,14 @@ function LinkToUserGroup(props: LinkToUserGroupProps) {
, [usergroups])
return (
-
-
-
+
+
+
+
Users that are not part of the UserGroup will no longer have access to this course
+
+
+
+
UserGroup Name
setSelectedUserGroup(e.target.value)}
@@ -65,6 +71,8 @@ function LinkToUserGroup(props: LinkToUserGroupProps) {
{ handleLink() }} className='bg-green-700 text-white font-bold px-4 py-2 rounded-md shadow'>Link
+
+
)
}
diff --git a/apps/web/components/Objects/Modals/Dash/OrgAccess/OrgInviteCodeGenerate.tsx b/apps/web/components/Objects/Modals/Dash/OrgAccess/OrgInviteCodeGenerate.tsx
new file mode 100644
index 00000000..bd63fd8d
--- /dev/null
+++ b/apps/web/components/Objects/Modals/Dash/OrgAccess/OrgInviteCodeGenerate.tsx
@@ -0,0 +1,95 @@
+import { useOrg } from '@components/Contexts/OrgContext'
+import { getAPIUrl } from '@services/config/config'
+import { createInviteCode, createInviteCodeWithUserGroup } from '@services/organizations/invites'
+import { swrFetcher } from '@services/utils/ts/requests'
+import { Shield, Ticket } from 'lucide-react'
+import React, { useEffect } from 'react'
+import toast from 'react-hot-toast'
+import useSWR, { mutate } from 'swr'
+
+type OrgInviteCodeGenerateProps = {
+ setInvitesModal: any
+}
+
+function OrgInviteCodeGenerate(props: OrgInviteCodeGenerateProps) {
+ const org = useOrg() as any
+ const [usergroup_id, setUsergroup_id] = React.useState(0);
+ const { data: usergroups } = useSWR(
+ org ? `${getAPIUrl()}usergroups/org/${org.id}` : null,
+ swrFetcher
+ )
+
+ async function createInviteWithUserGroup() {
+ let res = await createInviteCodeWithUserGroup(org.id, usergroup_id)
+ if (res.status == 200) {
+ mutate(`${getAPIUrl()}orgs/${org.id}/invites`)
+ props.setInvitesModal(false)
+ } else {
+ toast.error('Error ' + res.status + ': ' + res.data.detail)
+ }
+ }
+
+ async function createInvite() {
+ let res = await createInviteCode(org.id)
+ if (res.status == 200) {
+ mutate(`${getAPIUrl()}orgs/${org.id}/invites`)
+ props.setInvitesModal(false)
+ } else {
+ toast.error('Error ' + res.status + ': ' + res.data.detail)
+ }
+ }
+
+ useEffect(() => {
+ if (usergroups && usergroups.length > 0) {
+ setUsergroup_id(usergroups[0].id)
+ }
+ }
+ , [usergroups])
+ return (
+
+
+
+
Invite Code linked to a UserGroup
+
On Signup, Users will be automatically linked to a UserGroup of your choice
+
+
+ {usergroups?.map((usergroup: any) => (
+
+ {usergroup.name}
+
+ ))}
+
+
+
+
+ Generate
+
+
+
+
+
+
+
+
Normal Invite Code
+
On Signup, User will not be linked to any UserGroup
+
+
+
+ Generate
+
+
+
+
+
+ )
+}
+
+export default OrgInviteCodeGenerate
\ No newline at end of file
diff --git a/apps/web/services/organizations/invites.ts b/apps/web/services/organizations/invites.ts
index 06fdbd10..2fbb2bd2 100644
--- a/apps/web/services/organizations/invites.ts
+++ b/apps/web/services/organizations/invites.ts
@@ -10,6 +10,15 @@ export async function createInviteCode(org_id: any) {
return res
}
+export async function createInviteCodeWithUserGroup(org_id: any, usergroup_id: number) {
+ const result = await fetch(
+ `${getAPIUrl()}orgs/${org_id}/invites_with_usergroups?usergroup_id=${usergroup_id}`,
+ RequestBody('POST', null, null)
+ )
+ const res = await getResponseMetadata(result)
+ return res
+}
+
export async function deleteInviteCode(
org_id: any,
org_invite_code_uuid: string