From 252922b522e5622e440c39361a8b4e86cea72ae5 Mon Sep 17 00:00:00 2001 From: nitin <142569587+ehconitin@users.noreply.github.com> Date: Tue, 11 Feb 2025 22:40:28 +0530 Subject: [PATCH] Admin panel refactor (#10119) addressing > There are two patterns to avoid: Creating functions that return JSX like renderThing() -> this was taken already addressed in https://github.com/twentyhq/twenty/pull/10011 Making a hook that "stores" all the logic of a component - > this PR is addressing this particular pattern In essence, handlers should remain in the component and be connected to their events. And everything in a handler can be abstracted into its dedicated hook. For example: const { myReactiveState } = useRecoilValue(myReactiveStateComponentState); const { removeThingFromOtherThing } = useRemoveThingFromOtherThing(); const handleClick = () => { if (isDefined(myReactiveState)) { removeThingFromOtherThing(); } } Broadly speaking, this is how you can split large components into several sub-hooks. --------- Co-authored-by: Lucas Bordeau --- .../components/SettingsAdminEnvVariables.tsx | 115 ++++++++++-------- .../components/SettingsAdminGeneral.tsx | 72 +++++------ .../SettingsAdminWorkspaceContent.tsx | 105 ++++++++++++++-- .../admin-panel/hooks/useFeatureFlag.ts | 63 ---------- .../admin-panel/hooks/useFeatureFlagState.ts | 36 ++++++ .../admin-panel/hooks/useImpersonate.ts | 78 ------------ .../admin-panel/hooks/useImpersonationAuth.ts | 18 +++ .../hooks/useImpersonationRedirect.ts | 21 ++++ .../admin-panel/hooks/useUserLookup.ts | 42 ------- .../states/adminPanelErrorState.ts | 6 - .../environment-variables-group-metadata.ts | 6 +- 11 files changed, 266 insertions(+), 296 deletions(-) delete mode 100644 packages/twenty-front/src/modules/settings/admin-panel/hooks/useFeatureFlag.ts create mode 100644 packages/twenty-front/src/modules/settings/admin-panel/hooks/useFeatureFlagState.ts delete mode 100644 packages/twenty-front/src/modules/settings/admin-panel/hooks/useImpersonate.ts create mode 100644 packages/twenty-front/src/modules/settings/admin-panel/hooks/useImpersonationAuth.ts create mode 100644 packages/twenty-front/src/modules/settings/admin-panel/hooks/useImpersonationRedirect.ts delete mode 100644 packages/twenty-front/src/modules/settings/admin-panel/hooks/useUserLookup.ts delete mode 100644 packages/twenty-front/src/modules/settings/admin-panel/states/adminPanelErrorState.ts diff --git a/packages/twenty-front/src/modules/settings/admin-panel/components/SettingsAdminEnvVariables.tsx b/packages/twenty-front/src/modules/settings/admin-panel/components/SettingsAdminEnvVariables.tsx index ab3e0485b..56239efd3 100644 --- a/packages/twenty-front/src/modules/settings/admin-panel/components/SettingsAdminEnvVariables.tsx +++ b/packages/twenty-front/src/modules/settings/admin-panel/components/SettingsAdminEnvVariables.tsx @@ -36,6 +36,8 @@ const StyledShowMoreButton = styled(Button)<{ isSelected?: boolean }>` `} `; +const StyledEnvVariablesDescription = styled.div``; + export const SettingsAdminEnvVariables = () => { const { data: environmentVariables } = useGetEnvironmentVariablesGroupedQuery( { @@ -65,59 +67,68 @@ export const SettingsAdminEnvVariables = () => { ); return ( -
- {visibleGroups.map((group) => ( - - - {group.description !== '' && ( - {group.description} - )} - {group.variables.length > 0 && ( - - - - )} - - ))} + <> + + These are only the server values. Ensure your worker environment has the + same variables and values, this is required for asynchronous tasks like + email sync. + +
+ {visibleGroups.map((group) => ( + + + {group.description !== '' && ( + + {group.description} + + )} + {group.variables.length > 0 && ( + + + + )} + + ))} - {hiddenGroups.length > 0 && ( - <> - - {hiddenGroups.map((group) => ( - toggleGroupVisibility(group.name)} - title={group.name} - variant="secondary" - isSelected={selectedGroup === group.name} - > - {group.name} variables - - ))} - + {hiddenGroups.length > 0 && ( + <> + + {hiddenGroups.map((group) => ( + toggleGroupVisibility(group.name)} + title={group.name} + variant="secondary" + isSelected={selectedGroup === group.name} + > + {group.name} variables + + ))} + - {selectedGroupData && ( - - - {selectedGroupData.description !== '' && ( - - {selectedGroupData.description} - - )} - {selectedGroupData.variables.length > 0 && ( - - - - )} - - )} - - )} -
+ {selectedGroupData && ( + + + {selectedGroupData.description !== '' && ( + + {selectedGroupData.description} + + )} + {selectedGroupData.variables.length > 0 && ( + + + + )} + + )} + + )} +
+ ); }; diff --git a/packages/twenty-front/src/modules/settings/admin-panel/components/SettingsAdminGeneral.tsx b/packages/twenty-front/src/modules/settings/admin-panel/components/SettingsAdminGeneral.tsx index 356c0b425..0632b0966 100644 --- a/packages/twenty-front/src/modules/settings/admin-panel/components/SettingsAdminGeneral.tsx +++ b/packages/twenty-front/src/modules/settings/admin-panel/components/SettingsAdminGeneral.tsx @@ -1,10 +1,9 @@ import { canManageFeatureFlagsState } from '@/client-config/states/canManageFeatureFlagsState'; import { SettingsAdminWorkspaceContent } from '@/settings/admin-panel/components/SettingsAdminWorkspaceContent'; import { SETTINGS_ADMIN_USER_LOOKUP_WORKSPACE_TABS_ID } from '@/settings/admin-panel/constants/SettingsAdminUserLookupWorkspaceTabsId'; -import { useImpersonate } from '@/settings/admin-panel/hooks/useImpersonate'; -import { useUserLookup } from '@/settings/admin-panel/hooks/useUserLookup'; -import { adminPanelErrorState } from '@/settings/admin-panel/states/adminPanelErrorState'; import { userLookupResultState } from '@/settings/admin-panel/states/userLookupResultState'; +import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar'; +import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; import { TextInput } from '@/ui/input/components/TextInput'; import { TabList } from '@/ui/layout/tab/components/TabList'; import { useTabList } from '@/ui/layout/tab/hooks/useTabList'; @@ -12,7 +11,7 @@ import { DEFAULT_WORKSPACE_LOGO } from '@/ui/navigation/navigation-drawer/consta import styled from '@emotion/styled'; import { isNonEmptyString } from '@sniptt/guards'; import { useState } from 'react'; -import { useRecoilValue } from 'recoil'; +import { useRecoilState, useRecoilValue } from 'recoil'; import { getImageAbsoluteURI, isDefined } from 'twenty-shared'; import { Button, @@ -23,6 +22,7 @@ import { Section, } from 'twenty-ui'; import { REACT_APP_SERVER_BASE_URL } from '~/config'; +import { useUserLookupAdminPanelMutation } from '~/generated/graphql'; const StyledLinkContainer = styled.div` margin-right: ${({ theme }) => theme.spacing(2)}; @@ -35,11 +35,6 @@ const StyledContainer = styled.div` flex-direction: row; `; -const StyledErrorSection = styled.div` - color: ${({ theme }) => theme.font.color.danger}; - margin-top: ${({ theme }) => theme.spacing(2)}; -`; - const StyledUserInfo = styled.div` margin-bottom: ${({ theme }) => theme.spacing(5)}; `; @@ -60,40 +55,48 @@ const StyledContentContainer = styled.div` export const SettingsAdminGeneral = () => { const [userIdentifier, setUserIdentifier] = useState(''); - const [userId, setUserId] = useState(''); - - const { error: impersonateError } = useImpersonate(); + const { enqueueSnackBar } = useSnackBar(); const { activeTabId, setActiveTabId } = useTabList( SETTINGS_ADMIN_USER_LOOKUP_WORKSPACE_TABS_ID, ); - const userLookupResult = useRecoilValue(userLookupResultState); - const adminPanelError = useRecoilValue(adminPanelErrorState); + const [userLookupResult, setUserLookupResult] = useRecoilState( + userLookupResultState, + ); + const [isUserLookupLoading, setIsUserLookupLoading] = useState(false); - const { handleUserLookup, isLoading } = useUserLookup(); + const [userLookup] = useUserLookupAdminPanelMutation(); const canManageFeatureFlags = useRecoilValue(canManageFeatureFlagsState); const handleSearch = async () => { setActiveTabId(''); + setIsUserLookupLoading(true); + setUserLookupResult(null); - const result = await handleUserLookup(userIdentifier); + const response = await userLookup({ + variables: { userIdentifier }, + onCompleted: (data) => { + setIsUserLookupLoading(false); + if (isDefined(data?.userLookupAdminPanel)) { + setUserLookupResult(data.userLookupAdminPanel); + } + }, + onError: (error) => { + setIsUserLookupLoading(false); + enqueueSnackBar(error.message, { + variant: SnackBarVariant.Error, + }); + }, + }); - if (isDefined(result?.user?.id) && !adminPanelError) { - setUserId(result.user.id.trim()); - } + const result = response.data?.userLookupAdminPanel; - if ( - isDefined(result?.workspaces) && - result.workspaces.length > 0 && - !adminPanelError - ) { + if (isDefined(result?.workspaces) && result.workspaces.length > 0) { setActiveTabId(result.workspaces[0].id); } }; - const shouldShowUserData = userLookupResult && !adminPanelError; - const activeWorkspace = userLookupResult?.workspaces.find( (workspace) => workspace.id === activeTabId, ); @@ -139,7 +142,7 @@ export const SettingsAdminGeneral = () => { onInputEnter={handleSearch} placeholder="Enter user ID or email address" fullWidth - disabled={isLoading} + disabled={isUserLookupLoading} />