import { currentUserState } from '@/auth/states/currentUserState'; import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState'; import { canManageFeatureFlagsState } from '@/client-config/states/canManageFeatureFlagsState'; import { SettingsAdminTableCard } from '@/settings/admin-panel/components/SettingsAdminTableCard'; import { useFeatureFlagState } from '@/settings/admin-panel/hooks/useFeatureFlagState'; import { useImpersonationAuth } from '@/settings/admin-panel/hooks/useImpersonationAuth'; import { useImpersonationRedirect } from '@/settings/admin-panel/hooks/useImpersonationRedirect'; import { userLookupResultState } from '@/settings/admin-panel/states/userLookupResultState'; import { WorkspaceInfo } from '@/settings/admin-panel/types/WorkspaceInfo'; import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar'; import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; import { Table } from '@/ui/layout/table/components/Table'; import { TableBody } from '@/ui/layout/table/components/TableBody'; import { TableCell } from '@/ui/layout/table/components/TableCell'; import { TableHeader } from '@/ui/layout/table/components/TableHeader'; import { TableRow } from '@/ui/layout/table/components/TableRow'; import { DEFAULT_WORKSPACE_LOGO } from '@/ui/navigation/navigation-drawer/constants/DefaultWorkspaceLogo'; import styled from '@emotion/styled'; import { useLingui } from '@lingui/react/macro'; import { isNonEmptyString } from '@sniptt/guards'; import { useState } from 'react'; import { useRecoilState, useRecoilValue } from 'recoil'; import { REACT_APP_SERVER_BASE_URL } from '~/config'; import { FeatureFlagKey, useImpersonateMutation, useUpdateWorkspaceFeatureFlagMutation, } from '~/generated/graphql'; import { getImageAbsoluteURI, isDefined } from 'twenty-shared/utils'; import { AvatarChip } from 'twenty-ui/components'; import { Button, Toggle } from 'twenty-ui/input'; import { H2Title, IconEyeShare, IconHome, IconId, IconUser, } from 'twenty-ui/display'; import { Section } from 'twenty-ui/layout'; type SettingsAdminWorkspaceContentProps = { activeWorkspace: WorkspaceInfo | undefined; }; const StyledContainer = styled.div` display: flex; flex-direction: column; gap: ${({ theme }) => theme.spacing(3)}; margin-top: ${({ theme }) => theme.spacing(6)}; `; const StyledButtonContainer = styled.div` margin-top: ${({ theme }) => theme.spacing(3)}; `; export const SettingsAdminWorkspaceContent = ({ activeWorkspace, }: SettingsAdminWorkspaceContentProps) => { const canManageFeatureFlags = useRecoilValue(canManageFeatureFlagsState); const { enqueueSnackBar } = useSnackBar(); const [currentUser] = useRecoilState(currentUserState); const currentWorkspace = useRecoilValue(currentWorkspaceState); const [updateFeatureFlag] = useUpdateWorkspaceFeatureFlagMutation(); const [isImpersonateLoading, setIsImpersonationLoading] = useState(false); const { executeImpersonationAuth } = useImpersonationAuth(); const { executeImpersonationRedirect } = useImpersonationRedirect(); const [impersonate] = useImpersonateMutation(); const { updateFeatureFlagState } = useFeatureFlagState(); const userLookupResult = useRecoilValue(userLookupResultState); const { t } = useLingui(); const handleImpersonate = async (workspaceId: string) => { if (!userLookupResult?.user.id) { enqueueSnackBar(t`Please search for a user first`, { variant: SnackBarVariant.Error, }); return; } setIsImpersonationLoading(true); await impersonate({ variables: { userId: userLookupResult.user.id, workspaceId }, onCompleted: async (data) => { const { loginToken, workspace } = data.impersonate; const isCurrentWorkspace = workspace.id === currentWorkspace?.id; if (isCurrentWorkspace) { await executeImpersonationAuth(loginToken.token); return; } return executeImpersonationRedirect( workspace.workspaceUrls, loginToken.token, ); }, onError: (error) => { enqueueSnackBar(`Failed to impersonate user. ${error.message}`, { variant: SnackBarVariant.Error, }); }, }).finally(() => { setIsImpersonationLoading(false); }); }; const handleFeatureFlagUpdate = async ( workspaceId: string, featureFlag: FeatureFlagKey, value: boolean, ) => { const previousValue = userLookupResult?.workspaces .find((workspace) => workspace.id === workspaceId) ?.featureFlags.find((flag) => flag.key === featureFlag)?.value; updateFeatureFlagState(workspaceId, featureFlag, value); await updateFeatureFlag({ variables: { workspaceId, featureFlag, value, }, onError: (error) => { if (isDefined(previousValue)) { updateFeatureFlagState(workspaceId, featureFlag, previousValue); } enqueueSnackBar(`Failed to update feature flag. ${error.message}`, { variant: SnackBarVariant.Error, }); }, }); }; const workspaceInfoItems = [ { Icon: IconHome, label: t`Name`, value: ( ), }, { Icon: IconId, label: t`ID`, value: activeWorkspace?.id, }, { Icon: IconUser, label: t`Members`, value: activeWorkspace?.totalUsers, }, ]; if (!activeWorkspace) return null; return (
{currentUser?.canImpersonate && (
{canManageFeatureFlags && ( {t`Feature Flag`} {t`Status`} {activeWorkspace.featureFlags.map((flag) => ( {flag.key} handleFeatureFlagUpdate( activeWorkspace.id, flag.key, newValue, ) } /> ))}
)}
); };