8191 command k workflow trigger for selected record (#8315)

Closes #8191 


https://github.com/user-attachments/assets/694da229-cc91-4df2-97a0-49cd5dabcf12
This commit is contained in:
Raphaël Bosi
2024-11-05 13:37:29 +01:00
committed by GitHub
parent 0893774cc1
commit d1531aa1b6
44 changed files with 543 additions and 209 deletions

View File

@ -97,6 +97,7 @@ export const DeleteRecordsActionEffect = ({
useEffect(() => {
if (canDelete) {
addActionMenuEntry({
type: 'standard',
key: 'delete',
label: 'Delete',
position,

View File

@ -3,10 +3,12 @@ import {
displayedExportProgress,
useExportRecordData,
} from '@/action-menu/hooks/useExportRecordData';
import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { IconDatabaseExport } from 'twenty-ui';
import { useEffect } from 'react';
import { IconFileExport } from 'twenty-ui';
export const ExportRecordsActionEffect = ({
position,
@ -16,6 +18,9 @@ export const ExportRecordsActionEffect = ({
objectMetadataItem: ObjectMetadataItem;
}) => {
const { addActionMenuEntry, removeActionMenuEntry } = useActionMenuEntries();
const contextStoreNumberOfSelectedRecords = useRecoilComponentValueV2(
contextStoreNumberOfSelectedRecordsComponentState,
);
const { progress, download } = useExportRecordData({
delayMs: 100,
@ -26,10 +31,14 @@ export const ExportRecordsActionEffect = ({
useEffect(() => {
addActionMenuEntry({
type: 'standard',
key: 'export',
position,
label: displayedExportProgress(progress),
Icon: IconFileExport,
label: displayedExportProgress(
contextStoreNumberOfSelectedRecords > 0 ? 'selection' : 'all',
progress,
),
Icon: IconDatabaseExport,
accent: 'default',
onClick: () => download(),
});
@ -37,6 +46,14 @@ export const ExportRecordsActionEffect = ({
return () => {
removeActionMenuEntry('export');
};
}, [download, progress, addActionMenuEntry, removeActionMenuEntry, position]);
}, [
contextStoreNumberOfSelectedRecords,
download,
progress,
addActionMenuEntry,
removeActionMenuEntry,
position,
]);
return null;
};

View File

@ -44,6 +44,7 @@ export const ManageFavoritesActionEffect = ({
}
addActionMenuEntry({
type: 'standard',
key: 'manage-favorites',
label: isFavorite ? 'Remove from favorites' : 'Add to favorites',
position,

View File

@ -1,21 +1,20 @@
import { DeleteRecordsActionEffect } from '@/action-menu/actions/record-actions/components/DeleteRecordsActionEffect';
import { ExportRecordsActionEffect } from '@/action-menu/actions/record-actions/components/ExportRecordsActionEffect';
import { ManageFavoritesActionEffect } from '@/action-menu/actions/record-actions/components/ManageFavoritesActionEffect';
import { WorkflowRunRecordActionEffect } from '@/action-menu/actions/record-actions/workflow-run-record-actions/components/WorkflowRunRecordActionEffect';
import { contextStoreCurrentObjectMetadataIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataIdComponentState';
import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
import { useObjectMetadataItemById } from '@/object-metadata/hooks/useObjectMetadataItemById';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
const globalRecordActionEffects = [ExportRecordsActionEffect];
const singleRecordActionEffects = [
ManageFavoritesActionEffect,
ExportRecordsActionEffect,
DeleteRecordsActionEffect,
];
const multipleRecordActionEffects = [
ExportRecordsActionEffect,
DeleteRecordsActionEffect,
];
const multipleRecordActionEffects = [DeleteRecordsActionEffect];
export const RecordActionMenuEntriesSetter = () => {
const contextStoreNumberOfSelectedRecords = useRecoilComponentValueV2(
@ -36,10 +35,6 @@ export const RecordActionMenuEntriesSetter = () => {
);
}
if (!contextStoreNumberOfSelectedRecords) {
return null;
}
const actions =
contextStoreNumberOfSelectedRecords === 1
? singleRecordActionEffects
@ -47,13 +42,25 @@ export const RecordActionMenuEntriesSetter = () => {
return (
<>
{actions.map((ActionEffect, index) => (
{globalRecordActionEffects.map((ActionEffect, index) => (
<ActionEffect
key={index}
position={index}
objectMetadataItem={objectMetadataItem}
/>
))}
{actions.map((ActionEffect, index) => (
<ActionEffect
key={index}
position={globalRecordActionEffects.length + index}
objectMetadataItem={objectMetadataItem}
/>
))}
{contextStoreNumberOfSelectedRecords === 1 && (
<WorkflowRunRecordActionEffect
objectMetadataItem={objectMetadataItem}
/>
)}
</>
);
};

View File

@ -0,0 +1,104 @@
import { useActionMenuEntries } from '@/action-menu/hooks/useActionMenuEntries';
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { useAllActiveWorkflowVersionsForObject } from '@/workflow/hooks/useAllActiveWorkflowVersionsForObject';
import { useRunWorkflowVersion } from '@/workflow/hooks/useRunWorkflowVersion';
import { useTheme } from '@emotion/react';
import { useEffect } from 'react';
import { useRecoilValue } from 'recoil';
import { IconSettingsAutomation, isDefined } from 'twenty-ui';
import { capitalize } from '~/utils/string/capitalize';
export const WorkflowRunRecordActionEffect = ({
objectMetadataItem,
}: {
objectMetadataItem: ObjectMetadataItem;
}) => {
const { addActionMenuEntry, removeActionMenuEntry } = useActionMenuEntries();
const contextStoreTargetedRecordsRule = useRecoilComponentValueV2(
contextStoreTargetedRecordsRuleComponentState,
);
const selectedRecordId =
contextStoreTargetedRecordsRule.mode === 'selection'
? contextStoreTargetedRecordsRule.selectedRecordIds[0]
: undefined;
const selectedRecord = useRecoilValue(
recordStoreFamilyState(selectedRecordId ?? ''),
);
const { records: activeWorkflowVersions } =
useAllActiveWorkflowVersionsForObject({
objectNameSingular: objectMetadataItem.nameSingular,
triggerType: 'MANUAL',
});
const { runWorkflowVersion } = useRunWorkflowVersion();
const { enqueueSnackBar } = useSnackBar();
const theme = useTheme();
useEffect(() => {
if (!isDefined(objectMetadataItem) || objectMetadataItem.isRemote) {
return;
}
for (const [
index,
activeWorkflowVersion,
] of activeWorkflowVersions.entries()) {
addActionMenuEntry({
type: 'workflow-run',
key: `workflow-run-${activeWorkflowVersion.workflow.name}`,
label: capitalize(activeWorkflowVersion.workflow.name),
position: index,
Icon: IconSettingsAutomation,
onClick: async () => {
if (!isDefined(selectedRecord)) {
return;
}
await runWorkflowVersion(activeWorkflowVersion.id, selectedRecord);
enqueueSnackBar('', {
variant: SnackBarVariant.Success,
title: `${capitalize(activeWorkflowVersion.workflow.name)} starting...`,
icon: (
<IconSettingsAutomation
size={16}
color={theme.snackBar.success.color}
/>
),
});
},
});
}
return () => {
for (const activeWorkflowVersion of activeWorkflowVersions) {
removeActionMenuEntry(
`workflow-run-${activeWorkflowVersion.workflow.name}`,
);
}
};
}, [
activeWorkflowVersions,
addActionMenuEntry,
enqueueSnackBar,
objectMetadataItem,
removeActionMenuEntry,
runWorkflowVersion,
selectedRecord,
theme.snackBar.success.color,
]);
return null;
};

View File

@ -33,7 +33,7 @@ export const RecordIndexActionMenuBar = () => {
const pinnedEntries = actionMenuEntries.filter((entry) => entry.isPinned);
if (pinnedEntries.length === 0) {
if (contextStoreNumberOfSelectedRecords === 0) {
return null;
}

View File

@ -5,6 +5,7 @@ import { RecoilRoot } from 'recoil';
import { RecordIndexActionMenuBar } from '@/action-menu/components/RecordIndexActionMenuBar';
import { actionMenuEntriesComponentState } from '@/action-menu/states/actionMenuEntriesComponentState';
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
import { ActionMenuEntry } from '@/action-menu/types/ActionMenuEntry';
import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
@ -34,30 +35,33 @@ const meta: Meta<typeof RecordIndexActionMenuBar> = {
selectedRecordIds: ['1', '2', '3'],
},
);
set(
contextStoreNumberOfSelectedRecordsComponentState.atomFamily({
instanceId: 'story-action-menu',
}),
3,
);
const map = new Map<string, ActionMenuEntry>();
map.set('delete', {
isPinned: true,
type: 'standard',
key: 'delete',
label: 'Delete',
position: 0,
Icon: IconTrash,
onClick: deleteMock,
});
set(
actionMenuEntriesComponentState.atomFamily({
instanceId: 'story-action-menu',
}),
new Map([
[
'delete',
{
isPinned: true,
key: 'delete',
label: 'Delete',
position: 0,
Icon: IconTrash,
onClick: deleteMock,
},
],
]),
map,
);
set(
isBottomBarOpenedComponentState.atomFamily({
instanceId: 'action-bar-story-action-menu',

View File

@ -21,6 +21,7 @@ const markAsDoneMock = jest.fn();
export const Default: Story = {
args: {
entry: {
type: 'standard',
key: 'delete',
label: 'Delete',
position: 0,
@ -33,6 +34,7 @@ export const Default: Story = {
export const WithDangerAccent: Story = {
args: {
entry: {
type: 'standard',
key: 'delete',
label: 'Delete',
position: 0,
@ -46,6 +48,7 @@ export const WithDangerAccent: Story = {
export const WithInteraction: Story = {
args: {
entry: {
type: 'standard',
key: 'markAsDone',
label: 'Mark as done',
position: 0,

View File

@ -7,6 +7,7 @@ import { RecordIndexActionMenuDropdown } from '@/action-menu/components/RecordIn
import { actionMenuEntriesComponentState } from '@/action-menu/states/actionMenuEntriesComponentState';
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
import { recordIndexActionMenuDropdownPositionComponentState } from '@/action-menu/states/recordIndexActionMenuDropdownPositionComponentState';
import { ActionMenuEntry } from '@/action-menu/types/ActionMenuEntry';
import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState';
import { extractComponentState } from '@/ui/utilities/state/component-state/utils/extractComponentState';
import { IconCheckbox, IconHeart, IconTrash } from 'twenty-ui';
@ -29,43 +30,43 @@ const meta: Meta<typeof RecordIndexActionMenuDropdown> = {
),
{ x: 10, y: 10 },
);
const map = new Map<string, ActionMenuEntry>();
set(
actionMenuEntriesComponentState.atomFamily({
instanceId: 'story-action-menu',
}),
new Map([
[
'delete',
{
key: 'delete',
label: 'Delete',
position: 0,
Icon: IconTrash,
onClick: deleteMock,
},
],
[
'markAsDone',
{
key: 'markAsDone',
label: 'Mark as done',
position: 1,
Icon: IconCheckbox,
onClick: markAsDoneMock,
},
],
[
'addToFavorites',
{
key: 'addToFavorites',
label: 'Add to favorites',
position: 2,
Icon: IconHeart,
onClick: addToFavoritesMock,
},
],
]),
map,
);
map.set('delete', {
type: 'standard',
key: 'delete',
label: 'Delete',
position: 0,
Icon: IconTrash,
onClick: deleteMock,
});
map.set('markAsDone', {
type: 'standard',
key: 'markAsDone',
label: 'Mark as done',
position: 1,
Icon: IconCheckbox,
onClick: markAsDoneMock,
});
map.set('addToFavorites', {
type: 'standard',
key: 'addToFavorites',
label: 'Add to favorites',
position: 2,
Icon: IconHeart,
onClick: addToFavoritesMock,
});
set(
extractComponentState(
isDropdownOpenComponentState,

View File

@ -5,6 +5,7 @@ import { RecoilRoot } from 'recoil';
import { RecordShowRightDrawerActionMenuBar } from '@/action-menu/components/RecordShowRightDrawerActionMenuBar';
import { actionMenuEntriesComponentState } from '@/action-menu/states/actionMenuEntriesComponentState';
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
import { ActionMenuEntry } from '@/action-menu/types/ActionMenuEntry';
import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { MenuItemAccent } from '@/ui/navigation/menu-item/types/MenuItemAccent';
@ -42,44 +43,43 @@ const meta: Meta<typeof RecordShowRightDrawerActionMenuBar> = {
}),
1,
);
const map = new Map<string, ActionMenuEntry>();
set(
actionMenuEntriesComponentState.atomFamily({
instanceId: 'story-action-menu',
}),
new Map([
[
'addToFavorites',
{
key: 'addToFavorites',
label: 'Add to favorites',
position: 0,
Icon: IconHeart,
onClick: addToFavoritesMock,
},
],
[
'export',
{
key: 'export',
label: 'Export',
position: 1,
Icon: IconFileExport,
onClick: exportMock,
},
],
[
'delete',
{
key: 'delete',
label: 'Delete',
position: 2,
Icon: IconTrash,
onClick: deleteMock,
accent: 'danger' as MenuItemAccent,
},
],
]),
map,
);
map.set('addToFavorites', {
type: 'standard',
key: 'addToFavorites',
label: 'Add to favorites',
position: 0,
Icon: IconHeart,
onClick: addToFavoritesMock,
});
map.set('export', {
type: 'standard',
key: 'export',
label: 'Export',
position: 1,
Icon: IconFileExport,
onClick: exportMock,
});
map.set('delete', {
type: 'standard',
key: 'delete',
label: 'Delete',
position: 2,
Icon: IconTrash,
onClick: deleteMock,
accent: 'danger' as MenuItemAccent,
});
}}
>
<ActionMenuComponentInstanceContext.Provider

View File

@ -86,7 +86,7 @@ describe('csvDownloader', () => {
describe('displayedExportProgress', () => {
it.each([
[undefined, undefined, 'percentage', 'Export'],
[undefined, undefined, 'percentage', 'Export View as CSV'],
[20, 50, 'percentage', 'Export (40%)'],
[0, 100, 'number', 'Export (0)'],
[10, 10, 'percentage', 'Export (100%)'],
@ -96,7 +96,7 @@ describe('displayedExportProgress', () => {
'displays the export progress',
(exportedRecordCount, totalRecordCount, displayType, expected) => {
expect(
displayedExportProgress({
displayedExportProgress('all', {
exportedRecordCount,
totalRecordCount,
displayType: displayType as 'percentage' | 'number',

View File

@ -108,9 +108,12 @@ const percentage = (part: number, whole: number): number => {
return Math.round((part / whole) * 100);
};
export const displayedExportProgress = (progress?: ExportProgress): string => {
export const displayedExportProgress = (
mode: 'all' | 'selection' = 'all',
progress?: ExportProgress,
): string => {
if (isUndefinedOrNull(progress?.exportedRecordCount)) {
return 'Export';
return mode === 'all' ? 'Export View as CSV' : 'Export Selection as CSV';
}
if (

View File

@ -4,6 +4,7 @@ import { IconComponent } from 'twenty-ui';
import { MenuItemAccent } from '@/ui/navigation/menu-item/types/MenuItemAccent';
export type ActionMenuEntry = {
type: 'standard' | 'workflow-run';
key: string;
label: string;
position: number;

View File

@ -288,12 +288,20 @@ export const CommandMenu = () => {
: true) && cmd.type === CommandType.Create,
);
const matchingActionCommands = commandMenuCommands.filter(
const matchingStandardActionCommands = commandMenuCommands.filter(
(cmd) =>
(deferredCommandMenuSearch.length > 0
? checkInShortcuts(cmd, deferredCommandMenuSearch) ||
checkInLabels(cmd, deferredCommandMenuSearch)
: true) && cmd.type === CommandType.Action,
: true) && cmd.type === CommandType.StandardAction,
);
const matchingWorkflowRunCommands = commandMenuCommands.filter(
(cmd) =>
(deferredCommandMenuSearch.length > 0
? checkInShortcuts(cmd, deferredCommandMenuSearch) ||
checkInLabels(cmd, deferredCommandMenuSearch)
: true) && cmd.type === CommandType.WorkflowRun,
);
useListenClickOutside({
@ -321,7 +329,7 @@ export const CommandMenu = () => {
const selectableItemIds = copilotCommands
.map((cmd) => cmd.id)
.concat(matchingActionCommands.map((cmd) => cmd.id))
.concat(matchingStandardActionCommands.map((cmd) => cmd.id))
.concat(matchingCreateCommand.map((cmd) => cmd.id))
.concat(matchingNavigateCommand.map((cmd) => cmd.id))
.concat(people?.map((person) => person.id))
@ -330,7 +338,8 @@ export const CommandMenu = () => {
.concat(notes?.map((note) => note.id));
const isNoResults =
!matchingActionCommands.length &&
!matchingStandardActionCommands.length &&
!matchingWorkflowRunCommands.length &&
!matchingCreateCommand.length &&
!matchingNavigateCommand.length &&
!people?.length &&
@ -410,38 +419,44 @@ export const CommandMenu = () => {
</CommandGroup>
)}
{mainContextStoreComponentInstanceId && (
<CommandGroup heading="Actions">
{matchingActionCommands?.map((actionCommand) => (
<SelectableItem
itemId={actionCommand.id}
key={actionCommand.id}
>
<CommandMenuItem
id={actionCommand.id}
label={actionCommand.label}
Icon={actionCommand.Icon}
onClick={actionCommand.onCommandClick}
/>
</SelectableItem>
))}
</CommandGroup>
<>
<CommandGroup heading="Standard Actions">
{matchingStandardActionCommands?.map(
(standardActionCommand) => (
<SelectableItem
itemId={standardActionCommand.id}
key={standardActionCommand.id}
>
<CommandMenuItem
id={standardActionCommand.id}
label={standardActionCommand.label}
Icon={standardActionCommand.Icon}
onClick={standardActionCommand.onCommandClick}
/>
</SelectableItem>
),
)}
</CommandGroup>
<CommandGroup heading="Workflows">
{matchingWorkflowRunCommands?.map(
(workflowRunCommand) => (
<SelectableItem
itemId={workflowRunCommand.id}
key={workflowRunCommand.id}
>
<CommandMenuItem
id={workflowRunCommand.id}
label={workflowRunCommand.label}
Icon={workflowRunCommand.Icon}
onClick={workflowRunCommand.onCommandClick}
/>
</SelectableItem>
),
)}
</CommandGroup>
</>
)}
<CommandGroup heading="Create">
{matchingCreateCommand.map((cmd) => (
<SelectableItem itemId={cmd.id} key={cmd.id}>
<CommandMenuItem
id={cmd.id}
to={cmd.to}
key={cmd.id}
Icon={cmd.Icon}
label={cmd.label}
onClick={cmd.onCommandClick}
firstHotKey={cmd.firstHotKey}
secondHotKey={cmd.secondHotKey}
/>
</SelectableItem>
))}
</CommandGroup>
<CommandGroup heading="Navigate">
{matchingNavigateCommand.map((cmd) => (
<SelectableItem itemId={cmd.id} key={cmd.id}>
@ -458,6 +473,22 @@ export const CommandMenu = () => {
</SelectableItem>
))}
</CommandGroup>
<CommandGroup heading="Other">
{matchingCreateCommand.map((cmd) => (
<SelectableItem itemId={cmd.id} key={cmd.id}>
<CommandMenuItem
id={cmd.id}
to={cmd.to}
key={cmd.id}
Icon={cmd.Icon}
label={cmd.label}
onClick={cmd.onCommandClick}
firstHotKey={cmd.firstHotKey}
secondHotKey={cmd.secondHotKey}
/>
</SelectableItem>
))}
</CommandGroup>
<CommandGroup heading="People">
{people?.map((person) => (
<SelectableItem itemId={person.id} key={person.id}>

View File

@ -47,15 +47,29 @@ export const useCommandMenu = () => {
const actionCommands = actionMenuEntries
.getValue()
?.filter((actionMenuEntry) => actionMenuEntry.type === 'standard')
?.map((actionMenuEntry) => ({
id: actionMenuEntry.key,
label: actionMenuEntry.label,
Icon: actionMenuEntry.Icon,
onCommandClick: actionMenuEntry.onClick,
type: CommandType.Action,
type: CommandType.StandardAction,
}));
setCommands([...commands, ...actionCommands]);
const workflowRunCommands = actionMenuEntries
.getValue()
?.filter(
(actionMenuEntry) => actionMenuEntry.type === 'workflow-run',
)
?.map((actionMenuEntry) => ({
id: actionMenuEntry.key,
label: actionMenuEntry.label,
Icon: actionMenuEntry.Icon,
onCommandClick: actionMenuEntry.onClick,
type: CommandType.WorkflowRun,
}));
setCommands([...commands, ...actionCommands, ...workflowRunCommands]);
}
setIsCommandMenuOpened(true);

View File

@ -3,14 +3,19 @@ import { IconComponent } from 'twenty-ui';
export enum CommandType {
Navigate = 'Navigate',
Create = 'Create',
Action = 'Action',
StandardAction = 'StandardAction',
WorkflowRun = 'WorkflowRun',
}
export type Command = {
id: string;
to?: string;
label: string;
type: CommandType.Navigate | CommandType.Create | CommandType.Action;
type:
| CommandType.Navigate
| CommandType.Create
| CommandType.StandardAction
| CommandType.WorkflowRun;
Icon?: IconComponent;
firstHotKey?: string;
secondHotKey?: string;

View File

@ -1,4 +1,4 @@
import { ApolloClient, useMutation } from '@apollo/client';
import { useMutation } from '@apollo/client';
import { CREATE_ONE_DATABASE_CONNECTION } from '@/databases/graphql/mutations/createOneDatabaseConnection';
import { GET_MANY_DATABASE_CONNECTIONS } from '@/databases/graphql/queries/findManyDatabaseConnections';
@ -17,7 +17,7 @@ export const useCreateOneDatabaseConnection = () => {
CreateServerMutation,
CreateServerMutationVariables
>(CREATE_ONE_DATABASE_CONNECTION, {
client: apolloMetadataClient ?? ({} as ApolloClient<any>),
client: apolloMetadataClient,
});
const createOneDatabaseConnection = async (

View File

@ -1,4 +1,4 @@
import { ApolloClient, useMutation } from '@apollo/client';
import { useMutation } from '@apollo/client';
import { getOperationName } from '@apollo/client/utilities';
import { DELETE_ONE_DATABASE_CONNECTION } from '@/databases/graphql/mutations/deleteOneDatabaseConnection';
@ -17,7 +17,7 @@ export const useDeleteOneDatabaseConnection = () => {
DeleteServerMutation,
DeleteServerMutationVariables
>(DELETE_ONE_DATABASE_CONNECTION, {
client: apolloMetadataClient ?? ({} as ApolloClient<any>),
client: apolloMetadataClient,
});
const deleteOneDatabaseConnection = async (input: RemoteServerIdInput) => {

View File

@ -1,5 +1,5 @@
import { useApolloClient, useMutation } from '@apollo/client';
import { useCallback } from 'react';
import { ApolloClient, useApolloClient, useMutation } from '@apollo/client';
import { SYNC_REMOTE_TABLE } from '@/databases/graphql/mutations/syncRemoteTable';
import { modifyRemoteTableFromCache } from '@/databases/utils/modifyRemoteTableFromCache';
@ -28,7 +28,7 @@ export const useSyncRemoteTable = () => {
SyncRemoteTableMutation,
SyncRemoteTableMutationVariables
>(SYNC_REMOTE_TABLE, {
client: apolloMetadataClient ?? ({} as ApolloClient<any>),
client: apolloMetadataClient,
});
const syncRemoteTable = useCallback(

View File

@ -1,5 +1,5 @@
import { useApolloClient, useMutation } from '@apollo/client';
import { useCallback } from 'react';
import { ApolloClient, useApolloClient, useMutation } from '@apollo/client';
import { SYNC_REMOTE_TABLE_SCHEMA_CHANGES } from '@/databases/graphql/mutations/syncRemoteTableSchemaChanges';
import { modifyRemoteTableFromCache } from '@/databases/utils/modifyRemoteTableFromCache';
@ -29,7 +29,7 @@ export const useSyncRemoteTableSchemaChanges = () => {
SyncRemoteTableSchemaChangesMutation,
SyncRemoteTableSchemaChangesMutationVariables
>(SYNC_REMOTE_TABLE_SCHEMA_CHANGES, {
client: apolloMetadataClient ?? ({} as ApolloClient<any>),
client: apolloMetadataClient,
});
const syncRemoteTableSchemaChanges = useCallback(

View File

@ -1,5 +1,5 @@
import { useMutation } from '@apollo/client';
import { useCallback } from 'react';
import { ApolloClient, useMutation } from '@apollo/client';
import { UNSYNC_REMOTE_TABLE } from '@/databases/graphql/mutations/unsyncRemoteTable';
import { modifyRemoteTableFromCache } from '@/databases/utils/modifyRemoteTableFromCache';
@ -21,7 +21,7 @@ export const useUnsyncRemoteTable = () => {
UnsyncRemoteTableMutation,
UnsyncRemoteTableMutationVariables
>(UNSYNC_REMOTE_TABLE, {
client: apolloMetadataClient ?? ({} as ApolloClient<any>),
client: apolloMetadataClient,
});
const unsyncRemoteTable = useCallback(

View File

@ -1,4 +1,4 @@
import { ApolloClient, useMutation } from '@apollo/client';
import { useMutation } from '@apollo/client';
import { UPDATE_ONE_DATABASE_CONNECTION } from '@/databases/graphql/mutations/updateOneDatabaseConnection';
import { useApolloMetadataClient } from '@/object-metadata/hooks/useApolloMetadataClient';
@ -15,7 +15,7 @@ export const useUpdateOneDatabaseConnection = () => {
UpdateServerMutation,
UpdateServerMutationVariables
>(UPDATE_ONE_DATABASE_CONNECTION, {
client: apolloMetadataClient ?? ({} as ApolloClient<any>),
client: apolloMetadataClient,
});
const updateOneDatabaseConnection = async (

View File

@ -10,5 +10,9 @@ export const useApolloMetadataClient = () => {
return apolloClient;
}
if (!apolloMetadataClient) {
throw new Error('ApolloMetadataClient not found');
}
return apolloMetadataClient;
};

View File

@ -1,4 +1,4 @@
import { ApolloClient, useMutation } from '@apollo/client';
import { useMutation } from '@apollo/client';
import { getOperationName } from '@apollo/client/utilities';
import {
@ -19,7 +19,7 @@ export const useCreateOneFieldMetadataItem = () => {
CreateOneFieldMetadataItemMutation,
CreateOneFieldMetadataItemMutationVariables
>(CREATE_ONE_FIELD_METADATA_ITEM, {
client: apolloMetadataClient ?? ({} as ApolloClient<any>),
client: apolloMetadataClient,
});
const createOneFieldMetadataItem = async (input: CreateFieldInput) => {

View File

@ -1,4 +1,4 @@
import { ApolloClient, useApolloClient, useMutation } from '@apollo/client';
import { useApolloClient, useMutation } from '@apollo/client';
import { getOperationName } from '@apollo/client/utilities';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
@ -26,7 +26,7 @@ export const useCreateOneObjectMetadataItem = () => {
CreateOneObjectMetadataItemMutation,
CreateOneObjectMetadataItemMutationVariables
>(CREATE_ONE_OBJECT_METADATA_ITEM, {
client: apolloMetadataClient ?? ({} as ApolloClient<any>),
client: apolloMetadataClient,
});
const createOneObjectMetadataItem = async (input: CreateObjectInput) => {

View File

@ -1,4 +1,4 @@
import { ApolloClient, useMutation } from '@apollo/client';
import { useMutation } from '@apollo/client';
import { getOperationName } from '@apollo/client/utilities';
import {
@ -22,7 +22,7 @@ export const useCreateOneRelationMetadataItem = () => {
CreateOneRelationMetadataMutation,
CreateOneRelationMetadataMutationVariables
>(CREATE_ONE_RELATION_METADATA_ITEM, {
client: apolloMetadataClient ?? ({} as ApolloClient<any>),
client: apolloMetadataClient,
});
const createOneRelationMetadataItem = async (

View File

@ -1,4 +1,4 @@
import { ApolloClient, useMutation } from '@apollo/client';
import { useMutation } from '@apollo/client';
import { getOperationName } from '@apollo/client/utilities';
import {
@ -18,7 +18,7 @@ export const useDeleteOneFieldMetadataItem = () => {
DeleteOneFieldMetadataItemMutation,
DeleteOneFieldMetadataItemMutationVariables
>(DELETE_ONE_FIELD_METADATA_ITEM, {
client: apolloMetadataClient ?? ({} as ApolloClient<any>),
client: apolloMetadataClient,
});
const deleteOneFieldMetadataItem = async (

View File

@ -1,4 +1,4 @@
import { ApolloClient, useMutation } from '@apollo/client';
import { useMutation } from '@apollo/client';
import { getOperationName } from '@apollo/client/utilities';
import {
@ -18,7 +18,7 @@ export const useDeleteOneObjectMetadataItem = () => {
DeleteOneObjectMetadataItemMutation,
DeleteOneObjectMetadataItemMutationVariables
>(DELETE_ONE_OBJECT_METADATA_ITEM, {
client: apolloMetadataClient ?? ({} as ApolloClient<any>),
client: apolloMetadataClient,
});
const deleteOneObjectMetadataItem = async (

View File

@ -1,4 +1,4 @@
import { ApolloClient, useMutation } from '@apollo/client';
import { useMutation } from '@apollo/client';
import { getOperationName } from '@apollo/client/utilities';
import { DELETE_ONE_RELATION_METADATA_ITEM } from '@/object-metadata/graphql/mutations';
@ -18,7 +18,7 @@ export const useDeleteOneRelationMetadataItem = () => {
DeleteOneRelationMetadataItemMutation,
DeleteOneRelationMetadataItemMutationVariables
>(DELETE_ONE_RELATION_METADATA_ITEM, {
client: apolloMetadataClient ?? ({} as ApolloClient<any>),
client: apolloMetadataClient,
});
const deleteOneRelationMetadataItem = async (

View File

@ -21,6 +21,7 @@ import {
displayedExportProgress,
useExportRecordData,
} from '@/action-menu/hooks/useExportRecordData';
import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { useRecordGroupReorder } from '@/object-record/record-group/hooks/useRecordGroupReorder';
import { useRecordGroups } from '@/object-record/record-group/hooks/useRecordGroups';
@ -41,6 +42,7 @@ import { MenuItemToggle } from '@/ui/navigation/menu-item/components/MenuItemTog
import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { ViewFieldsVisibilityDropdownSection } from '@/views/components/ViewFieldsVisibilityDropdownSection';
import { ViewGroupsVisibilityDropdownSection } from '@/views/components/ViewGroupsVisibilityDropdownSection';
import { useGetCurrentView } from '@/views/hooks/useGetCurrentView';
@ -182,6 +184,12 @@ export const RecordIndexOptionsDropdownContent = ({
viewGroupFieldMetadataItem &&
(visibleRecordGroups.length > 0 || hiddenRecordGroups.length > 0);
const contextStoreNumberOfSelectedRecords = useRecoilComponentValueV2(
contextStoreNumberOfSelectedRecordsComponentState,
);
const mode = contextStoreNumberOfSelectedRecords > 0 ? 'selection' : 'all';
useEffect(() => {
if (currentMenu === 'hiddenViewGroups' && hiddenRecordGroups.length === 0) {
setCurrentMenu('viewGroups');
@ -214,7 +222,7 @@ export const RecordIndexOptionsDropdownContent = ({
<MenuItem
onClick={download}
LeftIcon={IconFileExport}
text={displayedExportProgress(progress)}
text={displayedExportProgress(mode, progress)}
/>
<MenuItem
onClick={() => {

View File

@ -1,14 +1,14 @@
import { ApolloClient, useMutation } from '@apollo/client';
import { useMutation } from '@apollo/client';
import { useApolloMetadataClient } from '@/object-metadata/hooks/useApolloMetadataClient';
import { CREATE_ONE_SERVERLESS_FUNCTION } from '@/settings/serverless-functions/graphql/mutations/createOneServerlessFunction';
import { FIND_MANY_SERVERLESS_FUNCTIONS } from '@/settings/serverless-functions/graphql/queries/findManyServerlessFunctions';
import { getOperationName } from '@apollo/client/utilities';
import {
CreateServerlessFunctionInput,
CreateOneServerlessFunctionItemMutation,
CreateOneServerlessFunctionItemMutationVariables,
CreateServerlessFunctionInput,
} from '~/generated-metadata/graphql';
import { getOperationName } from '@apollo/client/utilities';
import { FIND_MANY_SERVERLESS_FUNCTIONS } from '@/settings/serverless-functions/graphql/queries/findManyServerlessFunctions';
import { CREATE_ONE_SERVERLESS_FUNCTION } from '@/settings/serverless-functions/graphql/mutations/createOneServerlessFunction';
export const useCreateOneServerlessFunction = () => {
const apolloMetadataClient = useApolloMetadataClient();
@ -16,7 +16,7 @@ export const useCreateOneServerlessFunction = () => {
CreateOneServerlessFunctionItemMutation,
CreateOneServerlessFunctionItemMutationVariables
>(CREATE_ONE_SERVERLESS_FUNCTION, {
client: apolloMetadataClient ?? ({} as ApolloClient<any>),
client: apolloMetadataClient,
});
const createOneServerlessFunction = async (

View File

@ -1,13 +1,13 @@
import { useApolloMetadataClient } from '@/object-metadata/hooks/useApolloMetadataClient';
import { ApolloClient, useMutation } from '@apollo/client';
import { getOperationName } from '@apollo/client/utilities';
import { DELETE_ONE_SERVERLESS_FUNCTION } from '@/settings/serverless-functions/graphql/mutations/deleteOneServerlessFunction';
import { FIND_ONE_SERVERLESS_FUNCTION_SOURCE_CODE } from '@/settings/serverless-functions/graphql/queries/findOneServerlessFunctionSourceCode';
import { useMutation } from '@apollo/client';
import { getOperationName } from '@apollo/client/utilities';
import {
ServerlessFunctionIdInput,
DeleteOneServerlessFunctionMutation,
DeleteOneServerlessFunctionMutationVariables,
ServerlessFunctionIdInput,
} from '~/generated-metadata/graphql';
import { FIND_ONE_SERVERLESS_FUNCTION_SOURCE_CODE } from '@/settings/serverless-functions/graphql/queries/findOneServerlessFunctionSourceCode';
export const useDeleteOneServerlessFunction = () => {
const apolloMetadataClient = useApolloMetadataClient();
@ -15,7 +15,7 @@ export const useDeleteOneServerlessFunction = () => {
DeleteOneServerlessFunctionMutation,
DeleteOneServerlessFunctionMutationVariables
>(DELETE_ONE_SERVERLESS_FUNCTION, {
client: apolloMetadataClient ?? ({} as ApolloClient<any>),
client: apolloMetadataClient,
});
const deleteOneServerlessFunction = async (

View File

@ -1,10 +1,10 @@
import { useApolloMetadataClient } from '@/object-metadata/hooks/useApolloMetadataClient';
import { ApolloClient, useMutation } from '@apollo/client';
import { EXECUTE_ONE_SERVERLESS_FUNCTION } from '@/settings/serverless-functions/graphql/mutations/executeOneServerlessFunction';
import { useMutation } from '@apollo/client';
import {
ExecuteServerlessFunctionInput,
ExecuteOneServerlessFunctionMutation,
ExecuteOneServerlessFunctionMutationVariables,
ExecuteServerlessFunctionInput,
} from '~/generated-metadata/graphql';
export const useExecuteOneServerlessFunction = () => {
@ -13,7 +13,7 @@ export const useExecuteOneServerlessFunction = () => {
ExecuteOneServerlessFunctionMutation,
ExecuteOneServerlessFunctionMutationVariables
>(EXECUTE_ONE_SERVERLESS_FUNCTION, {
client: apolloMetadataClient ?? ({} as ApolloClient<any>),
client: apolloMetadataClient,
});
const executeOneServerlessFunction = async (

View File

@ -1,13 +1,13 @@
import { ApolloClient, useMutation } from '@apollo/client';
import { useApolloMetadataClient } from '@/object-metadata/hooks/useApolloMetadataClient';
import { PUBLISH_ONE_SERVERLESS_FUNCTION } from '@/settings/serverless-functions/graphql/mutations/publishOneServerlessFunction';
import { FIND_ONE_SERVERLESS_FUNCTION_SOURCE_CODE } from '@/settings/serverless-functions/graphql/queries/findOneServerlessFunctionSourceCode';
import { useMutation } from '@apollo/client';
import { getOperationName } from '@apollo/client/utilities';
import {
PublishServerlessFunctionInput,
PublishOneServerlessFunctionMutation,
PublishOneServerlessFunctionMutationVariables,
PublishServerlessFunctionInput,
} from '~/generated-metadata/graphql';
import { getOperationName } from '@apollo/client/utilities';
import { FIND_ONE_SERVERLESS_FUNCTION_SOURCE_CODE } from '@/settings/serverless-functions/graphql/queries/findOneServerlessFunctionSourceCode';
export const usePublishOneServerlessFunction = () => {
const apolloMetadataClient = useApolloMetadataClient();
@ -15,7 +15,7 @@ export const usePublishOneServerlessFunction = () => {
PublishOneServerlessFunctionMutation,
PublishOneServerlessFunctionMutationVariables
>(PUBLISH_ONE_SERVERLESS_FUNCTION, {
client: apolloMetadataClient ?? ({} as ApolloClient<any>),
client: apolloMetadataClient,
});
const publishOneServerlessFunction = async (

View File

@ -1,13 +1,13 @@
import { ApolloClient, useMutation } from '@apollo/client';
import { useApolloMetadataClient } from '@/object-metadata/hooks/useApolloMetadataClient';
import { UPDATE_ONE_SERVERLESS_FUNCTION } from '@/settings/serverless-functions/graphql/mutations/updateOneServerlessFunction';
import { FIND_MANY_SERVERLESS_FUNCTIONS } from '@/settings/serverless-functions/graphql/queries/findManyServerlessFunctions';
import { useMutation } from '@apollo/client';
import { getOperationName } from '@apollo/client/utilities';
import {
UpdateServerlessFunctionInput,
UpdateOneServerlessFunctionMutation,
UpdateOneServerlessFunctionMutationVariables,
UpdateServerlessFunctionInput,
} from '~/generated-metadata/graphql';
import { getOperationName } from '@apollo/client/utilities';
import { FIND_MANY_SERVERLESS_FUNCTIONS } from '@/settings/serverless-functions/graphql/queries/findManyServerlessFunctions';
export const useUpdateOneServerlessFunction = () => {
const apolloMetadataClient = useApolloMetadataClient();
@ -15,7 +15,7 @@ export const useUpdateOneServerlessFunction = () => {
UpdateOneServerlessFunctionMutation,
UpdateOneServerlessFunctionMutationVariables
>(UPDATE_ONE_SERVERLESS_FUNCTION, {
client: apolloMetadataClient ?? ({} as ApolloClient<any>),
client: apolloMetadataClient,
});
const updateOneServerlessFunction = async (

View File

@ -0,0 +1,9 @@
import { gql } from '@apollo/client';
export const RUN_WORKFLOW_VERSION = gql`
mutation RunWorkflowVersion($input: RunWorkflowVersionInput!) {
runWorkflowVersion(input: $input) {
workflowRunId
}
}
`;

View File

@ -1,5 +1,5 @@
import { useApolloMetadataClient } from '@/object-metadata/hooks/useApolloMetadataClient';
import { ApolloClient, useApolloClient, useMutation } from '@apollo/client';
import { useApolloClient, useMutation } from '@apollo/client';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
@ -18,7 +18,7 @@ export const useActivateWorkflowVersion = () => {
ActivateWorkflowVersionMutation,
ActivateWorkflowVersionMutationVariables
>(ACTIVATE_WORKFLOW_VERSION, {
client: apolloMetadataClient ?? ({} as ApolloClient<any>),
client: apolloMetadataClient,
});
const { objectMetadataItem: objectMetadataItemWorkflowVersion } =

View File

@ -0,0 +1,52 @@
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields';
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
import {
Workflow,
WorkflowTriggerType,
WorkflowVersion,
} from '@/workflow/types/Workflow';
export const useAllActiveWorkflowVersionsForObject = ({
objectNameSingular,
triggerType,
}: {
objectNameSingular: string;
triggerType: WorkflowTriggerType;
}) => {
const { objectMetadataItem } = useObjectMetadataItem({
objectNameSingular,
});
const { records } = useFindManyRecords<
WorkflowVersion & { workflow: Workflow }
>({
objectNameSingular: CoreObjectNameSingular.WorkflowVersion,
filter: {
and: [
{
status: {
eq: 'ACTIVE',
},
},
{
trigger: {
like: `%"type": "${triggerType}"%`,
},
},
{
trigger: {
like: `%"objectType": "${objectNameSingular}"%`,
},
},
],
},
recordGqlFields: {
...generateDepthOneRecordGqlFields({ objectMetadataItem }),
workflow: true,
},
});
return { records };
};

View File

@ -1,11 +1,11 @@
import { useApolloMetadataClient } from '@/object-metadata/hooks/useApolloMetadataClient';
import { ApolloClient, useMutation } from '@apollo/client';
import { COMPUTE_STEP_OUTPUT_SCHEMA } from '@/workflow/graphql/mutations/computeStepOutputSchema';
import { useMutation } from '@apollo/client';
import {
ComputeStepOutputSchemaInput,
ComputeStepOutputSchemaMutation,
ComputeStepOutputSchemaMutationVariables,
} from '~/generated/graphql';
import { COMPUTE_STEP_OUTPUT_SCHEMA } from '@/workflow/graphql/mutations/computeStepOutputSchema';
export const useComputeStepOutputSchema = () => {
const apolloMetadataClient = useApolloMetadataClient();
@ -13,7 +13,7 @@ export const useComputeStepOutputSchema = () => {
ComputeStepOutputSchemaMutation,
ComputeStepOutputSchemaMutationVariables
>(COMPUTE_STEP_OUTPUT_SCHEMA, {
client: apolloMetadataClient ?? ({} as ApolloClient<any>),
client: apolloMetadataClient,
});
const computeStepOutputSchema = async (

View File

@ -1,5 +1,5 @@
import { useApolloMetadataClient } from '@/object-metadata/hooks/useApolloMetadataClient';
import { ApolloClient, useApolloClient, useMutation } from '@apollo/client';
import { useApolloClient, useMutation } from '@apollo/client';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
@ -17,7 +17,7 @@ export const useDeactivateWorkflowVersion = () => {
ActivateWorkflowVersionMutation,
ActivateWorkflowVersionMutationVariables
>(DEACTIVATE_WORKFLOW_VERSION, {
client: apolloMetadataClient ?? ({} as ApolloClient<any>),
client: apolloMetadataClient,
});
const { objectMetadataItem: objectMetadataItemWorkflowVersion } =

View File

@ -0,0 +1,28 @@
import { useApolloMetadataClient } from '@/object-metadata/hooks/useApolloMetadataClient';
import { RUN_WORKFLOW_VERSION } from '@/workflow/graphql/mutations/runWorkflowVersion';
import { useMutation } from '@apollo/client';
import {
RunWorkflowVersionMutation,
RunWorkflowVersionMutationVariables,
} from '~/generated/graphql';
export const useRunWorkflowVersion = () => {
const apolloMetadataClient = useApolloMetadataClient();
const [mutate] = useMutation<
RunWorkflowVersionMutation,
RunWorkflowVersionMutationVariables
>(RUN_WORKFLOW_VERSION, {
client: apolloMetadataClient,
});
const runWorkflowVersion = async (
workflowVersionId: string,
payload: Record<string, any>,
) => {
await mutate({
variables: { input: { workflowVersionId, payload } },
});
};
return { runWorkflowVersion };
};