import { PaperclipIcon, UserIcon, Users } from "lucide-react"; import { Trans } from "next-i18next"; import { useMemo, useState } from "react"; import type { FormEvent } from "react"; import { Controller, useForm } from "react-hook-form"; import { classNames } from "@calcom/lib"; import { IS_TEAM_BILLING_ENABLED, WEBAPP_URL } from "@calcom/lib/constants"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { MembershipRole } from "@calcom/prisma/enums"; import { trpc } from "@calcom/trpc"; import { Button, Checkbox as CheckboxField, Dialog, DialogContent, DialogFooter, Form, Label, showToast, TextField, ToggleGroup, Select, TextAreaField, } from "@calcom/ui"; import { Link } from "@calcom/ui/components/icon"; import type { PendingMember } from "../lib/types"; import { GoogleWorkspaceInviteButton } from "./GoogleWorkspaceInviteButton"; type MemberInvitationModalProps = { isOpen: boolean; onExit: () => void; onSubmit: (values: NewMemberForm, resetFields: () => void) => void; onSettingsOpen?: () => void; teamId: number; members: PendingMember[]; token?: string; }; type MembershipRoleOption = { value: MembershipRole; label: string; }; export interface NewMemberForm { emailOrUsername: string | string[]; role: MembershipRole; sendInviteEmail: boolean; } type ModalMode = "INDIVIDUAL" | "BULK"; interface FileEvent extends FormEvent { target: EventTarget & T; } export default function MemberInvitationModal(props: MemberInvitationModalProps) { const { t } = useLocale(); const trpcContext = trpc.useContext(); const [modalImportMode, setModalInputMode] = useState("INDIVIDUAL"); const createInviteMutation = trpc.viewer.teams.createInvite.useMutation({ onSuccess(token) { copyInviteLinkToClipboard(token); trpcContext.viewer.teams.get.invalidate(); trpcContext.viewer.teams.list.invalidate(); }, onError: (error) => { showToast(error.message, "error"); }, }); const copyInviteLinkToClipboard = async (token: string) => { const inviteLink = `${WEBAPP_URL}/teams?token=${token}`; await navigator.clipboard.writeText(inviteLink); showToast(t("invite_link_copied"), "success"); }; const options: MembershipRoleOption[] = useMemo(() => { return [ { value: MembershipRole.MEMBER, label: t("member") }, { value: MembershipRole.ADMIN, label: t("admin") }, { value: MembershipRole.OWNER, label: t("owner") }, ]; }, [t]); const newMemberFormMethods = useForm(); const validateUniqueInvite = (value: string) => { return !( props.members.some((member) => member?.username === value) || props.members.some((member) => member?.email === value) ); }; const handleFileUpload = (e: FileEvent) => { if (!e.target.files?.length) { return; } const file = e.target.files[0]; if (file) { const reader = new FileReader(); reader.onload = (e) => { const contents = e?.target?.result as string; const values = contents?.split(",").map((email) => email.trim().toLocaleLowerCase()); newMemberFormMethods.setValue("emailOrUsername", values); }; reader.readAsText(file); } }; const resetFields = () => { newMemberFormMethods.reset(); newMemberFormMethods.setValue("emailOrUsername", ""); setModalInputMode("INDIVIDUAL"); }; return ( { props.onExit(); newMemberFormMethods.reset(); }}> Note: This will cost an extra seat ($15/m){" "} on your subscription. ) : null }>
setModalInputMode(val as ModalMode)} defaultValue="INDIVIDUAL" options={[ { value: "INDIVIDUAL", label: {t("invite_team_individual_segment")}, iconLeft: , }, { value: "BULK", label: {t("invite_team_bulk_segment")}, iconLeft: , }, ]} />
props.onSubmit(values, resetFields)}>
{/* Indivdual Invite */} {modalImportMode === "INDIVIDUAL" && ( { if (typeof value === "string") return validateUniqueInvite(value) || t("member_already_invited"); }, }} render={({ field: { onChange }, fieldState: { error } }) => ( <> onChange(e.target.value.trim().toLowerCase())} /> {error && {error.message}} )} /> )} {/* Bulk Invite */} {modalImportMode === "BULK" && (
( <> {/* TODO: Make this a fancy email input that styles on a successful email. */} { const emails = e.target.value .split(",") .map((email) => email.trim().toLocaleLowerCase()); return onChange(emails); }} /> {error && {error.message}} )} /> { newMemberFormMethods.setValue("emailOrUsername", data); }} />
)} (