[AI] Refactor settings search to use SearchInput and restructure Skills tab (#18876)

closes -
https://discord.com/channels/1130383047699738754/1480980523315626178
This commit is contained in:
nitin
2026-03-26 11:27:41 +05:30
committed by GitHub
parent b87762b1c2
commit 4985270e69
13 changed files with 363 additions and 426 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

@@ -23,7 +23,7 @@ import { Button } from 'twenty-ui/input';
import { Card, Section } from 'twenty-ui/layout';
import { SettingsAIMCP } from './components/SettingsAIMCP';
import { SettingsAIModelsTab } from './components/SettingsAIModelsTab';
import { SettingsSkillsTable } from './components/SettingsSkillsTable';
import { SettingsAgentSkills } from './components/SettingsAgentSkills';
import { SettingsToolsTable } from './components/SettingsToolsTable';
import { SETTINGS_AI_TABS } from './constants/SettingsAiTabs';
@@ -84,7 +84,7 @@ export const SettingsAI = () => {
componentInstanceId={SETTINGS_AI_TABS.COMPONENT_INSTANCE_ID}
/>
{isModelsTab && <SettingsAIModelsTab />}
{isSkillsTab && <SettingsSkillsTable />}
{isSkillsTab && <SettingsAgentSkills />}
{isToolsTab && <SettingsToolsTable />}
{isMoreTab && (
<>
@@ -2,7 +2,6 @@ import { styled } from '@linaria/react';
import { useLingui } from '@lingui/react/macro';
import { useContext, useState } from 'react';
import { SettingsTextInput } from '@/ui/input/components/SettingsTextInput';
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
@@ -12,13 +11,8 @@ import { TableHeader } from '@/ui/layout/table/components/TableHeader';
import { useSortedArray } from '@/ui/layout/table/hooks/useSortedArray';
import { SettingsPath } from 'twenty-shared/types';
import { getSettingsPath } from 'twenty-shared/utils';
import {
IconChevronRight,
IconFilter,
IconSearch,
IconSettingsAutomation,
} from 'twenty-ui/display';
import { Button } from 'twenty-ui/input';
import { IconChevronRight, IconSettingsAutomation } from 'twenty-ui/display';
import { SearchInput } from 'twenty-ui/input';
import { MenuItemToggle } from 'twenty-ui/navigation';
import { ThemeContext, themeCssVariables } from 'twenty-ui/theme-constants';
import { SETTINGS_AI_AGENT_TABLE_METADATA } from '~/pages/settings/ai/constants/SettingsAiAgentTableMetadata';
@@ -33,15 +27,8 @@ import {
SettingsAIAgentTableRow,
} from './SettingsAIAgentTableRow';
const StyledSearchAndFilterContainer = styled.div`
display: flex;
gap: ${themeCssVariables.spacing[2]};
const StyledSearchContainer = styled.div`
margin-bottom: ${themeCssVariables.spacing[2]};
width: 100%;
`;
const StyledSearchInputContainer = styled.div`
flex: 1;
`;
const StyledTableContainer = styled.div`
@@ -79,47 +66,36 @@ export const SettingsAIAgentsTable = () => {
return (
<>
<StyledSearchAndFilterContainer>
<StyledSearchInputContainer>
<SettingsTextInput
instanceId="settings-ai-agents-search"
LeftIcon={IconSearch}
placeholder={t`Search an agent...`}
value={searchTerm}
onChange={setSearchTerm}
fullWidth
/>
</StyledSearchInputContainer>
<Dropdown
dropdownId="settings-ai-agents-filter-dropdown"
dropdownPlacement="bottom-end"
dropdownOffset={{ x: 0, y: 8 }}
clickableComponent={
<Button
Icon={IconFilter}
size="medium"
variant="secondary"
accent="default"
ariaLabel={t`Filter`}
<StyledSearchContainer>
<SearchInput
placeholder={t`Search an agent...`}
value={searchTerm}
onChange={setSearchTerm}
filterDropdown={(filterButton) => (
<Dropdown
dropdownId="settings-ai-agents-filter-dropdown"
dropdownPlacement="bottom-end"
dropdownOffset={{ x: 0, y: 8 }}
clickableComponent={filterButton}
dropdownComponents={
<DropdownContent>
<DropdownMenuItemsContainer>
<MenuItemToggle
LeftIcon={IconSettingsAutomation}
onToggleChange={() =>
setShowWorkflowAgents(!showWorkflowAgents)
}
toggled={showWorkflowAgents}
text={t`Workflow agents`}
toggleSize="small"
/>
</DropdownMenuItemsContainer>
</DropdownContent>
}
/>
}
dropdownComponents={
<DropdownContent>
<DropdownMenuItemsContainer>
<MenuItemToggle
LeftIcon={IconSettingsAutomation}
onToggleChange={() =>
setShowWorkflowAgents(!showWorkflowAgents)
}
toggled={showWorkflowAgents}
text={t`Workflow agents`}
toggleSize="small"
/>
</DropdownMenuItemsContainer>
</DropdownContent>
}
)}
/>
</StyledSearchAndFilterContainer>
</StyledSearchContainer>
<StyledTableContainer>
<Table>
@@ -14,16 +14,11 @@ import { SettingsOptionCardContentToggle } from '@/settings/components/SettingsO
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
import { Select } from '@/ui/input/components/Select';
import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth';
import { SettingsTextInput } from '@/ui/input/components/SettingsTextInput';
import { useAtomState } from '@/ui/utilities/state/jotai/hooks/useAtomState';
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
import { t } from '@lingui/core/macro';
import {
H2Title,
IconBolt,
IconSearch,
IconTwentyStar,
} from 'twenty-ui/display';
import { H2Title, IconBolt, IconTwentyStar } from 'twenty-ui/display';
import { SearchInput } from 'twenty-ui/input';
import { Card, Section } from 'twenty-ui/layout';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import { useMutation } from '@apollo/client/react';
@@ -281,13 +276,10 @@ export const SettingsAIModelsTab = () => {
/>
<StyledSearchContainer>
<SettingsTextInput
instanceId="model-table-search"
LeftIcon={IconSearch}
<SearchInput
placeholder={t`Search a model...`}
value={searchQuery}
onChange={setSearchQuery}
fullWidth
/>
</StyledSearchContainer>
@@ -0,0 +1,162 @@
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { useSortedArray } from '@/ui/layout/table/hooks/useSortedArray';
import { styled } from '@linaria/react';
import { useLingui } from '@lingui/react/macro';
import { useContext, useMemo, useState } from 'react';
import { SettingsPath } from 'twenty-shared/types';
import { getSettingsPath } from 'twenty-shared/utils';
import { H2Title, IconArchive, IconPlus } from 'twenty-ui/display';
import { Button, SearchInput } from 'twenty-ui/input';
import { MenuItemToggle, UndecoratedLink } from 'twenty-ui/navigation';
import { ThemeContext, themeCssVariables } from 'twenty-ui/theme-constants';
import { useMutation, useQuery } from '@apollo/client/react';
import { Section } from 'twenty-ui/layout';
import {
ActivateSkillDocument,
DeleteSkillDocument,
FindManySkillsDocument,
} from '~/generated-metadata/graphql';
import { SETTINGS_SKILL_TABLE_METADATA } from '~/pages/settings/ai/constants/SettingsSkillTableMetadata';
import { normalizeSearchText } from '~/utils/normalizeSearchText';
import { SettingsAgentSkillsTable } from './SettingsAgentSkillsTable';
const StyledCoverImage = styled.div`
background-size: cover;
height: 160px;
overflow: hidden;
`;
const StyledFooterContainer = styled.div`
align-items: center;
display: flex;
justify-content: flex-end;
margin-top: ${themeCssVariables.spacing[4]};
`;
const StyledSearchInput = styled(SearchInput)`
margin-bottom: ${themeCssVariables.spacing[4]};
`;
export const SettingsAgentSkills = () => {
const { t } = useLingui();
const { colorScheme } = useContext(ThemeContext);
const { enqueueSuccessSnackBar, enqueueErrorSnackBar } = useSnackBar();
const { data, loading, refetch } = useQuery(FindManySkillsDocument);
const [activateSkill] = useMutation(ActivateSkillDocument);
const [deleteSkill] = useMutation(DeleteSkillDocument);
const [searchTerm, setSearchTerm] = useState('');
const [showDeactivated, setShowDeactivated] = useState(true);
const skills = data?.skills ?? [];
const sortedSkills = useSortedArray(skills, SETTINGS_SKILL_TABLE_METADATA);
const filteredSkills = useMemo(
() =>
sortedSkills.filter((skill) => {
const searchNormalized = normalizeSearchText(searchTerm);
const matchesSearch =
normalizeSearchText(skill.name).includes(searchNormalized) ||
normalizeSearchText(skill.label).includes(searchNormalized);
if (!matchesSearch) {
return false;
}
if (!skill.isActive && !showDeactivated) {
return false;
}
return true;
}),
[sortedSkills, searchTerm, showDeactivated],
);
const handleActivate = async (skillId: string) => {
try {
await activateSkill({ variables: { id: skillId } });
enqueueSuccessSnackBar({ message: t`Skill activated` });
refetch();
} catch {
enqueueErrorSnackBar({ message: t`Failed to activate skill` });
}
};
const handleDelete = async (skillId: string) => {
try {
await deleteSkill({ variables: { id: skillId } });
enqueueSuccessSnackBar({ message: t`Skill deleted` });
refetch();
} catch {
enqueueErrorSnackBar({ message: t`Failed to delete skill` });
}
};
const coverImage =
colorScheme === 'light'
? '/images/ai/ai-skills-cover-light.png'
: '/images/ai/ai-skills-cover-dark.png';
return (
<>
<StyledCoverImage style={{ backgroundImage: `url('${coverImage}')` }} />
<Section>
<H2Title
title={t`Skills`}
description={t`Use filter to see existing tools or create your own`}
/>
<StyledSearchInput
placeholder={t`Search a skill...`}
value={searchTerm}
onChange={setSearchTerm}
filterDropdown={(filterButton) => (
<Dropdown
dropdownId="settings-skills-filter-dropdown"
dropdownPlacement="bottom-end"
dropdownOffset={{ x: 0, y: 8 }}
clickableComponent={filterButton}
dropdownComponents={
<DropdownContent>
<DropdownMenuItemsContainer>
<MenuItemToggle
LeftIcon={IconArchive}
onToggleChange={() =>
setShowDeactivated(!showDeactivated)
}
toggled={showDeactivated}
text={t`Deactivated`}
toggleSize="small"
/>
</DropdownMenuItemsContainer>
</DropdownContent>
}
/>
)}
/>
<SettingsAgentSkillsTable
skills={filteredSkills}
loading={loading}
onActivate={handleActivate}
onDelete={handleDelete}
/>
<StyledFooterContainer>
<UndecoratedLink to={getSettingsPath(SettingsPath.AINewSkill)}>
<Button
Icon={IconPlus}
title={t`New Skill`}
size="small"
variant="secondary"
/>
</UndecoratedLink>
</StyledFooterContainer>
</Section>
</>
);
};
@@ -0,0 +1,97 @@
import { styled } from '@linaria/react';
import { useLingui } from '@lingui/react/macro';
import { useContext } from 'react';
import Skeleton from 'react-loading-skeleton';
import { SortableTableHeader } from '@/ui/layout/table/components/SortableTableHeader';
import { Table } from '@/ui/layout/table/components/Table';
import { TableHeader } from '@/ui/layout/table/components/TableHeader';
import { TableRow } from '@/ui/layout/table/components/TableRow';
import { SettingsPath } from 'twenty-shared/types';
import { getSettingsPath } from 'twenty-shared/utils';
import { IconChevronRight } from 'twenty-ui/display';
import { ThemeContext, themeCssVariables } from 'twenty-ui/theme-constants';
import { type FindManySkillsQuery } from '~/generated-metadata/graphql';
import { SettingsSkillInactiveMenuDropDown } from '~/pages/settings/ai/components/SettingsSkillInactiveMenuDropDown';
import { SETTINGS_SKILL_TABLE_METADATA } from '~/pages/settings/ai/constants/SettingsSkillTableMetadata';
import { SettingsSkillTableRow } from './SettingsSkillTableRow';
type Skill = FindManySkillsQuery['skills'][number];
type SettingsAgentSkillsTableProps = {
skills: Skill[];
loading: boolean;
onActivate: (skillId: string) => void;
onDelete: (skillId: string) => void;
};
const StyledTableHeaderRowContainer = styled.div`
margin-bottom: ${themeCssVariables.spacing[2]};
`;
export const SettingsAgentSkillsTable = ({
skills,
loading,
onActivate,
onDelete,
}: SettingsAgentSkillsTableProps) => {
const { theme } = useContext(ThemeContext);
const { t } = useLingui();
const showSkeleton = loading && skills.length === 0;
return (
<Table>
<StyledTableHeaderRowContainer>
<TableRow gridTemplateColumns="1fr 120px 36px">
{SETTINGS_SKILL_TABLE_METADATA.fields.map(
(settingsSkillTableMetadataField) => (
<SortableTableHeader
key={settingsSkillTableMetadataField.fieldName}
fieldName={settingsSkillTableMetadataField.fieldName}
label={t(settingsSkillTableMetadataField.fieldLabel)}
tableId={SETTINGS_SKILL_TABLE_METADATA.tableId}
align={settingsSkillTableMetadataField.align}
initialSort={SETTINGS_SKILL_TABLE_METADATA.initialSort}
/>
),
)}
<TableHeader />
</TableRow>
</StyledTableHeaderRowContainer>
{showSkeleton
? Array.from({ length: 3 }).map((_, index) => (
<Skeleton height={32} borderRadius={4} key={index} />
))
: skills.map((skill) => (
<SettingsSkillTableRow
key={skill.id}
skill={skill}
action={
skill.isActive ? (
<IconChevronRight
size={theme.icon.size.md}
stroke={theme.icon.stroke.sm}
/>
) : (
<SettingsSkillInactiveMenuDropDown
isCustomSkill={skill.isCustom}
skillId={skill.id}
onActivate={() => onActivate(skill.id)}
onDelete={() => onDelete(skill.id)}
/>
)
}
link={
skill.isActive
? getSettingsPath(SettingsPath.AISkillDetail, {
skillId: skill.id,
})
: undefined
}
/>
))}
</Table>
);
};
@@ -4,7 +4,7 @@ import { type ReactNode, useContext } from 'react';
import { SettingsItemTypeTag } from '@/settings/components/SettingsItemTypeTag';
import { TableCell } from '@/ui/layout/table/components/TableCell';
import { TableRow } from '@/ui/layout/table/components/TableRow';
import { useIcons, OverflowingTextWithTooltip } from 'twenty-ui/display';
import { OverflowingTextWithTooltip, useIcons } from 'twenty-ui/display';
import { ThemeContext, themeCssVariables } from 'twenty-ui/theme-constants';
import { type Skill } from '~/generated-metadata/graphql';
@@ -44,7 +44,7 @@ export const SettingsSkillTableRow = ({
overflow="hidden"
>
<StyledIconContainer>
<Icon size={theme.icon.size.md} />
<Icon size={theme.icon.size.md} color={theme.color.blue9} />
</StyledIconContainer>
<OverflowingTextWithTooltip text={skill.label} />
</TableCell>
@@ -1,226 +0,0 @@
import { styled } from '@linaria/react';
import { useLingui } from '@lingui/react/macro';
import { useContext, useMemo, useState } from 'react';
import Skeleton from 'react-loading-skeleton';
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
import { SettingsTextInput } from '@/ui/input/components/SettingsTextInput';
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { SortableTableHeader } from '@/ui/layout/table/components/SortableTableHeader';
import { Table } from '@/ui/layout/table/components/Table';
import { TableHeader } from '@/ui/layout/table/components/TableHeader';
import { TableRow } from '@/ui/layout/table/components/TableRow';
import { useSortedArray } from '@/ui/layout/table/hooks/useSortedArray';
import { SettingsPath } from 'twenty-shared/types';
import { getSettingsPath } from 'twenty-shared/utils';
import {
IconArchive,
IconChevronRight,
IconFilter,
IconPlus,
IconSearch,
} from 'twenty-ui/display';
import { Button } from 'twenty-ui/input';
import { MenuItemToggle, UndecoratedLink } from 'twenty-ui/navigation';
import { ThemeContext, themeCssVariables } from 'twenty-ui/theme-constants';
import { useMutation, useQuery } from '@apollo/client/react';
import {
ActivateSkillDocument,
DeleteSkillDocument,
FindManySkillsDocument,
} from '~/generated-metadata/graphql';
import { SettingsSkillInactiveMenuDropDown } from '~/pages/settings/ai/components/SettingsSkillInactiveMenuDropDown';
import { SETTINGS_SKILL_TABLE_METADATA } from '~/pages/settings/ai/constants/SettingsSkillTableMetadata';
import { normalizeSearchText } from '~/utils/normalizeSearchText';
import { SettingsSkillTableRow } from './SettingsSkillTableRow';
const StyledSearchAndFilterContainer = styled.div`
align-items: center;
display: flex;
gap: ${themeCssVariables.spacing[2]};
padding-bottom: ${themeCssVariables.spacing[2]};
`;
const StyledSearchInputWrapper = styled.div`
flex: 1;
`;
const StyledTableHeaderRowContainer = styled.div`
margin-bottom: ${themeCssVariables.spacing[2]};
`;
const StyledFooterContainer = styled.div`
align-items: center;
display: flex;
justify-content: flex-end;
margin-top: ${themeCssVariables.spacing[4]};
`;
export const SettingsSkillsTable = () => {
const { theme } = useContext(ThemeContext);
const { data, loading, refetch } = useQuery(FindManySkillsDocument);
const [activateSkill] = useMutation(ActivateSkillDocument);
const [deleteSkill] = useMutation(DeleteSkillDocument);
const { t } = useLingui();
const { enqueueSuccessSnackBar, enqueueErrorSnackBar } = useSnackBar();
const [searchTerm, setSearchTerm] = useState('');
const [showDeactivated, setShowDeactivated] = useState(true);
const skills = data?.skills ?? [];
const sortedSkills = useSortedArray(skills, SETTINGS_SKILL_TABLE_METADATA);
const filteredSkills = useMemo(
() =>
sortedSkills.filter((skill) => {
const searchNormalized = normalizeSearchText(searchTerm);
const matchesSearch =
normalizeSearchText(skill.name).includes(searchNormalized) ||
normalizeSearchText(skill.label).includes(searchNormalized);
if (!matchesSearch) {
return false;
}
if (!skill.isActive && !showDeactivated) {
return false;
}
return true;
}),
[sortedSkills, searchTerm, showDeactivated],
);
const showSkeleton = loading && skills.length === 0;
const handleActivate = async (skillId: string) => {
try {
await activateSkill({ variables: { id: skillId } });
enqueueSuccessSnackBar({ message: t`Skill activated` });
refetch();
} catch {
enqueueErrorSnackBar({ message: t`Failed to activate skill` });
}
};
const handleDelete = async (skillId: string) => {
try {
await deleteSkill({ variables: { id: skillId } });
enqueueSuccessSnackBar({ message: t`Skill deleted` });
refetch();
} catch {
enqueueErrorSnackBar({ message: t`Failed to delete skill` });
}
};
return (
<>
<StyledSearchAndFilterContainer>
<StyledSearchInputWrapper>
<SettingsTextInput
instanceId="skill-table-search"
LeftIcon={IconSearch}
placeholder={t`Search a skill...`}
value={searchTerm}
onChange={setSearchTerm}
fullWidth
/>
</StyledSearchInputWrapper>
<Dropdown
dropdownId="settings-skills-filter-dropdown"
dropdownPlacement="bottom-end"
dropdownOffset={{ x: 0, y: 8 }}
clickableComponent={
<Button
Icon={IconFilter}
size="medium"
variant="secondary"
accent="default"
ariaLabel={t`Filter`}
/>
}
dropdownComponents={
<DropdownContent>
<DropdownMenuItemsContainer>
<MenuItemToggle
LeftIcon={IconArchive}
onToggleChange={() => setShowDeactivated(!showDeactivated)}
toggled={showDeactivated}
text={t`Deactivated`}
toggleSize="small"
/>
</DropdownMenuItemsContainer>
</DropdownContent>
}
/>
</StyledSearchAndFilterContainer>
<Table>
<StyledTableHeaderRowContainer>
<TableRow gridTemplateColumns="1fr 120px 36px">
{SETTINGS_SKILL_TABLE_METADATA.fields.map(
(settingsSkillTableMetadataField) => (
<SortableTableHeader
key={settingsSkillTableMetadataField.fieldName}
fieldName={settingsSkillTableMetadataField.fieldName}
label={t(settingsSkillTableMetadataField.fieldLabel)}
tableId={SETTINGS_SKILL_TABLE_METADATA.tableId}
align={settingsSkillTableMetadataField.align}
initialSort={SETTINGS_SKILL_TABLE_METADATA.initialSort}
/>
),
)}
<TableHeader />
</TableRow>
</StyledTableHeaderRowContainer>
{showSkeleton
? Array.from({ length: 3 }).map((_, index) => (
<Skeleton height={32} borderRadius={4} key={index} />
))
: filteredSkills.map((skill) => (
<SettingsSkillTableRow
key={skill.id}
skill={skill}
action={
skill.isActive ? (
<IconChevronRight
size={theme.icon.size.md}
stroke={theme.icon.stroke.sm}
/>
) : (
<SettingsSkillInactiveMenuDropDown
isCustomSkill={skill.isCustom}
skillId={skill.id}
onActivate={() => handleActivate(skill.id)}
onDelete={() => handleDelete(skill.id)}
/>
)
}
link={
skill.isActive
? getSettingsPath(SettingsPath.AISkillDetail, {
skillId: skill.id,
})
: undefined
}
/>
))}
</Table>
<StyledFooterContainer>
<UndecoratedLink to={getSettingsPath(SettingsPath.AINewSkill)}>
<Button
Icon={IconPlus}
title={t`New Skill`}
size="small"
variant="secondary"
/>
</UndecoratedLink>
</StyledFooterContainer>
</>
);
};
@@ -8,19 +8,13 @@ import { usePersistLogicFunction } from '@/logic-functions/hooks/usePersistLogic
import { logicFunctionsState } from '@/settings/logic-functions/states/logicFunctionsState';
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
import { SettingsTextInput } from '@/ui/input/components/SettingsTextInput';
import { Table } from '@/ui/layout/table/components/Table';
import { TableHeader } from '@/ui/layout/table/components/TableHeader';
import { useNavigate } from 'react-router-dom';
import { SettingsPath } from 'twenty-shared/types';
import { getSettingsPath, isDefined } from 'twenty-shared/utils';
import {
H2Title,
IconChevronRight,
IconPlus,
IconSearch,
} from 'twenty-ui/display';
import { Button } from 'twenty-ui/input';
import { H2Title, IconChevronRight, IconPlus } from 'twenty-ui/display';
import { Button, SearchInput } from 'twenty-ui/input';
import { Section } from 'twenty-ui/layout';
import { ThemeContext, themeCssVariables } from 'twenty-ui/theme-constants';
@@ -32,18 +26,10 @@ import {
SettingsToolTableRow,
} from './SettingsToolTableRow';
const StyledSearchAndFilterContainer = styled.div`
align-items: center;
display: flex;
gap: ${themeCssVariables.spacing[2]};
const StyledSearchContainer = styled.div`
padding-bottom: ${themeCssVariables.spacing[2]};
`;
const StyledSearchInputContainer = styled.div`
flex: 1;
width: 100%;
`;
const StyledTableHeaderRowContainer = styled.div`
margin-bottom: ${themeCssVariables.spacing[2]};
`;
@@ -171,18 +157,13 @@ export const SettingsToolsTable = () => {
title={t`Custom`}
description={t`Custom tools created in your workspace`}
/>
<StyledSearchAndFilterContainer>
<StyledSearchInputContainer>
<SettingsTextInput
instanceId="custom-tool-table-search"
LeftIcon={IconSearch}
placeholder={t`Search a custom tool...`}
value={customSearchTerm}
onChange={setCustomSearchTerm}
fullWidth
/>
</StyledSearchInputContainer>
</StyledSearchAndFilterContainer>
<StyledSearchContainer>
<SearchInput
placeholder={t`Search a custom tool...`}
value={customSearchTerm}
onChange={setCustomSearchTerm}
/>
</StyledSearchContainer>
<Table>
<StyledTableHeaderRowContainer>
<TableRow
@@ -229,18 +210,13 @@ export const SettingsToolsTable = () => {
title={t`Built-in`}
description={t`Standard tools available to AI agents`}
/>
<StyledSearchAndFilterContainer>
<StyledSearchInputContainer>
<SettingsTextInput
instanceId="builtin-tool-table-search"
LeftIcon={IconSearch}
placeholder={t`Search a built-in tool...`}
value={builtInSearchTerm}
onChange={setBuiltInSearchTerm}
fullWidth
/>
</StyledSearchInputContainer>
</StyledSearchAndFilterContainer>
<StyledSearchContainer>
<SearchInput
placeholder={t`Search a built-in tool...`}
value={builtInSearchTerm}
onChange={setBuiltInSearchTerm}
/>
</StyledSearchContainer>
<Table>
<StyledTableHeaderRowContainer>
<TableRow
@@ -6,7 +6,6 @@ import {
SettingsObjectFieldItemTableRow,
} from '@/settings/data-model/object-details/components/SettingsObjectFieldItemTableRow';
import { settingsObjectFieldsFamilyState } from '@/settings/data-model/object-details/states/settingsObjectFieldsFamilyState';
import { SettingsTextInput } from '@/ui/input/components/SettingsTextInput';
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
@@ -24,28 +23,16 @@ import { useAtomFamilyStateValue } from '@/ui/utilities/state/jotai/hooks/useAto
import { useSetAtomFamilyState } from '@/ui/utilities/state/jotai/hooks/useSetAtomFamilyState';
import { useEffect, useMemo, useState } from 'react';
import { FieldMetadataType } from 'twenty-shared/types';
import {
IconArchive,
IconFilter,
IconSearch,
IconSettings,
} from 'twenty-ui/display';
import { Button } from 'twenty-ui/input';
import { IconArchive, IconSettings } from 'twenty-ui/display';
import { SearchInput } from 'twenty-ui/input';
import { MenuItemToggle } from 'twenty-ui/navigation';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import { useMapFieldMetadataItemToSettingsObjectDetailTableItem } from '~/pages/settings/data-model/hooks/useMapFieldMetadataItemToSettingsObjectDetailTableItem';
import { type SettingsObjectDetailTableItem } from '~/pages/settings/data-model/types/SettingsObjectDetailTableItem';
import { normalizeSearchText } from '~/utils/normalizeSearchText';
const StyledSearchAndFilterContainer = styled.div`
display: flex;
gap: ${themeCssVariables.spacing[2]};
const StyledSearchContainer = styled.div`
padding-bottom: ${themeCssVariables.spacing[2]};
width: 100%;
`;
const StyledSearchInputContainer = styled.div`
flex: 1;
`;
const SETTINGS_OBJECT_FIELD_TABLE_METADATA: TableMetadata<SettingsObjectDetailTableItem> =
@@ -163,55 +150,45 @@ export const SettingsObjectFieldTable = ({
return (
<>
<StyledSearchAndFilterContainer>
<StyledSearchInputContainer>
<SettingsTextInput
instanceId="object-field-table-search"
LeftIcon={IconSearch}
placeholder={t`Search a field...`}
value={searchTerm}
onChange={setSearchTerm}
/>
</StyledSearchInputContainer>
<Dropdown
dropdownId="settings-fields-filter-dropdown"
dropdownPlacement="bottom-end"
dropdownOffset={{ x: 0, y: 8 }}
clickableComponent={
<Button
Icon={IconFilter}
size="medium"
variant="secondary"
accent="default"
ariaLabel={t`Filter`}
<StyledSearchContainer>
<SearchInput
placeholder={t`Search a field...`}
value={searchTerm}
onChange={setSearchTerm}
filterDropdown={(filterButton) => (
<Dropdown
dropdownId="settings-fields-filter-dropdown"
dropdownPlacement="bottom-end"
dropdownOffset={{ x: 0, y: 8 }}
clickableComponent={filterButton}
dropdownComponents={
<DropdownContent>
<DropdownMenuItemsContainer>
<MenuItemToggle
LeftIcon={IconArchive}
onToggleChange={() => setShowInactive(!showInactive)}
toggled={showInactive}
text={t`Inactive`}
toggleSize="small"
/>
{isAdvancedModeEnabled && (
<MenuItemToggle
LeftIcon={IconSettings}
onToggleChange={() =>
setShowSystemFields(!showSystemFields)
}
toggled={showSystemFields}
text={t`System fields`}
toggleSize="small"
/>
)}
</DropdownMenuItemsContainer>
</DropdownContent>
}
/>
}
dropdownComponents={
<DropdownContent>
<DropdownMenuItemsContainer>
<MenuItemToggle
LeftIcon={IconArchive}
onToggleChange={() => setShowInactive(!showInactive)}
toggled={showInactive}
text={t`Inactive`}
toggleSize="small"
/>
{isAdvancedModeEnabled && (
<MenuItemToggle
LeftIcon={IconSettings}
onToggleChange={() =>
setShowSystemFields(!showSystemFields)
}
toggled={showSystemFields}
text={t`System fields`}
toggleSize="small"
/>
)}
</DropdownMenuItemsContainer>
</DropdownContent>
}
)}
/>
</StyledSearchAndFilterContainer>
</StyledSearchContainer>
<Table>
<TableRow
gridTemplateColumns={OBJECT_FIELD_TABLE_ROW_GRID_TEMPLATE_COLUMNS}
@@ -1,6 +1,5 @@
import { type EnrichedObjectMetadataItem } from '@/object-metadata/types/EnrichedObjectMetadataItem';
import { settingsObjectIndexesFamilyState } from '@/settings/data-model/object-details/states/settingsObjectIndexesFamilyState';
import { SettingsTextInput } from '@/ui/input/components/SettingsTextInput';
import { SortableTableHeader } from '@/ui/layout/table/components/SortableTableHeader';
import { Table } from '@/ui/layout/table/components/Table';
import { TableCell } from '@/ui/layout/table/components/TableCell';
@@ -15,7 +14,8 @@ import { isNonEmptyArray } from '@sniptt/guards';
import { useAtomFamilyStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomFamilyStateValue';
import { useSetAtomFamilyState } from '@/ui/utilities/state/jotai/hooks/useSetAtomFamilyState';
import { useEffect, useMemo, useState } from 'react';
import { IconSearch, IconSquareKey } from 'twenty-ui/display';
import { IconSquareKey } from 'twenty-ui/display';
import { SearchInput } from 'twenty-ui/input';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import { type SettingsObjectIndexesTableItem } from '~/pages/settings/data-model/types/SettingsObjectIndexesTableItem';
import { normalizeSearchText } from '~/utils/normalizeSearchText';
@@ -119,9 +119,7 @@ export const SettingsObjectIndexTable = ({
return (
<>
<StyledSearchInputContainer>
<SettingsTextInput
instanceId="object-index-table-search"
LeftIcon={IconSearch}
<SearchInput
placeholder={t`Search an index...`}
value={searchTerm}
onChange={setSearchTerm}
@@ -12,7 +12,6 @@ import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
import { SettingsTextInput } from '@/ui/input/components/SettingsTextInput';
import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer';
import { Table } from '@/ui/layout/table/components/Table';
import { TableHeader } from '@/ui/layout/table/components/TableHeader';
@@ -33,12 +32,11 @@ import {
IconChevronRight,
IconMail,
IconReload,
IconSearch,
IconTrash,
Status,
TooltipDelay,
} from 'twenty-ui/display';
import { IconButton } from 'twenty-ui/input';
import { IconButton, SearchInput } from 'twenty-ui/input';
import { Section } from 'twenty-ui/layout';
import { useQuery } from '@apollo/client/react';
import { GetWorkspaceInvitationsDocument } from '~/generated-metadata/graphql';
@@ -92,13 +90,6 @@ const StyledSearchContainer = styled.div`
padding-bottom: ${themeCssVariables.spacing[2]};
`;
const StyledSearchInputContainer = styled.div`
input {
background: ${themeCssVariables.background.transparent.lighter};
border: 1px solid ${themeCssVariables.border.color.medium};
}
`;
const StyledTableRows = styled.div`
padding-bottom: ${themeCssVariables.spacing[2]};
padding-top: ${themeCssVariables.spacing[2]};
@@ -385,17 +376,11 @@ export const SettingsWorkspaceMembers = () => {
description={t`Manage the members of your workspace here`}
/>
<StyledSearchContainer>
<StyledSearchInputContainer>
<SettingsTextInput
instanceId="workspace-members-search"
value={searchFilter}
onChange={handleSearchChange}
placeholder={t`Search a team member...`}
fullWidth
LeftIcon={IconSearch}
sizeVariant="lg"
/>
</StyledSearchInputContainer>
<SearchInput
value={searchFilter}
onChange={handleSearchChange}
placeholder={t`Search a team member...`}
/>
</StyledSearchContainer>
<StyledTableContainer hasMoreRows={hasNextPage}>
<Table>