import { observer } from "mobx-react"; import { CloseIcon, CopyIcon } from "outline-icons"; import * as React from "react"; import { useTranslation, Trans } from "react-i18next"; import { Link } from "react-router-dom"; import { toast } from "sonner"; import styled from "styled-components"; import { s } from "@shared/styles"; import { UserRole } 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 { ResizingHeightContainer } from "~/components/ResizingHeightContainer"; 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"; type Props = { onSubmit: () => void; }; type InviteRequest = { email: string; name: string; role: UserRole; }; function Invite({ onSubmit }: Props) { const [isSaving, setIsSaving] = React.useState(false); const [invites, setInvites] = React.useState([ { email: "", name: "", role: UserRole.Member, }, ]); const { users } = useStores(); 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.filter((i) => i.email)); onSubmit(); if (data.sent.length > 0) { toast.success(t("We sent out your invites!")); } else { toast.message(t("Those email addresses are already invited")); } } catch (err) { toast.error(err.message); } finally { setIsSaving(false); } }, [onSubmit, 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) { toast.message( t("Sorry, you can only send {{MAX_INVITES}} invites at a time", { MAX_INVITES: UserValidation.maxInvitesPerRequest, }) ); } setInvites((prevInvites) => { const newInvites = [...prevInvites]; newInvites.push({ email: "", name: "", role: invites[invites.length - 1].role, }); return newInvites; }); }, [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(() => { toast.success(t("Share link copied")); }, [t]); const handleRoleChange = React.useCallback( (role: UserRole, index: number) => { setInvites((prevInvites) => { const newInvites = [...prevInvites]; newInvites[index]["role"] = role; return newInvites; }); }, [] ); const handleKeyDown = React.useCallback( (ev: React.KeyboardEvent) => { if (ev.key === "Enter") { ev.preventDefault(); handleAdd(); } }, [handleAdd] ); return (
{team.guestSignin ? ( ) : ( {" "} {can.update && ( As an admin you can also{" "} enable email sign-in. )} )} {team.subdomain && ( ) : ( )}
); } const CopyBlock = styled("div")` margin: 2em 0; font-size: 14px; background: ${s("secondaryBackground")}; border-radius: 8px; padding: 16px 16px 8px; `; const Remove = styled("div")` color: ${s("textTertiary")}; margin-top: 4px; margin-right: -32px; `; export default observer(Invite);