mirror of
https://github.com/twentyhq/twenty.git
synced 2024-11-24 06:48:42 +03:00
Command menu search bar (#4337)
* Improve performance on findMany queries * Fix * Fix command menu not emptying the search on toggle * Fix tests
This commit is contained in:
parent
e7857d7fa3
commit
b08b361dc0
@ -1,10 +1,11 @@
|
||||
import { useMemo, useRef, useState } from 'react';
|
||||
import { useMemo, useRef } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
import { Key } from 'ts-key-enum';
|
||||
|
||||
import { useOpenActivityRightDrawer } from '@/activities/hooks/useOpenActivityRightDrawer';
|
||||
import { Activity } from '@/activities/types/Activity';
|
||||
import { commandMenuSearchState } from '@/command-menu/states/commandMenuSearchState';
|
||||
import { Company } from '@/companies/types/Company';
|
||||
import { useKeyboardShortcutMenu } from '@/keyboard-shortcut-menu/hooks/useKeyboardShortcutMenu';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
@ -105,23 +106,24 @@ export const CommandMenu = () => {
|
||||
|
||||
const openActivityRightDrawer = useOpenActivityRightDrawer();
|
||||
const isCommandMenuOpened = useRecoilValue(isCommandMenuOpenedState);
|
||||
const [search, setSearch] = useState('');
|
||||
const [commandMenuSearch, setCommandMenuSearch] = useRecoilState(
|
||||
commandMenuSearchState,
|
||||
);
|
||||
const commandMenuCommands = useRecoilValue(commandMenuCommandsState);
|
||||
const { closeKeyboardShortcutMenu } = useKeyboardShortcutMenu();
|
||||
|
||||
const handleSearchChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setSearch(event.target.value);
|
||||
setCommandMenuSearch(event.target.value);
|
||||
};
|
||||
|
||||
useScopedHotkeys(
|
||||
'ctrl+k,meta+k',
|
||||
() => {
|
||||
closeKeyboardShortcutMenu();
|
||||
setSearch('');
|
||||
toggleCommandMenu();
|
||||
},
|
||||
AppHotkeyScope.CommandMenu,
|
||||
[toggleCommandMenu, setSearch],
|
||||
[toggleCommandMenu],
|
||||
);
|
||||
|
||||
useScopedHotkeys(
|
||||
@ -136,12 +138,12 @@ export const CommandMenu = () => {
|
||||
const { records: people } = useFindManyRecords<Person>({
|
||||
skip: !isCommandMenuOpened,
|
||||
objectNameSingular: CoreObjectNameSingular.Person,
|
||||
filter: search
|
||||
filter: commandMenuSearch
|
||||
? makeOrFilterVariables([
|
||||
{ name: { firstName: { ilike: `%${search}%` } } },
|
||||
{ name: { lastName: { ilike: `%${search}%` } } },
|
||||
{ email: { ilike: `%${search}%` } },
|
||||
{ phone: { ilike: `%${search}%` } },
|
||||
{ name: { firstName: { ilike: `%${commandMenuSearch}%` } } },
|
||||
{ name: { lastName: { ilike: `%${commandMenuSearch}%` } } },
|
||||
{ email: { ilike: `%${commandMenuSearch}%` } },
|
||||
{ phone: { ilike: `%${commandMenuSearch}%` } },
|
||||
])
|
||||
: undefined,
|
||||
limit: 3,
|
||||
@ -150,9 +152,9 @@ export const CommandMenu = () => {
|
||||
const { records: companies } = useFindManyRecords<Company>({
|
||||
skip: !isCommandMenuOpened,
|
||||
objectNameSingular: CoreObjectNameSingular.Company,
|
||||
filter: search
|
||||
filter: commandMenuSearch
|
||||
? {
|
||||
name: { ilike: `%${search}%` },
|
||||
name: { ilike: `%${commandMenuSearch}%` },
|
||||
}
|
||||
: undefined,
|
||||
limit: 3,
|
||||
@ -161,10 +163,10 @@ export const CommandMenu = () => {
|
||||
const { records: activities } = useFindManyRecords<Activity>({
|
||||
skip: !isCommandMenuOpened,
|
||||
objectNameSingular: CoreObjectNameSingular.Activity,
|
||||
filter: search
|
||||
filter: commandMenuSearch
|
||||
? makeOrFilterVariables([
|
||||
{ title: { ilike: `%${search}%` } },
|
||||
{ body: { ilike: `%${search}%` } },
|
||||
{ title: { ilike: `%${commandMenuSearch}%` } },
|
||||
{ body: { ilike: `%${commandMenuSearch}%` } },
|
||||
])
|
||||
: undefined,
|
||||
limit: 3,
|
||||
@ -224,15 +226,17 @@ export const CommandMenu = () => {
|
||||
|
||||
const matchingNavigateCommand = commandMenuCommands.filter(
|
||||
(cmd) =>
|
||||
(search.length > 0
|
||||
? checkInShortcuts(cmd, search) || checkInLabels(cmd, search)
|
||||
(commandMenuSearch.length > 0
|
||||
? checkInShortcuts(cmd, commandMenuSearch) ||
|
||||
checkInLabels(cmd, commandMenuSearch)
|
||||
: true) && cmd.type === CommandType.Navigate,
|
||||
);
|
||||
|
||||
const matchingCreateCommand = commandMenuCommands.filter(
|
||||
(cmd) =>
|
||||
(search.length > 0
|
||||
? checkInShortcuts(cmd, search) || checkInLabels(cmd, search)
|
||||
(commandMenuSearch.length > 0
|
||||
? checkInShortcuts(cmd, commandMenuSearch) ||
|
||||
checkInLabels(cmd, commandMenuSearch)
|
||||
: true) && cmd.type === CommandType.Create,
|
||||
);
|
||||
|
||||
@ -254,7 +258,7 @@ export const CommandMenu = () => {
|
||||
<StyledDialog ref={commandMenuRef}>
|
||||
<StyledInput
|
||||
autoFocus
|
||||
value={search}
|
||||
value={commandMenuSearch}
|
||||
placeholder="Search"
|
||||
onChange={handleSearchChange}
|
||||
/>
|
||||
|
@ -2,6 +2,7 @@ import { useCallback } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useRecoilCallback, useSetRecoilState } from 'recoil';
|
||||
|
||||
import { commandMenuSearchState } from '@/command-menu/states/commandMenuSearchState';
|
||||
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
|
||||
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
|
||||
import { AppHotkeyScope } from '@/ui/utilities/hotkey/types/AppHotkeyScope';
|
||||
@ -42,17 +43,22 @@ export const useCommandMenu = () => {
|
||||
[goBackToPreviousHotkeyScope, resetSelectedItem, setIsCommandMenuOpened],
|
||||
);
|
||||
|
||||
const toggleCommandMenu = useRecoilCallback(({ snapshot }) => async () => {
|
||||
const isCommandMenuOpened = snapshot
|
||||
.getLoadable(isCommandMenuOpenedState)
|
||||
.getValue();
|
||||
const toggleCommandMenu = useRecoilCallback(
|
||||
({ snapshot, set }) =>
|
||||
async () => {
|
||||
const isCommandMenuOpened = snapshot
|
||||
.getLoadable(isCommandMenuOpenedState)
|
||||
.getValue();
|
||||
|
||||
if (isCommandMenuOpened) {
|
||||
closeCommandMenu();
|
||||
} else {
|
||||
openCommandMenu();
|
||||
}
|
||||
});
|
||||
set(commandMenuSearchState, '');
|
||||
|
||||
if (isCommandMenuOpened) {
|
||||
closeCommandMenu();
|
||||
} else {
|
||||
openCommandMenu();
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
const addToCommandMenu = useCallback(
|
||||
(addCommand: Command[]) => {
|
||||
|
@ -0,0 +1,6 @@
|
||||
import { atom } from 'recoil';
|
||||
|
||||
export const commandMenuSearchState = atom<string>({
|
||||
key: 'command-menu/commandMenuSearchState',
|
||||
default: '',
|
||||
});
|
@ -20,43 +20,20 @@ const getOneToManyRelation = () => {
|
||||
return {
|
||||
field: objectMetadataItem.fields.find((field) => field.name === 'company')!,
|
||||
res: `company
|
||||
{
|
||||
__typename
|
||||
id
|
||||
{
|
||||
__typename
|
||||
id
|
||||
xLink
|
||||
{
|
||||
label
|
||||
url
|
||||
}
|
||||
accountOwner
|
||||
{
|
||||
__typename
|
||||
id
|
||||
}
|
||||
linkedinLink
|
||||
{
|
||||
label
|
||||
url
|
||||
}
|
||||
attachments
|
||||
{
|
||||
edges {
|
||||
node {
|
||||
__typename
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
domainName
|
||||
opportunities
|
||||
{
|
||||
edges {
|
||||
node {
|
||||
__typename
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
annualRecurringRevenue
|
||||
{
|
||||
amountMicros
|
||||
@ -65,39 +42,12 @@ opportunities
|
||||
createdAt
|
||||
address
|
||||
updatedAt
|
||||
activityTargets
|
||||
{
|
||||
edges {
|
||||
node {
|
||||
__typename
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
favorites
|
||||
{
|
||||
edges {
|
||||
node {
|
||||
__typename
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
people
|
||||
{
|
||||
edges {
|
||||
node {
|
||||
__typename
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
name
|
||||
accountOwnerId
|
||||
employees
|
||||
id
|
||||
idealCustomerProfile
|
||||
}`,
|
||||
}`,
|
||||
};
|
||||
};
|
||||
|
||||
@ -133,27 +83,17 @@ const getOneToManyFromRelationField = () => {
|
||||
return {
|
||||
field,
|
||||
res: `opportunities
|
||||
{
|
||||
edges {
|
||||
node {
|
||||
__typename
|
||||
id
|
||||
personId
|
||||
{
|
||||
edges {
|
||||
node {
|
||||
__typename
|
||||
id
|
||||
personId
|
||||
pointOfContactId
|
||||
updatedAt
|
||||
company
|
||||
{
|
||||
__typename
|
||||
id
|
||||
}
|
||||
companyId
|
||||
pipelineStepId
|
||||
probability
|
||||
pipelineStep
|
||||
{
|
||||
__typename
|
||||
id
|
||||
}
|
||||
closeDate
|
||||
amount
|
||||
{
|
||||
@ -162,19 +102,9 @@ closeDate
|
||||
}
|
||||
id
|
||||
createdAt
|
||||
pointOfContact
|
||||
{
|
||||
__typename
|
||||
id
|
||||
}
|
||||
person
|
||||
{
|
||||
__typename
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}`,
|
||||
}`,
|
||||
};
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user