Skip to content

Commit ebfdc6c

Browse files
authored
1 parent 9fafb2c commit ebfdc6c

File tree

2 files changed

+126
-56
lines changed

2 files changed

+126
-56
lines changed

packages/twenty-front/src/pages/settings/data-model/SettingsObjectFieldTable.tsx

+66-35
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,18 @@ import {
44
StyledObjectFieldTableRow,
55
} from '@/settings/data-model/object-details/components/SettingsObjectFieldItemTableRow';
66
import { settingsObjectFieldsFamilyState } from '@/settings/data-model/object-details/states/settingsObjectFieldsFamilyState';
7+
import { TextInput } from '@/ui/input/components/TextInput';
78
import { SortableTableHeader } from '@/ui/layout/table/components/SortableTableHeader';
89
import { Table } from '@/ui/layout/table/components/Table';
910
import { TableHeader } from '@/ui/layout/table/components/TableHeader';
1011
import { TableSection } from '@/ui/layout/table/components/TableSection';
1112
import { useSortedArray } from '@/ui/layout/table/hooks/useSortedArray';
1213
import { TableMetadata } from '@/ui/layout/table/types/TableMetadata';
14+
import styled from '@emotion/styled';
1315
import { isNonEmptyArray } from '@sniptt/guards';
14-
15-
import { useEffect, useMemo } from 'react';
16+
import { useEffect, useMemo, useState } from 'react';
1617
import { useRecoilState } from 'recoil';
18+
import { IconSearch } from 'twenty-ui';
1719
import { useMapFieldMetadataItemToSettingsObjectDetailTableItem } from '~/pages/settings/data-model/hooks/useMapFieldMetadataItemToSettingsObjectDetailTableItem';
1820
import { SettingsObjectDetailTableItem } from '~/pages/settings/data-model/types/SettingsObjectDetailTableItem';
1921

@@ -75,6 +77,9 @@ const SETTINGS_OBJECT_DETAIL_TABLE_METADATA_CUSTOM: TableMetadata<SettingsObject
7577
},
7678
};
7779

