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;
};