import { observer } from "mobx-react"; import { LinkIcon, CloseIcon } from "outline-icons"; import * as React from "react"; import { useTranslation, Trans } from "react-i18next"; import { Link } from "react-router-dom"; import styled from "styled-components"; import { Role } from "@shared/types"; import { UserValidation } from "@shared/validations"; import Button from "~/components/Button"; import CopyToClipboard from "~/components/CopyToClipboard"; import Flex from "~/components/Flex"; import Input from "~/components/Input"; import InputSelectRole from "~/components/InputSelectRole"; import NudeButton from "~/components/NudeButton"; import Text from "~/components/Text"; import Tooltip from "~/components/Tooltip"; import useCurrentTeam from "~/hooks/useCurrentTeam"; import useCurrentUser from "~/hooks/useCurrentUser"; import usePolicy from "~/hooks/usePolicy"; import useStores from "~/hooks/useStores"; import useToasts from "~/hooks/useToasts"; type Props = { onSubmit: () => void; }; type InviteRequest = { email: string; name: string; role: Role; }; function Invite({ onSubmit }: Props) { const [isSaving, setIsSaving] = React.useState(false); const [linkCopied, setLinkCopied] = React.useState(false); const [invites, setInvites] = React.useState([ { email: "", name: "", role: "member", }, { email: "", name: "", role: "member", }, { email: "", name: "", role: "member", }, ]); const { users } = useStores(); const { showToast } = useToasts(); const user = useCurrentUser(); const team = useCurrentTeam(); const { t } = useTranslation(); const predictedDomain = user.email.split("@")[1]; const can = usePolicy(team); const handleSubmit = React.useCallback( async (ev: React.SyntheticEvent) => { ev.preventDefault(); setIsSaving(true); try { const data = await users.invite(invites); onSubmit(); if (data.sent.length > 0) { showToast(t("We sent out your invites!"), { type: "success", }); } else { showToast(t("Those email addresses are already invited"), { type: "success", }); } } catch (err) { showToast(err.message, { type: "error", }); } finally { setIsSaving(false); } }, [onSubmit, showToast, invites, t, users] ); const handleChange = React.useCallback((ev, index) => { setInvites((prevInvites) => { const newInvites = [...prevInvites]; newInvites[index][ev.target.name] = ev.target.value; return newInvites; }); }, []); const handleAdd = React.useCallback(() => { if (invites.length >= UserValidation.maxInvitesPerRequest) { showToast( t("Sorry, you can only send {{MAX_INVITES}} invites at a time", { MAX_INVITES: UserValidation.maxInvitesPerRequest, }), { type: "warning", } ); } setInvites((prevInvites) => { const newInvites = [...prevInvites]; newInvites.push({ email: "", name: "", role: "member", }); return newInvites; }); }, [showToast, invites, t]); const handleRemove = React.useCallback( (ev: React.SyntheticEvent, index: number) => { ev.preventDefault(); setInvites((prevInvites) => { const newInvites = [...prevInvites]; newInvites.splice(index, 1); return newInvites; }); }, [] ); const handleCopy = React.useCallback(() => { setLinkCopied(true); showToast(t("Share link copied"), { type: "success", }); }, [showToast, t]); const handleRoleChange = React.useCallback((role: Role, index: number) => { setInvites((prevInvites) => { const newInvites = [...prevInvites]; newInvites[index]["role"] = role; return newInvites; }); }, []); return (
{team.guestSignin ? ( ) : ( {" "} {can.update && ( As an admin you can also{" "} enable email sign-in. )} )} {team.subdomain && (    )} {invites.map((invite, index) => ( handleChange(ev, index)} placeholder={`example@${predictedDomain}`} value={invite.email} required={index === 0} autoFocus={index === 0} flex /> handleChange(ev, index)} value={invite.name} required={!!invite.email} flex /> handleRoleChange(role, index)} value={invite.role} labelHidden={index !== 0} short /> {index !== 0 && ( handleRemove(ev, index)}> )} {index === 0 && invites.length > 1 && ( )} ))} {invites.length <= UserValidation.maxInvitesPerRequest ? ( ) : ( )}
); } const CopyBlock = styled("div")` margin: 2em 0; font-size: 14px; background: ${(props) => props.theme.secondaryBackground}; border-radius: 8px; padding: 16px 16px 8px; `; const Remove = styled("div")` margin-top: 4px; `; const Spacer = styled.div` width: 24px; height: 24px; `; export default observer(Invite);