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 { useCommandMenu } from '../hooks/useCommandMenu';
import { commandMenuCommand } from '../states/commandMenuCommandsState';
import { isCommandMenuOpenedState } from '../states/isCommandMenuOpenedState';
import { CommandType } from '../types/Command';
import { CommandMenuItem } from './CommandMenuItem';
import {
@ -31,6 +33,8 @@ export function CommandMenu() {
const openActivityRightDrawer = useOpenActivityRightDrawer();
const isCommandMenuOpened = useRecoilValue(isCommandMenuOpenedState);
const [search, setSearch] = useState('');
const commands = useRecoilValue(commandMenuCommand);
useScopedHotkeys(
'ctrl+k,meta+k',
@ -77,36 +81,16 @@ export function CommandMenu() {
});
const activities = activityData?.searchResults ?? [];
const commands = [
{
to: '/people',
label: 'Go to People',
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 matchingNavigateCommand = commands.find(
(cmd) =>
cmd.shortcuts?.join('') === search?.toUpperCase() &&
cmd.type === CommandType.Navigate,
);
const matchingCommand = commands.find(
(cmd) => cmd.shortcuts.join('') === search?.toUpperCase(),
const matchingCreateCommand = commands.find(
(cmd) =>
cmd.shortcuts?.join('') === search?.toUpperCase() &&
cmd.type === CommandType.Create,
);
/*
@ -164,12 +148,40 @@ export function CommandMenu() {
/>
<StyledList>
<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">
<CommandMenuItem
to={matchingCommand.to}
label={matchingCommand.label}
shortcuts={matchingCommand.shortcuts}
to={matchingNavigateCommand.to}
label={matchingNavigateCommand.label}
shortcuts={matchingNavigateCommand.shortcuts}
/>
</StyledGroup>
)}
@ -223,17 +235,18 @@ export function CommandMenu() {
))}
</StyledGroup>
)}
{!matchingCommand && (
{!matchingNavigateCommand && (
<StyledGroup heading="Navigate">
{commands
.filter(
(cmd) =>
cmd.shortcuts?.join('').includes(search?.toUpperCase()) ||
cmd.label?.toUpperCase().includes(search?.toUpperCase()),
(cmd.shortcuts?.join('').includes(search?.toUpperCase()) ||
cmd.label?.toUpperCase().includes(search?.toUpperCase())) &&
cmd.type === CommandType.Navigate,
)
.map((cmd) => (
<CommandMenuItem
key={cmd.shortcuts.join('')}
key={cmd.shortcuts?.join('')}
to={cmd.to}
label={cmd.label}
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 { AppHotkeyScope } from '@/ui/utilities/hotkey/types/AppHotkeyScope';
import { commandMenuCommands } from '../constants/commandMenuCommands';
import { commandMenuCommand } from '../states/commandMenuCommandsState';
import { isCommandMenuOpenedState } from '../states/isCommandMenuOpenedState';
import { Command } from '../types/Command';
export function useCommandMenu() {
const [, setIsCommandMenuOpenedState] = useRecoilState(
isCommandMenuOpenedState,
);
const setCommands = useSetRecoilState(commandMenuCommand);
const {
setHotkeyScopeAndMemorizePreviousScope,
goBackToPreviousHotkeyScope,
@ -24,8 +28,18 @@ export function useCommandMenu() {
goBackToPreviousHotkeyScope();
}
function addToCommandMenu(addCommand: Command[]) {
setCommands((prev) => [...prev, ...addCommand]);
}
function setToIntitialCommandMenu() {
setCommands(commandMenuCommands);
}
return {
openCommandMenu,
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';
export function AppInternalHooks() {
return (
<>
<GotoHotkeysHooks />
<CommandMenuHook />
</>
);
}

View File

@ -1,15 +1,20 @@
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 { useOnboardingStatus } from '@/auth/hooks/useOnboardingStatus';
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 { AppPath } from '@/types/AppPath';
import { PageHotkeyScope } from '@/types/PageHotkeyScope';
import { SettingsPath } from '@/types/SettingsPath';
import { TableHotkeyScope } from '@/ui/table/types/TableHotkeyScope';
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
import { ActivityType, CommentableType } from '~/generated/graphql';
import { useIsMatchingLocation } from '../hooks/useIsMatchingLocation';
@ -27,6 +32,10 @@ export function AuthAutoRouter() {
const eventTracker = useEventTracker();
const { addToCommandMenu, setToIntitialCommandMenu } = useCommandMenu();
const openCreateActivity = useOpenCreateActivityDrawer();
useEffect(() => {
if (!previousLocation || previousLocation !== 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(() => {
eventTracker('pageview', {
location: {
@ -144,6 +222,9 @@ export function AuthAutoRouter() {
location,
previousLocation,
eventTracker,
addToCommandMenu,
openCreateActivity,
setToIntitialCommandMenu,
]);
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 <></>;
}