---
README.md | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/README.md b/README.md
index f0c96b0a32ac..ce63386d9dad 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,12 @@
+
+
+
+
+
From a940912edbf5e64e464cf3e8a267139d37872649 Mon Sep 17 00:00:00 2001
From: Thomas des Francs
Date: Tue, 1 Oct 2024 15:29:35 +0200
Subject: [PATCH 025/115] Github banner (#7371)
---
.../images/readme/Github Read-me banner.png | Bin 0 -> 18035 bytes
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 packages/twenty-website/public/images/readme/Github Read-me banner.png
diff --git a/packages/twenty-website/public/images/readme/Github Read-me banner.png b/packages/twenty-website/public/images/readme/Github Read-me banner.png
new file mode 100644
index 0000000000000000000000000000000000000000..2b87a59ccc44613f66959dab85de451508c15928
GIT binary patch
literal 18035
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
JU$c4q-vA95x={cC
literal 0
HcmV?d00001
From c505a8edd2e7ce3fce00c048a32493985c4facac Mon Sep 17 00:00:00 2001
From: nitin <142569587+ehconitin@users.noreply.github.com>
Date: Tue, 1 Oct 2024 19:23:59 +0530
Subject: [PATCH 026/115] minor fix - fixed forwardRef and unique key console
errors (#7337)
context - #7183
---
.../components/ObjectFilterDropdownFilterSelect.tsx | 2 +-
.../layout/dropdown/components/DropdownMenuSearchInput.tsx | 6 +++---
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelect.tsx b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelect.tsx
index 59ee04d92518..f0c2d66cdc7e 100644
--- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelect.tsx
+++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelect.tsx
@@ -96,9 +96,9 @@ export const ObjectFilterDropdownFilterSelect = () => {
(availableFilterDefinition, index) => (
diff --git a/packages/twenty-front/src/modules/ui/layout/dropdown/components/DropdownMenuSearchInput.tsx b/packages/twenty-front/src/modules/ui/layout/dropdown/components/DropdownMenuSearchInput.tsx
index b9f1bc87d286..ec761aa46680 100644
--- a/packages/twenty-front/src/modules/ui/layout/dropdown/components/DropdownMenuSearchInput.tsx
+++ b/packages/twenty-front/src/modules/ui/layout/dropdown/components/DropdownMenuSearchInput.tsx
@@ -36,15 +36,15 @@ const StyledInput = styled.input`
export const DropdownMenuSearchInput = forwardRef<
HTMLInputElement,
InputHTMLAttributes
->(({ value, onChange, placeholder = 'Search', type }) => {
+>(({ value, onChange, placeholder = 'Search', type }, forwardedRef) => {
const { inputRef } = useInputFocusWithoutScrollOnMount();
-
+ const ref = forwardedRef ?? inputRef;
return (
);
From 3a0c32a88d5eb3983ed8f1c34a0738da22bf126f Mon Sep 17 00:00:00 2001
From: Harshit Singh <73997189+harshit078@users.noreply.github.com>
Date: Tue, 1 Oct 2024 19:28:19 +0530
Subject: [PATCH 027/115] fix: Remove many to many relation option (#7360)
## Description
- This PR addresses the issue #7359
---
packages/twenty-front/src/generated-metadata/graphql.ts | 2 --
.../settings/data-model/constants/RelationTypes.ts | 8 --------
2 files changed, 10 deletions(-)
diff --git a/packages/twenty-front/src/generated-metadata/graphql.ts b/packages/twenty-front/src/generated-metadata/graphql.ts
index 09fac64ea57e..89445cb715cb 100644
--- a/packages/twenty-front/src/generated-metadata/graphql.ts
+++ b/packages/twenty-front/src/generated-metadata/graphql.ts
@@ -984,7 +984,6 @@ export type RelationDefinition = {
/** Relation definition type */
export enum RelationDefinitionType {
- ManyToMany = 'MANY_TO_MANY',
ManyToOne = 'MANY_TO_ONE',
OneToMany = 'ONE_TO_MANY',
OneToOne = 'ONE_TO_ONE'
@@ -992,7 +991,6 @@ export enum RelationDefinitionType {
/** Type of the relation */
export enum RelationMetadataType {
- ManyToMany = 'MANY_TO_MANY',
ManyToOne = 'MANY_TO_ONE',
OneToMany = 'ONE_TO_MANY',
OneToOne = 'ONE_TO_ONE'
diff --git a/packages/twenty-front/src/modules/settings/data-model/constants/RelationTypes.ts b/packages/twenty-front/src/modules/settings/data-model/constants/RelationTypes.ts
index 179f60fe8979..da5e9b8b9617 100644
--- a/packages/twenty-front/src/modules/settings/data-model/constants/RelationTypes.ts
+++ b/packages/twenty-front/src/modules/settings/data-model/constants/RelationTypes.ts
@@ -1,6 +1,5 @@
import {
IconComponent,
- IllustrationIconManyToMany,
IllustrationIconOneToMany,
IllustrationIconOneToOne,
} from 'twenty-ui';
@@ -35,11 +34,4 @@ export const RELATION_TYPES: Record<
imageSrc: OneToManySvg,
isImageFlipped: true,
},
- // Not supported yet
- [RelationDefinitionType.ManyToMany]: {
- label: 'Belongs to many',
- Icon: IllustrationIconManyToMany,
- imageSrc: OneToManySvg,
- isImageFlipped: true,
- },
};
From 35361093bf4e151d1009a3775a99d3d81cebeb16 Mon Sep 17 00:00:00 2001
From: Baptiste Devessier
Date: Tue, 1 Oct 2024 18:14:54 +0200
Subject: [PATCH 028/115] Delete workflow step (#7373)
- Allows the deletion of triggers and steps in workflows. If the
workflow can not be edited right now, we create a new draft version.
- The workflow right drawer can now render nothing. It's necessary to
behave that way because a deleted step will still be displayed for a
short amount of time in the drawer. The drawer will be filled with blank
content when it disappears.
https://github.com/user-attachments/assets/abd5184e-d3db-4fe7-8870-ccc78ff23d41
Closes #7057
---
.../RightDrawerWorkflowEditStepContent.tsx | 10 +-
.../WorkflowDiagramBaseStepNode.tsx | 15 +++
.../components/WorkflowDiagramStepNode.tsx | 32 +++++-
.../workflow/hooks/useDeleteOneStep.tsx | 76 ++++++++++++
.../utils/__tests__/removeStep.test.ts | 108 ++++++++++++++++++
.../workflow/utils/findStepPosition.ts | 41 +++++++
.../workflow/utils/findStepPositionOrThrow.ts | 36 ++----
.../src/modules/workflow/utils/removeStep.ts | 21 ++++
8 files changed, 308 insertions(+), 31 deletions(-)
create mode 100644 packages/twenty-front/src/modules/workflow/hooks/useDeleteOneStep.tsx
create mode 100644 packages/twenty-front/src/modules/workflow/utils/__tests__/removeStep.test.ts
create mode 100644 packages/twenty-front/src/modules/workflow/utils/findStepPosition.ts
create mode 100644 packages/twenty-front/src/modules/workflow/utils/removeStep.ts
diff --git a/packages/twenty-front/src/modules/workflow/components/RightDrawerWorkflowEditStepContent.tsx b/packages/twenty-front/src/modules/workflow/components/RightDrawerWorkflowEditStepContent.tsx
index 3f3734b077ed..a7b190bec3f6 100644
--- a/packages/twenty-front/src/modules/workflow/components/RightDrawerWorkflowEditStepContent.tsx
+++ b/packages/twenty-front/src/modules/workflow/components/RightDrawerWorkflowEditStepContent.tsx
@@ -7,7 +7,7 @@ import { useUpdateWorkflowVersionTrigger } from '@/workflow/hooks/useUpdateWorkf
import { workflowSelectedNodeState } from '@/workflow/states/workflowSelectedNodeState';
import { WorkflowWithCurrentVersion } from '@/workflow/types/Workflow';
import { assertUnreachable } from '@/workflow/utils/assertUnreachable';
-import { findStepPositionOrThrow } from '@/workflow/utils/findStepPositionOrThrow';
+import { findStepPosition } from '@/workflow/utils/findStepPosition';
import { useRecoilValue } from 'recoil';
import { isDefined } from 'twenty-ui';
@@ -43,10 +43,13 @@ const getStepDefinitionOrThrow = ({
);
}
- const selectedNodePosition = findStepPositionOrThrow({
+ const selectedNodePosition = findStepPosition({
steps: currentVersion.steps,
stepId: stepId,
});
+ if (!isDefined(selectedNodePosition)) {
+ return undefined;
+ }
return {
type: 'action',
@@ -76,6 +79,9 @@ export const RightDrawerWorkflowEditStepContent = ({
stepId: workflowSelectedNode,
workflow,
});
+ if (!isDefined(stepDefinition)) {
+ return null;
+ }
switch (stepDefinition.type) {
case 'trigger': {
diff --git a/packages/twenty-front/src/modules/workflow/components/WorkflowDiagramBaseStepNode.tsx b/packages/twenty-front/src/modules/workflow/components/WorkflowDiagramBaseStepNode.tsx
index 8c05d48baa09..8484a29e7eaf 100644
--- a/packages/twenty-front/src/modules/workflow/components/WorkflowDiagramBaseStepNode.tsx
+++ b/packages/twenty-front/src/modules/workflow/components/WorkflowDiagramBaseStepNode.tsx
@@ -2,6 +2,7 @@ import { WorkflowDiagramStepNodeData } from '@/workflow/types/WorkflowDiagram';
import styled from '@emotion/styled';
import { Handle, Position } from '@xyflow/react';
import React from 'react';
+import { isDefined } from 'twenty-ui';
import { capitalize } from '~/utils/string/capitalize';
type Variant = 'placeholder';
@@ -76,16 +77,24 @@ export const StyledTargetHandle = styled(Handle)`
visibility: hidden;
`;
+const StyledRightFloatingElementContainer = styled.div`
+ position: absolute;
+ transform: translateX(100%);
+ right: ${({ theme }) => theme.spacing(-2)};
+`;
+
export const WorkflowDiagramBaseStepNode = ({
nodeType,
label,
variant,
Icon,
+ RightFloatingElement,
}: {
nodeType: WorkflowDiagramStepNodeData['nodeType'];
label: string;
variant?: Variant;
Icon?: React.ReactNode;
+ RightFloatingElement?: React.ReactNode;
}) => {
return (
@@ -101,6 +110,12 @@ export const WorkflowDiagramBaseStepNode = ({
{label}
+
+ {isDefined(RightFloatingElement) ? (
+
+ {RightFloatingElement}
+
+ ) : null}
diff --git a/packages/twenty-front/src/modules/workflow/components/WorkflowDiagramStepNode.tsx b/packages/twenty-front/src/modules/workflow/components/WorkflowDiagramStepNode.tsx
index cc5f1a94eb3d..00b20bdae8d2 100644
--- a/packages/twenty-front/src/modules/workflow/components/WorkflowDiagramStepNode.tsx
+++ b/packages/twenty-front/src/modules/workflow/components/WorkflowDiagramStepNode.tsx
@@ -1,9 +1,15 @@
+import { FloatingIconButton } from '@/ui/input/button/components/FloatingIconButton';
import { WorkflowDiagramBaseStepNode } from '@/workflow/components/WorkflowDiagramBaseStepNode';
+import { useDeleteOneStep } from '@/workflow/hooks/useDeleteOneStep';
+import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
+import { workflowIdState } from '@/workflow/states/workflowIdState';
import { WorkflowDiagramStepNodeData } from '@/workflow/types/WorkflowDiagram';
import { assertUnreachable } from '@/workflow/utils/assertUnreachable';
+import { assertWorkflowWithCurrentVersionIsDefined } from '@/workflow/utils/assertWorkflowWithCurrentVersionIsDefined';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
-import { IconCode, IconMail, IconPlaylistAdd } from 'twenty-ui';
+import { useRecoilValue } from 'recoil';
+import { IconCode, IconMail, IconPlaylistAdd, IconTrash } from 'twenty-ui';
const StyledStepNodeLabelIconContainer = styled.div`
align-items: center;
@@ -15,12 +21,26 @@ const StyledStepNodeLabelIconContainer = styled.div`
`;
export const WorkflowDiagramStepNode = ({
+ id,
data,
+ selected,
}: {
+ id: string;
data: WorkflowDiagramStepNodeData;
+ selected?: boolean;
}) => {
const theme = useTheme();
+ const workflowId = useRecoilValue(workflowIdState);
+
+ const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(workflowId);
+ assertWorkflowWithCurrentVersionIsDefined(workflowWithCurrentVersion);
+
+ const { deleteOneStep } = useDeleteOneStep({
+ workflow: workflowWithCurrentVersion,
+ stepId: id,
+ });
+
const renderStepIcon = () => {
switch (data.nodeType) {
case 'trigger': {
@@ -67,6 +87,16 @@ export const WorkflowDiagramStepNode = ({
nodeType={data.nodeType}
label={data.label}
Icon={renderStepIcon()}
+ RightFloatingElement={
+ selected ? (
+ {
+ return deleteOneStep();
+ }}
+ />
+ ) : undefined
+ }
/>
);
};
diff --git a/packages/twenty-front/src/modules/workflow/hooks/useDeleteOneStep.tsx b/packages/twenty-front/src/modules/workflow/hooks/useDeleteOneStep.tsx
new file mode 100644
index 000000000000..0a44ccbc20a2
--- /dev/null
+++ b/packages/twenty-front/src/modules/workflow/hooks/useDeleteOneStep.tsx
@@ -0,0 +1,76 @@
+import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
+import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
+import { TRIGGER_STEP_ID } from '@/workflow/constants/TriggerStepId';
+import { useCreateNewWorkflowVersion } from '@/workflow/hooks/useCreateNewWorkflowVersion';
+import {
+ WorkflowVersion,
+ WorkflowWithCurrentVersion,
+} from '@/workflow/types/Workflow';
+import { removeStep } from '@/workflow/utils/removeStep';
+
+export const useDeleteOneStep = ({
+ stepId,
+ workflow,
+}: {
+ stepId: string;
+ workflow: WorkflowWithCurrentVersion;
+}) => {
+ const { updateOneRecord: updateOneWorkflowVersion } =
+ useUpdateOneRecord({
+ objectNameSingular: CoreObjectNameSingular.WorkflowVersion,
+ });
+
+ const { createNewWorkflowVersion } = useCreateNewWorkflowVersion({
+ workflowId: workflow.id,
+ });
+
+ const deleteOneStep = async () => {
+ if (workflow.currentVersion.status !== 'DRAFT') {
+ const newVersionName = `v${workflow.versions.length + 1}`;
+
+ if (stepId === TRIGGER_STEP_ID) {
+ await createNewWorkflowVersion({
+ name: newVersionName,
+ status: 'DRAFT',
+ trigger: null,
+ steps: workflow.currentVersion.steps,
+ });
+ } else {
+ await createNewWorkflowVersion({
+ name: newVersionName,
+ status: 'DRAFT',
+ trigger: workflow.currentVersion.trigger,
+ steps: removeStep({
+ steps: workflow.currentVersion.steps ?? [],
+ stepId,
+ }),
+ });
+ }
+
+ return;
+ }
+
+ if (stepId === TRIGGER_STEP_ID) {
+ await updateOneWorkflowVersion({
+ idToUpdate: workflow.currentVersion.id,
+ updateOneRecordInput: {
+ trigger: null,
+ },
+ });
+ } else {
+ await updateOneWorkflowVersion({
+ idToUpdate: workflow.currentVersion.id,
+ updateOneRecordInput: {
+ steps: removeStep({
+ steps: workflow.currentVersion.steps ?? [],
+ stepId,
+ }),
+ },
+ });
+ }
+ };
+
+ return {
+ deleteOneStep,
+ };
+};
diff --git a/packages/twenty-front/src/modules/workflow/utils/__tests__/removeStep.test.ts b/packages/twenty-front/src/modules/workflow/utils/__tests__/removeStep.test.ts
new file mode 100644
index 000000000000..01385411bf02
--- /dev/null
+++ b/packages/twenty-front/src/modules/workflow/utils/__tests__/removeStep.test.ts
@@ -0,0 +1,108 @@
+import { WorkflowStep, WorkflowVersion } from '@/workflow/types/Workflow';
+import { removeStep } from '../removeStep';
+
+it('returns a deep copy of the provided steps array instead of mutating it', () => {
+ const stepToBeRemoved = {
+ id: 'step-1',
+ name: '',
+ settings: {
+ errorHandlingOptions: {
+ retryOnFailure: { value: true },
+ continueOnFailure: { value: false },
+ },
+ serverlessFunctionId: 'first',
+ },
+ type: 'CODE',
+ valid: true,
+ } satisfies WorkflowStep;
+ const workflowVersionInitial = {
+ __typename: 'WorkflowVersion',
+ status: 'ACTIVE',
+ createdAt: '',
+ id: '1',
+ name: '',
+ steps: [stepToBeRemoved],
+ trigger: {
+ settings: { eventName: 'company.created' },
+ type: 'DATABASE_EVENT',
+ },
+ updatedAt: '',
+ workflowId: '',
+ } satisfies WorkflowVersion;
+
+ const stepsUpdated = removeStep({
+ steps: workflowVersionInitial.steps,
+ stepId: stepToBeRemoved.id,
+ });
+
+ expect(workflowVersionInitial.steps).not.toBe(stepsUpdated);
+});
+
+it('removes a step in a non-empty steps array', () => {
+ const stepToBeRemoved: WorkflowStep = {
+ id: 'step-2',
+ name: '',
+ settings: {
+ errorHandlingOptions: {
+ retryOnFailure: { value: true },
+ continueOnFailure: { value: false },
+ },
+ serverlessFunctionId: 'a5434be2-c10b-465c-acec-46492782a997',
+ },
+ type: 'CODE',
+ valid: true,
+ };
+ const workflowVersionInitial = {
+ __typename: 'WorkflowVersion',
+ status: 'ACTIVE',
+ createdAt: '',
+ id: '1',
+ name: '',
+ steps: [
+ {
+ id: 'step-1',
+ name: '',
+ settings: {
+ errorHandlingOptions: {
+ retryOnFailure: { value: true },
+ continueOnFailure: { value: false },
+ },
+ serverlessFunctionId: 'a5434be2-c10b-465c-acec-46492782a997',
+ },
+ type: 'CODE',
+ valid: true,
+ },
+ stepToBeRemoved,
+ {
+ id: 'step-3',
+ name: '',
+ settings: {
+ errorHandlingOptions: {
+ retryOnFailure: { value: true },
+ continueOnFailure: { value: false },
+ },
+ serverlessFunctionId: 'a5434be2-c10b-465c-acec-46492782a997',
+ },
+ type: 'CODE',
+ valid: true,
+ },
+ ],
+ trigger: {
+ settings: { eventName: 'company.created' },
+ type: 'DATABASE_EVENT',
+ },
+ updatedAt: '',
+ workflowId: '',
+ } satisfies WorkflowVersion;
+
+ const stepsUpdated = removeStep({
+ steps: workflowVersionInitial.steps,
+ stepId: stepToBeRemoved.id,
+ });
+
+ const expectedUpdatedSteps: Array = [
+ workflowVersionInitial.steps[0],
+ workflowVersionInitial.steps[2],
+ ];
+ expect(stepsUpdated).toEqual(expectedUpdatedSteps);
+});
diff --git a/packages/twenty-front/src/modules/workflow/utils/findStepPosition.ts b/packages/twenty-front/src/modules/workflow/utils/findStepPosition.ts
new file mode 100644
index 000000000000..3ae23dff0283
--- /dev/null
+++ b/packages/twenty-front/src/modules/workflow/utils/findStepPosition.ts
@@ -0,0 +1,41 @@
+import { TRIGGER_STEP_ID } from '@/workflow/constants/TriggerStepId';
+import { WorkflowStep } from '@/workflow/types/Workflow';
+import { isDefined } from 'twenty-ui';
+
+/**
+ * This function returns the reference of the array where the step should be positioned
+ * and at which index.
+ */
+export const findStepPosition = ({
+ steps,
+ stepId,
+}: {
+ steps: Array;
+ stepId: string | undefined;
+}): { steps: Array; index: number } | undefined => {
+ if (!isDefined(stepId) || stepId === TRIGGER_STEP_ID) {
+ return {
+ steps,
+ index: 0,
+ };
+ }
+
+ for (const [index, step] of steps.entries()) {
+ if (step.id === stepId) {
+ return {
+ steps,
+ index,
+ };
+ }
+
+ // TODO: When condition will have been implemented, put recursivity here.
+ // if (step.type === "CONDITION") {
+ // return findNodePosition({
+ // workflowSteps: step.conditions,
+ // stepId,
+ // })
+ // }
+ }
+
+ return undefined;
+};
diff --git a/packages/twenty-front/src/modules/workflow/utils/findStepPositionOrThrow.ts b/packages/twenty-front/src/modules/workflow/utils/findStepPositionOrThrow.ts
index bf10df7783fc..c20ffbcd4a68 100644
--- a/packages/twenty-front/src/modules/workflow/utils/findStepPositionOrThrow.ts
+++ b/packages/twenty-front/src/modules/workflow/utils/findStepPositionOrThrow.ts
@@ -1,41 +1,21 @@
-import { TRIGGER_STEP_ID } from '@/workflow/constants/TriggerStepId';
import { WorkflowStep } from '@/workflow/types/Workflow';
+import { findStepPosition } from '@/workflow/utils/findStepPosition';
import { isDefined } from 'twenty-ui';
/**
* This function returns the reference of the array where the step should be positioned
* and at which index.
*/
-export const findStepPositionOrThrow = ({
- steps,
- stepId,
-}: {
+export const findStepPositionOrThrow = (props: {
steps: Array;
stepId: string | undefined;
}): { steps: Array; index: number } => {
- if (!isDefined(stepId) || stepId === TRIGGER_STEP_ID) {
- return {
- steps,
- index: 0,
- };
+ const result = findStepPosition(props);
+ if (!isDefined(result)) {
+ throw new Error(
+ `Couldn't locate the step. Unreachable step id: ${props.stepId}.`,
+ );
}
- for (const [index, step] of steps.entries()) {
- if (step.id === stepId) {
- return {
- steps,
- index,
- };
- }
-
- // TODO: When condition will have been implemented, put recursivity here.
- // if (step.type === "CONDITION") {
- // return findNodePosition({
- // workflowSteps: step.conditions,
- // stepId,
- // })
- // }
- }
-
- throw new Error(`Couldn't locate the step. Unreachable step id: ${stepId}.`);
+ return result;
};
diff --git a/packages/twenty-front/src/modules/workflow/utils/removeStep.ts b/packages/twenty-front/src/modules/workflow/utils/removeStep.ts
new file mode 100644
index 000000000000..eb6fe41dda5b
--- /dev/null
+++ b/packages/twenty-front/src/modules/workflow/utils/removeStep.ts
@@ -0,0 +1,21 @@
+import { WorkflowStep } from '@/workflow/types/Workflow';
+import { findStepPositionOrThrow } from '@/workflow/utils/findStepPositionOrThrow';
+
+export const removeStep = ({
+ steps: stepsInitial,
+ stepId,
+}: {
+ steps: Array;
+ stepId: string | undefined;
+}) => {
+ const steps = structuredClone(stepsInitial);
+
+ const parentStepPosition = findStepPositionOrThrow({
+ steps,
+ stepId,
+ });
+
+ parentStepPosition.steps.splice(parentStepPosition.index, 1);
+
+ return steps;
+};
From a8c07bf77f349f4e2260fd6bb5ec16f0af0ebb74 Mon Sep 17 00:00:00 2001
From: Vijay <74645268+vijaysingh2219@users.noreply.github.com>
Date: Wed, 2 Oct 2024 12:01:53 +0530
Subject: [PATCH 029/115] update: Default Demo Login Credentials to
noah@demo.dev (#7308)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This PR updates the default login credentials for the demo environment.
The current default is set to tim@apple.dev, and we want to change it
to:
Email: `noah@demo.dev`
Password: `Applecar2025`
Changes:
- Updated default demo login email from tim@apple.dev to noah@demo.dev
Resolves #7304
---------
Co-authored-by: Félix Malfait
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index ce63386d9dad..86ed0fe51a6f 100644
--- a/README.md
+++ b/README.md
@@ -43,7 +43,7 @@ We felt the need for a CRM platform that empowers rather than constrains. We bel
Go to demo.twenty.com and login with the following credentials:
```
-email: noah@demo.dev
+email: tim@apple.dev
password: Applecar2025
```
From d7dd41e7e454cc4e50bb436871e30ca3ccefaebc Mon Sep 17 00:00:00 2001
From: Yura Levchenko
Date: Wed, 2 Oct 2024 08:58:51 +0200
Subject: [PATCH 030/115] Changed condition on which 'Add task' button is
displayed (#7333) (#7362)
This PR addresses issue #7333. It updates the condition for displaying
the 'Add task' button. The button is now only visible for the 'TODO'
section or when no 'TODO' block is present (i.e., there are no tasks in
this category).
Additionally, I removed the unused showAddButton, which is no longer
necessary due to the updated logic.
![image](https://github.com/user-attachments/assets/571542d7-1b0f-4b91-afcf-4592490f1f72)
![image](https://github.com/user-attachments/assets/46974459-d3cd-497a-a6b6-9302cbff7716)
---------
Co-authored-by: Nitin Koche
---
.../activities/tasks/components/ObjectTasks.tsx | 2 +-
.../activities/tasks/components/TaskGroups.tsx | 14 +++++++-------
2 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/packages/twenty-front/src/modules/activities/tasks/components/ObjectTasks.tsx b/packages/twenty-front/src/modules/activities/tasks/components/ObjectTasks.tsx
index a798a818d865..9e2dd7a4bc54 100644
--- a/packages/twenty-front/src/modules/activities/tasks/components/ObjectTasks.tsx
+++ b/packages/twenty-front/src/modules/activities/tasks/components/ObjectTasks.tsx
@@ -20,7 +20,7 @@ export const ObjectTasks = ({
return (
-
+
);
diff --git a/packages/twenty-front/src/modules/activities/tasks/components/TaskGroups.tsx b/packages/twenty-front/src/modules/activities/tasks/components/TaskGroups.tsx
index a6e4999773bc..ae484382ffcc 100644
--- a/packages/twenty-front/src/modules/activities/tasks/components/TaskGroups.tsx
+++ b/packages/twenty-front/src/modules/activities/tasks/components/TaskGroups.tsx
@@ -32,13 +32,9 @@ const StyledContainer = styled.div`
type TaskGroupsProps = {
filterDropdownId?: string;
targetableObjects?: ActivityTargetableObject[];
- showAddButton?: boolean;
};
-export const TaskGroups = ({
- targetableObjects,
- showAddButton,
-}: TaskGroupsProps) => {
+export const TaskGroups = ({ targetableObjects }: TaskGroupsProps) => {
const { tasks, tasksLoading } = useTasks({
targetableObjects: targetableObjects ?? [],
});
@@ -93,7 +89,11 @@ export const TaskGroups = ({
const sortedTasksByStatus = Object.entries(
groupBy(tasks, ({ status }) => status),
- ).toSorted(([statusA], [statusB]) => statusB.localeCompare(statusA));
+ ).sort(([statusA], [statusB]) => statusB.localeCompare(statusA));
+
+ const hasTodoStatus = sortedTasksByStatus.some(
+ ([status]) => status === 'TODO',
+ );
return (
@@ -103,7 +103,7 @@ export const TaskGroups = ({
title={status}
tasks={tasksByStatus}
button={
- showAddButton && (
+ (status === 'TODO' || !hasTodoStatus) && (
)
}
From 57eaa01d351bb86c55b1ee4c4f22c8c97b28be6e Mon Sep 17 00:00:00 2001
From: Ngan Phan
Date: Wed, 2 Oct 2024 00:39:46 -0700
Subject: [PATCH 031/115] Adjust Floating Input Padding and Border Color
(#7328)
This PR fix the padding and border color of floating text input #7286
The text area automatically has padding of 4px so I reset it to 0 and
adjusting container padding to 8px.
---
.../src/modules/ui/field/input/components/TextAreaInput.tsx | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/packages/twenty-front/src/modules/ui/field/input/components/TextAreaInput.tsx b/packages/twenty-front/src/modules/ui/field/input/components/TextAreaInput.tsx
index c2da43576095..f9778f17ba05 100644
--- a/packages/twenty-front/src/modules/ui/field/input/components/TextAreaInput.tsx
+++ b/packages/twenty-front/src/modules/ui/field/input/components/TextAreaInput.tsx
@@ -36,10 +36,10 @@ const StyledTextArea = styled(TextareaAutosize)`
`;
const StyledTextAreaContainer = styled.div`
- border: ${({ theme }) => `1px solid ${theme.border.color.light}`};
+ border: ${({ theme }) => `1px solid ${theme.border.color.medium}`};
position: relative;
width: 100%;
- padding: ${({ theme }) => theme.spacing(2)} ${({ theme }) => theme.spacing(1)};
+ padding: ${({ theme }) => theme.spacing(2)} ${({ theme }) => theme.spacing(0)};
border-radius: ${({ theme }) => theme.border.radius.sm};
background: ${({ theme }) => theme.background.primary};
`;
From 23001ac17d89a59a9d6063655163048672524a0b Mon Sep 17 00:00:00 2001
From: "gitstart-app[bot]"
<57568882+gitstart-app[bot]@users.noreply.github.com>
Date: Wed, 2 Oct 2024 17:04:07 +0200
Subject: [PATCH 032/115] Settings Advanced Mode (#7130)
### Description
- We implemented the Advanced Mode state and used this on a section of
the settings sidebar
- in DefaultLayout.tsx, was updated because of the 64 + 16(container
size of IconTool + the margins)
###
### Refs
#6147
Fixes #6147
---------
Co-authored-by: gitstart-twenty
Co-authored-by: gitstart-twenty <140154534+gitstart-twenty@users.noreply.github.com>
---
.../components/AppNavigationDrawer.tsx | 4 +-
.../SettingsNavigationDrawerItems.tsx | 91 +++++++++++++++----
.../hooks/useExpandedHeightAnimation.tsx | 57 ++++++++++++
.../modules/ui/layout/page/DefaultLayout.tsx | 12 +--
.../components/AdvancedSettingsToggle.tsx | 62 +++++++++++++
.../components/NavigationDrawer.tsx | 10 +-
.../components/NavigationDrawerBackButton.tsx | 1 +
.../components/NavigationDrawerSection.tsx | 1 +
.../states/isAdvancedModeEnabledState.ts | 8 ++
.../src/pages/settings/SettingsWorkspace.tsx | 4 +
.../display/icon/components/TablerIcons.ts | 1 +
11 files changed, 221 insertions(+), 30 deletions(-)
create mode 100644 packages/twenty-front/src/modules/settings/hooks/useExpandedHeightAnimation.tsx
create mode 100644 packages/twenty-front/src/modules/ui/navigation/link/components/AdvancedSettingsToggle.tsx
create mode 100644 packages/twenty-front/src/modules/ui/navigation/navigation-drawer/states/isAdvancedModeEnabledState.ts
diff --git a/packages/twenty-front/src/modules/navigation/components/AppNavigationDrawer.tsx b/packages/twenty-front/src/modules/navigation/components/AppNavigationDrawer.tsx
index da636fe025e5..a6d3b1045cc9 100644
--- a/packages/twenty-front/src/modules/navigation/components/AppNavigationDrawer.tsx
+++ b/packages/twenty-front/src/modules/navigation/components/AppNavigationDrawer.tsx
@@ -4,7 +4,6 @@ import { useRecoilValue, useSetRecoilState } from 'recoil';
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
import { SettingsNavigationDrawerItems } from '@/settings/components/SettingsNavigationDrawerItems';
import { SupportDropdown } from '@/support/components/SupportDropdown';
-import { GithubVersionLink } from '@/ui/navigation/link/components/GithubVersionLink';
import {
NavigationDrawer,
NavigationDrawerProps,
@@ -16,6 +15,7 @@ import { getImageAbsoluteURI } from '~/utils/image/getImageAbsoluteURI';
import { useIsSettingsPage } from '../hooks/useIsSettingsPage';
import { currentMobileNavigationDrawerState } from '../states/currentMobileNavigationDrawerState';
+import { AdvancedSettingsToggle } from '@/ui/navigation/link/components/AdvancedSettingsToggle';
import { MainNavigationDrawerItems } from './MainNavigationDrawerItems';
export type AppNavigationDrawerProps = {
@@ -44,7 +44,7 @@ export const AppNavigationDrawer = ({
isSubMenu: true,
title: 'Exit Settings',
children: ,
- footer: ,
+ footer: ,
}
: {
logo:
diff --git a/packages/twenty-front/src/modules/settings/components/SettingsNavigationDrawerItems.tsx b/packages/twenty-front/src/modules/settings/components/SettingsNavigationDrawerItems.tsx
index 04f11020758a..e721e8d8e60e 100644
--- a/packages/twenty-front/src/modules/settings/components/SettingsNavigationDrawerItems.tsx
+++ b/packages/twenty-front/src/modules/settings/components/SettingsNavigationDrawerItems.tsx
@@ -13,13 +13,16 @@ import {
IconMail,
IconRocket,
IconSettings,
+ IconTool,
IconUserCircle,
IconUsers,
+ MAIN_COLORS,
} from 'twenty-ui';
import { useAuth } from '@/auth/hooks/useAuth';
import { billingState } from '@/client-config/states/billingState';
import { SettingsNavigationDrawerItem } from '@/settings/components/SettingsNavigationDrawerItem';
+import { useExpandedHeightAnimation } from '@/settings/hooks/useExpandedHeightAnimation';
import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath';
import { SettingsPath } from '@/types/SettingsPath';
import {
@@ -29,10 +32,38 @@ import {
import { NavigationDrawerItemGroup } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItemGroup';
import { NavigationDrawerSection } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerSection';
import { NavigationDrawerSectionTitle } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerSectionTitle';
+import { isAdvancedModeEnabledState } from '@/ui/navigation/navigation-drawer/states/isAdvancedModeEnabledState';
import { getNavigationSubItemState } from '@/ui/navigation/navigation-drawer/utils/getNavigationSubItemState';
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
+import styled from '@emotion/styled';
+import { AnimatePresence, motion } from 'framer-motion';
import { matchPath, resolvePath, useLocation } from 'react-router-dom';
+const StyledNavigationDrawerSection = styled(NavigationDrawerSection)<{
+ withLeftMargin?: boolean;
+}>`
+ margin-left: ${({ withLeftMargin, theme }) =>
+ withLeftMargin && theme.spacing(5)};
+ margin-top: ${({ theme }) => theme.spacing(3)};
+`;
+
+const StyledIconContainer = styled.div`
+ border-right: 1px solid ${MAIN_COLORS.yellow};
+ display: flex;
+ margin-top: ${({ theme }) => theme.spacing(5)};
+ width: 16px;
+`;
+
+const StyledDeveloperSection = styled.div`
+ display: flex;
+ width: 100%;
+ gap: ${({ theme }) => theme.spacing(1)};
+`;
+
+const StyledIconTool = styled(IconTool)`
+ margin-right: ${({ theme }) => theme.spacing(0.5)};
+`;
+
type SettingsNavigationItem = {
label: string;
path: SettingsPath;
@@ -42,6 +73,10 @@ type SettingsNavigationItem = {
};
export const SettingsNavigationDrawerItems = () => {
+ const isAdvancedModeEnabled = useRecoilValue(isAdvancedModeEnabledState);
+ const { contentRef, motionAnimationVariants } = useExpandedHeightAnimation(
+ isAdvancedModeEnabled,
+ );
const { signOut } = useAuth();
const billing = useRecoilValue(billingState);
@@ -88,7 +123,7 @@ export const SettingsNavigationDrawerItems = () => {
return (
<>
-
+
{
/>
))}
-
-
+
+
{
Icon={IconHierarchy2}
matchSubPages
/>
-
- {isFunctionSettingsEnabled && (
-
- )}
{
Icon={IconCode}
/>
)}
-
-
+
+
+ {isAdvancedModeEnabled && (
+
+
+
+
+
+
+
+
+ {isFunctionSettingsEnabled && (
+
+ )}
+
+
+
+ )}
+
+
{
onClick={signOut}
Icon={IconDoorEnter}
/>
-
+
>
);
};
diff --git a/packages/twenty-front/src/modules/settings/hooks/useExpandedHeightAnimation.tsx b/packages/twenty-front/src/modules/settings/hooks/useExpandedHeightAnimation.tsx
new file mode 100644
index 000000000000..3a48c3bb20b9
--- /dev/null
+++ b/packages/twenty-front/src/modules/settings/hooks/useExpandedHeightAnimation.tsx
@@ -0,0 +1,57 @@
+import { useEffect, useRef, useState } from 'react';
+import { isDefined } from 'twenty-ui';
+
+const transitionValues = {
+ transition: {
+ opactity: { duration: 0.2 },
+ height: { duration: 0.4 },
+ },
+ transitionEnd: {
+ overflow: 'visible',
+ },
+};
+
+const commonStyles = {
+ opacity: 0,
+ height: 0,
+ overflow: 'hidden',
+ ...transitionValues,
+};
+
+const advancedSectionAnimationConfig = (
+ isExpanded: boolean,
+ measuredHeight: number,
+) => ({
+ initial: {
+ ...commonStyles,
+ },
+ animate: {
+ opacity: 1,
+ height: isExpanded ? measuredHeight : 0,
+ ...transitionValues,
+ overflow: 'hidden',
+ },
+ exit: {
+ ...commonStyles,
+ },
+});
+
+export const useExpandedHeightAnimation = (isExpanded: boolean) => {
+ const contentRef = useRef(null);
+ const [measuredHeight, setMeasuredHeight] = useState(0);
+
+ useEffect(() => {
+ if (isDefined(contentRef.current)) {
+ setMeasuredHeight(contentRef.current.scrollHeight);
+ }
+ }, [isExpanded]);
+
+ return {
+ contentRef,
+ measuredHeight,
+ motionAnimationVariants: advancedSectionAnimationConfig(
+ isExpanded,
+ measuredHeight,
+ ),
+ };
+};
diff --git a/packages/twenty-front/src/modules/ui/layout/page/DefaultLayout.tsx b/packages/twenty-front/src/modules/ui/layout/page/DefaultLayout.tsx
index 4108f6a5cee6..555e068d6290 100644
--- a/packages/twenty-front/src/modules/ui/layout/page/DefaultLayout.tsx
+++ b/packages/twenty-front/src/modules/ui/layout/page/DefaultLayout.tsx
@@ -1,7 +1,4 @@
-import { css, Global, useTheme } from '@emotion/react';
-import styled from '@emotion/styled';
-import { AnimatePresence, LayoutGroup, motion } from 'framer-motion';
-import { Outlet } from 'react-router-dom';
+import { AuthModal } from '@/auth/components/AuthModal';
import { CommandMenu } from '@/command-menu/components/CommandMenu';
import { AppErrorBoundary } from '@/error-handler/components/AppErrorBoundary';
import { KeyboardShortcutMenu } from '@/keyboard-shortcut-menu/components/KeyboardShortcutMenu';
@@ -14,7 +11,10 @@ import { useShowAuthModal } from '@/ui/layout/hooks/useShowAuthModal';
import { DESKTOP_NAV_DRAWER_WIDTHS } from '@/ui/navigation/navigation-drawer/constants/DesktopNavDrawerWidths';
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
import { useScreenSize } from '@/ui/utilities/screen-size/hooks/useScreenSize';
-import { AuthModal } from '@/auth/components/AuthModal';
+import { css, Global, useTheme } from '@emotion/react';
+import styled from '@emotion/styled';
+import { AnimatePresence, LayoutGroup, motion } from 'framer-motion';
+import { Outlet } from 'react-router-dom';
const StyledLayout = styled.div`
background: ${({ theme }) => theme.background.noisy};
@@ -85,7 +85,7 @@ export const DefaultLayout = () => {
? (windowsWidth -
(OBJECT_SETTINGS_WIDTH +
DESKTOP_NAV_DRAWER_WIDTHS.menu +
- 64)) /
+ 88)) /
2
: 0,
}}
diff --git a/packages/twenty-front/src/modules/ui/navigation/link/components/AdvancedSettingsToggle.tsx b/packages/twenty-front/src/modules/ui/navigation/link/components/AdvancedSettingsToggle.tsx
new file mode 100644
index 000000000000..a4581469146e
--- /dev/null
+++ b/packages/twenty-front/src/modules/ui/navigation/link/components/AdvancedSettingsToggle.tsx
@@ -0,0 +1,62 @@
+import { Toggle } from '@/ui/input/components/Toggle';
+import { isAdvancedModeEnabledState } from '@/ui/navigation/navigation-drawer/states/isAdvancedModeEnabledState';
+import styled from '@emotion/styled';
+import { useRecoilState } from 'recoil';
+import { IconTool, MAIN_COLORS } from 'twenty-ui';
+
+const StyledContainer = styled.div`
+ align-items: center;
+ display: flex;
+ width: 100%;
+ gap: ${({ theme }) => theme.spacing(2)};
+`;
+
+const StyledText = styled.span`
+ color: ${({ theme }) => theme.font.color.secondary};
+ font-size: ${({ theme }) => theme.font.size.sm};
+ font-weight: ${({ theme }) => theme.font.weight.medium};
+ padding: ${({ theme }) => theme.spacing(1)};
+`;
+
+const StyledIconContainer = styled.div`
+ border-right: 1px solid ${MAIN_COLORS.yellow};
+ display: flex;
+ height: 16px;
+`;
+
+const StyledToggleContainer = styled.div`
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ width: 100%;
+`;
+
+const StyledIconTool = styled(IconTool)`
+ margin-right: ${({ theme }) => theme.spacing(0.5)};
+`;
+
+export const AdvancedSettingsToggle = () => {
+ const [isAdvancedModeEnabled, setIsAdvancedModeEnabled] = useRecoilState(
+ isAdvancedModeEnabledState,
+ );
+
+ const onChange = (newValue: boolean) => {
+ setIsAdvancedModeEnabled(newValue);
+ };
+
+ return (
+
+
+
+
+
+ Advanced
+
+
+
+ );
+};
diff --git a/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/NavigationDrawer.tsx b/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/NavigationDrawer.tsx
index 35fb3cae0d54..28b3640cc394 100644
--- a/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/NavigationDrawer.tsx
+++ b/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/NavigationDrawer.tsx
@@ -40,6 +40,7 @@ const StyledContainer = styled.div<{ isSubMenu?: boolean }>`
${({ isSubMenu, theme }) =>
isSubMenu
? css`
+ padding-left: ${theme.spacing(0)};
padding-right: ${theme.spacing(8)};
`
: ''}
@@ -48,13 +49,12 @@ const StyledContainer = styled.div<{ isSubMenu?: boolean }>`
width: 100%;
}
`;
-
-const StyledItemsContainer = styled.div`
+const StyledItemsContainer = styled.div<{ isSubMenu?: boolean }>`
display: flex;
flex-direction: column;
- gap: ${({ theme }) => theme.spacing(3)};
margin-bottom: auto;
overflow-y: auto;
+ ${({ isSubMenu, theme }) => !isSubMenu && `gap: ${theme.spacing(3)}`}
`;
export const NavigationDrawer = ({
@@ -111,7 +111,9 @@ export const NavigationDrawer = ({
showCollapseButton={isHovered}
/>
)}
- {children}
+
+ {children}
+
{footer}
diff --git a/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/NavigationDrawerBackButton.tsx b/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/NavigationDrawerBackButton.tsx
index 96490d7f0a7e..d906f45aa881 100644
--- a/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/NavigationDrawerBackButton.tsx
+++ b/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/NavigationDrawerBackButton.tsx
@@ -35,6 +35,7 @@ const StyledContainer = styled.div`
flex-direction: row;
height: ${({ theme }) => theme.spacing(8)};
justify-content: space-between;
+ margin-left: ${({ theme }) => theme.spacing(5)};
`;
export const NavigationDrawerBackButton = ({
diff --git a/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/NavigationDrawerSection.tsx b/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/NavigationDrawerSection.tsx
index bea1d0f8e46a..c1fd938476a8 100644
--- a/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/NavigationDrawerSection.tsx
+++ b/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/NavigationDrawerSection.tsx
@@ -4,6 +4,7 @@ const StyledSection = styled.div`
display: flex;
flex-direction: column;
gap: ${({ theme }) => theme.betweenSiblingsGap};
+ width: 100%;
`;
export { StyledSection as NavigationDrawerSection };
diff --git a/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/states/isAdvancedModeEnabledState.ts b/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/states/isAdvancedModeEnabledState.ts
new file mode 100644
index 000000000000..05fd34d432a3
--- /dev/null
+++ b/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/states/isAdvancedModeEnabledState.ts
@@ -0,0 +1,8 @@
+import { atom } from 'recoil';
+import { localStorageEffect } from '~/utils/recoil-effects';
+
+export const isAdvancedModeEnabledState = atom({
+ key: 'isAdvancedModeEnabledAtom',
+ default: false,
+ effects: [localStorageEffect()],
+});
diff --git a/packages/twenty-front/src/pages/settings/SettingsWorkspace.tsx b/packages/twenty-front/src/pages/settings/SettingsWorkspace.tsx
index 359961d24d87..4f32d08b2374 100644
--- a/packages/twenty-front/src/pages/settings/SettingsWorkspace.tsx
+++ b/packages/twenty-front/src/pages/settings/SettingsWorkspace.tsx
@@ -9,6 +9,7 @@ import { WorkspaceLogoUploader } from '@/settings/workspace/components/Workspace
import { SettingsPath } from '@/types/SettingsPath';
import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer';
import { Section } from '@/ui/layout/section/components/Section';
+import { GithubVersionLink } from '@/ui/navigation/link/components/GithubVersionLink';
export const SettingsWorkspace = () => (
(
+