mirror of
https://github.com/rzmk/learnhouse.git
synced 2025-12-19 04:19:25 +00:00
feat: scope invite code to UserGroups
This commit is contained in:
parent
e173a32e3c
commit
ae677bc133
8 changed files with 212 additions and 54 deletions
|
|
@ -3,6 +3,7 @@ from typing import Literal
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
from fastapi import HTTPException, Request, UploadFile, status
|
from fastapi import HTTPException, Request, UploadFile, status
|
||||||
from sqlmodel import Session, select
|
from sqlmodel import Session, select
|
||||||
|
from src.services.users.usergroups import add_users_to_usergroup
|
||||||
from src.services.users.emails import (
|
from src.services.users.emails import (
|
||||||
send_account_creation_email,
|
send_account_creation_email,
|
||||||
)
|
)
|
||||||
|
|
@ -124,20 +125,27 @@ async def create_user_with_invite(
|
||||||
):
|
):
|
||||||
|
|
||||||
# Check if invite code exists
|
# Check if invite code exists
|
||||||
inviteCOde = await get_invite_code(
|
inviteCode = await get_invite_code(
|
||||||
request, org_id, invite_code, current_user, db_session
|
request, org_id, invite_code, current_user, db_session
|
||||||
)
|
)
|
||||||
|
|
||||||
# Check if invite code contains UserGroup
|
if not inviteCode:
|
||||||
#TODO
|
|
||||||
|
|
||||||
|
|
||||||
if not inviteCOde:
|
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=400,
|
status_code=400,
|
||||||
detail="Invite code is incorrect",
|
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)
|
user = await create_user(request, db_session, current_user, user_object, org_id)
|
||||||
|
|
||||||
return user
|
return user
|
||||||
|
|
@ -350,6 +358,7 @@ async def update_user_password(
|
||||||
|
|
||||||
return user
|
return user
|
||||||
|
|
||||||
|
|
||||||
async def read_user_by_id(
|
async def read_user_by_id(
|
||||||
request: Request,
|
request: Request,
|
||||||
db_session: Session,
|
db_session: Session,
|
||||||
|
|
@ -467,8 +476,10 @@ async def authorize_user_action(
|
||||||
)
|
)
|
||||||
|
|
||||||
# RBAC check
|
# RBAC check
|
||||||
authorized = await authorization_verify_based_on_roles_and_authorship_and_usergroups(
|
authorized = (
|
||||||
request, current_user.id, action, resource_uuid, db_session
|
await authorization_verify_based_on_roles_and_authorship_and_usergroups(
|
||||||
|
request, current_user.id, action, resource_uuid, db_session
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
if authorized:
|
if authorized:
|
||||||
|
|
|
||||||
|
|
@ -29,12 +29,12 @@ function UsersSettingsPage({ params }: { params: SettingsParams }) {
|
||||||
setH2Label('Manage your organization users, assign roles and permissions')
|
setH2Label('Manage your organization users, assign roles and permissions')
|
||||||
}
|
}
|
||||||
if (params.subpage == 'signups') {
|
if (params.subpage == 'signups') {
|
||||||
setH1Label('Signup Access')
|
setH1Label('Signups & Invite Codes')
|
||||||
setH2Label('Choose from where users can join your organization')
|
setH2Label('Choose from where users can join your organization')
|
||||||
}
|
}
|
||||||
if (params.subpage == 'add') {
|
if (params.subpage == 'add') {
|
||||||
setH1Label('Invite users')
|
setH1Label('Invite Members')
|
||||||
setH2Label('Invite users to join your organization')
|
setH2Label('Invite members to join your organization')
|
||||||
}
|
}
|
||||||
if (params.subpage == 'usergroups') {
|
if (params.subpage == 'usergroups') {
|
||||||
setH1Label('UserGroups')
|
setH1Label('UserGroups')
|
||||||
|
|
@ -95,23 +95,6 @@ function UsersSettingsPage({ params }: { params: SettingsParams }) {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
|
||||||
href={
|
|
||||||
getUriWithOrg(params.orgslug, '') + `/dash/users/settings/add`
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className={`py-2 w-fit text-center border-black transition-all ease-linear ${params.subpage.toString() === 'add'
|
|
||||||
? 'border-b-4'
|
|
||||||
: 'opacity-50'
|
|
||||||
} cursor-pointer`}
|
|
||||||
>
|
|
||||||
<div className="flex items-center space-x-2.5 mx-2">
|
|
||||||
<UserPlus size={16} />
|
|
||||||
<div>Invite users</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Link>
|
|
||||||
<Link
|
<Link
|
||||||
href={
|
href={
|
||||||
getUriWithOrg(params.orgslug, '') + `/dash/users/settings/signups`
|
getUriWithOrg(params.orgslug, '') + `/dash/users/settings/signups`
|
||||||
|
|
@ -125,10 +108,28 @@ function UsersSettingsPage({ params }: { params: SettingsParams }) {
|
||||||
>
|
>
|
||||||
<div className="flex items-center space-x-2.5 mx-2">
|
<div className="flex items-center space-x-2.5 mx-2">
|
||||||
<ScanEye size={16} />
|
<ScanEye size={16} />
|
||||||
<div>Signup Access</div>
|
<div>Signups & Invite Codes</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
|
<Link
|
||||||
|
href={
|
||||||
|
getUriWithOrg(params.orgslug, '') + `/dash/users/settings/add`
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={`py-2 w-fit text-center border-black transition-all ease-linear ${params.subpage.toString() === 'add'
|
||||||
|
? 'border-b-4'
|
||||||
|
: 'opacity-50'
|
||||||
|
} cursor-pointer`}
|
||||||
|
>
|
||||||
|
<div className="flex items-center space-x-2.5 mx-2">
|
||||||
|
<UserPlus size={16} />
|
||||||
|
<div>Invite Members</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<motion.div
|
<motion.div
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import Link from 'next/link'
|
||||||
import { getUriWithOrg } from '@services/config/config'
|
import { getUriWithOrg } from '@services/config/config'
|
||||||
import { useSession } from '@components/Contexts/SessionContext'
|
import { useSession } from '@components/Contexts/SessionContext'
|
||||||
import React, { useEffect } from 'react'
|
import React, { useEffect } from 'react'
|
||||||
import { MailWarning, Shield, UserPlus } from 'lucide-react'
|
import { MailWarning, Shield, Ticket, UserPlus } from 'lucide-react'
|
||||||
import { useOrg } from '@components/Contexts/OrgContext'
|
import { useOrg } from '@components/Contexts/OrgContext'
|
||||||
import UserAvatar from '@components/Objects/UserAvatar'
|
import UserAvatar from '@components/Objects/UserAvatar'
|
||||||
import OpenSignUpComponent from './OpenSignup'
|
import OpenSignUpComponent from './OpenSignup'
|
||||||
|
|
@ -201,7 +201,7 @@ const NoTokenScreen = (props: any) => {
|
||||||
onClick={validateCode}
|
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"
|
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"
|
||||||
>
|
>
|
||||||
<Shield size={18} />
|
<Ticket size={18} />
|
||||||
<p>Submit </p>
|
<p>Submit </p>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -109,7 +109,7 @@ function EditCourseAccess(props: EditCourseAccessProps) {
|
||||||
status="info"
|
status="info"
|
||||||
></ConfirmationModal>
|
></ConfirmationModal>
|
||||||
</div>
|
</div>
|
||||||
<UserGroupsSection usergroups={usergroups} />
|
{!isPublic ? ( <UserGroupsSection usergroups={usergroups} />) : null}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
@ -137,7 +137,7 @@ function UserGroupsSection({ usergroups }: { usergroups: any[] }) {
|
||||||
<h1 className="font-bold text-xl text-gray-800">UserGroups</h1>
|
<h1 className="font-bold text-xl text-gray-800">UserGroups</h1>
|
||||||
<h2 className="text-gray-500 text-sm">
|
<h2 className="text-gray-500 text-sm">
|
||||||
{' '}
|
{' '}
|
||||||
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{' '}
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
<table className="table-auto w-full text-left whitespace-nowrap rounded-md overflow-hidden">
|
<table className="table-auto w-full text-left whitespace-nowrap rounded-md overflow-hidden">
|
||||||
|
|
@ -186,13 +186,14 @@ function UserGroupsSection({ usergroups }: { usergroups: any[] }) {
|
||||||
setUserGroupModal(!userGroupModal)
|
setUserGroupModal(!userGroupModal)
|
||||||
}
|
}
|
||||||
minHeight="no-min"
|
minHeight="no-min"
|
||||||
|
minWidth='md'
|
||||||
dialogContent={
|
dialogContent={
|
||||||
<LinkToUserGroup setUserGroupModal={setUserGroupModal} />
|
<LinkToUserGroup setUserGroupModal={setUserGroupModal} />
|
||||||
|
|
||||||
}
|
}
|
||||||
dialogTitle="Link Course to a UserGroup"
|
dialogTitle="Link Course to a UserGroup"
|
||||||
dialogDescription={
|
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={
|
dialogTrigger={
|
||||||
<button
|
<button
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import PageLoading from '@components/Objects/Loaders/PageLoading'
|
||||||
import ConfirmationModal from '@components/StyledElements/ConfirmationModal/ConfirmationModal'
|
import ConfirmationModal from '@components/StyledElements/ConfirmationModal/ConfirmationModal'
|
||||||
import { getAPIUrl, getUriWithOrg } from '@services/config/config'
|
import { getAPIUrl, getUriWithOrg } from '@services/config/config'
|
||||||
import { swrFetcher } from '@services/utils/ts/requests'
|
import { swrFetcher } from '@services/utils/ts/requests'
|
||||||
import { Globe, Shield, X } from 'lucide-react'
|
import { Globe, Shield, Ticket, User, UserSquare, Users, X } from 'lucide-react'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import React, { useEffect } from 'react'
|
import React, { useEffect } from 'react'
|
||||||
import useSWR, { mutate } from 'swr'
|
import useSWR, { mutate } from 'swr'
|
||||||
|
|
@ -16,6 +16,8 @@ import {
|
||||||
import Toast from '@components/StyledElements/Toast/Toast'
|
import Toast from '@components/StyledElements/Toast/Toast'
|
||||||
import toast from 'react-hot-toast'
|
import toast from 'react-hot-toast'
|
||||||
import { useRouter } from 'next/navigation'
|
import { useRouter } from 'next/navigation'
|
||||||
|
import Modal from '@components/StyledElements/Modal/Modal'
|
||||||
|
import OrgInviteCodeGenerate from '@components/Objects/Modals/Dash/OrgAccess/OrgInviteCodeGenerate'
|
||||||
|
|
||||||
function OrgAccess() {
|
function OrgAccess() {
|
||||||
const org = useOrg() as any
|
const org = useOrg() as any
|
||||||
|
|
@ -25,6 +27,7 @@ function OrgAccess() {
|
||||||
)
|
)
|
||||||
const [isLoading, setIsLoading] = React.useState(false)
|
const [isLoading, setIsLoading] = React.useState(false)
|
||||||
const [joinMethod, setJoinMethod] = React.useState('closed')
|
const [joinMethod, setJoinMethod] = React.useState('closed')
|
||||||
|
const [invitesModal, setInvitesModal] = React.useState(false)
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
async function getOrgJoinMethod() {
|
async function getOrgJoinMethod() {
|
||||||
|
|
@ -37,14 +40,7 @@ function OrgAccess() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createInvite() {
|
|
||||||
let res = await createInviteCode(org.id)
|
|
||||||
if (res.status == 200) {
|
|
||||||
mutate(`${getAPIUrl()}orgs/${org.id}/invites`)
|
|
||||||
} else {
|
|
||||||
toast.error('Error ' + res.status + ': ' + res.data.detail)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function deleteInvite(invite: any) {
|
async function deleteInvite(invite: any) {
|
||||||
let res = await deleteInviteCode(org.id, invite.invite_code_uuid)
|
let res = await deleteInviteCode(org.id, invite.invite_code_uuid)
|
||||||
|
|
@ -125,7 +121,7 @@ function OrgAccess() {
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
<div className="flex flex-col space-y-1 justify-center items-center h-full">
|
<div className="flex flex-col space-y-1 justify-center items-center h-full">
|
||||||
<Shield className="text-slate-400" size={40}></Shield>
|
<Ticket className="text-slate-400" size={40}></Ticket>
|
||||||
<div className="text-2xl text-slate-700 font-bold">
|
<div className="text-2xl text-slate-700 font-bold">
|
||||||
Closed
|
Closed
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -161,6 +157,7 @@ function OrgAccess() {
|
||||||
<tr className="font-bolder text-sm">
|
<tr className="font-bolder text-sm">
|
||||||
<th className="py-3 px-4">Code</th>
|
<th className="py-3 px-4">Code</th>
|
||||||
<th className="py-3 px-4">Signup link</th>
|
<th className="py-3 px-4">Signup link</th>
|
||||||
|
<th className="py-3 px-4">Type</th>
|
||||||
<th className="py-3 px-4">Expiration date</th>
|
<th className="py-3 px-4">Expiration date</th>
|
||||||
<th className="py-3 px-4">Actions</th>
|
<th className="py-3 px-4">Actions</th>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
@ -188,6 +185,19 @@ function OrgAccess() {
|
||||||
)}
|
)}
|
||||||
</Link>
|
</Link>
|
||||||
</td>
|
</td>
|
||||||
|
<td className="py-3 px-4">
|
||||||
|
{invite.usergroup_id ? (
|
||||||
|
<div className="flex space-x-2 items-center">
|
||||||
|
<UserSquare className="w-4 h-4" />
|
||||||
|
<span>Linked to a UserGroup</span>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="flex space-x-2 items-center">
|
||||||
|
<Users className="w-4 h-4" />
|
||||||
|
<span>Normal</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</td>
|
||||||
<td className="py-3 px-4">
|
<td className="py-3 px-4">
|
||||||
{dayjs(invite.expiration_date)
|
{dayjs(invite.expiration_date)
|
||||||
.add(1, 'year')
|
.add(1, 'year')
|
||||||
|
|
@ -215,13 +225,36 @@ function OrgAccess() {
|
||||||
</tbody>
|
</tbody>
|
||||||
</>
|
</>
|
||||||
</table>
|
</table>
|
||||||
<div className='flex flex-row-reverse mt-3 mr-2'><button
|
<div className='flex flex-row-reverse mt-3 mr-2'>
|
||||||
onClick={() => createInvite()}
|
<Modal
|
||||||
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"
|
isDialogOpen={
|
||||||
>
|
invitesModal
|
||||||
<Shield className="w-4 h-4" />
|
}
|
||||||
<span> Generate invite code</span>
|
onOpenChange={() =>
|
||||||
</button></div>
|
setInvitesModal(!invitesModal)
|
||||||
|
}
|
||||||
|
minHeight="no-min"
|
||||||
|
minWidth='lg'
|
||||||
|
dialogContent={
|
||||||
|
<OrgInviteCodeGenerate
|
||||||
|
setInvitesModal={setInvitesModal}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
dialogTitle="Generate Invite Code"
|
||||||
|
dialogDescription={
|
||||||
|
'Generate a new invite code for your organization'
|
||||||
|
}
|
||||||
|
dialogTrigger={
|
||||||
|
<button
|
||||||
|
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"
|
||||||
|
>
|
||||||
|
<Ticket className="w-4 h-4" />
|
||||||
|
<span> Generate invite code</span>
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import { useOrg } from '@components/Contexts/OrgContext';
|
||||||
import { getAPIUrl } from '@services/config/config';
|
import { getAPIUrl } from '@services/config/config';
|
||||||
import { linkResourcesToUserGroup } from '@services/usergroups/usergroups';
|
import { linkResourcesToUserGroup } from '@services/usergroups/usergroups';
|
||||||
import { swrFetcher } from '@services/utils/ts/requests';
|
import { swrFetcher } from '@services/utils/ts/requests';
|
||||||
|
import { AlertTriangle, Info } from 'lucide-react';
|
||||||
import React, { useEffect } from 'react'
|
import React, { useEffect } from 'react'
|
||||||
import toast from 'react-hot-toast';
|
import toast from 'react-hot-toast';
|
||||||
import useSWR, { mutate } from 'swr'
|
import useSWR, { mutate } from 'swr'
|
||||||
|
|
@ -47,9 +48,14 @@ function LinkToUserGroup(props: LinkToUserGroupProps) {
|
||||||
, [usergroups])
|
, [usergroups])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='p-4 flex-row flex justify-between items-center'>
|
<div className='flex flex-col space-y-1 '>
|
||||||
|
<div className='flex bg-yellow-100 text-yellow-900 mx-auto w-fit mt-3 px-4 py-2 space-x-2 text-sm rounded-full items-center'>
|
||||||
<div className='py-3'>
|
<Info size={19} />
|
||||||
|
<h1 className=' font-medium'>Users that are not part of the UserGroup will no longer have access to this course</h1>
|
||||||
|
</div>
|
||||||
|
<div className='p-4 flex-row flex justify-between items-center'>
|
||||||
|
|
||||||
|
<div className='py-1'>
|
||||||
<span className='px-3 text-gray-400 font-bold rounded-full py-1 bg-gray-100 mx-3'>UserGroup Name </span>
|
<span className='px-3 text-gray-400 font-bold rounded-full py-1 bg-gray-100 mx-3'>UserGroup Name </span>
|
||||||
<select
|
<select
|
||||||
onChange={(e) => setSelectedUserGroup(e.target.value)}
|
onChange={(e) => setSelectedUserGroup(e.target.value)}
|
||||||
|
|
@ -65,6 +71,8 @@ function LinkToUserGroup(props: LinkToUserGroupProps) {
|
||||||
<button onClick={() => { handleLink() }} className='bg-green-700 text-white font-bold px-4 py-2 rounded-md shadow'>Link</button>
|
<button onClick={() => { handleLink() }} className='bg-green-700 text-white font-bold px-4 py-2 rounded-md shadow'>Link</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 (
|
||||||
|
<div className='flex space-x-2 pt-2'>
|
||||||
|
<div className='flex bg-slate-100 w-full h-[140px] rounded-lg'>
|
||||||
|
<div className='flex flex-col mx-auto'>
|
||||||
|
<h1 className='mx-auto pt-4 text-gray-600 font-medium'>Invite Code linked to a UserGroup</h1>
|
||||||
|
<h2 className='mx-auto text-xs text-gray-600 font-medium'>On Signup, Users will be automatically linked to a UserGroup of your choice</h2>
|
||||||
|
<div className='flex items-center space-x-4 pt-3 mx-auto'>
|
||||||
|
<select
|
||||||
|
defaultValue={usergroup_id}
|
||||||
|
className='flex p-2 w-fit rounded-md text-sm bg-gray-100'>
|
||||||
|
{usergroups?.map((usergroup: any) => (
|
||||||
|
<option key={usergroup.id} value={usergroup.id}>
|
||||||
|
{usergroup.name}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
<div className=''>
|
||||||
|
<button
|
||||||
|
onClick={createInviteWithUserGroup}
|
||||||
|
className="flex space-x-2 w-fit hover:cursor-pointer p-1 px-3 bg-green-700 rounded-md font-bold items-center text-sm text-green-100"
|
||||||
|
>
|
||||||
|
<Ticket className="w-4 h-4" />
|
||||||
|
<span> Generate </span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className='flex bg-slate-100 w-full h-[140px] rounded-lg'>
|
||||||
|
<div className='flex flex-col mx-auto'>
|
||||||
|
<h1 className='mx-auto pt-4 text-gray-600 font-medium'>Normal Invite Code</h1>
|
||||||
|
<h2 className='mx-auto text-xs text-gray-600 font-medium'>On Signup, User will not be linked to any UserGroup</h2>
|
||||||
|
<div className='mx-auto pt-4'>
|
||||||
|
<button
|
||||||
|
onClick={createInvite}
|
||||||
|
className="flex space-x-2 w-fit hover:cursor-pointer p-1 px-3 bg-green-700 rounded-md font-bold items-center text-sm text-green-100"
|
||||||
|
>
|
||||||
|
<Ticket className="w-4 h-4" />
|
||||||
|
<span> Generate </span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default OrgInviteCodeGenerate
|
||||||
|
|
@ -10,6 +10,15 @@ export async function createInviteCode(org_id: any) {
|
||||||
return res
|
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(
|
export async function deleteInviteCode(
|
||||||
org_id: any,
|
org_id: any,
|
||||||
org_invite_code_uuid: string
|
org_invite_code_uuid: string
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue