From a6e81cb122ee38d4bdb90253aa4ce21d91c27fce Mon Sep 17 00:00:00 2001 From: swve Date: Mon, 18 Mar 2024 23:23:03 +0000 Subject: [PATCH] feat: email invites frontend --- .devcontainer/devcontainer.json | 3 +- apps/api/src/services/email/utils.py | 10 +- .../dash/users/settings/[subpage]/page.tsx | 2 + .../Dashboard/Users/OrgAccess/OrgAccess.tsx | 9 +- .../Users/OrgUsersAdd/OrgUsersAdd.tsx | 140 ++++++++++++++++++ apps/web/services/organizations/invites.ts | 13 ++ package.json | 2 +- 7 files changed, 168 insertions(+), 11 deletions(-) create mode 100644 apps/web/components/Dashboard/Users/OrgUsersAdd/OrgUsersAdd.tsx 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() { - + Generate invite code + + 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

+ + +
+
+ +
+
+ + +
+

+ Invited Users +

+

+ {' '} + Users who have been invited to join your organization{' '} +

+
+ + + + + + + + + <> + + {invited_users?.map((invited_user: any) => ( + + + + + + + + ))} + + +
EmailSignup StatusEmail sent
{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",