2bb7fb2e9b
## Summary - Reuse the shared settings title presentation for read-only and editable settings page titles. - Polish settings AI/app icons, breadcrumb cropping, and admin detail title icons. - Align admin panel table/card typography and spacing with existing settings tables. ## Before/After <img width="1524" height="2214" alt="Settings pages before and after" src="https://github.com/user-attachments/assets/91037b25-b442-4eb2-b244-1d8280ce2cd9" /> <img width="2200" height="3268" alt="Additional settings UI before and after" src="https://github.com/user-attachments/assets/f10d7283-7031-4958-8539-649c3067cd31" /> <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/22305?utm_source=github" target="_blank" rel="noopener noreferrer" data-no-image-dialog="true"><picture><source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a> <!-- End of auto-generated description by cubic. -->
98 lines
3.8 KiB
TypeScript
98 lines
3.8 KiB
TypeScript
import { useApolloAdminClient } from '@/settings/admin-panel/apollo/hooks/useApolloAdminClient';
|
|
import { SettingsSectionSkeletonLoader } from '@/settings/components/SettingsSectionSkeletonLoader';
|
|
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 { useQuery } from '@apollo/client/react';
|
|
import { styled } from '@linaria/react';
|
|
import { t } from '@lingui/core/macro';
|
|
import { useContext } from 'react';
|
|
import { SettingsPath } from 'twenty-shared/types';
|
|
import { getSettingsPath } from 'twenty-shared/utils';
|
|
import { IconChevronRight } from 'twenty-ui/icon';
|
|
import { OverflowingTextWithTooltip } from 'twenty-ui/surfaces';
|
|
import { H2Title } from 'twenty-ui/typography';
|
|
import { Section } from 'twenty-ui/layout';
|
|
import { ThemeContext, themeCssVariables } from 'twenty-ui/theme-constants';
|
|
import { GetServerAdminsDocument } from '~/generated-admin/graphql';
|
|
|
|
const SERVER_ADMINS_GRID_TEMPLATE_COLUMNS = '1fr 2fr 1fr 36px';
|
|
|
|
const StyledEmptyState = styled.div`
|
|
color: ${themeCssVariables.font.color.tertiary};
|
|
padding: ${themeCssVariables.spacing[4]} 0;
|
|
`;
|
|
|
|
export const SettingsAdminServerAdmins = () => {
|
|
const { theme } = useContext(ThemeContext);
|
|
const apolloAdminClient = useApolloAdminClient();
|
|
|
|
const { data, loading, error } = useQuery(GetServerAdminsDocument, {
|
|
client: apolloAdminClient,
|
|
});
|
|
|
|
const serverAdmins = data?.getServerAdmins ?? [];
|
|
|
|
return (
|
|
<Section>
|
|
<H2Title
|
|
title={t`Administrators`}
|
|
description={t`Users with server-level access. Open a user to grant or revoke access; use the search below to find anyone.`}
|
|
/>
|
|
{loading ? (
|
|
<SettingsSectionSkeletonLoader />
|
|
) : error ? (
|
|
<StyledEmptyState>{t`Failed to load server administrators.`}</StyledEmptyState>
|
|
) : serverAdmins.length === 0 ? (
|
|
<StyledEmptyState>{t`No server administrators found.`}</StyledEmptyState>
|
|
) : (
|
|
<Table>
|
|
<TableRow gridTemplateColumns={SERVER_ADMINS_GRID_TEMPLATE_COLUMNS}>
|
|
<TableHeader>{t`Administrator`}</TableHeader>
|
|
<TableHeader>{t`Admin panel`}</TableHeader>
|
|
<TableHeader>{t`Impersonation`}</TableHeader>
|
|
<TableHeader />
|
|
</TableRow>
|
|
<TableBody>
|
|
{serverAdmins.map((admin) => {
|
|
const adminLabel =
|
|
`${admin.firstName || ''} ${admin.lastName || ''}`.trim() ||
|
|
admin.email;
|
|
|
|
return (
|
|
<TableRow
|
|
key={admin.id}
|
|
gridTemplateColumns={SERVER_ADMINS_GRID_TEMPLATE_COLUMNS}
|
|
to={getSettingsPath(SettingsPath.AdminPanelUserDetail, {
|
|
userId: admin.id,
|
|
})}
|
|
>
|
|
<TableCell
|
|
color={themeCssVariables.font.color.primary}
|
|
overflow="hidden"
|
|
>
|
|
<OverflowingTextWithTooltip text={adminLabel} />
|
|
</TableCell>
|
|
<TableCell>
|
|
{admin.canAccessFullAdminPanel ? t`Yes` : '—'}
|
|
</TableCell>
|
|
<TableCell>{admin.canImpersonate ? t`Yes` : '—'}</TableCell>
|
|
<TableCell align="center">
|
|
<IconChevronRight
|
|
size={theme.icon.size.md}
|
|
stroke={theme.icon.stroke.sm}
|
|
color={theme.font.color.tertiary}
|
|
/>
|
|
</TableCell>
|
|
</TableRow>
|
|
);
|
|
})}
|
|
</TableBody>
|
|
</Table>
|
|
)}
|
|
</Section>
|
|
);
|
|
};
|