80+
const StyledSearchInput = styled(TextInput)`
81+
width: 100%;
82+
`;
7883
export type SettingsObjectFieldTableProps = {
7984
objectMetadataItem: ObjectMetadataItem;
8085
mode: 'view' | 'new-field';
@@ -85,6 +90,8 @@ export const SettingsObjectFieldTable = ({
8590
objectMetadataItem,
8691
mode,
8792
}: SettingsObjectFieldTableProps) => {
93+
const [searchTerm, setSearchTerm] = useState('');
94+
8895
const tableMetadata = objectMetadataItem.isCustom
8996
? SETTINGS_OBJECT_DETAIL_TABLE_METADATA_CUSTOM
9097
: SETTINGS_OBJECT_DETAIL_TABLE_METADATA_STANDARD;
@@ -144,51 +151,75 @@ export const SettingsObjectFieldTable = ({
144151
tableMetadata,
145152
);
146153

154+
const filteredActiveItems = useMemo(
155+
() =>
156+
sortedActiveObjectSettingsDetailItems.filter(
157+
(item) =>
158+
item.label.toLowerCase().includes(searchTerm.toLowerCase()) ||
159+
item.dataType.toLowerCase().includes(searchTerm.toLowerCase()),
160+
),
161+
[sortedActiveObjectSettingsDetailItems, searchTerm],
162+
);
163+
164+
const filteredDisabledItems = useMemo(
165+
() =>
166+
sortedDisabledObjectSettingsDetailItems.filter(
167+
(item) =>
168+
item.label.toLowerCase().includes(searchTerm.toLowerCase()) ||
169+
item.dataType.toLowerCase().includes(searchTerm.toLowerCase()),
170+
),
171+
[sortedDisabledObjectSettingsDetailItems, searchTerm],
172+
);
173+
147174
return (
148-
<Table>
149-
<StyledObjectFieldTableRow>
150-
{tableMetadata.fields.map((item) => (
151-
<SortableTableHeader
152-
key={item.fieldName}
153-
fieldName={item.fieldName}
154-
label={item.fieldLabel}
155-
tableId={tableMetadata.tableId}
156-
initialSort={tableMetadata.initialSort}
157-
/>
158-
))}
159-
<TableHeader></TableHeader>
160-
</StyledObjectFieldTableRow>
161-
{isNonEmptyArray(sortedActiveObjectSettingsDetailItems) && (
162-
<TableSection title="Active">
163-
{sortedActiveObjectSettingsDetailItems.map(
164-
(objectSettingsDetailItem) => (
175+
<>
176+
<StyledSearchInput
177+
LeftIcon={IconSearch}
178+
placeholder="Search a field..."
179+
value={searchTerm}
180+
onChange={setSearchTerm}
181+
/>
182+
<Table>
183+
<StyledObjectFieldTableRow>
184+
{tableMetadata.fields.map((item) => (
185+
<SortableTableHeader
186+
key={item.fieldName}
187+
fieldName={item.fieldName}
188+
label={item.fieldLabel}
189+
tableId={tableMetadata.tableId}
190+
initialSort={tableMetadata.initialSort}
191+
/>
192+
))}
193+
<TableHeader></TableHeader>
194+
</StyledObjectFieldTableRow>
195+
{isNonEmptyArray(filteredActiveItems) && (
196+
<TableSection title="Active">
197+
{filteredActiveItems.map((objectSettingsDetailItem) => (
165198
<SettingsObjectFieldItemTableRow
166199
key={objectSettingsDetailItem.fieldMetadataItem.id}
167200
settingsObjectDetailTableItem={objectSettingsDetailItem}
168201
status="active"
169202
mode={mode}
170203
/>
171-
),
172-
)}
173-
</TableSection>
174-
)}
175-
{isNonEmptyArray(sortedDisabledObjectSettingsDetailItems) && (
176-
<TableSection
177-
isInitiallyExpanded={mode === 'new-field' ? true : false}
178-
title="Inactive"
179-
>
180-
{sortedDisabledObjectSettingsDetailItems.map(
181-
(objectSettingsDetailItem) => (
204+
))}
205+
</TableSection>
206+
)}
207+
{isNonEmptyArray(filteredDisabledItems) && (
208+
<TableSection
209+
isInitiallyExpanded={mode === 'new-field' ? true : false}
210+
title="Inactive"
211+
>
212+
{filteredDisabledItems.map((objectSettingsDetailItem) => (
182213
<SettingsObjectFieldItemTableRow
183214
key={objectSettingsDetailItem.fieldMetadataItem.id}
184215
settingsObjectDetailTableItem={objectSettingsDetailItem}
185216
status="disabled"
186217
mode={mode}
187218
/>
188-
),
189-
)}
190-
</TableSection>
191-
)}
192-
</Table>
219+
))}
220+
</TableSection>
221+
)}
222+
</Table>
223+
</>
193224
);
194225
};

packages/twenty-front/src/pages/settings/data-model/SettingsObjects.tsx

+60-21
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
import { useTheme } from '@emotion/react';
22
import styled from '@emotion/styled';
3-
import { H2Title, IconChevronRight, IconHierarchy2, IconPlus } from 'twenty-ui';
3+
import {
4+
H2Title,
5+
IconChevronRight,
6+
IconHierarchy2,
7+
IconPlus,
8+
IconSearch,
9+
} from 'twenty-ui';
410

511
import { useDeleteOneObjectMetadataItem } from '@/object-metadata/hooks/useDeleteOneObjectMetadataItem';
612
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
@@ -18,6 +24,7 @@ import { getObjectTypeLabel } from '@/settings/data-model/utils/getObjectTypeLab
1824
import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath';
1925
import { SettingsPath } from '@/types/SettingsPath';
2026
import { Button } from '@/ui/input/button/components/Button';
27+
import { TextInput } from '@/ui/input/components/TextInput';
2128
import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer';
2229
import { Section } from '@/ui/layout/section/components/Section';
2330
import { SortableTableHeader } from '@/ui/layout/table/components/SortableTableHeader';
@@ -27,17 +34,19 @@ import { TableSection } from '@/ui/layout/table/components/TableSection';
2734
import { useSortedArray } from '@/ui/layout/table/hooks/useSortedArray';
2835
import { UndecoratedLink } from '@/ui/navigation/link/components/UndecoratedLink';
2936
import { isNonEmptyArray } from '@sniptt/guards';
30-
import { useMemo } from 'react';
37+
import { useMemo, useState } from 'react';
3138
import { SETTINGS_OBJECT_TABLE_METADATA } from '~/pages/settings/data-model/constants/SettingsObjectTableMetadata';
3239
import { SettingsObjectTableItem } from '~/pages/settings/data-model/types/SettingsObjectTableItem';
3340

3441
const StyledIconChevronRight = styled(IconChevronRight)`
3542
color: ${({ theme }) => theme.font.color.tertiary};
3643
`;
37-
44+
const StyledSearchInput = styled(TextInput)`
45+
width: 100%;
46+
`;
3847
export const SettingsObjects = () => {
3948
const theme = useTheme();
40-
49+
const [searchTerm, setSearchTerm] = useState('');
4150
const { deleteOneObjectMetadataItem } = useDeleteOneObjectMetadataItem();
4251
const { updateOneObjectMetadataItem } = useUpdateOneObjectMetadataItem();
4352

@@ -102,7 +111,25 @@ export const SettingsObjects = () => {
102111
inactiveObjectSettingsArray,
103112
SETTINGS_OBJECT_TABLE_METADATA,
104113
);
114+
const filteredActiveObjectSettingsItems = useMemo(
115+
() =>
116+
sortedActiveObjectSettingsItems.filter(
117+
(item) =>
118+
item.labelPlural.toLowerCase().includes(searchTerm.toLowerCase()) ||
119+
item.objectTypeLabel.toLowerCase().includes(searchTerm.toLowerCase()),
120+
),
121+
[sortedActiveObjectSettingsItems, searchTerm],
122+
);
105123

124+
const filteredInactiveObjectSettingsItems = useMemo(
125+
() =>
126+
sortedInactiveObjectSettingsItems.filter(
127+
(item) =>
128+
item.labelPlural.toLowerCase().includes(searchTerm.toLowerCase()) ||
129+
item.objectTypeLabel.toLowerCase().includes(searchTerm.toLowerCase()),
130+
),
131+
[sortedInactiveObjectSettingsItems, searchTerm],
132+
);
106133
return (
107134
<SubMenuTopBarContainer
108135
Icon={IconHierarchy2}
@@ -123,6 +150,14 @@ export const SettingsObjects = () => {
123150
<SettingsObjectCoverImage />
124151
<Section>
125152
<H2Title title="Existing objects" />
153+
154+
<StyledSearchInput
155+
LeftIcon={IconSearch}
156+
placeholder="Search an object..."
157+
value={searchTerm}
158+
onChange={setSearchTerm}
159+
/>
160+
126161
<Table>
127162
<StyledObjectTableRow>
128163
{SETTINGS_OBJECT_TABLE_METADATA.fields.map(
@@ -141,27 +176,31 @@ export const SettingsObjects = () => {
141176
</StyledObjectTableRow>
142177
{isNonEmptyArray(sortedActiveObjectSettingsItems) && (
143178
<TableSection title="Active">
144-
{sortedActiveObjectSettingsItems.map((objectSettingsItem) => (
145-
<SettingsObjectMetadataItemTableRow
146-
key={objectSettingsItem.objectMetadataItem.namePlural}
147-
objectMetadataItem={objectSettingsItem.objectMetadataItem}
148-
totalObjectCount={objectSettingsItem.totalObjectCount}
149-
action={
150-
<StyledIconChevronRight
151-
size={theme.icon.size.md}
152-
stroke={theme.icon.stroke.sm}
153-
/>
154-
}
155-
link={`/settings/objects/${getObjectSlug(
156-
objectSettingsItem.objectMetadataItem,
157-
)}`}
158-
/>
159-
))}
179+
{filteredActiveObjectSettingsItems.map(
180+
(objectSettingsItem) => (
181+
<SettingsObjectMetadataItemTableRow
182+
key={objectSettingsItem.objectMetadataItem.namePlural}
183+
objectMetadataItem={
184+
objectSettingsItem.objectMetadataItem
185+
}
186+
totalObjectCount={objectSettingsItem.totalObjectCount}
187+
action={
188+
<StyledIconChevronRight
189+
size={theme.icon.size.md}
190+
stroke={theme.icon.stroke.sm}
191+
/>
192+
}
193+
link={`/settings/objects/${getObjectSlug(
194+
objectSettingsItem.objectMetadataItem,
195+
)}`}
196+
/>
197+
),
198+
)}
160199
</TableSection>
161200
)}
162201
{isNonEmptyArray(inactiveObjectMetadataItems) && (
163202
<TableSection title="Inactive">
164-
{sortedInactiveObjectSettingsItems.map(
203+
{filteredInactiveObjectSettingsItems.map(
165204
(objectSettingsItem) => (
166205
<SettingsObjectMetadataItemTableRow
167206
key={objectSettingsItem.objectMetadataItem.namePlural}

0 commit comments

Comments
 (0)