feat: scope invite code to UserGroups

This commit is contained in:
swve 2024-03-30 23:05:27 +00:00
parent e173a32e3c
commit ae677bc133
8 changed files with 212 additions and 54 deletions

View file

@ -109,7 +109,7 @@ function EditCourseAccess(props: EditCourseAccessProps) {
status="info"
></ConfirmationModal>
</div>
<UserGroupsSection usergroups={usergroups} />
{!isPublic ? ( <UserGroupsSection usergroups={usergroups} />) : null}
</div>
</div>
)
@ -137,7 +137,7 @@ function UserGroupsSection({ usergroups }: { usergroups: any[] }) {
<h1 className="font-bold text-xl text-gray-800">UserGroups</h1>
<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>
</div>
<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)
}
minHeight="no-min"
minWidth='md'
dialogContent={
<LinkToUserGroup setUserGroupModal={setUserGroupModal} />
}
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={
<button

View file

@ -3,7 +3,7 @@ import PageLoading from '@components/Objects/Loaders/PageLoading'
import ConfirmationModal from '@components/StyledElements/ConfirmationModal/ConfirmationModal'
import { getAPIUrl, getUriWithOrg } from '@services/config/config'
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 React, { useEffect } from 'react'
import useSWR, { mutate } from 'swr'
@ -16,6 +16,8 @@ import {
import Toast from '@components/StyledElements/Toast/Toast'
import toast from 'react-hot-toast'
import { useRouter } from 'next/navigation'
import Modal from '@components/StyledElements/Modal/Modal'
import OrgInviteCodeGenerate from '@components/Objects/Modals/Dash/OrgAccess/OrgInviteCodeGenerate'
function OrgAccess() {
const org = useOrg() as any
@ -25,6 +27,7 @@ function OrgAccess() {
)
const [isLoading, setIsLoading] = React.useState(false)
const [joinMethod, setJoinMethod] = React.useState('closed')
const [invitesModal, setInvitesModal] = React.useState(false)
const router = useRouter()
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) {
let res = await deleteInviteCode(org.id, invite.invite_code_uuid)
@ -125,7 +121,7 @@ function OrgAccess() {
</div>
) : null}
<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">
Closed
</div>
@ -161,6 +157,7 @@ function OrgAccess() {
<tr className="font-bolder text-sm">
<th className="py-3 px-4">Code</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">Actions</th>
</tr>
@ -188,6 +185,19 @@ function OrgAccess() {
)}
</Link>
</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">
{dayjs(invite.expiration_date)
.add(1, 'year')
@ -215,13 +225,36 @@ function OrgAccess() {
</tbody>
</>
</table>
<div className='flex flex-row-reverse mt-3 mr-2'><button
onClick={() => 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"
>
<Shield className="w-4 h-4" />
<span> Generate invite code</span>
</button></div>
<div className='flex flex-row-reverse mt-3 mr-2'>
<Modal
isDialogOpen={
invitesModal
}
onOpenChange={() =>
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>

View file

@ -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 (
<div className='p-4 flex-row flex justify-between items-center'>
<div className='py-3'>
<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'>
<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>
<select
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>
</div>
</div>
</div>
)
}

View file

@ -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