Skip to content

Commit

Permalink
8191 command k workflow trigger for selected record (#8315)
Browse files Browse the repository at this point in the history
  • Loading branch information
bosiraphael authored Nov 5, 2024
1 parent 0893774 commit d1531aa
Show file tree
Hide file tree
Showing 44 changed files with 533 additions and 199 deletions.
42 changes: 41 additions & 1 deletion packages/twenty-front/src/generated/graphql.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as Apollo from '@apollo/client';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
export type Maybe<T> = T | null;
export type InputMaybe<T> = Maybe<T>;
export type Exact<T extends { [key: string]: unknown }> = { [K in keyof T]: T[K] };
Expand Down Expand Up @@ -1849,6 +1849,13 @@ export type DeactivateWorkflowVersionMutationVariables = Exact<{

export type DeactivateWorkflowVersionMutation = { __typename?: 'Mutation', deactivateWorkflowVersion: boolean };

export type RunWorkflowVersionMutationVariables = Exact<{
input: RunWorkflowVersionInput;
}>;


export type RunWorkflowVersionMutation = { __typename?: 'Mutation', runWorkflowVersion: { __typename?: 'WorkflowRun', workflowRunId: any } };

export type DeleteWorkspaceInvitationMutationVariables = Exact<{
appTokenId: Scalars['String'];
}>;
Expand Down Expand Up @@ -3525,6 +3532,39 @@ export function useDeactivateWorkflowVersionMutation(baseOptions?: Apollo.Mutati
export type DeactivateWorkflowVersionMutationHookResult = ReturnType<typeof useDeactivateWorkflowVersionMutation>;
export type DeactivateWorkflowVersionMutationResult = Apollo.MutationResult<DeactivateWorkflowVersionMutation>;
export type DeactivateWorkflowVersionMutationOptions = Apollo.BaseMutationOptions<DeactivateWorkflowVersionMutation, DeactivateWorkflowVersionMutationVariables>;
export const RunWorkflowVersionDocument = gql`
mutation RunWorkflowVersion($input: RunWorkflowVersionInput!) {
runWorkflowVersion(input: $input) {
workflowRunId
}
}
`;
export type RunWorkflowVersionMutationFn = Apollo.MutationFunction<RunWorkflowVersionMutation, RunWorkflowVersionMutationVariables>;

/**
* __useRunWorkflowVersionMutation__
*
* To run a mutation, you first call `useRunWorkflowVersionMutation` within a React component and pass it any options that fit your needs.
* When your component renders, `useRunWorkflowVersionMutation` returns a tuple that includes:
* - A mutate function that you can call at any time to execute the mutation
* - An object with fields that represent the current status of the mutation's execution
*
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
*
* @example
* const [runWorkflowVersionMutation, { data, loading, error }] = useRunWorkflowVersionMutation({
* variables: {
* input: // value for 'input'
* },
* });
*/
export function useRunWorkflowVersionMutation(baseOptions?: Apollo.MutationHookOptions<RunWorkflowVersionMutation, RunWorkflowVersionMutationVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useMutation<RunWorkflowVersionMutation, RunWorkflowVersionMutationVariables>(RunWorkflowVersionDocument, options);
}
export type RunWorkflowVersionMutationHookResult = ReturnType<typeof useRunWorkflowVersionMutation>;
export type RunWorkflowVersionMutationResult = Apollo.MutationResult<RunWorkflowVersionMutation>;
export type RunWorkflowVersionMutationOptions = Apollo.BaseMutationOptions<RunWorkflowVersionMutation, RunWorkflowVersionMutationVariables>;
export const DeleteWorkspaceInvitationDocument = gql`
mutation DeleteWorkspaceInvitation($appTokenId: String!) {
deleteWorkspaceInvitation(appTokenId: $appTokenId)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ export const DeleteRecordsActionEffect = ({
useEffect(() => {
if (canDelete) {
addActionMenuEntry({
type: 'standard',
key: 'delete',
label: 'Delete',
position,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -16,6 +18,9 @@ export const ExportRecordsActionEffect = ({
objectMetadataItem: ObjectMetadataItem;
}) => {
const { addActionMenuEntry, removeActionMenuEntry } = useActionMenuEntries();
const contextStoreNumberOfSelectedRecords = useRecoilComponentValueV2(
contextStoreNumberOfSelectedRecordsComponentState,
);

const { progress, download } = useExportRecordData({
delayMs: 100,
Expand All @@ -26,17 +31,29 @@ 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(),
});

return () => {
removeActionMenuEntry('export');
};
}, [download, progress, addActionMenuEntry, removeActionMenuEntry, position]);
}, [
contextStoreNumberOfSelectedRecords,
download,
progress,
addActionMenuEntry,
removeActionMenuEntry,
position,
]);

return null;
};
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export const ManageFavoritesActionEffect = ({
}

addActionMenuEntry({
type: 'standard',
key: 'manage-favorites',
label: isFavorite ? 'Remove from favorites' : 'Add to favorites',
position,
Expand Down
Original file line number Diff line number Diff line change
@@ -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(
Expand All @@ -36,24 +35,32 @@ export const RecordActionMenuEntriesSetter = () => {
);
}

if (!contextStoreNumberOfSelectedRecords) {
return null;
}

const actions =
contextStoreNumberOfSelectedRecords === 1
? singleRecordActionEffects
: multipleRecordActionEffects;

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}
/>
)}
</>
);
};
Original file line number Diff line number Diff line change
@@ -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;
};
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export const RecordIndexActionMenuBar = () => {

const pinnedEntries = actionMenuEntries.filter((entry) => entry.isPinned);

if (pinnedEntries.length === 0) {
if (contextStoreNumberOfSelectedRecords === 0) {
return null;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const markAsDoneMock = jest.fn();
export const Default: Story = {
args: {
entry: {
type: 'standard',
key: 'delete',
label: 'Delete',
position: 0,
Expand All @@ -33,6 +34,7 @@ export const Default: Story = {
export const WithDangerAccent: Story = {
args: {
entry: {
type: 'standard',
key: 'delete',
label: 'Delete',
position: 0,
Expand All @@ -46,6 +48,7 @@ export const WithDangerAccent: Story = {
export const WithInteraction: Story = {
args: {
entry: {
type: 'standard',
key: 'markAsDone',
label: 'Mark as done',
position: 0,
Expand Down
Loading

0 comments on commit d1531aa

Please sign in to comment.