Add task and note create option in comand menu (#1115)

* add task and note create option in comand menu

* Re-run CIs

---------

Co-authored-by: Weiko <corentin@twenty.com>
Co-authored-by: Charles Bochet <charlesBochet@users.noreply.github.com>
This commit is contained in:
Sunil Kumar Behera 2023-08-10 02:39:32 +05:30 committed by GitHub
parent 22b4bffcde
commit 4a388b8ed5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 223 additions and 39 deletions

View File

@ -15,7 +15,9 @@ import {
import { getLogoUrlFromDomainName } from '~/utils'; import { getLogoUrlFromDomainName } from '~/utils';
import { useCommandMenu } from '../hooks/useCommandMenu'; import { useCommandMenu } from '../hooks/useCommandMenu';
import { commandMenuCommand } from '../states/commandMenuCommandsState';
import { isCommandMenuOpenedState } from '../states/isCommandMenuOpenedState'; import { isCommandMenuOpenedState } from '../states/isCommandMenuOpenedState';
import { CommandType } from '../types/Command';
import { CommandMenuItem } from './CommandMenuItem'; import { CommandMenuItem } from './CommandMenuItem';
import { import {
@ -31,6 +33,8 @@ export function CommandMenu() {
const openActivityRightDrawer = useOpenActivityRightDrawer(); const openActivityRightDrawer = useOpenActivityRightDrawer();
const isCommandMenuOpened = useRecoilValue(isCommandMenuOpenedState); const isCommandMenuOpened = useRecoilValue(isCommandMenuOpenedState);
const [search, setSearch] = useState(''); const [search, setSearch] = useState('');
const commands = useRecoilValue(commandMenuCommand);
useScopedHotkeys( useScopedHotkeys(
'ctrl+k,meta+k', 'ctrl+k,meta+k',
@ -77,36 +81,16 @@ export function CommandMenu() {
}); });
const activities = activityData?.searchResults ?? []; const activities = activityData?.searchResults ?? [];
const commands = [ const matchingNavigateCommand = commands.find(
{ (cmd) =>
to: '/people', cmd.shortcuts?.join('') === search?.toUpperCase() &&
label: 'Go to People', cmd.type === CommandType.Navigate,
shortcuts: ['G', 'P'], );
},
{
to: '/companies',
label: 'Go to Companies',
shortcuts: ['G', 'C'],
},
{
to: '/opportunities',
label: 'Go to Opportunities',
shortcuts: ['G', 'O'],
},
{
to: '/settings/profile',
label: 'Go to Settings',
shortcuts: ['G', 'S'],
},
{
to: '/tasks',
label: 'Go to Tasks',
shortcuts: ['G', 'T'],
},
];
const matchingCommand = commands.find( const matchingCreateCommand = commands.find(
(cmd) => cmd.shortcuts.join('') === search?.toUpperCase(), (cmd) =>
cmd.shortcuts?.join('') === search?.toUpperCase() &&
cmd.type === CommandType.Create,
); );
/* /*
@ -164,12 +148,40 @@ export function CommandMenu() {
/> />
<StyledList> <StyledList>
<StyledEmpty>No results found.</StyledEmpty> <StyledEmpty>No results found.</StyledEmpty>
{matchingCommand && ( {!matchingCreateCommand && (
<StyledGroup heading="Create">
{commands
.filter((cmd) => cmd.type === CommandType.Create)
.map((cmd) => (
<CommandMenuItem
key={cmd.label}
to={cmd.to}
label={cmd.label}
icon={cmd.icon}
shortcuts={cmd.shortcuts || []}
onClick={cmd.onCommandClick}
/>
))}
</StyledGroup>
)}
{matchingCreateCommand && (
<StyledGroup heading="Create">
<CommandMenuItem
key={matchingCreateCommand.label}
to={matchingCreateCommand.to}
label={matchingCreateCommand.label}
icon={matchingCreateCommand.icon}
shortcuts={matchingCreateCommand.shortcuts || []}
onClick={matchingCreateCommand.onCommandClick}
/>
</StyledGroup>
)}
{matchingNavigateCommand && (
<StyledGroup heading="Navigate"> <StyledGroup heading="Navigate">
<CommandMenuItem <CommandMenuItem
to={matchingCommand.to} to={matchingNavigateCommand.to}
label={matchingCommand.label} label={matchingNavigateCommand.label}
shortcuts={matchingCommand.shortcuts} shortcuts={matchingNavigateCommand.shortcuts}
/> />
</StyledGroup> </StyledGroup>
)} )}
@ -223,17 +235,18 @@ export function CommandMenu() {
))} ))}
</StyledGroup> </StyledGroup>
)} )}
{!matchingCommand && ( {!matchingNavigateCommand && (
<StyledGroup heading="Navigate"> <StyledGroup heading="Navigate">
{commands {commands
.filter( .filter(
(cmd) => (cmd) =>
cmd.shortcuts?.join('').includes(search?.toUpperCase()) || (cmd.shortcuts?.join('').includes(search?.toUpperCase()) ||
cmd.label?.toUpperCase().includes(search?.toUpperCase()), cmd.label?.toUpperCase().includes(search?.toUpperCase())) &&
cmd.type === CommandType.Navigate,
) )
.map((cmd) => ( .map((cmd) => (
<CommandMenuItem <CommandMenuItem
key={cmd.shortcuts.join('')} key={cmd.shortcuts?.join('')}
to={cmd.to} to={cmd.to}
label={cmd.label} label={cmd.label}
shortcuts={cmd.shortcuts} shortcuts={cmd.shortcuts}

View File

@ -0,0 +1,34 @@
import { Command, CommandType } from '../types/Command';
export const commandMenuCommands: Command[] = [
{
to: '/people',
label: 'Go to People',
type: CommandType.Navigate,
shortcuts: ['G', 'P'],
},
{
to: '/companies',
label: 'Go to Companies',
type: CommandType.Navigate,
shortcuts: ['G', 'C'],
},
{
to: '/opportunities',
label: 'Go to Opportunities',
type: CommandType.Navigate,
shortcuts: ['G', 'O'],
},
{
to: '/settings/profile',
label: 'Go to Settings',
type: CommandType.Navigate,
shortcuts: ['G', 'S'],
},
{
to: '/tasks',
label: 'Go to Tasks',
type: CommandType.Navigate,
shortcuts: ['G', 'T'],
},
];

View File

@ -1,14 +1,18 @@
import { useRecoilState } from 'recoil'; import { useRecoilState, useSetRecoilState } from 'recoil';
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope'; import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
import { AppHotkeyScope } from '@/ui/utilities/hotkey/types/AppHotkeyScope'; import { AppHotkeyScope } from '@/ui/utilities/hotkey/types/AppHotkeyScope';
import { commandMenuCommands } from '../constants/commandMenuCommands';
import { commandMenuCommand } from '../states/commandMenuCommandsState';
import { isCommandMenuOpenedState } from '../states/isCommandMenuOpenedState'; import { isCommandMenuOpenedState } from '../states/isCommandMenuOpenedState';
import { Command } from '../types/Command';
export function useCommandMenu() { export function useCommandMenu() {
const [, setIsCommandMenuOpenedState] = useRecoilState( const [, setIsCommandMenuOpenedState] = useRecoilState(
isCommandMenuOpenedState, isCommandMenuOpenedState,
); );
const setCommands = useSetRecoilState(commandMenuCommand);
const { const {
setHotkeyScopeAndMemorizePreviousScope, setHotkeyScopeAndMemorizePreviousScope,
goBackToPreviousHotkeyScope, goBackToPreviousHotkeyScope,
@ -24,8 +28,18 @@ export function useCommandMenu() {
goBackToPreviousHotkeyScope(); goBackToPreviousHotkeyScope();
} }
function addToCommandMenu(addCommand: Command[]) {
setCommands((prev) => [...prev, ...addCommand]);
}
function setToIntitialCommandMenu() {
setCommands(commandMenuCommands);
}
return { return {
openCommandMenu, openCommandMenu,
closeCommandMenu, closeCommandMenu,
addToCommandMenu,
setToIntitialCommandMenu,
}; };
} }

View File

@ -0,0 +1,14 @@
import { atom } from 'recoil';
import { Command, CommandType } from '../types/Command';
export const commandMenuCommand = atom<Command[]>({
key: 'command-menu/commandMenuCommand',
default: [
{
to: '',
label: '',
type: CommandType.Navigate,
},
],
});

View File

@ -0,0 +1,13 @@
export enum CommandType {
Navigate = 'Navigate',
Create = 'Create',
}
export type Command = {
to: string;
label: string;
type: CommandType.Navigate | CommandType.Create;
icon?: JSX.Element;
shortcuts?: string[];
onCommandClick?: () => void;
};

View File

@ -1,9 +1,11 @@
import { CommandMenuHook } from './CommandMenuHook';
import { GotoHotkeysHooks } from './GotoHotkeysHooks'; import { GotoHotkeysHooks } from './GotoHotkeysHooks';
export function AppInternalHooks() { export function AppInternalHooks() {
return ( return (
<> <>
<GotoHotkeysHooks /> <GotoHotkeysHooks />
<CommandMenuHook />
</> </>
); );
} }

View File

@ -1,15 +1,20 @@
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { useLocation, useNavigate } from 'react-router-dom'; import { matchPath, useLocation, useNavigate } from 'react-router-dom';
import { IconCheckbox, IconNotes } from '@tabler/icons-react';
import { useOpenCreateActivityDrawer } from '@/activities/hooks/useOpenCreateActivityDrawer';
import { useEventTracker } from '@/analytics/hooks/useEventTracker'; import { useEventTracker } from '@/analytics/hooks/useEventTracker';
import { useOnboardingStatus } from '@/auth/hooks/useOnboardingStatus'; import { useOnboardingStatus } from '@/auth/hooks/useOnboardingStatus';
import { OnboardingStatus } from '@/auth/utils/getOnboardingStatus'; import { OnboardingStatus } from '@/auth/utils/getOnboardingStatus';
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
import { CommandType } from '@/command-menu/types/Command';
import { AppBasePath } from '@/types/AppBasePath'; import { AppBasePath } from '@/types/AppBasePath';
import { AppPath } from '@/types/AppPath'; import { AppPath } from '@/types/AppPath';
import { PageHotkeyScope } from '@/types/PageHotkeyScope'; import { PageHotkeyScope } from '@/types/PageHotkeyScope';
import { SettingsPath } from '@/types/SettingsPath'; import { SettingsPath } from '@/types/SettingsPath';
import { TableHotkeyScope } from '@/ui/table/types/TableHotkeyScope'; import { TableHotkeyScope } from '@/ui/table/types/TableHotkeyScope';
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope'; import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
import { ActivityType, CommentableType } from '~/generated/graphql';
import { useIsMatchingLocation } from '../hooks/useIsMatchingLocation'; import { useIsMatchingLocation } from '../hooks/useIsMatchingLocation';
@ -27,6 +32,10 @@ export function AuthAutoRouter() {
const eventTracker = useEventTracker(); const eventTracker = useEventTracker();
const { addToCommandMenu, setToIntitialCommandMenu } = useCommandMenu();
const openCreateActivity = useOpenCreateActivityDrawer();
useEffect(() => { useEffect(() => {
if (!previousLocation || previousLocation !== location.pathname) { if (!previousLocation || previousLocation !== location.pathname) {
setPreviousLocation(location.pathname); setPreviousLocation(location.pathname);
@ -129,6 +138,75 @@ export function AuthAutoRouter() {
} }
} }
setToIntitialCommandMenu();
switch (true) {
case isMatchingLocation(AppPath.CompanyShowPage): {
const companyId = matchPath(
{ path: '/companies/:id' },
location.pathname,
)?.params.id;
const entity = !!companyId
? { id: companyId, type: CommentableType.Company }
: undefined;
addToCommandMenu([
{
to: '',
label: 'Create Task',
type: CommandType.Create,
icon: <IconCheckbox />,
onCommandClick: () => openCreateActivity(ActivityType.Task, entity),
},
{
to: '',
label: 'Create Note',
type: CommandType.Create,
icon: <IconNotes />,
onCommandClick: () => openCreateActivity(ActivityType.Note, entity),
},
]);
break;
}
case isMatchingLocation(AppPath.PersonShowPage): {
const personId = matchPath({ path: '/person/:id' }, location.pathname)
?.params.id;
const entity = !!personId
? { id: personId, type: CommentableType.Person }
: undefined;
addToCommandMenu([
{
to: '',
label: 'Create Task',
type: CommandType.Create,
icon: <IconCheckbox />,
onCommandClick: () => openCreateActivity(ActivityType.Task, entity),
},
{
to: '',
label: 'Create Note',
type: CommandType.Create,
icon: <IconNotes />,
onCommandClick: () => openCreateActivity(ActivityType.Note, entity),
},
]);
break;
}
default:
addToCommandMenu([
{
to: '',
label: 'Create Task',
type: CommandType.Create,
icon: <IconCheckbox />,
onCommandClick: () => openCreateActivity(ActivityType.Task),
},
]);
break;
}
setTimeout(() => { setTimeout(() => {
eventTracker('pageview', { eventTracker('pageview', {
location: { location: {
@ -144,6 +222,9 @@ export function AuthAutoRouter() {
location, location,
previousLocation, previousLocation,
eventTracker, eventTracker,
addToCommandMenu,
openCreateActivity,
setToIntitialCommandMenu,
]); ]);
return <></>; return <></>;

View File

@ -0,0 +1,13 @@
import { useSetRecoilState } from 'recoil';
import { commandMenuCommands } from '@/command-menu/constants/commandMenuCommands';
import { commandMenuCommand } from '@/command-menu/states/commandMenuCommandsState';
export function CommandMenuHook() {
const setCommands = useSetRecoilState(commandMenuCommand);
const commands = commandMenuCommands;
setCommands(commands);
return <></>;
}