Twenty config admin panel integration (#11755)
closes https://github.com/twentyhq/core-team-issues/issues/761 closes https://github.com/twentyhq/core-team-issues/issues/762 --------- Co-authored-by: Félix Malfait <felix@twenty.com>
This commit is contained in:
+2
-2
@@ -20,8 +20,8 @@ export const SettingsAdminContent = () => {
|
||||
disabled: !canAccessFullAdminPanel && !canImpersonate,
|
||||
},
|
||||
{
|
||||
id: SETTINGS_ADMIN_TABS.ENV_VARIABLES,
|
||||
title: t`Env Variables`,
|
||||
id: SETTINGS_ADMIN_TABS.CONFIG_VARIABLES,
|
||||
title: t`Config Variables`,
|
||||
Icon: IconVariable,
|
||||
disabled: !canAccessFullAdminPanel,
|
||||
},
|
||||
|
||||
-63
@@ -1,63 +0,0 @@
|
||||
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { useDebouncedCallback } from 'use-debounce';
|
||||
import { IconCopy, OverflowingTextWithTooltip } from 'twenty-ui/display';
|
||||
|
||||
const StyledEllipsisLabel = styled.div`
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
`;
|
||||
|
||||
const StyledExpandedEllipsisLabel = styled.div`
|
||||
white-space: normal;
|
||||
word-break: break-all;
|
||||
`;
|
||||
|
||||
const StyledCopyContainer = styled.span`
|
||||
cursor: pointer;
|
||||
`;
|
||||
export const SettingsAdminEnvCopyableText = ({
|
||||
text,
|
||||
displayText,
|
||||
multiline = false,
|
||||
maxRows,
|
||||
}: {
|
||||
text: string;
|
||||
displayText?: React.ReactNode;
|
||||
multiline?: boolean;
|
||||
maxRows?: number;
|
||||
}) => {
|
||||
const { enqueueSnackBar } = useSnackBar();
|
||||
const theme = useTheme();
|
||||
const { t } = useLingui();
|
||||
|
||||
const copyToClipboardDebounced = useDebouncedCallback((value: string) => {
|
||||
navigator.clipboard.writeText(value);
|
||||
enqueueSnackBar(t`Copied to clipboard!`, {
|
||||
variant: SnackBarVariant.Success,
|
||||
icon: <IconCopy size={theme.icon.size.md} />,
|
||||
});
|
||||
}, 200);
|
||||
|
||||
return (
|
||||
<StyledCopyContainer onClick={() => copyToClipboardDebounced(text)}>
|
||||
{maxRows ? (
|
||||
<OverflowingTextWithTooltip
|
||||
text={displayText?.toString() || text}
|
||||
displayedMaxRows={maxRows}
|
||||
isTooltipMultiline={multiline}
|
||||
/>
|
||||
) : multiline ? (
|
||||
<StyledExpandedEllipsisLabel>
|
||||
{displayText || text}
|
||||
</StyledExpandedEllipsisLabel>
|
||||
) : (
|
||||
<StyledEllipsisLabel>{displayText || text}</StyledEllipsisLabel>
|
||||
)}
|
||||
</StyledCopyContainer>
|
||||
);
|
||||
};
|
||||
-68
@@ -1,68 +0,0 @@
|
||||
import { SettingsAdminEnvVariablesTable } from '@/settings/admin-panel/components/SettingsAdminEnvVariablesTable';
|
||||
import { SettingsAdminTabSkeletonLoader } from '@/settings/admin-panel/components/SettingsAdminTabSkeletonLoader';
|
||||
import { SettingsListItemCardContent } from '@/settings/components/SettingsListItemCardContent';
|
||||
import { SettingsPath } from '@/types/SettingsPath';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { H2Title, IconHeartRateMonitor } from 'twenty-ui/display';
|
||||
import { Card, Section } from 'twenty-ui/layout';
|
||||
import { useGetConfigVariablesGroupedQuery } from '~/generated/graphql';
|
||||
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
|
||||
|
||||
const StyledGroupContainer = styled.div``;
|
||||
|
||||
const StyledInfoText = styled.div`
|
||||
color: ${({ theme }) => theme.font.color.secondary};
|
||||
`;
|
||||
|
||||
const StyledCard = styled(Card)`
|
||||
margin-bottom: ${({ theme }) => theme.spacing(8)};
|
||||
`;
|
||||
|
||||
export const SettingsAdminEnvVariables = () => {
|
||||
const theme = useTheme();
|
||||
const { data: configVariables, loading: configVariablesLoading } =
|
||||
useGetConfigVariablesGroupedQuery({
|
||||
fetchPolicy: 'network-only',
|
||||
});
|
||||
|
||||
const visibleGroups =
|
||||
configVariables?.getConfigVariablesGrouped.groups.filter(
|
||||
(group) => !group.isHiddenOnLoad,
|
||||
) ?? [];
|
||||
|
||||
if (configVariablesLoading) {
|
||||
return <SettingsAdminTabSkeletonLoader />;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Section>
|
||||
<StyledInfoText>
|
||||
{t`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.`}
|
||||
</StyledInfoText>
|
||||
</Section>
|
||||
{visibleGroups.map((group) => (
|
||||
<StyledGroupContainer key={group.name}>
|
||||
<H2Title title={group.name} description={group.description} />
|
||||
{group.variables.length > 0 && (
|
||||
<SettingsAdminEnvVariablesTable variables={group.variables} />
|
||||
)}
|
||||
</StyledGroupContainer>
|
||||
))}
|
||||
|
||||
<Section>
|
||||
<StyledCard rounded>
|
||||
<SettingsListItemCardContent
|
||||
label={t`Other Variables`}
|
||||
to={getSettingsPath(SettingsPath.AdminPanelOtherEnvVariables)}
|
||||
rightComponent={null}
|
||||
LeftIcon={IconHeartRateMonitor}
|
||||
LeftIconColor={theme.font.color.tertiary}
|
||||
/>
|
||||
</StyledCard>
|
||||
</Section>
|
||||
</>
|
||||
);
|
||||
};
|
||||
-170
@@ -1,170 +0,0 @@
|
||||
import { SettingsAdminEnvCopyableText } from '@/settings/admin-panel/components/SettingsAdminEnvCopyableText';
|
||||
import { SettingsAdminTableCard } from '@/settings/admin-panel/components/SettingsAdminTableCard';
|
||||
import { TableCell } from '@/ui/layout/table/components/TableCell';
|
||||
import { TableRow } from '@/ui/layout/table/components/TableRow';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { motion } from 'framer-motion';
|
||||
import { useState } from 'react';
|
||||
import { IconChevronRight, IconEye, IconEyeOff } from 'twenty-ui/display';
|
||||
import { LightIconButton } from 'twenty-ui/input';
|
||||
import { AnimatedExpandableContainer } from 'twenty-ui/layout';
|
||||
|
||||
type SettingsAdminEnvVariablesRowProps = {
|
||||
variable: {
|
||||
name: string;
|
||||
description: string;
|
||||
value: string;
|
||||
isSensitive: boolean;
|
||||
};
|
||||
isExpanded: boolean;
|
||||
onExpandToggle: (name: string) => void;
|
||||
};
|
||||
|
||||
const StyledTruncatedCell = styled(TableCell)`
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
cursor: pointer;
|
||||
`;
|
||||
|
||||
const StyledButton = styled(motion.button)`
|
||||
align-items: center;
|
||||
border: none;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding-inline: ${({ theme }) => theme.spacing(1)};
|
||||
background-color: transparent;
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
box-sizing: border-box;
|
||||
cursor: pointer;
|
||||
`;
|
||||
|
||||
const MotionIconChevronDown = motion(IconChevronRight);
|
||||
|
||||
const StyledEllipsisLabel = styled.div`
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
`;
|
||||
|
||||
const StyledValueContainer = styled.div`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
gap: ${({ theme }) => theme.spacing(1)};
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const StyledTableRow = styled(TableRow)<{ isExpanded: boolean }>`
|
||||
background-color: ${({ isExpanded, theme }) =>
|
||||
isExpanded ? theme.background.transparent.light : 'transparent'};
|
||||
`;
|
||||
|
||||
const StyledExpandableContainer = styled.div`
|
||||
width: 100%;
|
||||
padding-top: ${({ theme }) => theme.spacing(2)};
|
||||
padding-bottom: ${({ theme }) => theme.spacing(2)};
|
||||
`;
|
||||
|
||||
export const SettingsAdminEnvVariablesRow = ({
|
||||
variable,
|
||||
isExpanded,
|
||||
onExpandToggle,
|
||||
}: SettingsAdminEnvVariablesRowProps) => {
|
||||
const [showSensitiveValue, setShowSensitiveValue] = useState(false);
|
||||
const theme = useTheme();
|
||||
|
||||
const displayValue =
|
||||
variable.value === ''
|
||||
? 'null'
|
||||
: variable.isSensitive && !showSensitiveValue
|
||||
? '••••••'
|
||||
: variable.value;
|
||||
|
||||
const handleToggleVisibility = (event: React.MouseEvent) => {
|
||||
event.stopPropagation();
|
||||
setShowSensitiveValue(!showSensitiveValue);
|
||||
};
|
||||
|
||||
const environmentVariablesDetails = [
|
||||
{
|
||||
label: 'Name',
|
||||
value: <SettingsAdminEnvCopyableText text={variable.name} />,
|
||||
},
|
||||
{
|
||||
label: 'Description',
|
||||
value: (
|
||||
<SettingsAdminEnvCopyableText
|
||||
text={variable.description}
|
||||
maxRows={1}
|
||||
multiline={true}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
label: 'Value',
|
||||
value: (
|
||||
<StyledValueContainer>
|
||||
<SettingsAdminEnvCopyableText
|
||||
text={variable.value}
|
||||
displayText={displayValue}
|
||||
multiline={true}
|
||||
/>
|
||||
{variable.isSensitive && variable.value !== '' && (
|
||||
<LightIconButton
|
||||
Icon={showSensitiveValue ? IconEyeOff : IconEye}
|
||||
size="small"
|
||||
accent="secondary"
|
||||
onClick={handleToggleVisibility}
|
||||
/>
|
||||
)}
|
||||
</StyledValueContainer>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<StyledTableRow
|
||||
onClick={() => onExpandToggle(variable.name)}
|
||||
gridAutoColumns="5fr 4fr 3fr 1fr"
|
||||
isExpanded={isExpanded}
|
||||
>
|
||||
<StyledTruncatedCell color={theme.font.color.primary}>
|
||||
<StyledEllipsisLabel>{variable.name}</StyledEllipsisLabel>
|
||||
</StyledTruncatedCell>
|
||||
<StyledTruncatedCell>
|
||||
<StyledEllipsisLabel>{variable.description}</StyledEllipsisLabel>
|
||||
</StyledTruncatedCell>
|
||||
<StyledTruncatedCell align="right">
|
||||
<StyledEllipsisLabel>{displayValue}</StyledEllipsisLabel>
|
||||
</StyledTruncatedCell>
|
||||
<TableCell align="right">
|
||||
<StyledButton
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onExpandToggle(variable.name);
|
||||
}}
|
||||
>
|
||||
<MotionIconChevronDown
|
||||
size={theme.icon.size.md}
|
||||
color={theme.font.color.tertiary}
|
||||
initial={false}
|
||||
animate={{ rotate: isExpanded ? 90 : 0 }}
|
||||
/>
|
||||
</StyledButton>
|
||||
</TableCell>
|
||||
</StyledTableRow>
|
||||
<AnimatedExpandableContainer isExpanded={isExpanded} mode="fit-content">
|
||||
<StyledExpandableContainer>
|
||||
<SettingsAdminTableCard
|
||||
items={environmentVariablesDetails}
|
||||
gridAutoColumns="1fr 4fr"
|
||||
/>
|
||||
</StyledExpandableContainer>
|
||||
</AnimatedExpandableContainer>
|
||||
</>
|
||||
);
|
||||
};
|
||||
-51
@@ -1,51 +0,0 @@
|
||||
import { SettingsAdminEnvVariablesRow } from '@/settings/admin-panel/components/SettingsAdminEnvVariablesRow';
|
||||
import { Table } from '@/ui/layout/table/components/Table';
|
||||
import { TableBody } from '@/ui/layout/table/components/TableBody';
|
||||
import { TableHeader } from '@/ui/layout/table/components/TableHeader';
|
||||
import { TableRow } from '@/ui/layout/table/components/TableRow';
|
||||
import styled from '@emotion/styled';
|
||||
import { useState } from 'react';
|
||||
|
||||
const StyledTableBody = styled(TableBody)`
|
||||
border-bottom: 1px solid ${({ theme }) => theme.border.color.light};
|
||||
`;
|
||||
|
||||
type SettingsAdminEnvVariablesTableProps = {
|
||||
variables: Array<{
|
||||
name: string;
|
||||
description: string;
|
||||
value: string;
|
||||
isSensitive: boolean;
|
||||
}>;
|
||||
};
|
||||
|
||||
export const SettingsAdminEnvVariablesTable = ({
|
||||
variables,
|
||||
}: SettingsAdminEnvVariablesTableProps) => {
|
||||
const [expandedRowName, setExpandedRowName] = useState<string | null>(null);
|
||||
|
||||
const handleExpandToggle = (name: string) => {
|
||||
setExpandedRowName(expandedRowName === name ? null : name);
|
||||
};
|
||||
|
||||
return (
|
||||
<Table>
|
||||
<TableRow gridAutoColumns="5fr 4fr 3fr 1fr">
|
||||
<TableHeader>Name</TableHeader>
|
||||
<TableHeader>Description</TableHeader>
|
||||
<TableHeader align="right">Value</TableHeader>
|
||||
<TableHeader align="right"></TableHeader>
|
||||
</TableRow>
|
||||
<StyledTableBody>
|
||||
{variables.map((variable) => (
|
||||
<SettingsAdminEnvVariablesRow
|
||||
key={variable.name}
|
||||
variable={variable}
|
||||
isExpanded={expandedRowName === variable.name}
|
||||
onExpandToggle={handleExpandToggle}
|
||||
/>
|
||||
))}
|
||||
</StyledTableBody>
|
||||
</Table>
|
||||
);
|
||||
};
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
import { SettingsAdminEnvVariables } from '@/settings/admin-panel/components/SettingsAdminEnvVariables';
|
||||
import { SettingsAdminGeneral } from '@/settings/admin-panel/components/SettingsAdminGeneral';
|
||||
import { SettingsAdminConfigVariables } from '@/settings/admin-panel/config-variables/components/SettingsAdminConfigVariables';
|
||||
import { SETTINGS_ADMIN_TABS } from '@/settings/admin-panel/constants/SettingsAdminTabs';
|
||||
import { SETTINGS_ADMIN_TABS_ID } from '@/settings/admin-panel/constants/SettingsAdminTabsId';
|
||||
import { SettingsAdminHealthStatus } from '@/settings/admin-panel/health-status/components/SettingsAdminHealthStatus';
|
||||
@@ -15,8 +15,8 @@ export const SettingsAdminTabContent = () => {
|
||||
switch (activeTabId) {
|
||||
case SETTINGS_ADMIN_TABS.GENERAL:
|
||||
return <SettingsAdminGeneral />;
|
||||
case SETTINGS_ADMIN_TABS.ENV_VARIABLES:
|
||||
return <SettingsAdminEnvVariables />;
|
||||
case SETTINGS_ADMIN_TABS.CONFIG_VARIABLES:
|
||||
return <SettingsAdminConfigVariables />;
|
||||
case SETTINGS_ADMIN_TABS.HEALTH_STATUS:
|
||||
return <SettingsAdminHealthStatus />;
|
||||
default:
|
||||
|
||||
Reference in New Issue
Block a user