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:
@ -0,0 +1,211 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { useState } from 'react';
|
||||
import { Form, useParams } from 'react-router-dom';
|
||||
|
||||
import { isConfigVariablesInDbEnabledState } from '@/client-config/states/isConfigVariablesInDbEnabledState';
|
||||
import { ConfigVariableHelpText } from '@/settings/admin-panel/config-variables/components/ConfigVariableHelpText';
|
||||
import { ConfigVariableValueInput } from '@/settings/admin-panel/config-variables/components/ConfigVariableValueInput';
|
||||
import { useConfigVariableActions } from '@/settings/admin-panel/config-variables/hooks/useConfigVariableActions';
|
||||
import { useConfigVariableForm } from '@/settings/admin-panel/config-variables/hooks/useConfigVariableForm';
|
||||
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
|
||||
import { SettingsSkeletonLoader } from '@/settings/components/SettingsSkeletonLoader';
|
||||
import { SettingsPath } from '@/types/SettingsPath';
|
||||
import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal';
|
||||
import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { ConfigVariableValue } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { H3Title, IconCheck, IconPencil, IconX } from 'twenty-ui/display';
|
||||
import { Button } from 'twenty-ui/input';
|
||||
import {
|
||||
ConfigSource,
|
||||
useGetDatabaseConfigVariableQuery,
|
||||
} from '~/generated/graphql';
|
||||
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
|
||||
|
||||
const StyledForm = styled(Form)`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: ${({ theme }) => theme.spacing(4)};
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const StyledH3Title = styled(H3Title)`
|
||||
margin-top: ${({ theme }) => theme.spacing(2)};
|
||||
`;
|
||||
|
||||
const StyledRow = styled.div`
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
gap: ${({ theme }) => theme.spacing(2)};
|
||||
`;
|
||||
|
||||
const StyledButtonContainer = styled.div`
|
||||
display: flex;
|
||||
& > :not(:first-of-type) > button {
|
||||
border-left: none;
|
||||
}
|
||||
`;
|
||||
|
||||
export const SettingsAdminConfigVariableDetails = () => {
|
||||
const { variableName } = useParams();
|
||||
const { t } = useLingui();
|
||||
const [isEditing, setIsEditing] = useState(false);
|
||||
const [isConfirmationModalOpen, setIsConfirmationModalOpen] = useState(false);
|
||||
const isConfigVariablesInDbEnabled = useRecoilValue(
|
||||
isConfigVariablesInDbEnabledState,
|
||||
);
|
||||
|
||||
const { data: configVariableData, loading } =
|
||||
useGetDatabaseConfigVariableQuery({
|
||||
variables: { key: variableName ?? '' },
|
||||
fetchPolicy: 'network-only',
|
||||
});
|
||||
|
||||
const variable = configVariableData?.getDatabaseConfigVariable;
|
||||
|
||||
const { handleUpdateVariable, handleDeleteVariable } =
|
||||
useConfigVariableActions(variable?.name ?? '');
|
||||
|
||||
const {
|
||||
handleSubmit,
|
||||
setValue,
|
||||
isSubmitting,
|
||||
watch,
|
||||
hasValueChanged,
|
||||
isValueValid,
|
||||
} = useConfigVariableForm(variable);
|
||||
|
||||
if (loading === true || isDefined(variable) === false) {
|
||||
return <SettingsSkeletonLoader />;
|
||||
}
|
||||
|
||||
const isEnvOnly = variable.isEnvOnly;
|
||||
const isFromDatabase = variable.source === ConfigSource.DATABASE;
|
||||
|
||||
const onSubmit = async (formData: { value: ConfigVariableValue }) => {
|
||||
await handleUpdateVariable(formData.value, isFromDatabase);
|
||||
setIsEditing(false);
|
||||
};
|
||||
|
||||
const handleEditClick = () => {
|
||||
setIsEditing(true);
|
||||
};
|
||||
|
||||
const handleXButtonClick = () => {
|
||||
if (isFromDatabase && hasValueChanged) {
|
||||
setValue('value', variable.value);
|
||||
setIsEditing(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isFromDatabase && !hasValueChanged) {
|
||||
setIsConfirmationModalOpen(true);
|
||||
return;
|
||||
}
|
||||
|
||||
setValue('value', variable.value);
|
||||
setIsEditing(false);
|
||||
};
|
||||
|
||||
const handleConfirmReset = () => {
|
||||
handleDeleteVariable();
|
||||
setIsEditing(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<SubMenuTopBarContainer
|
||||
links={[
|
||||
{
|
||||
children: t`Other`,
|
||||
href: getSettingsPath(SettingsPath.AdminPanel),
|
||||
},
|
||||
{
|
||||
children: t`Admin Panel`,
|
||||
href: getSettingsPath(SettingsPath.AdminPanel),
|
||||
},
|
||||
{
|
||||
children: t`Config Variables`,
|
||||
href: getSettingsPath(
|
||||
SettingsPath.AdminPanel,
|
||||
undefined,
|
||||
undefined,
|
||||
'config-variables',
|
||||
),
|
||||
},
|
||||
{
|
||||
children: variable.name,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<SettingsPageContainer>
|
||||
<StyledH3Title
|
||||
title={variable.name}
|
||||
description={variable.description}
|
||||
/>
|
||||
|
||||
<StyledForm onSubmit={handleSubmit(onSubmit)}>
|
||||
<StyledRow>
|
||||
<ConfigVariableValueInput
|
||||
variable={variable}
|
||||
value={watch('value')}
|
||||
onChange={(value) => setValue('value', value)}
|
||||
disabled={isEnvOnly || !isEditing}
|
||||
/>
|
||||
|
||||
{!isEditing ? (
|
||||
<Button
|
||||
Icon={IconPencil}
|
||||
variant="primary"
|
||||
onClick={handleEditClick}
|
||||
type="button"
|
||||
disabled={isEnvOnly || !isConfigVariablesInDbEnabled}
|
||||
/>
|
||||
) : (
|
||||
<StyledButtonContainer>
|
||||
<Button
|
||||
Icon={IconCheck}
|
||||
variant="secondary"
|
||||
position="left"
|
||||
type="submit"
|
||||
disabled={isSubmitting || !isValueValid || !hasValueChanged}
|
||||
/>
|
||||
<Button
|
||||
Icon={IconX}
|
||||
variant="secondary"
|
||||
position="right"
|
||||
onClick={handleXButtonClick}
|
||||
type="button"
|
||||
disabled={isSubmitting}
|
||||
/>
|
||||
</StyledButtonContainer>
|
||||
)}
|
||||
</StyledRow>
|
||||
|
||||
<ConfigVariableHelpText
|
||||
variable={variable}
|
||||
hasValueChanged={hasValueChanged}
|
||||
/>
|
||||
</StyledForm>
|
||||
</SettingsPageContainer>
|
||||
</SubMenuTopBarContainer>
|
||||
|
||||
<ConfirmationModal
|
||||
isOpen={isConfirmationModalOpen}
|
||||
setIsOpen={(isOpen) => {
|
||||
setIsConfirmationModalOpen(isOpen);
|
||||
if (!isOpen) {
|
||||
setIsEditing(false);
|
||||
}
|
||||
}}
|
||||
title={t`Reset variable`}
|
||||
subtitle={t`This will revert the database value to environment/default value. The database override will be removed and the system will use the environment settings.`}
|
||||
onConfirmClick={handleConfirmReset}
|
||||
confirmButtonText={t`Reset`}
|
||||
confirmButtonAccent="danger"
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@ -1,60 +0,0 @@
|
||||
import { SettingsAdminEnvVariablesTable } from '@/settings/admin-panel/components/SettingsAdminEnvVariablesTable';
|
||||
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
|
||||
import { SettingsSkeletonLoader } from '@/settings/components/SettingsSkeletonLoader';
|
||||
import { SettingsPath } from '@/types/SettingsPath';
|
||||
import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer';
|
||||
import styled from '@emotion/styled';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { H2Title } from 'twenty-ui/display';
|
||||
import { useGetConfigVariablesGroupedQuery } from '~/generated/graphql';
|
||||
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
|
||||
|
||||
const StyledGroupContainer = styled.div``;
|
||||
|
||||
export const SettingsAdminSecondaryEnvVariables = () => {
|
||||
const {
|
||||
data: secondaryConfigVariables,
|
||||
loading: secondaryConfigVariablesLoading,
|
||||
} = useGetConfigVariablesGroupedQuery({
|
||||
fetchPolicy: 'network-only',
|
||||
});
|
||||
|
||||
const hiddenGroups =
|
||||
secondaryConfigVariables?.getConfigVariablesGrouped.groups.filter(
|
||||
(group) => group.isHiddenOnLoad,
|
||||
) ?? [];
|
||||
|
||||
if (secondaryConfigVariablesLoading) {
|
||||
return <SettingsSkeletonLoader />;
|
||||
}
|
||||
|
||||
return (
|
||||
<SubMenuTopBarContainer
|
||||
title={t`Other Environment Variables`}
|
||||
links={[
|
||||
{
|
||||
children: t`Other`,
|
||||
href: getSettingsPath(SettingsPath.AdminPanel),
|
||||
},
|
||||
{
|
||||
children: t`Admin Panel`,
|
||||
href: getSettingsPath(SettingsPath.AdminPanel),
|
||||
},
|
||||
{
|
||||
children: t`Other Environment Variables`,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<SettingsPageContainer>
|
||||
{hiddenGroups.map((group) => (
|
||||
<StyledGroupContainer key={group.name}>
|
||||
<H2Title title={group.name} description={group.description} />
|
||||
{group.variables.length > 0 && (
|
||||
<SettingsAdminEnvVariablesTable variables={group.variables} />
|
||||
)}
|
||||
</StyledGroupContainer>
|
||||
))}
|
||||
</SettingsPageContainer>
|
||||
</SubMenuTopBarContainer>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user