import { debounce } from "lodash"; import { observer } from "mobx-react"; import { CloseIcon, PadlockIcon } from "outline-icons"; import { useState } from "react"; import * as React from "react"; import { useTranslation, Trans } from "react-i18next"; import styled from "styled-components"; import Button from "~/components/Button"; import ConfirmationDialog from "~/components/ConfirmationDialog"; import Fade from "~/components/Fade"; import Flex from "~/components/Flex"; import Heading from "~/components/Heading"; import Input from "~/components/Input"; import InputSelect from "~/components/InputSelect"; import NudeButton from "~/components/NudeButton"; import Scene from "~/components/Scene"; import Switch from "~/components/Switch"; import Text from "~/components/Text"; import Tooltip from "~/components/Tooltip"; import env from "~/env"; import useCurrentTeam from "~/hooks/useCurrentTeam"; import useStores from "~/hooks/useStores"; import useToasts from "~/hooks/useToasts"; import isCloudHosted from "~/utils/isCloudHosted"; import SettingRow from "./components/SettingRow"; function Security() { const { auth, dialogs } = useStores(); const team = useCurrentTeam(); const { t } = useTranslation(); const { showToast } = useToasts(); const [data, setData] = useState({ sharing: team.sharing, documentEmbeds: team.documentEmbeds, guestSignin: team.guestSignin, defaultUserRole: team.defaultUserRole, memberCollectionCreate: team.memberCollectionCreate, inviteRequired: team.inviteRequired, allowedDomains: team.allowedDomains, }); const authenticationMethods = team.signinMethods; const showSuccessMessage = React.useMemo( () => debounce(() => { showToast(t("Settings saved"), { type: "success", }); }, 250), [showToast, t] ); const [domainsChanged, setDomainsChanged] = useState(false); const saveData = React.useCallback( async (newData) => { try { setData(newData); await auth.updateTeam(newData); showSuccessMessage(); } catch (err) { showToast(err.message, { type: "error", }); } finally { setDomainsChanged(false); } }, [auth, showSuccessMessage, showToast] ); const handleChange = React.useCallback( async (ev: React.ChangeEvent) => { await saveData({ ...data, [ev.target.id]: ev.target.checked }); }, [data, saveData] ); const handleDefaultRoleChange = React.useCallback( async (newDefaultRole: string) => { await saveData({ ...data, defaultUserRole: newDefaultRole }); }, [data, saveData] ); const handleAllowSignupsChange = React.useCallback( async (ev: React.ChangeEvent) => { const inviteRequired = !ev.target.checked; const newData = { ...data, inviteRequired }; if (inviteRequired) { dialogs.openModal({ isCentered: true, title: t("Are you sure you want to disable authorized signups?"), content: ( { await saveData(newData); }} submitText={t("I’m sure — Disable")} savingText={`${t("Disabling")}…`} danger > , }} /> ), }); return; } await saveData(newData); }, [data, saveData, t, dialogs, authenticationMethods] ); const handleRemoveDomain = async (index: number) => { const newData = { ...data, }; newData.allowedDomains && newData.allowedDomains.splice(index, 1); setData(newData); setDomainsChanged(true); }; const handleAddDomain = () => { const newData = { ...data, allowedDomains: [...(data.allowedDomains || []), ""], }; setData(newData); }; const createOnDomainChangedHandler = (index: number) => ( ev: React.ChangeEvent ) => { const newData = { ...data }; newData.allowedDomains![index] = ev.currentTarget.value; setData(newData); setDomainsChanged(true); }; return ( }> {t("Security")} Settings that impact the access, security, and content of your knowledge base. {isCloudHosted && ( , }} /> } > )} {data.allowedDomains && data.allowedDomains.map((domain, index) => ( handleRemoveDomain(index)}> ))} {!data.allowedDomains?.length || data.allowedDomains[data.allowedDomains.length - 1] !== "" ? ( ) : ( )} {domainsChanged && ( )} ); } const Remove = styled("div")` margin-top: 6px; `; export default observer(Security);