feat: soft delete (#6576)

Implement soft delete on standards and custom objects.
This is a temporary solution, when we drop `pg_graphql` we should rely
on the `softDelete` functions of TypeORM.

---------

Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
This commit is contained in:
Jérémy M
2024-08-16 21:20:02 +02:00
committed by GitHub
parent 20d84755bb
commit db54469c8a
118 changed files with 1675 additions and 492 deletions

View File

@ -1,7 +1,7 @@
import { zodResolver } from '@hookform/resolvers/zod';
import { FormProvider, useForm } from 'react-hook-form';
import { useNavigate } from 'react-router-dom';
import { H2Title, IconSettings } from 'twenty-ui';
import { H2Title, IconHierarchy2 } from 'twenty-ui';
import { z } from 'zod';
import { useCreateOneObjectMetadataItem } from '@/object-metadata/hooks/useCreateOneObjectMetadataItem';
@ -70,25 +70,30 @@ export const SettingsNewObject = () => {
return (
// eslint-disable-next-line react/jsx-props-no-spreading
<FormProvider {...formConfig}>
<SubMenuTopBarContainer Icon={IconSettings} title="Settings">
<SubMenuTopBarContainer
Icon={IconHierarchy2}
title={
<Breadcrumb
links={[
{
children: 'Objects',
href: settingsObjectsPagePath,
},
{ children: 'New' },
]}
/>
}
actionButton={
<SaveAndCancelButtons
isSaveDisabled={!canSave}
isCancelDisabled={isSubmitting}
onCancel={() => navigate(settingsObjectsPagePath)}
onSave={formConfig.handleSubmit(handleSave)}
/>
}
>
<SettingsPageContainer>
<SettingsHeaderContainer>
<Breadcrumb
links={[
{
children: 'Objects',
href: settingsObjectsPagePath,
},
{ children: 'New' },
]}
/>
<SaveAndCancelButtons
isSaveDisabled={!canSave}
isCancelDisabled={isSubmitting}
onCancel={() => navigate(settingsObjectsPagePath)}
onSave={formConfig.handleSubmit(handleSave)}
/>
</SettingsHeaderContainer>
<SettingsHeaderContainer></SettingsHeaderContainer>
<Section>
<H2Title
title="About"

View File

@ -1,12 +1,7 @@
import styled from '@emotion/styled';
import { useNavigate } from 'react-router-dom';
import { H2Title, IconPlus, IconSettings } from 'twenty-ui';
import { useUpdateOneObjectMetadataItem } from '@/object-metadata/hooks/useUpdateOneObjectMetadataItem';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
import { getDisabledFieldMetadataItems } from '@/object-metadata/utils/getDisabledFieldMetadataItems';
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
import { SettingsObjectSummaryCard } from '@/settings/data-model/object-details/components/SettingsObjectSummaryCard';
import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath';
import { SettingsPath } from '@/types/SettingsPath';
@ -15,7 +10,10 @@ import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer'
import { Section } from '@/ui/layout/section/components/Section';
import { Breadcrumb } from '@/ui/navigation/bread-crumb/components/Breadcrumb';
import { UndecoratedLink } from '@/ui/navigation/link/components/UndecoratedLink';
import styled from '@emotion/styled';
import { isNonEmptyArray } from '@sniptt/guards';
import { useNavigate } from 'react-router-dom';
import { H2Title, IconHierarchy2, IconPlus } from 'twenty-ui';
import { SettingsObjectFieldTable } from '~/pages/settings/data-model/SettingsObjectFieldTable';
const StyledDiv = styled.div`
@ -49,14 +47,18 @@ export const SettingsObjectDetailPageContent = ({
const shouldDisplayAddFieldButton = !objectMetadataItem.isRemote;
return (
<SubMenuTopBarContainer Icon={IconSettings} title="Settings">
<SettingsPageContainer>
<SubMenuTopBarContainer
Icon={IconHierarchy2}
title={
<Breadcrumb
links={[
{ children: 'Objects', href: '/settings/objects' },
{ children: objectMetadataItem.labelPlural },
]}
/>
}
>
<SettingsPageContainer>
<Section>
<H2Title title="About" description="Manage your object" />
<SettingsObjectSummaryCard

View File

@ -5,12 +5,13 @@ import pick from 'lodash.pick';
import { useEffect } from 'react';
import { FormProvider, useForm } from 'react-hook-form';
import { useNavigate, useParams } from 'react-router-dom';
import { H2Title, IconArchive, IconSettings } from 'twenty-ui';
import { H2Title, IconArchive, IconHierarchy2 } from 'twenty-ui';
import { z } from 'zod';
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
import { useUpdateOneObjectMetadataItem } from '@/object-metadata/hooks/useUpdateOneObjectMetadataItem';
import { getObjectSlug } from '@/object-metadata/utils/getObjectSlug';
import { RecordFieldValueSelectorContextProvider } from '@/object-record/record-store/contexts/RecordFieldValueSelectorContext';
import { SaveAndCancelButtons } from '@/settings/components/SaveAndCancelButtons/SaveAndCancelButtons';
import { SettingsHeaderContainer } from '@/settings/components/SettingsHeaderContainer';
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
@ -30,7 +31,6 @@ import { Button } from '@/ui/input/button/components/Button';
import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer';
import { Section } from '@/ui/layout/section/components/Section';
import { Breadcrumb } from '@/ui/navigation/bread-crumb/components/Breadcrumb';
import { RecordFieldValueSelectorContextProvider } from '@/object-record/record-store/contexts/RecordFieldValueSelectorContext';
const objectEditFormSchema = z
.object({})
@ -108,22 +108,26 @@ export const SettingsObjectEdit = () => {
return (
<RecordFieldValueSelectorContextProvider>
<FormProvider {...formConfig}>
<SubMenuTopBarContainer Icon={IconSettings} title="Settings">
<SubMenuTopBarContainer
Icon={IconHierarchy2}
title={
<Breadcrumb
links={[
{
children: 'Objects',
href: settingsObjectsPagePath,
},
{
children: activeObjectMetadataItem.labelPlural,
href: `${settingsObjectsPagePath}/${objectSlug}`,
},
{ children: 'Edit' },
]}
/>
}
>
<SettingsPageContainer>
<SettingsHeaderContainer>
<Breadcrumb
links={[
{
children: 'Objects',
href: settingsObjectsPagePath,
},
{
children: activeObjectMetadataItem.labelPlural,
href: `${settingsObjectsPagePath}/${objectSlug}`,
},
{ children: 'Edit' },
]}
/>
{activeObjectMetadataItem.isCustom && (
<SaveAndCancelButtons
isSaveDisabled={!canSave}

View File

@ -6,7 +6,7 @@ import pick from 'lodash.pick';
import { useEffect } from 'react';
import { FormProvider, useForm } from 'react-hook-form';
import { useNavigate, useParams } from 'react-router-dom';
import { H2Title, IconArchive, IconSettings } from 'twenty-ui';
import { H2Title, IconArchive, IconHierarchy2 } from 'twenty-ui';
import { z } from 'zod';
import { useFieldMetadataItem } from '@/object-metadata/hooks/useFieldMetadataItem';
@ -172,19 +172,23 @@ export const SettingsObjectFieldEdit = () => {
<RecordFieldValueSelectorContextProvider>
{/* eslint-disable-next-line react/jsx-props-no-spreading */}
<FormProvider {...formConfig}>
<SubMenuTopBarContainer Icon={IconSettings} title="Settings">
<SubMenuTopBarContainer
Icon={IconHierarchy2}
title={
<Breadcrumb
links={[
{ children: 'Objects', href: '/settings/objects' },
{
children: activeObjectMetadataItem.labelPlural,
href: `/settings/objects/${objectSlug}`,
},
{ children: activeMetadataField.label },
]}
/>
}
>
<SettingsPageContainer>
<SettingsHeaderContainer>
<Breadcrumb
links={[
{ children: 'Objects', href: '/settings/objects' },
{
children: activeObjectMetadataItem.labelPlural,
href: `/settings/objects/${objectSlug}`,
},
{ children: activeMetadataField.label },
]}
/>
{shouldDisplaySaveAndCancel && (
<SaveAndCancelButtons
isSaveDisabled={!canSave}

View File

@ -5,7 +5,6 @@ import { H2Title, IconPlus, IconSettings } from 'twenty-ui';
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
import { SaveAndCancelButtons } from '@/settings/components/SaveAndCancelButtons/SaveAndCancelButtons';
import { SettingsHeaderContainer } from '@/settings/components/SettingsHeaderContainer';
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
import { useFieldMetadataItem } from '@/object-metadata/hooks/useFieldMetadataItem';
@ -85,27 +84,31 @@ export const SettingsObjectNewFieldStep1 = () => {
if (!activeObjectMetadataItem) return null;
return (
<SubMenuTopBarContainer Icon={IconSettings} title="Settings">
<SettingsPageContainer>
<SettingsHeaderContainer>
<Breadcrumb
links={[
{ children: 'Objects', href: '/settings/objects' },
{
children: activeObjectMetadataItem.labelPlural,
href: `/settings/objects/${objectSlug}`,
},
{ children: 'New Field' },
]}
<SubMenuTopBarContainer
Icon={IconSettings}
title={
<Breadcrumb
links={[
{ children: 'Objects', href: '/settings/objects' },
{
children: activeObjectMetadataItem.labelPlural,
href: `/settings/objects/${objectSlug}`,
},
{ children: 'New Field' },
]}
/>
}
actionButton={
!activeObjectMetadataItem.isRemote && (
<SaveAndCancelButtons
isSaveDisabled={!canSave}
onCancel={() => navigate(`/settings/objects/${objectSlug}`)}
onSave={handleSave}
/>
{!activeObjectMetadataItem.isRemote && (
<SaveAndCancelButtons
isSaveDisabled={!canSave}
onCancel={() => navigate(`/settings/objects/${objectSlug}`)}
onSave={handleSave}
/>
)}
</SettingsHeaderContainer>
)
}
>
<SettingsPageContainer>
<StyledSection>
<H2Title
title="Check deactivated fields"

View File

@ -1,12 +1,25 @@
import { ReactFlowProvider } from 'reactflow';
import { IconSettings } from 'twenty-ui';
import { SettingsDataModelOverview } from '@/settings/data-model/graph-overview/components/SettingsDataModelOverview';
import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer';
import { Breadcrumb } from '@/ui/navigation/bread-crumb/components/Breadcrumb';
import { IconHierarchy2 } from 'twenty-ui';
export const SettingsObjectOverview = () => {
return (
<SubMenuTopBarContainer Icon={IconSettings} title="Settings">
<SubMenuTopBarContainer
Icon={IconHierarchy2}
title={
<Breadcrumb
links={[
{ children: 'Data model', href: '/settings/objects' },
{
children: 'Overview',
},
]}
/>
}
>
<ReactFlowProvider>
<SettingsDataModelOverview />
</ReactFlowProvider>

View File

@ -1,19 +1,12 @@
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import {
H1Title,
H2Title,
IconChevronRight,
IconPlus,
IconSettings,
} from 'twenty-ui';
import { H2Title, IconChevronRight, IconHierarchy2, IconPlus } from 'twenty-ui';
import { useDeleteOneObjectMetadataItem } from '@/object-metadata/hooks/useDeleteOneObjectMetadataItem';
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
import { useUpdateOneObjectMetadataItem } from '@/object-metadata/hooks/useUpdateOneObjectMetadataItem';
import { getObjectSlug } from '@/object-metadata/utils/getObjectSlug';
import { useCombinedGetTotalCount } from '@/object-record/multiple-objects/hooks/useCombinedGetTotalCount';
import { SettingsHeaderContainer } from '@/settings/components/SettingsHeaderContainer';
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
import {
SettingsObjectMetadataItemTableRow,
@ -42,10 +35,6 @@ const StyledIconChevronRight = styled(IconChevronRight)`
color: ${({ theme }) => theme.font.color.tertiary};
`;
const StyledH1Title = styled(H1Title)`
margin-bottom: 0;
`;
export const SettingsObjects = () => {
const theme = useTheme();
@ -115,19 +104,21 @@ export const SettingsObjects = () => {
);
return (
<SubMenuTopBarContainer Icon={IconSettings} title="Settings">
<SubMenuTopBarContainer
Icon={IconHierarchy2}
title="Data model"
actionButton={
<UndecoratedLink to={getSettingsPagePath(SettingsPath.NewObject)}>
<Button
Icon={IconPlus}
title="Add object"
accent="blue"
size="small"
/>
</UndecoratedLink>
}
>
<SettingsPageContainer>
<SettingsHeaderContainer>
<StyledH1Title title="Objects" />
<UndecoratedLink to={getSettingsPagePath(SettingsPath.NewObject)}>
<Button
Icon={IconPlus}
title="Add object"
accent="blue"
size="small"
/>
</UndecoratedLink>
</SettingsHeaderContainer>
<>
<SettingsObjectCoverImage />
<Section>