diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index 29ab464b..06fb9a11 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -14,7 +14,8 @@
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"ms-python.isort",
- "redhat.vscode-yaml"
+ "redhat.vscode-yaml",
+ "bradlc.vscode-tailwindcss"
],
"settings": {
"[python]": {
diff --git a/apps/api/src/services/email/utils.py b/apps/api/src/services/email/utils.py
index 7fd43f69..872d9675 100644
--- a/apps/api/src/services/email/utils.py
+++ b/apps/api/src/services/email/utils.py
@@ -1,19 +1,19 @@
+from pydantic import EmailStr
import resend
from config.config import get_learnhouse_config
-def send_email(to: str, subject: str, body: str):
+
+def send_email(to: EmailStr, subject: str, body: str):
lh_config = get_learnhouse_config()
params = {
- "from": f"LearnHouse <"
- + lh_config.mailing_config.system_email_address
- + ">",
+ "from": "LearnHouse <" + lh_config.mailing_config.system_email_address + ">",
"to": [to],
"subject": subject,
"html": body,
}
-
resend.api_key = lh_config.mailing_config.resend_api_key
email = resend.Emails.send(params)
return email
+
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 16bc5c5e..e5af8896 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
@@ -9,6 +9,7 @@ import { useSession } from '@components/Contexts/SessionContext'
import { useOrg } from '@components/Contexts/OrgContext'
import OrgUsers from '@components/Dashboard/Users/OrgUsers/OrgUsers'
import OrgAccess from '@components/Dashboard/Users/OrgAccess/OrgAccess'
+import OrgUsersAdd from '@components/Dashboard/Users/OrgUsersAdd/OrgUsersAdd'
export type SettingsParams = {
subpage: string
@@ -120,6 +121,7 @@ function UsersSettingsPage({ params }: { params: SettingsParams }) {
>
{params.subpage == 'users' ? : ''}
{params.subpage == 'signups' ? : ''}
+ {params.subpage == 'add' ? : ''}
)
diff --git a/apps/web/components/Dashboard/Users/OrgAccess/OrgAccess.tsx b/apps/web/components/Dashboard/Users/OrgAccess/OrgAccess.tsx
index 7392ae7b..a4de5467 100644
--- a/apps/web/components/Dashboard/Users/OrgAccess/OrgAccess.tsx
+++ b/apps/web/components/Dashboard/Users/OrgAccess/OrgAccess.tsx
@@ -216,13 +216,14 @@ function OrgAccess() {
>
-
+
>
diff --git a/apps/web/components/Dashboard/Users/OrgUsersAdd/OrgUsersAdd.tsx b/apps/web/components/Dashboard/Users/OrgUsersAdd/OrgUsersAdd.tsx
new file mode 100644
index 00000000..d0217849
--- /dev/null
+++ b/apps/web/components/Dashboard/Users/OrgUsersAdd/OrgUsersAdd.tsx
@@ -0,0 +1,140 @@
+import { useOrg } from '@components/Contexts/OrgContext'
+import PageLoading from '@components/Objects/Loaders/PageLoading'
+import Toast from '@components/StyledElements/Toast/Toast'
+import ToolTip from '@components/StyledElements/Tooltip/Tooltip'
+import { getAPIUrl } from '@services/config/config'
+import { inviteBatchUsers } from '@services/organizations/invites'
+import { swrFetcher } from '@services/utils/ts/requests'
+import { Info, Shield, UserPlus } from 'lucide-react'
+import React, { useEffect } from 'react'
+import toast from 'react-hot-toast'
+import useSWR, { mutate } from 'swr'
+
+function OrgUsersAdd() {
+ const org = useOrg() as any
+ const [isLoading, setIsLoading] = React.useState(false)
+ const [invitedUsers, setInvitedUsers] = React.useState('');
+ const [selectedInviteCode, setSelectedInviteCode] = React.useState('');
+
+ async function sendInvites() {
+ setIsLoading(true)
+ let res = await inviteBatchUsers(org.id, invitedUsers, selectedInviteCode)
+ if (res.status == 200) {
+ mutate(`${getAPIUrl()}orgs/${org?.id}/invites/users`)
+ setIsLoading(false)
+ } else {
+ toast.error('Error ' + res.status + ': ' + res.data.detail)
+ setIsLoading(false)
+ }
+
+ }
+
+ const { data: invites } = useSWR(
+ org ? `${getAPIUrl()}orgs/${org?.id}/invites` : null,
+ swrFetcher
+ )
+ const { data: invited_users } = useSWR(
+ org ? `${getAPIUrl()}orgs/${org?.id}/invites/users` : null,
+ swrFetcher
+ )
+
+ useEffect(() => {
+ if (invites) {
+ setSelectedInviteCode(invites?.[0]?.invite_code_uuid)
+ }
+ console.log('dev,',selectedInviteCode)
+ }
+ , [invites, invited_users])
+
+ return (
+ <>
+
+ {!isLoading ? (
+ <>
+
+
+
+
Invite users to your Organization
+
+ {' '}
+ Send invite via email, separated by comma{' '}
+
+
+
+
+
+
+
+
+
Invite Code
+
+
+
+
+
+
+ Send invites via email
+
+
+
+
+
+
+
+ Invited Users
+
+
+ {' '}
+ Users who have been invited to join your organization{' '}
+
+
+
+
+
+ | Email |
+ Signup Status |
+ Email sent |
+
+
+ <>
+
+ {invited_users?.map((invited_user: any) => (
+
+ | {invited_user.email} |
+ {invited_user.pending ? Pending : Signed } |
+ {invited_user.email_sent ? Sent : No } |
+
+
+
+ ))}
+
+ >
+
+
+
+
+ >
+ ) : (
+
+ )
+ }
+ >
+ )
+}
+
+export default OrgUsersAdd
\ No newline at end of file
diff --git a/apps/web/services/organizations/invites.ts b/apps/web/services/organizations/invites.ts
index cdaca243..06fdbd10 100644
--- a/apps/web/services/organizations/invites.ts
+++ b/apps/web/services/organizations/invites.ts
@@ -42,3 +42,16 @@ export async function validateInviteCode(org_id: any, invite_code: string) {
const res = await getResponseMetadata(result)
return res
}
+
+export async function inviteBatchUsers(
+ org_id: any,
+ emails: string,
+ invite_code_uuid: string
+) {
+ const result = await fetch(
+ `${getAPIUrl()}orgs/${org_id}/invites/users/batch?emails=${emails}&invite_code_uuid=${invite_code_uuid}`,
+ RequestBody('POST', null, null)
+ )
+ const res = await getResponseMetadata(result)
+ return res
+}
diff --git a/package.json b/package.json
index 4417c76c..250e5da7 100644
--- a/package.json
+++ b/package.json
@@ -4,7 +4,7 @@
"build": "turbo run build",
"dev": "turbo run dev",
"lint": "turbo run lint",
- "format": "prettier --write \"**/*.{ts,tsx,md}\"",
+ "format": "prettier --write \"**/*.{ts,tsx,md}\""
},
"devDependencies": {
"eslint": "^8.51.0",