import { useApolloAdminClient } from '@/settings/admin-panel/apollo/hooks/useApolloAdminClient'; import { TwoFactorAuthenticationVerificationCodeDash } from '@/settings/two-factor-authentication/components/TwoFactorAuthenticationVerificationCodeDash'; import { TwoFactorAuthenticationVerificationCodeSlot } from '@/settings/two-factor-authentication/components/TwoFactorAuthenticationVerificationCodeSlot'; import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown'; import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal'; import { useModal } from '@/ui/layout/modal/hooks/useModal'; import { CombinedGraphQLErrors } from '@apollo/client/errors'; import { useMutation, useQuery } from '@apollo/client/react'; import { styled } from '@linaria/react'; import { t } from '@lingui/core/macro'; import { OTPInput } from 'input-otp'; import { useState } from 'react'; import { Status } from 'twenty-ui/data-display'; import { IconDotsVertical } from 'twenty-ui/icon'; import { LightIconButton } from 'twenty-ui/input'; import { MenuItem } from 'twenty-ui/navigation'; import { themeCssVariables } from 'twenty-ui/theme-constants'; import { GetServerAdminsDocument, UpdateServerAdminAccessDocument, } from '~/generated-admin/graphql'; type ServerAdminAccessUpdate = { canAccessFullAdminPanel?: boolean; canImpersonate?: boolean; }; type PendingServerAdminChange = { description: string; isRevoking: boolean; update: ServerAdminAccessUpdate; }; const SERVER_ADMIN_ACCESS_CONFIRMATION_MODAL_ID = 'server-admin-access-confirmation'; const StyledValue = styled.div` align-items: center; display: flex; gap: ${themeCssVariables.spacing[1]}; `; const StyledChips = styled.div` align-items: center; display: flex; flex: 1; gap: ${themeCssVariables.spacing[2]}; `; const StyledNoAccess = styled.span` color: ${themeCssVariables.font.color.tertiary}; flex: 1; `; const StyledConfirmationContent = styled.div` align-items: center; display: flex; flex-direction: column; gap: ${themeCssVariables.spacing[4]}; `; const StyledOTPContainer = styled.div` display: flex; gap: ${themeCssVariables.spacing[1]}; `; export const SettingsAdminServerAdminAccess = ({ userId, userLabel, }: { userId: string; userLabel: string; }) => { const dropdownId = `server-admin-access-${userId}`; const apolloAdminClient = useApolloAdminClient(); const { openModal } = useModal(); const { closeDropdown } = useCloseDropdown(); const { enqueueSuccessSnackBar, enqueueErrorSnackBar } = useSnackBar(); const [pendingChange, setPendingChange] = useState(null); const [otp, setOtp] = useState(''); const { data, refetch } = useQuery(GetServerAdminsDocument, { client: apolloAdminClient, }); const [updateServerAdminAccess] = useMutation( UpdateServerAdminAccessDocument, { client: apolloAdminClient }, ); const serverAdmins = data?.getServerAdmins ?? []; const currentAccess = serverAdmins.find((admin) => admin.id === userId); const canAccessFullAdminPanel = currentAccess?.canAccessFullAdminPanel ?? false; const canImpersonate = currentAccess?.canImpersonate ?? false; const fullAdminCount = serverAdmins.filter( (admin) => admin.canAccessFullAdminPanel, ).length; const isLastFullAdmin = canAccessFullAdminPanel && fullAdminCount <= 1; const hasAnyAccess = canAccessFullAdminPanel || canImpersonate; const hasFullAccess = canAccessFullAdminPanel && canImpersonate; const requestChange = (change: PendingServerAdminChange) => { closeDropdown(dropdownId); setOtp(''); setPendingChange(change); openModal(SERVER_ADMIN_ACCESS_CONFIRMATION_MODAL_ID); }; const handleConfirm = async () => { if (pendingChange === null) { return; } try { await updateServerAdminAccess({ variables: { userId, otp: otp.length > 0 ? otp : undefined, ...pendingChange.update, }, }); await refetch(); enqueueSuccessSnackBar({ message: t`Server administrator access updated.`, }); } catch (error) { enqueueErrorSnackBar({ ...(CombinedGraphQLErrors.is(error) ? { apolloError: error } : { message: t`Failed to update server administrator access.` }), }); } finally { setOtp(''); setPendingChange(null); } }; return ( <> {hasAnyAccess ? ( {canAccessFullAdminPanel && ( )} {canImpersonate && ( )} ) : ( {t`No access`} )} } dropdownComponents={ requestChange({ description: t`full admin panel access`, isRevoking: canAccessFullAdminPanel, update: { canAccessFullAdminPanel: !canAccessFullAdminPanel, }, }) } /> requestChange({ description: t`impersonation`, isRevoking: canImpersonate, update: { canImpersonate: !canImpersonate }, }) } /> {!hasFullAccess && ( requestChange({ description: t`full server access`, isRevoking: false, update: { canAccessFullAdminPanel: true, canImpersonate: true, }, }) } /> )} } /> { setOtp(''); setPendingChange(null); }} subtitle={
{pendingChange?.isRevoking ? t`This will revoke ${pendingChange?.description ?? ''} for ${userLabel}.` : t`This will grant ${pendingChange?.description ?? ''} to ${userLabel}.`}
{t`Enter your two-factor authentication code to confirm.`}
( {slots.slice(0, 3).map((slot, index) => ( ))} {slots.slice(3).map((slot, index) => ( ))} )} />
} /> ); };