Skip to content

Commit a61d157

Browse files
thaisguigonWeiko
authored andcommitted
feat: edit link in Links field (#5447)
Closes #5376
1 parent 55f1186 commit a61d157

File tree

2 files changed

+58
-11
lines changed

2 files changed

+58
-11
lines changed

packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/LinksFieldInput.tsx

+48-9
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { useMemo, useRef, useState } from 'react';
22
import styled from '@emotion/styled';
33
import { Key } from 'ts-key-enum';
4-
import { IconPlus } from 'twenty-ui';
4+
import { IconCheck, IconPlus } from 'twenty-ui';
55

66
import { useLinksField } from '@/object-record/record-field/meta-types/hooks/useLinksField';
77
import { LinksFieldMenuItem } from '@/object-record/record-field/meta-types/input/components/LinksFieldMenuItem';
@@ -17,6 +17,7 @@ import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useLis
1717
import { moveArrayItem } from '~/utils/array/moveArrayItem';
1818
import { toSpliced } from '~/utils/array/toSpliced';
1919
import { isDefined } from '~/utils/isDefined';
20+
import { absoluteUrlSchema } from '~/utils/validation-schemas/absoluteUrlSchema';
2021

2122
const StyledDropdownMenu = styled(DropdownMenu)`
2223
left: -1px;
@@ -68,14 +69,44 @@ export const LinksFieldInput = ({
6869

6970
const [isInputDisplayed, setIsInputDisplayed] = useState(false);
7071
const [inputValue, setInputValue] = useState('');
72+
const [linkToEditIndex, setLinkToEditIndex] = useState(-1);
73+
const isAddingNewLink = linkToEditIndex === -1;
7174

72-
const handleAddLink = () => {
73-
if (!inputValue) return;
75+
const handleAddButtonClick = () => {
76+
setLinkToEditIndex(-1);
77+
setIsInputDisplayed(true);
78+
};
7479

75-
setIsInputDisplayed(false);
76-
setInputValue('');
80+
const handleEditButtonClick = (index: number) => {
81+
setLinkToEditIndex(index);
82+
setInputValue(links[index].url);
83+
setIsInputDisplayed(true);
84+
};
7785

78-
const nextLinks = [...links, { label: '', url: inputValue }];
86+
const urlInputValidation = inputValue
87+
? absoluteUrlSchema.safeParse(inputValue)
88+
: null;
89+
90+
const handleSubmitInput = () => {
91+
if (!urlInputValidation?.success) return;
92+
93+
const validatedInputValue = urlInputValidation.data;
94+
95+
// Don't persist if value hasn't changed.
96+
if (
97+
!isAddingNewLink &&
98+
validatedInputValue === links[linkToEditIndex].url
99+
) {
100+
setIsInputDisplayed(false);
101+
setInputValue('');
102+
onCancel?.();
103+
return;
104+
}
105+
106+
const linkValue = { label: '', url: validatedInputValue };
107+
const nextLinks = isAddingNewLink
108+
? [...links, linkValue]
109+
: toSpliced(links, linkToEditIndex, 1, linkValue);
79110
const [nextPrimaryLink, ...nextSecondaryLinks] = nextLinks;
80111

81112
onSubmit?.(() =>
@@ -85,6 +116,9 @@ export const LinksFieldInput = ({
85116
secondaryLinks: nextSecondaryLinks,
86117
}),
87118
);
119+
120+
setIsInputDisplayed(false);
121+
setInputValue('');
88122
};
89123

90124
const handleSetPrimaryLink = (index: number) => {
@@ -124,6 +158,7 @@ export const LinksFieldInput = ({
124158
dropdownId={`${hotkeyScope}-links-${index}`}
125159
isPrimary={index === 0}
126160
label={label}
161+
onEdit={() => handleEditButtonClick(index)}
127162
onSetAsPrimary={() => handleSetPrimaryLink(index)}
128163
onDelete={() => handleDeleteLink(index)}
129164
url={url}
@@ -140,15 +175,19 @@ export const LinksFieldInput = ({
140175
value={inputValue}
141176
hotkeyScope={hotkeyScope}
142177
onChange={(event) => setInputValue(event.target.value)}
143-
onEnter={handleAddLink}
178+
onEnter={handleSubmitInput}
144179
rightComponent={
145-
<LightIconButton Icon={IconPlus} onClick={handleAddLink} />
180+
<LightIconButton
181+
Icon={isAddingNewLink ? IconPlus : IconCheck}
182+
disabled={!urlInputValidation?.success}
183+
onClick={handleSubmitInput}
184+
/>
146185
}
147186
/>
148187
) : (
149188
<DropdownMenuItemsContainer>
150189
<MenuItem
151-
onClick={() => setIsInputDisplayed(true)}
190+
onClick={handleAddButtonClick}
152191
LeftIcon={IconPlus}
153192
text="Add link"
154193
/>

packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/LinksFieldMenuItem.tsx

+10-2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
IconBookmarkPlus,
66
IconComponent,
77
IconDotsVertical,
8+
IconPencil,
89
IconTrash,
910
} from 'twenty-ui';
1011

@@ -18,8 +19,9 @@ type LinksFieldMenuItemProps = {
1819
dropdownId: string;
1920
isPrimary?: boolean;
2021
label: string;
21-
onSetAsPrimary: () => void;
22-
onDelete: () => void;
22+
onEdit?: () => void;
23+
onSetAsPrimary?: () => void;
24+
onDelete?: () => void;
2325
url: string;
2426
};
2527

@@ -33,6 +35,7 @@ export const LinksFieldMenuItem = ({
3335
dropdownId,
3436
isPrimary,
3537
label,
38+
onEdit,
3639
onSetAsPrimary,
3740
onDelete,
3841
url,
@@ -71,6 +74,11 @@ export const LinksFieldMenuItem = ({
7174
text="Set as Primary"
7275
onClick={onSetAsPrimary}
7376
/>
77+
<MenuItem
78+
LeftIcon={IconPencil}
79+
text="Edit"
80+
onClick={onEdit}
81+
/>
7482
<MenuItem
7583
accent="danger"
7684
LeftIcon={IconTrash}

0 commit comments

Comments
 (0)