Files
twenty/packages/twenty-front/src/modules/settings/admin-panel/components/SettingsAdminGeneral.tsx
Paul Rastoin 4a4e65fe4a [REFACTOR] Twenty UI multi barrel (#11301)
# Introduction
closes https://github.com/twentyhq/core-team-issues/issues/591
Same than for `twenty-shared` made in
https://github.com/twentyhq/twenty/pull/11083.

## TODO
- [x] Manual migrate twenty-website twenty-ui imports

## What's next:
- Generate barrel and migration script factorization within own package
+ tests
- Refactoring using preconstruct ? TimeBox
- Lint circular dependencies
- Lint import from barrel and forbid them

### Preconstruct
We need custom rollup plugins addition, but preconstruct does not expose
its rollup configuration. It might be possible to handle this using the
babel overrides. But was a big tunnel.
We could give it a try afterwards ! ( allowing cjs interop and stuff
like that )
Stuck to vite lib app

Closed related PRs:
- https://github.com/twentyhq/twenty/pull/11294
- https://github.com/twentyhq/twenty/pull/11203
2025-04-03 09:47:55 +00:00

206 lines
6.6 KiB
TypeScript

import { canManageFeatureFlagsState } from '@/client-config/states/canManageFeatureFlagsState';
import { SettingsAdminWorkspaceContent } from '@/settings/admin-panel/components/SettingsAdminWorkspaceContent';
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 { DEFAULT_WORKSPACE_LOGO } from '@/ui/navigation/navigation-drawer/constants/DefaultWorkspaceLogo';
import styled from '@emotion/styled';
import { t } from '@lingui/core/macro';
import { isNonEmptyString } from '@sniptt/guards';
import { useState } from 'react';
import { useRecoilState, useRecoilValue } from 'recoil';
import { REACT_APP_SERVER_BASE_URL } from '~/config';
import { useUserLookupAdminPanelMutation } from '~/generated/graphql';
import { currentUserState } from '@/auth/states/currentUserState';
import { SettingsAdminTableCard } from '@/settings/admin-panel/components/SettingsAdminTableCard';
import { SettingsAdminVersionContainer } from '@/settings/admin-panel/components/SettingsAdminVersionContainer';
import { SETTINGS_ADMIN_USER_LOOKUP_WORKSPACE_TABS_ID } from '@/settings/admin-panel/constants/SettingsAdminUserLookupWorkspaceTabsId';
import { activeTabIdComponentState } from '@/ui/layout/tab/states/activeTabIdComponentState';
import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2';
import { getImageAbsoluteURI, isDefined } from 'twenty-shared/utils';
import { Button } from 'twenty-ui/input';
import {
H2Title,
IconId,
IconMail,
IconSearch,
IconUser,
} from 'twenty-ui/display';
import { Section } from 'twenty-ui/layout';
const StyledContainer = styled.div`
align-items: center;
display: flex;
flex-direction: row;
gap: ${({ theme }) => theme.spacing(2)};
`;
export const SettingsAdminGeneral = () => {
const [userIdentifier, setUserIdentifier] = useState('');
const { enqueueSnackBar } = useSnackBar();
const [activeTabId, setActiveTabId] = useRecoilComponentStateV2(
activeTabIdComponentState,
SETTINGS_ADMIN_USER_LOOKUP_WORKSPACE_TABS_ID,
);
const [userLookupResult, setUserLookupResult] = useRecoilState(
userLookupResultState,
);
const [isUserLookupLoading, setIsUserLookupLoading] = useState(false);
const [userLookup] = useUserLookupAdminPanelMutation();
const currentUser = useRecoilValue(currentUserState);
const canAccessFullAdminPanel = currentUser?.canAccessFullAdminPanel;
const canImpersonate = currentUser?.canImpersonate;
const canManageFeatureFlags = useRecoilValue(canManageFeatureFlagsState);
const handleSearch = async () => {
setActiveTabId('');
setIsUserLookupLoading(true);
setUserLookupResult(null);
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,
});
},
});
const result = response.data?.userLookupAdminPanel;
if (isDefined(result?.workspaces) && result.workspaces.length > 0) {
setActiveTabId(result.workspaces[0].id);
}
};
const activeWorkspace = userLookupResult?.workspaces.find(
(workspace) => workspace.id === activeTabId,
);
const tabs =
userLookupResult?.workspaces.map((workspace) => ({
id: workspace.id,
title: workspace.name,
logo:
getImageAbsoluteURI({
imageUrl: isNonEmptyString(workspace.logo)
? workspace.logo
: DEFAULT_WORKSPACE_LOGO,
baseUrl: REACT_APP_SERVER_BASE_URL,
}) ?? '',
})) ?? [];
const userFullName = `${userLookupResult?.user.firstName || ''} ${
userLookupResult?.user.lastName || ''
}`.trim();
const userInfoItems = [
{
Icon: IconUser,
label: t`Name`,
value: userFullName,
},
{
Icon: IconMail,
label: t`Email`,
value: userLookupResult?.user.email,
},
{
Icon: IconId,
label: t`ID`,
value: userLookupResult?.user.id,
},
];
return (
<>
{canAccessFullAdminPanel && (
<Section>
<H2Title
title={t`About`}
description={t`Version of the application`}
/>
<SettingsAdminVersionContainer />
</Section>
)}
{canImpersonate && (
<Section>
<H2Title
title={
canManageFeatureFlags
? t`Feature Flags & Impersonation`
: t`User Impersonation`
}
description={
canManageFeatureFlags
? t`Look up users and manage their workspace feature flags or impersonate them.`
: t`Look up users to impersonate them.`
}
/>
<StyledContainer>
<TextInput
value={userIdentifier}
onChange={setUserIdentifier}
onInputEnter={handleSearch}
placeholder={t`Enter user ID or email address`}
fullWidth
disabled={isUserLookupLoading}
/>
<Button
Icon={IconSearch}
variant="primary"
accent="blue"
title={t`Search`}
onClick={handleSearch}
disabled={!userIdentifier.trim() || isUserLookupLoading}
/>
</StyledContainer>
</Section>
)}
{isDefined(userLookupResult) && (
<>
<Section>
<H2Title title={t`User Info`} description={t`About this user`} />
<SettingsAdminTableCard
items={userInfoItems}
rounded
gridAutoColumns="1fr 4fr"
/>
</Section>
<Section>
<H2Title
title={t`Workspaces`}
description={t`All workspaces this user is a member of`}
/>
<TabList
tabs={tabs}
behaveAsLinks={false}
componentInstanceId={SETTINGS_ADMIN_USER_LOOKUP_WORKSPACE_TABS_ID}
/>
<SettingsAdminWorkspaceContent activeWorkspace={activeWorkspace} />
</Section>
</>
)}
</>
);
};