Implement contextual actions for the workflows (#8814)
Implemented the following actions for the workflows: - Test Workflow - Discard Draft - Activate Draft - Activate Workflow Last Published Version - Deactivate Workflow - See Workflow Executions - See Workflow Active Version - See Workflow Versions History And the following actions for the workflow versions: - Use As Draft - See Workflow Versions History - See Workflow Executions Video example: https://github.com/user-attachments/assets/016956a6-6f2e-4de5-9605-d6e14526cbb3 A few of these actions are links to the relations of the workflow object (link to a filtered table). To generalize this behavior, I will create an hook named `useSeeRelationsActionSingleRecordAction` to automatically generate links to a showPage if the relation is a Many To One or to a filtered table if the relation is a One to Many for all the record types. I will also create actions which will allow to create a relation. --------- Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
@ -1,9 +1,9 @@
|
|||||||
import { MultipleRecordsActionMenuEntrySetterEffect } from '@/action-menu/actions/record-actions/multiple-records/components/MultipleRecordsActionMenuEntrySetterEffect';
|
import { MultipleRecordsActionMenuEntrySetterEffect } from '@/action-menu/actions/record-actions/multiple-records/components/MultipleRecordsActionMenuEntrySetterEffect';
|
||||||
import { NoSelectionActionMenuEntrySetterEffect } from '@/action-menu/actions/record-actions/no-selection/components/NoSelectionActionMenuEntrySetterEffect';
|
import { NoSelectionActionMenuEntrySetterEffect } from '@/action-menu/actions/record-actions/no-selection/components/NoSelectionActionMenuEntrySetterEffect';
|
||||||
import { SingleRecordActionMenuEntrySetterEffect } from '@/action-menu/actions/record-actions/single-record/components/SingleRecordActionMenuEntrySetterEffect';
|
import { SingleRecordActionMenuEntrySetter } from '@/action-menu/actions/record-actions/single-record/components/SingleRecordActionMenuEntrySetter';
|
||||||
import { WorkflowRunRecordActionMenuEntrySetterEffect } from '@/action-menu/actions/record-actions/workflow-run-record-actions/components/WorkflowRunRecordActionMenuEntrySetter';
|
import { WorkflowRunRecordActionMenuEntrySetterEffect } from '@/action-menu/actions/record-actions/workflow-run-record-actions/components/WorkflowRunRecordActionMenuEntrySetter';
|
||||||
import { contextStoreCurrentObjectMetadataIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataIdComponentState';
|
import { contextStoreCurrentObjectMetadataIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataIdComponentState';
|
||||||
import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
|
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||||
import { useObjectMetadataItemById } from '@/object-metadata/hooks/useObjectMetadataItemById';
|
import { useObjectMetadataItemById } from '@/object-metadata/hooks/useObjectMetadataItemById';
|
||||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||||
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
||||||
@ -32,30 +32,35 @@ const ActionEffects = ({
|
|||||||
objectId: objectMetadataItemId,
|
objectId: objectMetadataItemId,
|
||||||
});
|
});
|
||||||
|
|
||||||
const contextStoreNumberOfSelectedRecords = useRecoilComponentValueV2(
|
const contextStoreTargetedRecordsRule = useRecoilComponentValueV2(
|
||||||
contextStoreNumberOfSelectedRecordsComponentState,
|
contextStoreTargetedRecordsRuleComponentState,
|
||||||
);
|
);
|
||||||
|
|
||||||
const isWorkflowEnabled = useIsFeatureEnabled('IS_WORKFLOW_ENABLED');
|
const isWorkflowEnabled = useIsFeatureEnabled('IS_WORKFLOW_ENABLED');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{contextStoreNumberOfSelectedRecords === 0 && (
|
{contextStoreTargetedRecordsRule.mode === 'selection' &&
|
||||||
<NoSelectionActionMenuEntrySetterEffect
|
contextStoreTargetedRecordsRule.selectedRecordIds.length === 0 && (
|
||||||
objectMetadataItem={objectMetadataItem}
|
<NoSelectionActionMenuEntrySetterEffect
|
||||||
/>
|
objectMetadataItem={objectMetadataItem}
|
||||||
)}
|
/>
|
||||||
{contextStoreNumberOfSelectedRecords === 1 && (
|
)}
|
||||||
<SingleRecordActionMenuEntrySetterEffect
|
{contextStoreTargetedRecordsRule.mode === 'selection' &&
|
||||||
objectMetadataItem={objectMetadataItem}
|
contextStoreTargetedRecordsRule.selectedRecordIds.length === 1 && (
|
||||||
/>
|
<>
|
||||||
)}
|
<SingleRecordActionMenuEntrySetter
|
||||||
{contextStoreNumberOfSelectedRecords === 1 && isWorkflowEnabled && (
|
objectMetadataItem={objectMetadataItem}
|
||||||
<WorkflowRunRecordActionMenuEntrySetterEffect
|
/>
|
||||||
objectMetadataItem={objectMetadataItem}
|
{isWorkflowEnabled && (
|
||||||
/>
|
<WorkflowRunRecordActionMenuEntrySetterEffect
|
||||||
)}
|
objectMetadataItem={objectMetadataItem}
|
||||||
{contextStoreNumberOfSelectedRecords > 1 && (
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{(contextStoreTargetedRecordsRule.mode === 'exclusion' ||
|
||||||
|
contextStoreTargetedRecordsRule.selectedRecordIds.length > 1) && (
|
||||||
<MultipleRecordsActionMenuEntrySetterEffect
|
<MultipleRecordsActionMenuEntrySetterEffect
|
||||||
objectMetadataItem={objectMetadataItem}
|
objectMetadataItem={objectMetadataItem}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -0,0 +1,132 @@
|
|||||||
|
import { actionMenuEntriesComponentState } from '@/action-menu/states/actionMenuEntriesComponentState';
|
||||||
|
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
|
||||||
|
import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
|
||||||
|
import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
|
||||||
|
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||||
|
import { expect } from '@storybook/test';
|
||||||
|
import { renderHook } from '@testing-library/react';
|
||||||
|
import { act } from 'react';
|
||||||
|
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
|
||||||
|
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
|
||||||
|
import { useDeleteMultipleRecordsAction } from '../useDeleteMultipleRecordsAction';
|
||||||
|
|
||||||
|
jest.mock('@/object-record/hooks/useDeleteManyRecords', () => ({
|
||||||
|
useDeleteManyRecords: () => ({
|
||||||
|
deleteManyRecords: jest.fn(),
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
jest.mock('@/favorites/hooks/useDeleteFavorite', () => ({
|
||||||
|
useDeleteFavorite: () => ({
|
||||||
|
deleteFavorite: jest.fn(),
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
jest.mock('@/favorites/hooks/useFavorites', () => ({
|
||||||
|
useFavorites: () => ({
|
||||||
|
sortedFavorites: [],
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
jest.mock('@/object-record/record-table/hooks/useRecordTable', () => ({
|
||||||
|
useRecordTable: () => ({
|
||||||
|
resetTableRowSelection: jest.fn(),
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const companyMockObjectMetadataItem = generatedMockObjectMetadataItems.find(
|
||||||
|
(item) => item.nameSingular === 'company',
|
||||||
|
)!;
|
||||||
|
|
||||||
|
const JestMetadataAndApolloMocksWrapper = getJestMetadataAndApolloMocksWrapper({
|
||||||
|
apolloMocks: [],
|
||||||
|
onInitializeRecoilSnapshot: ({ set }) => {
|
||||||
|
set(
|
||||||
|
contextStoreNumberOfSelectedRecordsComponentState.atomFamily({
|
||||||
|
instanceId: '1',
|
||||||
|
}),
|
||||||
|
3,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('useDeleteMultipleRecordsAction', () => {
|
||||||
|
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||||
|
<JestMetadataAndApolloMocksWrapper>
|
||||||
|
<ContextStoreComponentInstanceContext.Provider
|
||||||
|
value={{
|
||||||
|
instanceId: '1',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ActionMenuComponentInstanceContext.Provider
|
||||||
|
value={{
|
||||||
|
instanceId: '1',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</ActionMenuComponentInstanceContext.Provider>
|
||||||
|
</ContextStoreComponentInstanceContext.Provider>
|
||||||
|
</JestMetadataAndApolloMocksWrapper>
|
||||||
|
);
|
||||||
|
|
||||||
|
it('should register delete action', () => {
|
||||||
|
const { result } = renderHook(
|
||||||
|
() => {
|
||||||
|
const actionMenuEntries = useRecoilComponentValueV2(
|
||||||
|
actionMenuEntriesComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
actionMenuEntries,
|
||||||
|
useDeleteMultipleRecordsAction: useDeleteMultipleRecordsAction({
|
||||||
|
objectMetadataItem: companyMockObjectMetadataItem,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
{ wrapper },
|
||||||
|
);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.useDeleteMultipleRecordsAction.registerDeleteMultipleRecordsAction(
|
||||||
|
{ position: 1 },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.current.actionMenuEntries.size).toBe(1);
|
||||||
|
expect(
|
||||||
|
result.current.actionMenuEntries.get('delete-multiple-records'),
|
||||||
|
).toBeDefined();
|
||||||
|
expect(
|
||||||
|
result.current.actionMenuEntries.get('delete-multiple-records')?.position,
|
||||||
|
).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should unregister delete action', () => {
|
||||||
|
const { result } = renderHook(
|
||||||
|
() => {
|
||||||
|
const actionMenuEntries = useRecoilComponentValueV2(
|
||||||
|
actionMenuEntriesComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
actionMenuEntries,
|
||||||
|
useDeleteMultipleRecordsAction: useDeleteMultipleRecordsAction({
|
||||||
|
objectMetadataItem: companyMockObjectMetadataItem,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
{ wrapper },
|
||||||
|
);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.useDeleteMultipleRecordsAction.registerDeleteMultipleRecordsAction(
|
||||||
|
{ position: 1 },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.current.actionMenuEntries.size).toBe(1);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.useDeleteMultipleRecordsAction.unregisterDeleteMultipleRecordsAction();
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.current.actionMenuEntries.size).toBe(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,105 @@
|
|||||||
|
import { actionMenuEntriesComponentState } from '@/action-menu/states/actionMenuEntriesComponentState';
|
||||||
|
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
|
||||||
|
import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
|
||||||
|
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||||
|
import { expect } from '@storybook/test';
|
||||||
|
import { renderHook } from '@testing-library/react';
|
||||||
|
import { act } from 'react';
|
||||||
|
import { JestObjectMetadataItemSetter } from '~/testing/jest/JestObjectMetadataItemSetter';
|
||||||
|
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
|
||||||
|
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
|
||||||
|
import { useExportMultipleRecordsAction } from '../useExportMultipleRecordsAction';
|
||||||
|
|
||||||
|
const companyMockObjectMetadataItem = generatedMockObjectMetadataItems.find(
|
||||||
|
(item) => item.nameSingular === 'company',
|
||||||
|
)!;
|
||||||
|
|
||||||
|
const JestMetadataAndApolloMocksWrapper = getJestMetadataAndApolloMocksWrapper({
|
||||||
|
apolloMocks: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('useExportMultipleRecordsAction', () => {
|
||||||
|
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||||
|
<JestMetadataAndApolloMocksWrapper>
|
||||||
|
<JestObjectMetadataItemSetter>
|
||||||
|
<ContextStoreComponentInstanceContext.Provider
|
||||||
|
value={{
|
||||||
|
instanceId: '1',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ActionMenuComponentInstanceContext.Provider
|
||||||
|
value={{
|
||||||
|
instanceId: '1',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</ActionMenuComponentInstanceContext.Provider>
|
||||||
|
</ContextStoreComponentInstanceContext.Provider>
|
||||||
|
</JestObjectMetadataItemSetter>
|
||||||
|
</JestMetadataAndApolloMocksWrapper>
|
||||||
|
);
|
||||||
|
|
||||||
|
it('should register export multiple records action', () => {
|
||||||
|
const { result } = renderHook(
|
||||||
|
() => {
|
||||||
|
const actionMenuEntries = useRecoilComponentValueV2(
|
||||||
|
actionMenuEntriesComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
actionMenuEntries,
|
||||||
|
useExportMultipleRecordsAction: useExportMultipleRecordsAction({
|
||||||
|
objectMetadataItem: companyMockObjectMetadataItem,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
{ wrapper },
|
||||||
|
);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.useExportMultipleRecordsAction.registerExportMultipleRecordsAction(
|
||||||
|
{ position: 1 },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.current.actionMenuEntries.size).toBe(1);
|
||||||
|
expect(
|
||||||
|
result.current.actionMenuEntries.get('export-multiple-records'),
|
||||||
|
).toBeDefined();
|
||||||
|
expect(
|
||||||
|
result.current.actionMenuEntries.get('export-multiple-records')?.position,
|
||||||
|
).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should unregister export multiple records action', () => {
|
||||||
|
const { result } = renderHook(
|
||||||
|
() => {
|
||||||
|
const actionMenuEntries = useRecoilComponentValueV2(
|
||||||
|
actionMenuEntriesComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
actionMenuEntries,
|
||||||
|
useExportMultipleRecordsAction: useExportMultipleRecordsAction({
|
||||||
|
objectMetadataItem: companyMockObjectMetadataItem,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
{ wrapper },
|
||||||
|
);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.useExportMultipleRecordsAction.registerExportMultipleRecordsAction(
|
||||||
|
{ position: 1 },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.current.actionMenuEntries.size).toBe(1);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.useExportMultipleRecordsAction.unregisterExportMultipleRecordsAction();
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.current.actionMenuEntries.size).toBe(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -22,10 +22,8 @@ import { useCallback, useContext, useState } from 'react';
|
|||||||
import { IconTrash, isDefined } from 'twenty-ui';
|
import { IconTrash, isDefined } from 'twenty-ui';
|
||||||
|
|
||||||
export const useDeleteMultipleRecordsAction = ({
|
export const useDeleteMultipleRecordsAction = ({
|
||||||
position,
|
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
}: {
|
}: {
|
||||||
position: number;
|
|
||||||
objectMetadataItem: ObjectMetadataItem;
|
objectMetadataItem: ObjectMetadataItem;
|
||||||
}) => {
|
}) => {
|
||||||
const { addActionMenuEntry, removeActionMenuEntry } = useActionMenuEntries();
|
const { addActionMenuEntry, removeActionMenuEntry } = useActionMenuEntries();
|
||||||
@ -106,7 +104,11 @@ export const useDeleteMultipleRecordsAction = ({
|
|||||||
const { isInRightDrawer, onActionExecutedCallback } =
|
const { isInRightDrawer, onActionExecutedCallback } =
|
||||||
useContext(ActionMenuContext);
|
useContext(ActionMenuContext);
|
||||||
|
|
||||||
const registerDeleteMultipleRecordsAction = () => {
|
const registerDeleteMultipleRecordsAction = ({
|
||||||
|
position,
|
||||||
|
}: {
|
||||||
|
position: number;
|
||||||
|
}) => {
|
||||||
if (canDelete) {
|
if (canDelete) {
|
||||||
addActionMenuEntry({
|
addActionMenuEntry({
|
||||||
type: ActionMenuEntryType.Standard,
|
type: ActionMenuEntryType.Standard,
|
||||||
|
|||||||
@ -12,10 +12,8 @@ import {
|
|||||||
} from '@/object-record/record-index/export/hooks/useExportRecords';
|
} from '@/object-record/record-index/export/hooks/useExportRecords';
|
||||||
|
|
||||||
export const useExportMultipleRecordsAction = ({
|
export const useExportMultipleRecordsAction = ({
|
||||||
position,
|
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
}: {
|
}: {
|
||||||
position: number;
|
|
||||||
objectMetadataItem: ObjectMetadataItem;
|
objectMetadataItem: ObjectMetadataItem;
|
||||||
}) => {
|
}) => {
|
||||||
const { addActionMenuEntry, removeActionMenuEntry } = useActionMenuEntries();
|
const { addActionMenuEntry, removeActionMenuEntry } = useActionMenuEntries();
|
||||||
@ -27,7 +25,11 @@ export const useExportMultipleRecordsAction = ({
|
|||||||
filename: `${objectMetadataItem.nameSingular}.csv`,
|
filename: `${objectMetadataItem.nameSingular}.csv`,
|
||||||
});
|
});
|
||||||
|
|
||||||
const registerExportMultipleRecordsAction = () => {
|
const registerExportMultipleRecordsAction = ({
|
||||||
|
position,
|
||||||
|
}: {
|
||||||
|
position: number;
|
||||||
|
}) => {
|
||||||
addActionMenuEntry({
|
addActionMenuEntry({
|
||||||
type: ActionMenuEntryType.Standard,
|
type: ActionMenuEntryType.Standard,
|
||||||
scope: ActionMenuEntryScope.RecordSelection,
|
scope: ActionMenuEntryScope.RecordSelection,
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { useDeleteMultipleRecordsAction } from '@/action-menu/actions/record-actions/multiple-records/hooks/useDeleteMultipleRecordsAction';
|
import { useDeleteMultipleRecordsAction } from '@/action-menu/actions/record-actions/multiple-records/hooks/useDeleteMultipleRecordsAction';
|
||||||
import { useExportViewNoSelectionRecordAction } from '@/action-menu/actions/record-actions/no-selection/hooks/useExportMultipleRecordsAction';
|
import { useExportMultipleRecordsAction } from '@/action-menu/actions/record-actions/multiple-records/hooks/useExportMultipleRecordsAction';
|
||||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||||
|
|
||||||
export const useMultipleRecordsActions = ({
|
export const useMultipleRecordsActions = ({
|
||||||
@ -11,26 +11,24 @@ export const useMultipleRecordsActions = ({
|
|||||||
registerDeleteMultipleRecordsAction,
|
registerDeleteMultipleRecordsAction,
|
||||||
unregisterDeleteMultipleRecordsAction,
|
unregisterDeleteMultipleRecordsAction,
|
||||||
} = useDeleteMultipleRecordsAction({
|
} = useDeleteMultipleRecordsAction({
|
||||||
position: 0,
|
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
});
|
});
|
||||||
|
|
||||||
const {
|
const {
|
||||||
registerExportViewNoSelectionRecordsAction,
|
registerExportMultipleRecordsAction,
|
||||||
unregisterExportViewNoSelectionRecordsAction,
|
unregisterExportMultipleRecordsAction,
|
||||||
} = useExportViewNoSelectionRecordAction({
|
} = useExportMultipleRecordsAction({
|
||||||
position: 1,
|
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
});
|
});
|
||||||
|
|
||||||
const registerMultipleRecordsActions = () => {
|
const registerMultipleRecordsActions = () => {
|
||||||
registerDeleteMultipleRecordsAction();
|
registerDeleteMultipleRecordsAction({ position: 1 });
|
||||||
registerExportViewNoSelectionRecordsAction();
|
registerExportMultipleRecordsAction({ position: 2 });
|
||||||
};
|
};
|
||||||
|
|
||||||
const unregisterMultipleRecordsActions = () => {
|
const unregisterMultipleRecordsActions = () => {
|
||||||
unregisterDeleteMultipleRecordsAction();
|
unregisterDeleteMultipleRecordsAction();
|
||||||
unregisterExportViewNoSelectionRecordsAction();
|
unregisterExportMultipleRecordsAction();
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -0,0 +1,108 @@
|
|||||||
|
import { actionMenuEntriesComponentState } from '@/action-menu/states/actionMenuEntriesComponentState';
|
||||||
|
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
|
||||||
|
import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
|
||||||
|
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||||
|
import { expect } from '@storybook/test';
|
||||||
|
import { renderHook } from '@testing-library/react';
|
||||||
|
import { act } from 'react';
|
||||||
|
import { JestObjectMetadataItemSetter } from '~/testing/jest/JestObjectMetadataItemSetter';
|
||||||
|
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
|
||||||
|
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
|
||||||
|
|
||||||
|
import { useExportViewNoSelectionRecordAction } from '@/action-menu/actions/record-actions/no-selection/hooks/useExportViewNoSelectionRecordAction';
|
||||||
|
const companyMockObjectMetadataItem = generatedMockObjectMetadataItems.find(
|
||||||
|
(item) => item.nameSingular === 'company',
|
||||||
|
)!;
|
||||||
|
|
||||||
|
const JestMetadataAndApolloMocksWrapper = getJestMetadataAndApolloMocksWrapper({
|
||||||
|
apolloMocks: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('useExportViewNoSelectionRecordAction', () => {
|
||||||
|
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||||
|
<JestMetadataAndApolloMocksWrapper>
|
||||||
|
<JestObjectMetadataItemSetter>
|
||||||
|
<ContextStoreComponentInstanceContext.Provider
|
||||||
|
value={{
|
||||||
|
instanceId: '1',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ActionMenuComponentInstanceContext.Provider
|
||||||
|
value={{
|
||||||
|
instanceId: '1',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</ActionMenuComponentInstanceContext.Provider>
|
||||||
|
</ContextStoreComponentInstanceContext.Provider>
|
||||||
|
</JestObjectMetadataItemSetter>
|
||||||
|
</JestMetadataAndApolloMocksWrapper>
|
||||||
|
);
|
||||||
|
|
||||||
|
it('should register export view action', () => {
|
||||||
|
const { result } = renderHook(
|
||||||
|
() => {
|
||||||
|
const actionMenuEntries = useRecoilComponentValueV2(
|
||||||
|
actionMenuEntriesComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
actionMenuEntries,
|
||||||
|
useExportViewNoSelectionRecordAction:
|
||||||
|
useExportViewNoSelectionRecordAction({
|
||||||
|
objectMetadataItem: companyMockObjectMetadataItem,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
{ wrapper },
|
||||||
|
);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.useExportViewNoSelectionRecordAction.registerExportViewNoSelectionRecordsAction(
|
||||||
|
{ position: 1 },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.current.actionMenuEntries.size).toBe(1);
|
||||||
|
expect(
|
||||||
|
result.current.actionMenuEntries.get('export-view-no-selection'),
|
||||||
|
).toBeDefined();
|
||||||
|
expect(
|
||||||
|
result.current.actionMenuEntries.get('export-view-no-selection')
|
||||||
|
?.position,
|
||||||
|
).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should unregister export view action', () => {
|
||||||
|
const { result } = renderHook(
|
||||||
|
() => {
|
||||||
|
const actionMenuEntries = useRecoilComponentValueV2(
|
||||||
|
actionMenuEntriesComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
actionMenuEntries,
|
||||||
|
useExportViewNoSelectionRecordAction:
|
||||||
|
useExportViewNoSelectionRecordAction({
|
||||||
|
objectMetadataItem: companyMockObjectMetadataItem,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
{ wrapper },
|
||||||
|
);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.useExportViewNoSelectionRecordAction.registerExportViewNoSelectionRecordsAction(
|
||||||
|
{ position: 1 },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.current.actionMenuEntries.size).toBe(1);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.useExportViewNoSelectionRecordAction.unregisterExportViewNoSelectionRecordsAction();
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.current.actionMenuEntries.size).toBe(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -12,10 +12,8 @@ import {
|
|||||||
} from '@/object-record/record-index/export/hooks/useExportRecords';
|
} from '@/object-record/record-index/export/hooks/useExportRecords';
|
||||||
|
|
||||||
export const useExportViewNoSelectionRecordAction = ({
|
export const useExportViewNoSelectionRecordAction = ({
|
||||||
position,
|
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
}: {
|
}: {
|
||||||
position: number;
|
|
||||||
objectMetadataItem: ObjectMetadataItem;
|
objectMetadataItem: ObjectMetadataItem;
|
||||||
}) => {
|
}) => {
|
||||||
const { addActionMenuEntry, removeActionMenuEntry } = useActionMenuEntries();
|
const { addActionMenuEntry, removeActionMenuEntry } = useActionMenuEntries();
|
||||||
@ -27,7 +25,11 @@ export const useExportViewNoSelectionRecordAction = ({
|
|||||||
filename: `${objectMetadataItem.nameSingular}.csv`,
|
filename: `${objectMetadataItem.nameSingular}.csv`,
|
||||||
});
|
});
|
||||||
|
|
||||||
const registerExportViewNoSelectionRecordsAction = () => {
|
const registerExportViewNoSelectionRecordsAction = ({
|
||||||
|
position,
|
||||||
|
}: {
|
||||||
|
position: number;
|
||||||
|
}) => {
|
||||||
addActionMenuEntry({
|
addActionMenuEntry({
|
||||||
type: ActionMenuEntryType.Standard,
|
type: ActionMenuEntryType.Standard,
|
||||||
scope: ActionMenuEntryScope.Global,
|
scope: ActionMenuEntryScope.Global,
|
||||||
@ -1,4 +1,4 @@
|
|||||||
import { useExportViewNoSelectionRecordAction } from '@/action-menu/actions/record-actions/no-selection/hooks/useExportMultipleRecordsAction';
|
import { useExportViewNoSelectionRecordAction } from '@/action-menu/actions/record-actions/no-selection/hooks/useExportViewNoSelectionRecordAction';
|
||||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||||
|
|
||||||
export const useNoSelectionRecordActions = ({
|
export const useNoSelectionRecordActions = ({
|
||||||
@ -10,12 +10,11 @@ export const useNoSelectionRecordActions = ({
|
|||||||
registerExportViewNoSelectionRecordsAction,
|
registerExportViewNoSelectionRecordsAction,
|
||||||
unregisterExportViewNoSelectionRecordsAction,
|
unregisterExportViewNoSelectionRecordsAction,
|
||||||
} = useExportViewNoSelectionRecordAction({
|
} = useExportViewNoSelectionRecordAction({
|
||||||
position: 0,
|
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
});
|
});
|
||||||
|
|
||||||
const registerNoSelectionRecordActions = () => {
|
const registerNoSelectionRecordActions = () => {
|
||||||
registerExportViewNoSelectionRecordsAction();
|
registerExportViewNoSelectionRecordsAction({ position: 1 });
|
||||||
};
|
};
|
||||||
|
|
||||||
const unregisterNoSelectionRecordActions = () => {
|
const unregisterNoSelectionRecordActions = () => {
|
||||||
|
|||||||
@ -0,0 +1,26 @@
|
|||||||
|
import { SingleRecordActionMenuEntrySetterEffect } from '@/action-menu/actions/record-actions/single-record/components/SingleRecordActionMenuEntrySetterEffect';
|
||||||
|
import { WorkflowSingleRecordActionMenuEntrySetterEffect } from '@/action-menu/actions/record-actions/single-record/workflow-actions/components/WorkflowSingleRecordActionMenuEntrySetterEffect';
|
||||||
|
import { WorkflowVersionsSingleRecordActionMenuEntrySetterEffect } from '@/action-menu/actions/record-actions/single-record/workflow-version-actions/components/WorkflowVersionsSingleRecordActionMenuEntrySetterEffect';
|
||||||
|
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||||
|
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||||
|
|
||||||
|
export const SingleRecordActionMenuEntrySetter = ({
|
||||||
|
objectMetadataItem,
|
||||||
|
}: {
|
||||||
|
objectMetadataItem: ObjectMetadataItem;
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<SingleRecordActionMenuEntrySetterEffect
|
||||||
|
objectMetadataItem={objectMetadataItem}
|
||||||
|
/>
|
||||||
|
{objectMetadataItem.nameSingular === CoreObjectNameSingular.Workflow && (
|
||||||
|
<WorkflowSingleRecordActionMenuEntrySetterEffect />
|
||||||
|
)}
|
||||||
|
{objectMetadataItem.nameSingular ===
|
||||||
|
CoreObjectNameSingular.WorkflowVersion && (
|
||||||
|
<WorkflowVersionsSingleRecordActionMenuEntrySetterEffect />
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -0,0 +1 @@
|
|||||||
|
export const NUMBER_OF_STANDARD_SINGLE_RECORD_ACTIONS_ON_ALL_OBJECTS = 2;
|
||||||
@ -0,0 +1,121 @@
|
|||||||
|
import { actionMenuEntriesComponentState } from '@/action-menu/states/actionMenuEntriesComponentState';
|
||||||
|
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
|
||||||
|
import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
|
||||||
|
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||||
|
import { expect } from '@storybook/test';
|
||||||
|
import { renderHook } from '@testing-library/react';
|
||||||
|
import { act } from 'react';
|
||||||
|
import { RecoilRoot } from 'recoil';
|
||||||
|
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
|
||||||
|
import { useDeleteSingleRecordAction } from '../useDeleteSingleRecordAction';
|
||||||
|
|
||||||
|
jest.mock('@/object-record/hooks/useDeleteOneRecord', () => ({
|
||||||
|
useDeleteOneRecord: () => ({
|
||||||
|
deleteOneRecord: jest.fn(),
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
jest.mock('@/favorites/hooks/useDeleteFavorite', () => ({
|
||||||
|
useDeleteFavorite: () => ({
|
||||||
|
deleteFavorite: jest.fn(),
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
jest.mock('@/favorites/hooks/useFavorites', () => ({
|
||||||
|
useFavorites: () => ({
|
||||||
|
sortedFavorites: [],
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
jest.mock('@/object-record/record-table/hooks/useRecordTable', () => ({
|
||||||
|
useRecordTable: () => ({
|
||||||
|
resetTableRowSelection: jest.fn(),
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const companyMockObjectMetadataItem = generatedMockObjectMetadataItems.find(
|
||||||
|
(item) => item.nameSingular === 'company',
|
||||||
|
)!;
|
||||||
|
|
||||||
|
describe('useDeleteSingleRecordAction', () => {
|
||||||
|
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||||
|
<RecoilRoot>
|
||||||
|
<ContextStoreComponentInstanceContext.Provider
|
||||||
|
value={{
|
||||||
|
instanceId: '1',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ActionMenuComponentInstanceContext.Provider
|
||||||
|
value={{
|
||||||
|
instanceId: '1',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</ActionMenuComponentInstanceContext.Provider>
|
||||||
|
</ContextStoreComponentInstanceContext.Provider>
|
||||||
|
</RecoilRoot>
|
||||||
|
);
|
||||||
|
|
||||||
|
it('should register delete action', () => {
|
||||||
|
const { result } = renderHook(
|
||||||
|
() => {
|
||||||
|
const actionMenuEntries = useRecoilComponentValueV2(
|
||||||
|
actionMenuEntriesComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
actionMenuEntries,
|
||||||
|
useDeleteSingleRecordAction: useDeleteSingleRecordAction({
|
||||||
|
recordId: 'record1',
|
||||||
|
objectMetadataItem: companyMockObjectMetadataItem,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
{ wrapper },
|
||||||
|
);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.useDeleteSingleRecordAction.registerDeleteSingleRecordAction(
|
||||||
|
{ position: 1 },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.current.actionMenuEntries.size).toBe(1);
|
||||||
|
expect(
|
||||||
|
result.current.actionMenuEntries.get('delete-single-record'),
|
||||||
|
).toBeDefined();
|
||||||
|
expect(
|
||||||
|
result.current.actionMenuEntries.get('delete-single-record')?.position,
|
||||||
|
).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should unregister delete action', () => {
|
||||||
|
const { result } = renderHook(
|
||||||
|
() => {
|
||||||
|
const actionMenuEntries = useRecoilComponentValueV2(
|
||||||
|
actionMenuEntriesComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
actionMenuEntries,
|
||||||
|
useDeleteSingleRecordAction: useDeleteSingleRecordAction({
|
||||||
|
recordId: 'record1',
|
||||||
|
objectMetadataItem: companyMockObjectMetadataItem,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
{ wrapper },
|
||||||
|
);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.useDeleteSingleRecordAction.registerDeleteSingleRecordAction(
|
||||||
|
{ position: 1 },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.current.actionMenuEntries.size).toBe(1);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.useDeleteSingleRecordAction.unregisterDeleteSingleRecordAction();
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.current.actionMenuEntries.size).toBe(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,108 @@
|
|||||||
|
import { actionMenuEntriesComponentState } from '@/action-menu/states/actionMenuEntriesComponentState';
|
||||||
|
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
|
||||||
|
import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
|
||||||
|
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||||
|
import { expect } from '@storybook/test';
|
||||||
|
import { renderHook } from '@testing-library/react';
|
||||||
|
import { act } from 'react';
|
||||||
|
import { JestObjectMetadataItemSetter } from '~/testing/jest/JestObjectMetadataItemSetter';
|
||||||
|
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
|
||||||
|
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
|
||||||
|
import { useManageFavoritesSingleRecordAction } from '../useManageFavoritesSingleRecordAction';
|
||||||
|
|
||||||
|
const companyMockObjectMetadataItem = generatedMockObjectMetadataItems.find(
|
||||||
|
(item) => item.nameSingular === 'company',
|
||||||
|
)!;
|
||||||
|
|
||||||
|
const JestMetadataAndApolloMocksWrapper = getJestMetadataAndApolloMocksWrapper({
|
||||||
|
apolloMocks: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('useManageFavoritesSingleRecordAction', () => {
|
||||||
|
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||||
|
<JestMetadataAndApolloMocksWrapper>
|
||||||
|
<JestObjectMetadataItemSetter>
|
||||||
|
<ContextStoreComponentInstanceContext.Provider
|
||||||
|
value={{
|
||||||
|
instanceId: '1',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ActionMenuComponentInstanceContext.Provider
|
||||||
|
value={{
|
||||||
|
instanceId: '1',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</ActionMenuComponentInstanceContext.Provider>
|
||||||
|
</ContextStoreComponentInstanceContext.Provider>
|
||||||
|
</JestObjectMetadataItemSetter>
|
||||||
|
</JestMetadataAndApolloMocksWrapper>
|
||||||
|
);
|
||||||
|
|
||||||
|
it('should register manage favorites action', () => {
|
||||||
|
const { result } = renderHook(
|
||||||
|
() => {
|
||||||
|
const actionMenuEntries = useRecoilComponentValueV2(
|
||||||
|
actionMenuEntriesComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
actionMenuEntries,
|
||||||
|
useManageFavoritesSingleRecordAction:
|
||||||
|
useManageFavoritesSingleRecordAction({
|
||||||
|
recordId: 'record1',
|
||||||
|
objectMetadataItem: companyMockObjectMetadataItem,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
{ wrapper },
|
||||||
|
);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.useManageFavoritesSingleRecordAction.registerManageFavoritesSingleRecordAction(
|
||||||
|
{ position: 1 },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.current.actionMenuEntries.size).toBe(1);
|
||||||
|
expect(
|
||||||
|
result.current.actionMenuEntries.get('manage-favorites-single-record'),
|
||||||
|
).toBeDefined();
|
||||||
|
expect(
|
||||||
|
result.current.actionMenuEntries.get('manage-favorites-single-record')
|
||||||
|
?.position,
|
||||||
|
).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should unregister manage favorites action', () => {
|
||||||
|
const { result } = renderHook(
|
||||||
|
() => {
|
||||||
|
const actionMenuEntries = useRecoilComponentValueV2(
|
||||||
|
actionMenuEntriesComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
actionMenuEntries,
|
||||||
|
useManageFavoritesSingleRecordAction:
|
||||||
|
useManageFavoritesSingleRecordAction({
|
||||||
|
recordId: 'record1',
|
||||||
|
objectMetadataItem: companyMockObjectMetadataItem,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
{ wrapper },
|
||||||
|
);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.useManageFavoritesSingleRecordAction.registerManageFavoritesSingleRecordAction(
|
||||||
|
{ position: 1 },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.useManageFavoritesSingleRecordAction.unregisterManageFavoritesSingleRecordAction();
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.current.actionMenuEntries.size).toBe(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -4,7 +4,6 @@ import {
|
|||||||
ActionMenuEntryScope,
|
ActionMenuEntryScope,
|
||||||
ActionMenuEntryType,
|
ActionMenuEntryType,
|
||||||
} from '@/action-menu/types/ActionMenuEntry';
|
} from '@/action-menu/types/ActionMenuEntry';
|
||||||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
|
||||||
import { useDeleteFavorite } from '@/favorites/hooks/useDeleteFavorite';
|
import { useDeleteFavorite } from '@/favorites/hooks/useDeleteFavorite';
|
||||||
import { useFavorites } from '@/favorites/hooks/useFavorites';
|
import { useFavorites } from '@/favorites/hooks/useFavorites';
|
||||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||||
@ -12,15 +11,14 @@ import { useDeleteOneRecord } from '@/object-record/hooks/useDeleteOneRecord';
|
|||||||
import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable';
|
import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable';
|
||||||
import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal';
|
import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal';
|
||||||
import { useRightDrawer } from '@/ui/layout/right-drawer/hooks/useRightDrawer';
|
import { useRightDrawer } from '@/ui/layout/right-drawer/hooks/useRightDrawer';
|
||||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
|
||||||
import { useCallback, useContext, useState } from 'react';
|
import { useCallback, useContext, useState } from 'react';
|
||||||
import { IconTrash, isDefined } from 'twenty-ui';
|
import { IconTrash, isDefined } from 'twenty-ui';
|
||||||
|
|
||||||
export const useDeleteSingleRecordAction = ({
|
export const useDeleteSingleRecordAction = ({
|
||||||
position,
|
recordId,
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
}: {
|
}: {
|
||||||
position: number;
|
recordId: string;
|
||||||
objectMetadataItem: ObjectMetadataItem;
|
objectMetadataItem: ObjectMetadataItem;
|
||||||
}) => {
|
}) => {
|
||||||
const { addActionMenuEntry, removeActionMenuEntry } = useActionMenuEntries();
|
const { addActionMenuEntry, removeActionMenuEntry } = useActionMenuEntries();
|
||||||
@ -39,39 +37,26 @@ export const useDeleteSingleRecordAction = ({
|
|||||||
const { sortedFavorites: favorites } = useFavorites();
|
const { sortedFavorites: favorites } = useFavorites();
|
||||||
const { deleteFavorite } = useDeleteFavorite();
|
const { deleteFavorite } = useDeleteFavorite();
|
||||||
|
|
||||||
const contextStoreTargetedRecordsRule = useRecoilComponentValueV2(
|
|
||||||
contextStoreTargetedRecordsRuleComponentState,
|
|
||||||
);
|
|
||||||
|
|
||||||
const { closeRightDrawer } = useRightDrawer();
|
const { closeRightDrawer } = useRightDrawer();
|
||||||
|
|
||||||
const recordIdToDelete =
|
|
||||||
contextStoreTargetedRecordsRule.mode === 'selection'
|
|
||||||
? contextStoreTargetedRecordsRule.selectedRecordIds?.[0]
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
const handleDeleteClick = useCallback(async () => {
|
const handleDeleteClick = useCallback(async () => {
|
||||||
if (!isDefined(recordIdToDelete)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
resetTableRowSelection();
|
resetTableRowSelection();
|
||||||
|
|
||||||
const foundFavorite = favorites?.find(
|
const foundFavorite = favorites?.find(
|
||||||
(favorite) => favorite.recordId === recordIdToDelete,
|
(favorite) => favorite.recordId === recordId,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (isDefined(foundFavorite)) {
|
if (isDefined(foundFavorite)) {
|
||||||
deleteFavorite(foundFavorite.id);
|
deleteFavorite(foundFavorite.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
await deleteOneRecord(recordIdToDelete);
|
await deleteOneRecord(recordId);
|
||||||
}, [
|
}, [
|
||||||
deleteFavorite,
|
deleteFavorite,
|
||||||
deleteOneRecord,
|
deleteOneRecord,
|
||||||
favorites,
|
favorites,
|
||||||
recordIdToDelete,
|
|
||||||
resetTableRowSelection,
|
resetTableRowSelection,
|
||||||
|
recordId,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const isRemoteObject = objectMetadataItem.isRemote;
|
const isRemoteObject = objectMetadataItem.isRemote;
|
||||||
@ -79,8 +64,12 @@ export const useDeleteSingleRecordAction = ({
|
|||||||
const { isInRightDrawer, onActionExecutedCallback } =
|
const { isInRightDrawer, onActionExecutedCallback } =
|
||||||
useContext(ActionMenuContext);
|
useContext(ActionMenuContext);
|
||||||
|
|
||||||
const registerDeleteSingleRecordAction = () => {
|
const registerDeleteSingleRecordAction = ({
|
||||||
if (isRemoteObject || !isDefined(recordIdToDelete)) {
|
position,
|
||||||
|
}: {
|
||||||
|
position: number;
|
||||||
|
}) => {
|
||||||
|
if (isRemoteObject) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -3,51 +3,42 @@ import {
|
|||||||
ActionMenuEntryScope,
|
ActionMenuEntryScope,
|
||||||
ActionMenuEntryType,
|
ActionMenuEntryType,
|
||||||
} from '@/action-menu/types/ActionMenuEntry';
|
} from '@/action-menu/types/ActionMenuEntry';
|
||||||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
|
||||||
import { useCreateFavorite } from '@/favorites/hooks/useCreateFavorite';
|
import { useCreateFavorite } from '@/favorites/hooks/useCreateFavorite';
|
||||||
import { useDeleteFavorite } from '@/favorites/hooks/useDeleteFavorite';
|
import { useDeleteFavorite } from '@/favorites/hooks/useDeleteFavorite';
|
||||||
import { useFavorites } from '@/favorites/hooks/useFavorites';
|
import { useFavorites } from '@/favorites/hooks/useFavorites';
|
||||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
|
||||||
import { useRecoilValue } from 'recoil';
|
import { useRecoilValue } from 'recoil';
|
||||||
import { IconHeart, IconHeartOff, isDefined } from 'twenty-ui';
|
import { IconHeart, IconHeartOff, isDefined } from 'twenty-ui';
|
||||||
|
|
||||||
export const useManageFavoritesSingleRecordAction = ({
|
export const useManageFavoritesSingleRecordAction = ({
|
||||||
position,
|
recordId,
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
}: {
|
}: {
|
||||||
position: number;
|
recordId: string;
|
||||||
objectMetadataItem: ObjectMetadataItem;
|
objectMetadataItem: ObjectMetadataItem;
|
||||||
}) => {
|
}) => {
|
||||||
const { addActionMenuEntry, removeActionMenuEntry } = useActionMenuEntries();
|
const { addActionMenuEntry, removeActionMenuEntry } = useActionMenuEntries();
|
||||||
|
|
||||||
const contextStoreTargetedRecordsRule = useRecoilComponentValueV2(
|
|
||||||
contextStoreTargetedRecordsRuleComponentState,
|
|
||||||
);
|
|
||||||
|
|
||||||
const { sortedFavorites: favorites } = useFavorites();
|
const { sortedFavorites: favorites } = useFavorites();
|
||||||
|
|
||||||
const { createFavorite } = useCreateFavorite();
|
const { createFavorite } = useCreateFavorite();
|
||||||
|
|
||||||
const { deleteFavorite } = useDeleteFavorite();
|
const { deleteFavorite } = useDeleteFavorite();
|
||||||
|
|
||||||
const selectedRecordId =
|
const selectedRecord = useRecoilValue(recordStoreFamilyState(recordId));
|
||||||
contextStoreTargetedRecordsRule.mode === 'selection'
|
|
||||||
? contextStoreTargetedRecordsRule.selectedRecordIds[0]
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
const selectedRecord = useRecoilValue(
|
|
||||||
recordStoreFamilyState(selectedRecordId ?? ''),
|
|
||||||
);
|
|
||||||
|
|
||||||
const foundFavorite = favorites?.find(
|
const foundFavorite = favorites?.find(
|
||||||
(favorite) => favorite.recordId === selectedRecordId,
|
(favorite) => favorite.recordId === recordId,
|
||||||
);
|
);
|
||||||
|
|
||||||
const isFavorite = !!selectedRecordId && !!foundFavorite;
|
const isFavorite = !!foundFavorite;
|
||||||
|
|
||||||
const registerManageFavoritesSingleRecordAction = () => {
|
const registerManageFavoritesSingleRecordAction = ({
|
||||||
|
position,
|
||||||
|
}: {
|
||||||
|
position: number;
|
||||||
|
}) => {
|
||||||
if (!isDefined(objectMetadataItem) || objectMetadataItem.isRemote) {
|
if (!isDefined(objectMetadataItem) || objectMetadataItem.isRemote) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1,17 +1,33 @@
|
|||||||
import { useDeleteSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useDeleteSingleRecordAction';
|
import { useDeleteSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useDeleteSingleRecordAction';
|
||||||
import { useManageFavoritesSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useManageFavoritesSingleRecordAction';
|
import { useManageFavoritesSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useManageFavoritesSingleRecordAction';
|
||||||
|
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||||
|
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||||
|
import { isDefined } from 'twenty-ui';
|
||||||
|
|
||||||
export const useSingleRecordActions = ({
|
export const useSingleRecordActions = ({
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
}: {
|
}: {
|
||||||
objectMetadataItem: ObjectMetadataItem;
|
objectMetadataItem: ObjectMetadataItem;
|
||||||
}) => {
|
}) => {
|
||||||
|
const contextStoreTargetedRecordsRule = useRecoilComponentValueV2(
|
||||||
|
contextStoreTargetedRecordsRuleComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
|
const selectedRecordId =
|
||||||
|
contextStoreTargetedRecordsRule.mode === 'selection'
|
||||||
|
? contextStoreTargetedRecordsRule.selectedRecordIds[0]
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
if (!isDefined(selectedRecordId)) {
|
||||||
|
throw new Error('Selected record ID is required');
|
||||||
|
}
|
||||||
|
|
||||||
const {
|
const {
|
||||||
registerManageFavoritesSingleRecordAction,
|
registerManageFavoritesSingleRecordAction,
|
||||||
unregisterManageFavoritesSingleRecordAction,
|
unregisterManageFavoritesSingleRecordAction,
|
||||||
} = useManageFavoritesSingleRecordAction({
|
} = useManageFavoritesSingleRecordAction({
|
||||||
position: 0,
|
recordId: selectedRecordId,
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -19,13 +35,13 @@ export const useSingleRecordActions = ({
|
|||||||
registerDeleteSingleRecordAction,
|
registerDeleteSingleRecordAction,
|
||||||
unregisterDeleteSingleRecordAction,
|
unregisterDeleteSingleRecordAction,
|
||||||
} = useDeleteSingleRecordAction({
|
} = useDeleteSingleRecordAction({
|
||||||
position: 1,
|
recordId: selectedRecordId,
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
});
|
});
|
||||||
|
|
||||||
const registerSingleRecordActions = () => {
|
const registerSingleRecordActions = () => {
|
||||||
registerManageFavoritesSingleRecordAction();
|
registerManageFavoritesSingleRecordAction({ position: 1 });
|
||||||
registerDeleteSingleRecordAction();
|
registerDeleteSingleRecordAction({ position: 2 });
|
||||||
};
|
};
|
||||||
|
|
||||||
const unregisterSingleRecordActions = () => {
|
const unregisterSingleRecordActions = () => {
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
import { NUMBER_OF_STANDARD_SINGLE_RECORD_ACTIONS_ON_ALL_OBJECTS } from '@/action-menu/actions/record-actions/single-record/constants/NumberOfStandardSingleRecordActionsOnAllObjects';
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
import { useWorkflowSingleRecordActions } from '../hooks/useWorkflowSingleRecordActions';
|
||||||
|
|
||||||
|
export const WorkflowSingleRecordActionMenuEntrySetterEffect = () => {
|
||||||
|
const { registerSingleRecordActions, unregisterSingleRecordActions } =
|
||||||
|
useWorkflowSingleRecordActions();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
registerSingleRecordActions({
|
||||||
|
startPosition:
|
||||||
|
NUMBER_OF_STANDARD_SINGLE_RECORD_ACTIONS_ON_ALL_OBJECTS + 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
unregisterSingleRecordActions();
|
||||||
|
};
|
||||||
|
}, [registerSingleRecordActions, unregisterSingleRecordActions]);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
@ -0,0 +1,123 @@
|
|||||||
|
import { actionMenuEntriesComponentState } from '@/action-menu/states/actionMenuEntriesComponentState';
|
||||||
|
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
|
||||||
|
import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
|
||||||
|
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||||
|
import { expect } from '@storybook/test';
|
||||||
|
import { renderHook } from '@testing-library/react';
|
||||||
|
import { act } from 'react';
|
||||||
|
import { JestObjectMetadataItemSetter } from '~/testing/jest/JestObjectMetadataItemSetter';
|
||||||
|
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
|
||||||
|
import { useActivateWorkflowDraftWorkflowSingleRecordAction } from '../useActivateWorkflowDraftWorkflowSingleRecordAction';
|
||||||
|
|
||||||
|
const JestMetadataAndApolloMocksWrapper = getJestMetadataAndApolloMocksWrapper({
|
||||||
|
apolloMocks: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
jest.mock('@/workflow/hooks/useWorkflowWithCurrentVersion', () => ({
|
||||||
|
useWorkflowWithCurrentVersion: () => ({
|
||||||
|
id: 'workflowId',
|
||||||
|
currentVersion: {
|
||||||
|
id: 'currentVersionId',
|
||||||
|
trigger: 'trigger',
|
||||||
|
status: 'DRAFT',
|
||||||
|
steps: [
|
||||||
|
{
|
||||||
|
id: 'stepId1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'stepId2',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('useActivateWorkflowDraftWorkflowSingleRecordAction', () => {
|
||||||
|
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||||
|
<JestMetadataAndApolloMocksWrapper>
|
||||||
|
<JestObjectMetadataItemSetter>
|
||||||
|
<ContextStoreComponentInstanceContext.Provider
|
||||||
|
value={{
|
||||||
|
instanceId: '1',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ActionMenuComponentInstanceContext.Provider
|
||||||
|
value={{
|
||||||
|
instanceId: '1',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</ActionMenuComponentInstanceContext.Provider>
|
||||||
|
</ContextStoreComponentInstanceContext.Provider>
|
||||||
|
</JestObjectMetadataItemSetter>
|
||||||
|
</JestMetadataAndApolloMocksWrapper>
|
||||||
|
);
|
||||||
|
|
||||||
|
it('should register activate workflow draft workflow action', () => {
|
||||||
|
const { result } = renderHook(
|
||||||
|
() => {
|
||||||
|
const actionMenuEntries = useRecoilComponentValueV2(
|
||||||
|
actionMenuEntriesComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
actionMenuEntries,
|
||||||
|
useActivateWorkflowDraftWorkflowSingleRecordAction:
|
||||||
|
useActivateWorkflowDraftWorkflowSingleRecordAction({
|
||||||
|
workflowId: 'workflowId',
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
{ wrapper },
|
||||||
|
);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.useActivateWorkflowDraftWorkflowSingleRecordAction.registerActivateWorkflowDraftWorkflowSingleRecordAction(
|
||||||
|
{ position: 1 },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.current.actionMenuEntries.size).toBe(1);
|
||||||
|
expect(
|
||||||
|
result.current.actionMenuEntries.get(
|
||||||
|
'activate-workflow-draft-single-record',
|
||||||
|
),
|
||||||
|
).toBeDefined();
|
||||||
|
expect(
|
||||||
|
result.current.actionMenuEntries.get(
|
||||||
|
'activate-workflow-draft-single-record',
|
||||||
|
)?.position,
|
||||||
|
).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should unregister activate workflow draft workflow action', () => {
|
||||||
|
const { result } = renderHook(
|
||||||
|
() => {
|
||||||
|
const actionMenuEntries = useRecoilComponentValueV2(
|
||||||
|
actionMenuEntriesComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
actionMenuEntries,
|
||||||
|
useActivateWorkflowDraftWorkflowSingleRecordAction:
|
||||||
|
useActivateWorkflowDraftWorkflowSingleRecordAction({
|
||||||
|
workflowId: 'workflow1',
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
{ wrapper },
|
||||||
|
);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.useActivateWorkflowDraftWorkflowSingleRecordAction.registerActivateWorkflowDraftWorkflowSingleRecordAction(
|
||||||
|
{ position: 1 },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.useActivateWorkflowDraftWorkflowSingleRecordAction.unregisterActivateWorkflowDraftWorkflowSingleRecordAction();
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.current.actionMenuEntries.size).toBe(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,124 @@
|
|||||||
|
import { actionMenuEntriesComponentState } from '@/action-menu/states/actionMenuEntriesComponentState';
|
||||||
|
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
|
||||||
|
import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
|
||||||
|
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||||
|
import { expect } from '@storybook/test';
|
||||||
|
import { renderHook } from '@testing-library/react';
|
||||||
|
import { act } from 'react';
|
||||||
|
import { JestObjectMetadataItemSetter } from '~/testing/jest/JestObjectMetadataItemSetter';
|
||||||
|
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
|
||||||
|
import { useActivateWorkflowLastPublishedVersionWorkflowSingleRecordAction } from '../useActivateWorkflowLastPublishedVersionWorkflowSingleRecordAction';
|
||||||
|
|
||||||
|
const JestMetadataAndApolloMocksWrapper = getJestMetadataAndApolloMocksWrapper({
|
||||||
|
apolloMocks: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
jest.mock('@/workflow/hooks/useWorkflowWithCurrentVersion', () => ({
|
||||||
|
useWorkflowWithCurrentVersion: () => ({
|
||||||
|
id: 'workflowId',
|
||||||
|
currentVersion: {
|
||||||
|
id: 'currentVersionId',
|
||||||
|
trigger: 'trigger',
|
||||||
|
status: 'DEACTIVATED',
|
||||||
|
steps: [
|
||||||
|
{
|
||||||
|
id: 'stepId1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'stepId2',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
lastPublishedVersionId: 'lastPublishedVersionId',
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('useActivateWorkflowLastPublishedVersionWorkflowSingleRecordAction', () => {
|
||||||
|
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||||
|
<JestMetadataAndApolloMocksWrapper>
|
||||||
|
<JestObjectMetadataItemSetter>
|
||||||
|
<ContextStoreComponentInstanceContext.Provider
|
||||||
|
value={{
|
||||||
|
instanceId: '1',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ActionMenuComponentInstanceContext.Provider
|
||||||
|
value={{
|
||||||
|
instanceId: '1',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</ActionMenuComponentInstanceContext.Provider>
|
||||||
|
</ContextStoreComponentInstanceContext.Provider>
|
||||||
|
</JestObjectMetadataItemSetter>
|
||||||
|
</JestMetadataAndApolloMocksWrapper>
|
||||||
|
);
|
||||||
|
|
||||||
|
it('should register activate workflow last published version workflow action', () => {
|
||||||
|
const { result } = renderHook(
|
||||||
|
() => {
|
||||||
|
const actionMenuEntries = useRecoilComponentValueV2(
|
||||||
|
actionMenuEntriesComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
actionMenuEntries,
|
||||||
|
useActivateWorkflowLastPublishedVersionWorkflowSingleRecordAction:
|
||||||
|
useActivateWorkflowLastPublishedVersionWorkflowSingleRecordAction({
|
||||||
|
workflowId: 'workflowId',
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
{ wrapper },
|
||||||
|
);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.useActivateWorkflowLastPublishedVersionWorkflowSingleRecordAction.registerActivateWorkflowLastPublishedVersionWorkflowSingleRecordAction(
|
||||||
|
{ position: 1 },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.current.actionMenuEntries.size).toBe(1);
|
||||||
|
expect(
|
||||||
|
result.current.actionMenuEntries.get(
|
||||||
|
'activate-workflow-last-published-version-single-record',
|
||||||
|
),
|
||||||
|
).toBeDefined();
|
||||||
|
expect(
|
||||||
|
result.current.actionMenuEntries.get(
|
||||||
|
'activate-workflow-last-published-version-single-record',
|
||||||
|
)?.position,
|
||||||
|
).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should unregister activate workflow last published version workflow action', () => {
|
||||||
|
const { result } = renderHook(
|
||||||
|
() => {
|
||||||
|
const actionMenuEntries = useRecoilComponentValueV2(
|
||||||
|
actionMenuEntriesComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
actionMenuEntries,
|
||||||
|
useActivateWorkflowLastPublishedVersionWorkflowSingleRecordAction:
|
||||||
|
useActivateWorkflowLastPublishedVersionWorkflowSingleRecordAction({
|
||||||
|
workflowId: 'workflow1',
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
{ wrapper },
|
||||||
|
);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.useActivateWorkflowLastPublishedVersionWorkflowSingleRecordAction.registerActivateWorkflowLastPublishedVersionWorkflowSingleRecordAction(
|
||||||
|
{ position: 1 },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.useActivateWorkflowLastPublishedVersionWorkflowSingleRecordAction.unregisterActivateWorkflowLastPublishedVersionWorkflowSingleRecordAction();
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.current.actionMenuEntries.size).toBe(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,113 @@
|
|||||||
|
import { actionMenuEntriesComponentState } from '@/action-menu/states/actionMenuEntriesComponentState';
|
||||||
|
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
|
||||||
|
import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
|
||||||
|
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||||
|
import { expect } from '@storybook/test';
|
||||||
|
import { renderHook } from '@testing-library/react';
|
||||||
|
import { act } from 'react';
|
||||||
|
import { JestObjectMetadataItemSetter } from '~/testing/jest/JestObjectMetadataItemSetter';
|
||||||
|
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
|
||||||
|
import { useDeactivateWorkflowWorkflowSingleRecordAction } from '../useDeactivateWorkflowWorkflowSingleRecordAction';
|
||||||
|
|
||||||
|
const JestMetadataAndApolloMocksWrapper = getJestMetadataAndApolloMocksWrapper({
|
||||||
|
apolloMocks: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
jest.mock('@/workflow/hooks/useWorkflowWithCurrentVersion', () => ({
|
||||||
|
useWorkflowWithCurrentVersion: () => ({
|
||||||
|
id: 'workflowId',
|
||||||
|
currentVersion: {
|
||||||
|
id: 'currentVersionId',
|
||||||
|
trigger: 'trigger',
|
||||||
|
status: 'ACTIVE',
|
||||||
|
},
|
||||||
|
lastPublishedVersionId: 'lastPublishedVersionId',
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('useDeactivateWorkflowWorkflowSingleRecordAction', () => {
|
||||||
|
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||||
|
<JestMetadataAndApolloMocksWrapper>
|
||||||
|
<JestObjectMetadataItemSetter>
|
||||||
|
<ContextStoreComponentInstanceContext.Provider
|
||||||
|
value={{
|
||||||
|
instanceId: '1',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ActionMenuComponentInstanceContext.Provider
|
||||||
|
value={{
|
||||||
|
instanceId: '1',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</ActionMenuComponentInstanceContext.Provider>
|
||||||
|
</ContextStoreComponentInstanceContext.Provider>
|
||||||
|
</JestObjectMetadataItemSetter>
|
||||||
|
</JestMetadataAndApolloMocksWrapper>
|
||||||
|
);
|
||||||
|
|
||||||
|
it('should register activate workflow last published version workflow action', () => {
|
||||||
|
const { result } = renderHook(
|
||||||
|
() => {
|
||||||
|
const actionMenuEntries = useRecoilComponentValueV2(
|
||||||
|
actionMenuEntriesComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
actionMenuEntries,
|
||||||
|
useDeactivateWorkflowWorkflowSingleRecordAction:
|
||||||
|
useDeactivateWorkflowWorkflowSingleRecordAction({
|
||||||
|
workflowId: 'workflowId',
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
{ wrapper },
|
||||||
|
);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.useDeactivateWorkflowWorkflowSingleRecordAction.registerDeactivateWorkflowWorkflowSingleRecordAction(
|
||||||
|
{ position: 1 },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.current.actionMenuEntries.size).toBe(1);
|
||||||
|
expect(
|
||||||
|
result.current.actionMenuEntries.get('deactivate-workflow-single-record'),
|
||||||
|
).toBeDefined();
|
||||||
|
expect(
|
||||||
|
result.current.actionMenuEntries.get('deactivate-workflow-single-record')
|
||||||
|
?.position,
|
||||||
|
).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should unregister deactivate workflow workflow action', () => {
|
||||||
|
const { result } = renderHook(
|
||||||
|
() => {
|
||||||
|
const actionMenuEntries = useRecoilComponentValueV2(
|
||||||
|
actionMenuEntriesComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
actionMenuEntries,
|
||||||
|
useDeactivateWorkflowWorkflowSingleRecordAction:
|
||||||
|
useDeactivateWorkflowWorkflowSingleRecordAction({
|
||||||
|
workflowId: 'workflow1',
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
{ wrapper },
|
||||||
|
);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.useDeactivateWorkflowWorkflowSingleRecordAction.registerDeactivateWorkflowWorkflowSingleRecordAction(
|
||||||
|
{ position: 1 },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.useDeactivateWorkflowWorkflowSingleRecordAction.unregisterDeactivateWorkflowWorkflowSingleRecordAction();
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.current.actionMenuEntries.size).toBe(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,128 @@
|
|||||||
|
import { actionMenuEntriesComponentState } from '@/action-menu/states/actionMenuEntriesComponentState';
|
||||||
|
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
|
||||||
|
import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
|
||||||
|
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||||
|
import { expect } from '@storybook/test';
|
||||||
|
import { renderHook } from '@testing-library/react';
|
||||||
|
import { act } from 'react';
|
||||||
|
import { JestObjectMetadataItemSetter } from '~/testing/jest/JestObjectMetadataItemSetter';
|
||||||
|
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
|
||||||
|
import { useDiscardDraftWorkflowSingleRecordAction } from '../useDiscardDraftWorkflowSingleRecordAction';
|
||||||
|
|
||||||
|
const JestMetadataAndApolloMocksWrapper = getJestMetadataAndApolloMocksWrapper({
|
||||||
|
apolloMocks: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
jest.mock('@/workflow/hooks/useWorkflowWithCurrentVersion', () => ({
|
||||||
|
useWorkflowWithCurrentVersion: () => ({
|
||||||
|
id: 'workflowId',
|
||||||
|
currentVersion: {
|
||||||
|
id: 'currentVersionId',
|
||||||
|
trigger: 'trigger',
|
||||||
|
status: 'DRAFT',
|
||||||
|
},
|
||||||
|
lastPublishedVersionId: 'lastPublishedVersionId',
|
||||||
|
versions: [
|
||||||
|
{
|
||||||
|
id: 'currentVersionId',
|
||||||
|
trigger: 'trigger',
|
||||||
|
status: 'DRAFT',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'lastPublishedVersionId',
|
||||||
|
trigger: 'trigger',
|
||||||
|
status: 'ACTIVE',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('useDiscardDraftWorkflowSingleRecordAction', () => {
|
||||||
|
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||||
|
<JestMetadataAndApolloMocksWrapper>
|
||||||
|
<JestObjectMetadataItemSetter>
|
||||||
|
<ContextStoreComponentInstanceContext.Provider
|
||||||
|
value={{
|
||||||
|
instanceId: '1',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ActionMenuComponentInstanceContext.Provider
|
||||||
|
value={{
|
||||||
|
instanceId: '1',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</ActionMenuComponentInstanceContext.Provider>
|
||||||
|
</ContextStoreComponentInstanceContext.Provider>
|
||||||
|
</JestObjectMetadataItemSetter>
|
||||||
|
</JestMetadataAndApolloMocksWrapper>
|
||||||
|
);
|
||||||
|
|
||||||
|
it('should register discard workflow draft workflow action', () => {
|
||||||
|
const { result } = renderHook(
|
||||||
|
() => {
|
||||||
|
const actionMenuEntries = useRecoilComponentValueV2(
|
||||||
|
actionMenuEntriesComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
actionMenuEntries,
|
||||||
|
useDiscardDraftWorkflowSingleRecordAction:
|
||||||
|
useDiscardDraftWorkflowSingleRecordAction({
|
||||||
|
workflowId: 'workflowId',
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
{ wrapper },
|
||||||
|
);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.useDiscardDraftWorkflowSingleRecordAction.registerDiscardDraftWorkflowSingleRecordAction(
|
||||||
|
{ position: 1 },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.current.actionMenuEntries.size).toBe(1);
|
||||||
|
expect(
|
||||||
|
result.current.actionMenuEntries.get(
|
||||||
|
'discard-workflow-draft-single-record',
|
||||||
|
),
|
||||||
|
).toBeDefined();
|
||||||
|
expect(
|
||||||
|
result.current.actionMenuEntries.get(
|
||||||
|
'discard-workflow-draft-single-record',
|
||||||
|
)?.position,
|
||||||
|
).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should unregister deactivate workflow workflow action', () => {
|
||||||
|
const { result } = renderHook(
|
||||||
|
() => {
|
||||||
|
const actionMenuEntries = useRecoilComponentValueV2(
|
||||||
|
actionMenuEntriesComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
actionMenuEntries,
|
||||||
|
useDiscardDraftWorkflowSingleRecordAction:
|
||||||
|
useDiscardDraftWorkflowSingleRecordAction({
|
||||||
|
workflowId: 'workflow1',
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
{ wrapper },
|
||||||
|
);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.useDiscardDraftWorkflowSingleRecordAction.registerDiscardDraftWorkflowSingleRecordAction(
|
||||||
|
{ position: 1 },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.useDiscardDraftWorkflowSingleRecordAction.unregisterDiscardDraftWorkflowSingleRecordAction();
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.current.actionMenuEntries.size).toBe(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,64 @@
|
|||||||
|
import { useActionMenuEntries } from '@/action-menu/hooks/useActionMenuEntries';
|
||||||
|
import {
|
||||||
|
ActionMenuEntryScope,
|
||||||
|
ActionMenuEntryType,
|
||||||
|
} from '@/action-menu/types/ActionMenuEntry';
|
||||||
|
import { useActivateWorkflowVersion } from '@/workflow/hooks/useActivateWorkflowVersion';
|
||||||
|
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||||
|
import { IconPower, isDefined } from 'twenty-ui';
|
||||||
|
|
||||||
|
export const useActivateWorkflowDraftWorkflowSingleRecordAction = ({
|
||||||
|
workflowId,
|
||||||
|
}: {
|
||||||
|
workflowId: string;
|
||||||
|
}) => {
|
||||||
|
const { addActionMenuEntry, removeActionMenuEntry } = useActionMenuEntries();
|
||||||
|
|
||||||
|
const { activateWorkflowVersion } = useActivateWorkflowVersion();
|
||||||
|
|
||||||
|
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(workflowId);
|
||||||
|
|
||||||
|
const registerActivateWorkflowDraftWorkflowSingleRecordAction = ({
|
||||||
|
position,
|
||||||
|
}: {
|
||||||
|
position: number;
|
||||||
|
}) => {
|
||||||
|
if (
|
||||||
|
!isDefined(workflowWithCurrentVersion?.currentVersion?.trigger) ||
|
||||||
|
!isDefined(workflowWithCurrentVersion.currentVersion?.steps)
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isDraft =
|
||||||
|
workflowWithCurrentVersion.currentVersion.status === 'DRAFT';
|
||||||
|
|
||||||
|
if (!isDraft) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
addActionMenuEntry({
|
||||||
|
key: 'activate-workflow-draft-single-record',
|
||||||
|
label: 'Activate Draft',
|
||||||
|
position,
|
||||||
|
Icon: IconPower,
|
||||||
|
type: ActionMenuEntryType.Standard,
|
||||||
|
scope: ActionMenuEntryScope.RecordSelection,
|
||||||
|
onClick: () => {
|
||||||
|
activateWorkflowVersion({
|
||||||
|
workflowVersionId: workflowWithCurrentVersion.currentVersion.id,
|
||||||
|
workflowId: workflowWithCurrentVersion.id,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const unregisterActivateWorkflowDraftWorkflowSingleRecordAction = () => {
|
||||||
|
removeActionMenuEntry('activate-workflow-draft-single-record');
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
registerActivateWorkflowDraftWorkflowSingleRecordAction,
|
||||||
|
unregisterActivateWorkflowDraftWorkflowSingleRecordAction,
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -0,0 +1,61 @@
|
|||||||
|
import { useActionMenuEntries } from '@/action-menu/hooks/useActionMenuEntries';
|
||||||
|
import {
|
||||||
|
ActionMenuEntryScope,
|
||||||
|
ActionMenuEntryType,
|
||||||
|
} from '@/action-menu/types/ActionMenuEntry';
|
||||||
|
import { useActivateWorkflowVersion } from '@/workflow/hooks/useActivateWorkflowVersion';
|
||||||
|
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||||
|
import { IconPower, isDefined } from 'twenty-ui';
|
||||||
|
|
||||||
|
export const useActivateWorkflowLastPublishedVersionWorkflowSingleRecordAction =
|
||||||
|
({ workflowId }: { workflowId: string }) => {
|
||||||
|
const { addActionMenuEntry, removeActionMenuEntry } =
|
||||||
|
useActionMenuEntries();
|
||||||
|
|
||||||
|
const { activateWorkflowVersion } = useActivateWorkflowVersion();
|
||||||
|
|
||||||
|
const workflowWithCurrentVersion =
|
||||||
|
useWorkflowWithCurrentVersion(workflowId);
|
||||||
|
|
||||||
|
const registerActivateWorkflowLastPublishedVersionWorkflowSingleRecordAction =
|
||||||
|
({ position }: { position: number }) => {
|
||||||
|
if (
|
||||||
|
!isDefined(workflowWithCurrentVersion) ||
|
||||||
|
!isDefined(workflowWithCurrentVersion.currentVersion.trigger) ||
|
||||||
|
!isDefined(workflowWithCurrentVersion.lastPublishedVersionId) ||
|
||||||
|
workflowWithCurrentVersion.currentVersion.status === 'ACTIVE' ||
|
||||||
|
!isDefined(workflowWithCurrentVersion.currentVersion?.steps) ||
|
||||||
|
workflowWithCurrentVersion.currentVersion?.steps.length === 0
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
addActionMenuEntry({
|
||||||
|
key: 'activate-workflow-last-published-version-single-record',
|
||||||
|
label: 'Activate last published version',
|
||||||
|
position,
|
||||||
|
Icon: IconPower,
|
||||||
|
type: ActionMenuEntryType.Standard,
|
||||||
|
scope: ActionMenuEntryScope.RecordSelection,
|
||||||
|
onClick: () => {
|
||||||
|
activateWorkflowVersion({
|
||||||
|
workflowVersionId:
|
||||||
|
workflowWithCurrentVersion.lastPublishedVersionId,
|
||||||
|
workflowId: workflowWithCurrentVersion.id,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const unregisterActivateWorkflowLastPublishedVersionWorkflowSingleRecordAction =
|
||||||
|
() => {
|
||||||
|
removeActionMenuEntry(
|
||||||
|
'activate-workflow-last-published-version-single-record',
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
registerActivateWorkflowLastPublishedVersionWorkflowSingleRecordAction,
|
||||||
|
unregisterActivateWorkflowLastPublishedVersionWorkflowSingleRecordAction,
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -0,0 +1,55 @@
|
|||||||
|
import { useActionMenuEntries } from '@/action-menu/hooks/useActionMenuEntries';
|
||||||
|
import {
|
||||||
|
ActionMenuEntryScope,
|
||||||
|
ActionMenuEntryType,
|
||||||
|
} from '@/action-menu/types/ActionMenuEntry';
|
||||||
|
import { useDeactivateWorkflowVersion } from '@/workflow/hooks/useDeactivateWorkflowVersion';
|
||||||
|
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||||
|
import { IconPlayerPause, isDefined } from 'twenty-ui';
|
||||||
|
|
||||||
|
export const useDeactivateWorkflowWorkflowSingleRecordAction = ({
|
||||||
|
workflowId,
|
||||||
|
}: {
|
||||||
|
workflowId: string;
|
||||||
|
}) => {
|
||||||
|
const { addActionMenuEntry, removeActionMenuEntry } = useActionMenuEntries();
|
||||||
|
|
||||||
|
const { deactivateWorkflowVersion } = useDeactivateWorkflowVersion();
|
||||||
|
|
||||||
|
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(workflowId);
|
||||||
|
|
||||||
|
const isWorkflowActive =
|
||||||
|
isDefined(workflowWithCurrentVersion) &&
|
||||||
|
workflowWithCurrentVersion.currentVersion.status === 'ACTIVE';
|
||||||
|
|
||||||
|
const registerDeactivateWorkflowWorkflowSingleRecordAction = ({
|
||||||
|
position,
|
||||||
|
}: {
|
||||||
|
position: number;
|
||||||
|
}) => {
|
||||||
|
if (!isDefined(workflowWithCurrentVersion) || !isWorkflowActive) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
addActionMenuEntry({
|
||||||
|
key: 'deactivate-workflow-single-record',
|
||||||
|
label: 'Deactivate Workflow',
|
||||||
|
position,
|
||||||
|
Icon: IconPlayerPause,
|
||||||
|
type: ActionMenuEntryType.Standard,
|
||||||
|
scope: ActionMenuEntryScope.RecordSelection,
|
||||||
|
onClick: () => {
|
||||||
|
deactivateWorkflowVersion(workflowWithCurrentVersion.currentVersion.id);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const unregisterDeactivateWorkflowWorkflowSingleRecordAction = () => {
|
||||||
|
removeActionMenuEntry('deactivate-workflow-single-record');
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
registerDeactivateWorkflowWorkflowSingleRecordAction,
|
||||||
|
unregisterDeactivateWorkflowWorkflowSingleRecordAction,
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -0,0 +1,63 @@
|
|||||||
|
import { useActionMenuEntries } from '@/action-menu/hooks/useActionMenuEntries';
|
||||||
|
import {
|
||||||
|
ActionMenuEntryScope,
|
||||||
|
ActionMenuEntryType,
|
||||||
|
} from '@/action-menu/types/ActionMenuEntry';
|
||||||
|
import { useDeleteOneWorkflowVersion } from '@/workflow/hooks/useDeleteOneWorkflowVersion';
|
||||||
|
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||||
|
import { IconTrash, isDefined } from 'twenty-ui';
|
||||||
|
|
||||||
|
export const useDiscardDraftWorkflowSingleRecordAction = ({
|
||||||
|
workflowId,
|
||||||
|
}: {
|
||||||
|
workflowId: string;
|
||||||
|
}) => {
|
||||||
|
const { addActionMenuEntry, removeActionMenuEntry } = useActionMenuEntries();
|
||||||
|
|
||||||
|
const { deleteOneWorkflowVersion } = useDeleteOneWorkflowVersion();
|
||||||
|
|
||||||
|
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(workflowId);
|
||||||
|
|
||||||
|
const registerDiscardDraftWorkflowSingleRecordAction = ({
|
||||||
|
position,
|
||||||
|
}: {
|
||||||
|
position: number;
|
||||||
|
}) => {
|
||||||
|
if (
|
||||||
|
!isDefined(workflowWithCurrentVersion) ||
|
||||||
|
workflowWithCurrentVersion.versions.length < 2
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isDraft =
|
||||||
|
workflowWithCurrentVersion.currentVersion.status === 'DRAFT';
|
||||||
|
|
||||||
|
if (!isDraft) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
addActionMenuEntry({
|
||||||
|
key: 'discard-workflow-draft-single-record',
|
||||||
|
label: 'Discard Draft',
|
||||||
|
position,
|
||||||
|
Icon: IconTrash,
|
||||||
|
type: ActionMenuEntryType.Standard,
|
||||||
|
scope: ActionMenuEntryScope.RecordSelection,
|
||||||
|
onClick: () => {
|
||||||
|
deleteOneWorkflowVersion({
|
||||||
|
workflowVersionId: workflowWithCurrentVersion.currentVersion.id,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const unregisterDiscardDraftWorkflowSingleRecordAction = () => {
|
||||||
|
removeActionMenuEntry('discard-workflow-draft-single-record');
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
registerDiscardDraftWorkflowSingleRecordAction,
|
||||||
|
unregisterDiscardDraftWorkflowSingleRecordAction,
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -0,0 +1,60 @@
|
|||||||
|
import { useActionMenuEntries } from '@/action-menu/hooks/useActionMenuEntries';
|
||||||
|
import {
|
||||||
|
ActionMenuEntryScope,
|
||||||
|
ActionMenuEntryType,
|
||||||
|
} from '@/action-menu/types/ActionMenuEntry';
|
||||||
|
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||||
|
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||||
|
import { useActiveWorkflowVersion } from '@/workflow/hooks/useActiveWorkflowVersion';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import { useRecoilValue } from 'recoil';
|
||||||
|
import { IconHistory, isDefined } from 'twenty-ui';
|
||||||
|
|
||||||
|
export const useSeeWorkflowActiveVersionWorkflowSingleRecordAction = ({
|
||||||
|
workflowId,
|
||||||
|
}: {
|
||||||
|
workflowId: string;
|
||||||
|
}) => {
|
||||||
|
const { addActionMenuEntry, removeActionMenuEntry } = useActionMenuEntries();
|
||||||
|
|
||||||
|
const workflow = useRecoilValue(recordStoreFamilyState(workflowId));
|
||||||
|
|
||||||
|
const isDraft = workflow?.statuses?.includes('DRAFT');
|
||||||
|
|
||||||
|
const workflowActiveVersion = useActiveWorkflowVersion(workflowId);
|
||||||
|
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const registerSeeWorkflowActiveVersionWorkflowSingleRecordAction = ({
|
||||||
|
position,
|
||||||
|
}: {
|
||||||
|
position: number;
|
||||||
|
}) => {
|
||||||
|
if (!isDefined(workflowActiveVersion) || !isDraft) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
addActionMenuEntry({
|
||||||
|
key: 'see-workflow-active-version-single-record',
|
||||||
|
label: 'See active version',
|
||||||
|
position,
|
||||||
|
type: ActionMenuEntryType.Standard,
|
||||||
|
scope: ActionMenuEntryScope.RecordSelection,
|
||||||
|
Icon: IconHistory,
|
||||||
|
onClick: () => {
|
||||||
|
navigate(
|
||||||
|
`/object/${CoreObjectNameSingular.WorkflowVersion}/${workflowActiveVersion.id}`,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const unregisterSeeWorkflowActiveVersionWorkflowSingleRecordAction = () => {
|
||||||
|
removeActionMenuEntry('see-workflow-active-version-single-record');
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
registerSeeWorkflowActiveVersionWorkflowSingleRecordAction,
|
||||||
|
unregisterSeeWorkflowActiveVersionWorkflowSingleRecordAction,
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -0,0 +1,66 @@
|
|||||||
|
import { useActionMenuEntries } from '@/action-menu/hooks/useActionMenuEntries';
|
||||||
|
import {
|
||||||
|
ActionMenuEntryScope,
|
||||||
|
ActionMenuEntryType,
|
||||||
|
} from '@/action-menu/types/ActionMenuEntry';
|
||||||
|
import { CoreObjectNamePlural } from '@/object-metadata/types/CoreObjectNamePlural';
|
||||||
|
import { FilterQueryParams } from '@/views/hooks/internal/useViewFromQueryParams';
|
||||||
|
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
|
||||||
|
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||||
|
import qs from 'qs';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import { IconHistoryToggle, isDefined } from 'twenty-ui';
|
||||||
|
|
||||||
|
export const useSeeWorkflowRunsWorkflowSingleRecordAction = ({
|
||||||
|
workflowId,
|
||||||
|
}: {
|
||||||
|
workflowId: string;
|
||||||
|
}) => {
|
||||||
|
const { addActionMenuEntry, removeActionMenuEntry } = useActionMenuEntries();
|
||||||
|
|
||||||
|
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(workflowId);
|
||||||
|
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const registerSeeWorkflowRunsWorkflowSingleRecordAction = ({
|
||||||
|
position,
|
||||||
|
}: {
|
||||||
|
position: number;
|
||||||
|
}) => {
|
||||||
|
if (!isDefined(workflowWithCurrentVersion)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const filterQueryParams: FilterQueryParams = {
|
||||||
|
filter: {
|
||||||
|
workflow: {
|
||||||
|
[ViewFilterOperand.Is]: [workflowWithCurrentVersion.id],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const filterLinkHref = `/objects/${CoreObjectNamePlural.WorkflowRun}?${qs.stringify(
|
||||||
|
filterQueryParams,
|
||||||
|
)}`;
|
||||||
|
|
||||||
|
addActionMenuEntry({
|
||||||
|
key: 'see-workflow-runs-single-record',
|
||||||
|
label: 'See runs',
|
||||||
|
position,
|
||||||
|
type: ActionMenuEntryType.Standard,
|
||||||
|
scope: ActionMenuEntryScope.RecordSelection,
|
||||||
|
Icon: IconHistoryToggle,
|
||||||
|
onClick: () => {
|
||||||
|
navigate(filterLinkHref);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const unregisterSeeWorkflowRunsWorkflowSingleRecordAction = () => {
|
||||||
|
removeActionMenuEntry('see-workflow-runs-single-record');
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
registerSeeWorkflowRunsWorkflowSingleRecordAction,
|
||||||
|
unregisterSeeWorkflowRunsWorkflowSingleRecordAction,
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -0,0 +1,66 @@
|
|||||||
|
import { useActionMenuEntries } from '@/action-menu/hooks/useActionMenuEntries';
|
||||||
|
import {
|
||||||
|
ActionMenuEntryScope,
|
||||||
|
ActionMenuEntryType,
|
||||||
|
} from '@/action-menu/types/ActionMenuEntry';
|
||||||
|
import { CoreObjectNamePlural } from '@/object-metadata/types/CoreObjectNamePlural';
|
||||||
|
import { FilterQueryParams } from '@/views/hooks/internal/useViewFromQueryParams';
|
||||||
|
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
|
||||||
|
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||||
|
import qs from 'qs';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import { IconHistory, isDefined } from 'twenty-ui';
|
||||||
|
|
||||||
|
export const useSeeWorkflowVersionsHistoryWorkflowSingleRecordAction = ({
|
||||||
|
workflowId,
|
||||||
|
}: {
|
||||||
|
workflowId: string;
|
||||||
|
}) => {
|
||||||
|
const { addActionMenuEntry, removeActionMenuEntry } = useActionMenuEntries();
|
||||||
|
|
||||||
|
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(workflowId);
|
||||||
|
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const registerSeeWorkflowVersionsHistoryWorkflowSingleRecordAction = ({
|
||||||
|
position,
|
||||||
|
}: {
|
||||||
|
position: number;
|
||||||
|
}) => {
|
||||||
|
if (!isDefined(workflowWithCurrentVersion)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const filterQueryParams: FilterQueryParams = {
|
||||||
|
filter: {
|
||||||
|
workflow: {
|
||||||
|
[ViewFilterOperand.Is]: [workflowWithCurrentVersion.id],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const filterLinkHref = `/objects/${CoreObjectNamePlural.WorkflowVersion}?${qs.stringify(
|
||||||
|
filterQueryParams,
|
||||||
|
)}`;
|
||||||
|
|
||||||
|
addActionMenuEntry({
|
||||||
|
key: 'see-workflow-versions-history-single-record',
|
||||||
|
label: 'See versions history',
|
||||||
|
position,
|
||||||
|
type: ActionMenuEntryType.Standard,
|
||||||
|
scope: ActionMenuEntryScope.RecordSelection,
|
||||||
|
Icon: IconHistory,
|
||||||
|
onClick: () => {
|
||||||
|
navigate(filterLinkHref);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const unregisterSeeWorkflowVersionsHistoryWorkflowSingleRecordAction = () => {
|
||||||
|
removeActionMenuEntry('see-workflow-versions-history-single-record');
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
registerSeeWorkflowVersionsHistoryWorkflowSingleRecordAction,
|
||||||
|
unregisterSeeWorkflowVersionsHistoryWorkflowSingleRecordAction,
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -0,0 +1,60 @@
|
|||||||
|
import { useActionMenuEntries } from '@/action-menu/hooks/useActionMenuEntries';
|
||||||
|
import {
|
||||||
|
ActionMenuEntryScope,
|
||||||
|
ActionMenuEntryType,
|
||||||
|
} from '@/action-menu/types/ActionMenuEntry';
|
||||||
|
import { useRunWorkflowVersion } from '@/workflow/hooks/useRunWorkflowVersion';
|
||||||
|
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||||
|
import { IconPlayerPlay, isDefined } from 'twenty-ui';
|
||||||
|
|
||||||
|
export const useTestWorkflowWorkflowSingleRecordAction = ({
|
||||||
|
workflowId,
|
||||||
|
}: {
|
||||||
|
workflowId: string;
|
||||||
|
}) => {
|
||||||
|
const { addActionMenuEntry, removeActionMenuEntry } = useActionMenuEntries();
|
||||||
|
|
||||||
|
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(workflowId);
|
||||||
|
|
||||||
|
const { runWorkflowVersion } = useRunWorkflowVersion();
|
||||||
|
|
||||||
|
const registerTestWorkflowWorkflowSingleRecordAction = ({
|
||||||
|
position,
|
||||||
|
}: {
|
||||||
|
position: number;
|
||||||
|
}) => {
|
||||||
|
if (
|
||||||
|
!isDefined(workflowWithCurrentVersion?.currentVersion?.trigger) ||
|
||||||
|
workflowWithCurrentVersion.currentVersion.trigger.type !== 'MANUAL' ||
|
||||||
|
isDefined(
|
||||||
|
workflowWithCurrentVersion.currentVersion.trigger.settings.objectType,
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
addActionMenuEntry({
|
||||||
|
key: 'test-workflow-single-record',
|
||||||
|
label: 'Test workflow',
|
||||||
|
position,
|
||||||
|
type: ActionMenuEntryType.Standard,
|
||||||
|
scope: ActionMenuEntryScope.RecordSelection,
|
||||||
|
Icon: IconPlayerPlay,
|
||||||
|
onClick: () => {
|
||||||
|
runWorkflowVersion({
|
||||||
|
workflowVersionId: workflowWithCurrentVersion.currentVersion.id,
|
||||||
|
workflowName: workflowWithCurrentVersion.name,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const unregisterTestWorkflowWorkflowSingleRecordAction = () => {
|
||||||
|
removeActionMenuEntry('test-workflow-single-record');
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
registerTestWorkflowWorkflowSingleRecordAction,
|
||||||
|
unregisterTestWorkflowWorkflowSingleRecordAction,
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -0,0 +1,127 @@
|
|||||||
|
import { useActivateWorkflowDraftWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useActivateWorkflowDraftWorkflowSingleRecordAction';
|
||||||
|
import { useActivateWorkflowLastPublishedVersionWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useActivateWorkflowLastPublishedVersionWorkflowSingleRecordAction';
|
||||||
|
import { useDeactivateWorkflowWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useDeactivateWorkflowWorkflowSingleRecordAction';
|
||||||
|
import { useDiscardDraftWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useDiscardDraftWorkflowSingleRecordAction';
|
||||||
|
import { useSeeWorkflowActiveVersionWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useSeeWorkflowActiveVersionWorkflowSingleRecordAction';
|
||||||
|
import { useSeeWorkflowRunsWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useSeeWorkflowRunsWorkflowSingleRecordAction';
|
||||||
|
import { useSeeWorkflowVersionsHistoryWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useSeeWorkflowVersionsHistoryWorkflowSingleRecordAction';
|
||||||
|
import { useTestWorkflowWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useTestWorkflowWorkflowSingleRecordAction';
|
||||||
|
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||||
|
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||||
|
import { isDefined } from 'twenty-ui';
|
||||||
|
|
||||||
|
export const useWorkflowSingleRecordActions = () => {
|
||||||
|
const contextStoreTargetedRecordsRule = useRecoilComponentValueV2(
|
||||||
|
contextStoreTargetedRecordsRuleComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
|
const selectedRecordId =
|
||||||
|
contextStoreTargetedRecordsRule.mode === 'selection'
|
||||||
|
? contextStoreTargetedRecordsRule.selectedRecordIds?.[0]
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
if (!isDefined(selectedRecordId)) {
|
||||||
|
throw new Error('Selected record ID is required');
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
registerTestWorkflowWorkflowSingleRecordAction,
|
||||||
|
unregisterTestWorkflowWorkflowSingleRecordAction,
|
||||||
|
} = useTestWorkflowWorkflowSingleRecordAction({
|
||||||
|
workflowId: selectedRecordId,
|
||||||
|
});
|
||||||
|
|
||||||
|
const {
|
||||||
|
registerActivateWorkflowLastPublishedVersionWorkflowSingleRecordAction,
|
||||||
|
unregisterActivateWorkflowLastPublishedVersionWorkflowSingleRecordAction,
|
||||||
|
} = useActivateWorkflowLastPublishedVersionWorkflowSingleRecordAction({
|
||||||
|
workflowId: selectedRecordId,
|
||||||
|
});
|
||||||
|
|
||||||
|
const {
|
||||||
|
registerDeactivateWorkflowWorkflowSingleRecordAction,
|
||||||
|
unregisterDeactivateWorkflowWorkflowSingleRecordAction,
|
||||||
|
} = useDeactivateWorkflowWorkflowSingleRecordAction({
|
||||||
|
workflowId: selectedRecordId,
|
||||||
|
});
|
||||||
|
|
||||||
|
const {
|
||||||
|
registerSeeWorkflowRunsWorkflowSingleRecordAction,
|
||||||
|
unregisterSeeWorkflowRunsWorkflowSingleRecordAction,
|
||||||
|
} = useSeeWorkflowRunsWorkflowSingleRecordAction({
|
||||||
|
workflowId: selectedRecordId,
|
||||||
|
});
|
||||||
|
|
||||||
|
const {
|
||||||
|
registerSeeWorkflowVersionsHistoryWorkflowSingleRecordAction,
|
||||||
|
unregisterSeeWorkflowVersionsHistoryWorkflowSingleRecordAction,
|
||||||
|
} = useSeeWorkflowVersionsHistoryWorkflowSingleRecordAction({
|
||||||
|
workflowId: selectedRecordId,
|
||||||
|
});
|
||||||
|
|
||||||
|
const {
|
||||||
|
registerSeeWorkflowActiveVersionWorkflowSingleRecordAction,
|
||||||
|
unregisterSeeWorkflowActiveVersionWorkflowSingleRecordAction,
|
||||||
|
} = useSeeWorkflowActiveVersionWorkflowSingleRecordAction({
|
||||||
|
workflowId: selectedRecordId,
|
||||||
|
});
|
||||||
|
|
||||||
|
const {
|
||||||
|
registerActivateWorkflowDraftWorkflowSingleRecordAction,
|
||||||
|
unregisterActivateWorkflowDraftWorkflowSingleRecordAction,
|
||||||
|
} = useActivateWorkflowDraftWorkflowSingleRecordAction({
|
||||||
|
workflowId: selectedRecordId,
|
||||||
|
});
|
||||||
|
|
||||||
|
const {
|
||||||
|
registerDiscardDraftWorkflowSingleRecordAction,
|
||||||
|
unregisterDiscardDraftWorkflowSingleRecordAction,
|
||||||
|
} = useDiscardDraftWorkflowSingleRecordAction({
|
||||||
|
workflowId: selectedRecordId,
|
||||||
|
});
|
||||||
|
|
||||||
|
const registerSingleRecordActions = ({
|
||||||
|
startPosition,
|
||||||
|
}: {
|
||||||
|
startPosition: number;
|
||||||
|
}) => {
|
||||||
|
registerTestWorkflowWorkflowSingleRecordAction({ position: startPosition });
|
||||||
|
registerDiscardDraftWorkflowSingleRecordAction({
|
||||||
|
position: startPosition + 1,
|
||||||
|
});
|
||||||
|
registerActivateWorkflowDraftWorkflowSingleRecordAction({
|
||||||
|
position: startPosition + 2,
|
||||||
|
});
|
||||||
|
registerActivateWorkflowLastPublishedVersionWorkflowSingleRecordAction({
|
||||||
|
position: startPosition + 3,
|
||||||
|
});
|
||||||
|
registerDeactivateWorkflowWorkflowSingleRecordAction({
|
||||||
|
position: startPosition + 4,
|
||||||
|
});
|
||||||
|
registerSeeWorkflowRunsWorkflowSingleRecordAction({
|
||||||
|
position: startPosition + 5,
|
||||||
|
});
|
||||||
|
registerSeeWorkflowActiveVersionWorkflowSingleRecordAction({
|
||||||
|
position: startPosition + 6,
|
||||||
|
});
|
||||||
|
registerSeeWorkflowVersionsHistoryWorkflowSingleRecordAction({
|
||||||
|
position: startPosition + 7,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const unregisterSingleRecordActions = () => {
|
||||||
|
unregisterTestWorkflowWorkflowSingleRecordAction();
|
||||||
|
unregisterActivateWorkflowLastPublishedVersionWorkflowSingleRecordAction();
|
||||||
|
unregisterDiscardDraftWorkflowSingleRecordAction();
|
||||||
|
unregisterActivateWorkflowDraftWorkflowSingleRecordAction();
|
||||||
|
unregisterDeactivateWorkflowWorkflowSingleRecordAction();
|
||||||
|
unregisterSeeWorkflowRunsWorkflowSingleRecordAction();
|
||||||
|
unregisterSeeWorkflowActiveVersionWorkflowSingleRecordAction();
|
||||||
|
unregisterSeeWorkflowVersionsHistoryWorkflowSingleRecordAction();
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
registerSingleRecordActions,
|
||||||
|
unregisterSingleRecordActions,
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
import { NUMBER_OF_STANDARD_SINGLE_RECORD_ACTIONS_ON_ALL_OBJECTS } from '@/action-menu/actions/record-actions/single-record/constants/NumberOfStandardSingleRecordActionsOnAllObjects';
|
||||||
|
import { useWorkflowVersionsSingleRecordActions } from '@/action-menu/actions/record-actions/single-record/workflow-version-actions/hooks/useWorkflowVersionsSingleRecordActions';
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
|
||||||
|
export const WorkflowVersionsSingleRecordActionMenuEntrySetterEffect = () => {
|
||||||
|
const { registerSingleRecordActions, unregisterSingleRecordActions } =
|
||||||
|
useWorkflowVersionsSingleRecordActions();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
registerSingleRecordActions({
|
||||||
|
startPosition:
|
||||||
|
NUMBER_OF_STANDARD_SINGLE_RECORD_ACTIONS_ON_ALL_OBJECTS + 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
unregisterSingleRecordActions();
|
||||||
|
};
|
||||||
|
}, [registerSingleRecordActions, unregisterSingleRecordActions]);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
@ -0,0 +1,78 @@
|
|||||||
|
import { useActionMenuEntries } from '@/action-menu/hooks/useActionMenuEntries';
|
||||||
|
import {
|
||||||
|
ActionMenuEntryScope,
|
||||||
|
ActionMenuEntryType,
|
||||||
|
} from '@/action-menu/types/ActionMenuEntry';
|
||||||
|
import { CoreObjectNamePlural } from '@/object-metadata/types/CoreObjectNamePlural';
|
||||||
|
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||||
|
import { FilterQueryParams } from '@/views/hooks/internal/useViewFromQueryParams';
|
||||||
|
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
|
||||||
|
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||||
|
import qs from 'qs';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import { useRecoilValue } from 'recoil';
|
||||||
|
import { IconHistoryToggle, isDefined } from 'twenty-ui';
|
||||||
|
|
||||||
|
export const useSeeWorkflowExecutionsWorkflowVersionSingleRecordAction = ({
|
||||||
|
workflowVersionId,
|
||||||
|
}: {
|
||||||
|
workflowVersionId: string;
|
||||||
|
}) => {
|
||||||
|
const { addActionMenuEntry, removeActionMenuEntry } = useActionMenuEntries();
|
||||||
|
|
||||||
|
const workflowVersion = useRecoilValue(
|
||||||
|
recordStoreFamilyState(workflowVersionId),
|
||||||
|
);
|
||||||
|
|
||||||
|
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(
|
||||||
|
workflowVersion?.workflow.id,
|
||||||
|
);
|
||||||
|
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const registerSeeWorkflowExecutionsWorkflowVersionSingleRecordAction = ({
|
||||||
|
position,
|
||||||
|
}: {
|
||||||
|
position: number;
|
||||||
|
}) => {
|
||||||
|
if (!isDefined(workflowWithCurrentVersion)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const filterQueryParams: FilterQueryParams = {
|
||||||
|
filter: {
|
||||||
|
workflow: {
|
||||||
|
[ViewFilterOperand.Is]: [workflowWithCurrentVersion.id],
|
||||||
|
},
|
||||||
|
workflowVersion: {
|
||||||
|
[ViewFilterOperand.Is]: [workflowVersionId],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const filterLinkHref = `/objects/${CoreObjectNamePlural.WorkflowRun}?${qs.stringify(
|
||||||
|
filterQueryParams,
|
||||||
|
)}`;
|
||||||
|
|
||||||
|
addActionMenuEntry({
|
||||||
|
key: 'see-workflow-executions-single-record',
|
||||||
|
label: 'See executions',
|
||||||
|
position,
|
||||||
|
type: ActionMenuEntryType.Standard,
|
||||||
|
scope: ActionMenuEntryScope.RecordSelection,
|
||||||
|
Icon: IconHistoryToggle,
|
||||||
|
onClick: () => {
|
||||||
|
navigate(filterLinkHref);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const unregisterSeeWorkflowExecutionsWorkflowVersionSingleRecordAction =
|
||||||
|
() => {
|
||||||
|
removeActionMenuEntry('see-workflow-executions-single-record');
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
registerSeeWorkflowExecutionsWorkflowVersionSingleRecordAction,
|
||||||
|
unregisterSeeWorkflowExecutionsWorkflowVersionSingleRecordAction,
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
import { useSeeWorkflowVersionsHistoryWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useSeeWorkflowVersionsHistoryWorkflowSingleRecordAction';
|
||||||
|
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||||
|
import { useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
|
export const useSeeWorkflowVersionsHistoryWorkflowVersionSingleRecordAction = ({
|
||||||
|
workflowVersionId,
|
||||||
|
}: {
|
||||||
|
workflowVersionId: string;
|
||||||
|
}) => {
|
||||||
|
const workflowVersion = useRecoilValue(
|
||||||
|
recordStoreFamilyState(workflowVersionId),
|
||||||
|
);
|
||||||
|
|
||||||
|
const {
|
||||||
|
registerSeeWorkflowVersionsHistoryWorkflowSingleRecordAction:
|
||||||
|
registerSeeWorkflowVersionsHistoryWorkflowVersionSingleRecordAction,
|
||||||
|
unregisterSeeWorkflowVersionsHistoryWorkflowSingleRecordAction:
|
||||||
|
unregisterSeeWorkflowVersionsHistoryWorkflowVersionSingleRecordAction,
|
||||||
|
} = useSeeWorkflowVersionsHistoryWorkflowSingleRecordAction({
|
||||||
|
workflowId: workflowVersion?.workflow.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
registerSeeWorkflowVersionsHistoryWorkflowVersionSingleRecordAction,
|
||||||
|
unregisterSeeWorkflowVersionsHistoryWorkflowVersionSingleRecordAction,
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -0,0 +1,92 @@
|
|||||||
|
import { useActionMenuEntries } from '@/action-menu/hooks/useActionMenuEntries';
|
||||||
|
import {
|
||||||
|
ActionMenuEntryScope,
|
||||||
|
ActionMenuEntryType,
|
||||||
|
} from '@/action-menu/types/ActionMenuEntry';
|
||||||
|
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||||
|
import { OverrideWorkflowDraftConfirmationModal } from '@/workflow/components/OverrideWorkflowDraftConfirmationModal';
|
||||||
|
import { useCreateNewWorkflowVersion } from '@/workflow/hooks/useCreateNewWorkflowVersion';
|
||||||
|
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||||
|
import { openOverrideWorkflowDraftConfirmationModalState } from '@/workflow/states/openOverrideWorkflowDraftConfirmationModalState';
|
||||||
|
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
||||||
|
import { IconPencil, isDefined } from 'twenty-ui';
|
||||||
|
|
||||||
|
export const useUseAsDraftWorkflowVersionSingleRecordAction = ({
|
||||||
|
workflowVersionId,
|
||||||
|
}: {
|
||||||
|
workflowVersionId: string;
|
||||||
|
}) => {
|
||||||
|
const { addActionMenuEntry, removeActionMenuEntry } = useActionMenuEntries();
|
||||||
|
|
||||||
|
const workflowVersion = useRecoilValue(
|
||||||
|
recordStoreFamilyState(workflowVersionId),
|
||||||
|
);
|
||||||
|
|
||||||
|
const workflow = useWorkflowWithCurrentVersion(
|
||||||
|
workflowVersion?.workflow?.id ?? '',
|
||||||
|
);
|
||||||
|
|
||||||
|
const { createNewWorkflowVersion } = useCreateNewWorkflowVersion();
|
||||||
|
|
||||||
|
const setOpenOverrideWorkflowDraftConfirmationModal = useSetRecoilState(
|
||||||
|
openOverrideWorkflowDraftConfirmationModalState,
|
||||||
|
);
|
||||||
|
|
||||||
|
const registerUseAsDraftWorkflowVersionSingleRecordAction = ({
|
||||||
|
position,
|
||||||
|
}: {
|
||||||
|
position: number;
|
||||||
|
}) => {
|
||||||
|
if (
|
||||||
|
!isDefined(workflowVersion) ||
|
||||||
|
!isDefined(workflow) ||
|
||||||
|
!isDefined(workflow.statuses) ||
|
||||||
|
workflowVersion.status === 'DRAFT'
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasAlreadyDraftVersion = workflow.statuses.includes('DRAFT');
|
||||||
|
|
||||||
|
addActionMenuEntry({
|
||||||
|
key: 'use-workflow-version-as-draft-single-record',
|
||||||
|
label: 'Use as draft',
|
||||||
|
position,
|
||||||
|
Icon: IconPencil,
|
||||||
|
type: ActionMenuEntryType.Standard,
|
||||||
|
scope: ActionMenuEntryScope.RecordSelection,
|
||||||
|
onClick: async () => {
|
||||||
|
if (hasAlreadyDraftVersion) {
|
||||||
|
setOpenOverrideWorkflowDraftConfirmationModal(true);
|
||||||
|
} else {
|
||||||
|
await createNewWorkflowVersion({
|
||||||
|
workflowId: workflowVersion.workflow.id,
|
||||||
|
name: `v${workflow.versions.length + 1}`,
|
||||||
|
status: 'DRAFT',
|
||||||
|
trigger: workflowVersion.trigger,
|
||||||
|
steps: workflowVersion.steps,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ConfirmationModal: (
|
||||||
|
<OverrideWorkflowDraftConfirmationModal
|
||||||
|
draftWorkflowVersionId={workflow?.currentVersion?.id ?? ''}
|
||||||
|
workflowId={workflow?.id ?? ''}
|
||||||
|
workflowVersionUpdateInput={{
|
||||||
|
steps: workflowVersion.steps,
|
||||||
|
trigger: workflowVersion.trigger,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const unregisterUseAsDraftWorkflowVersionSingleRecordAction = () => {
|
||||||
|
removeActionMenuEntry('use-workflow-version-as-draft-single-record');
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
registerUseAsDraftWorkflowVersionSingleRecordAction,
|
||||||
|
unregisterUseAsDraftWorkflowVersionSingleRecordAction,
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -0,0 +1,70 @@
|
|||||||
|
import { useSeeWorkflowExecutionsWorkflowVersionSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-version-actions/hooks/useSeeWorkflowExecutionsWorkflowVersionSingleRecordAction';
|
||||||
|
import { useSeeWorkflowVersionsHistoryWorkflowVersionSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-version-actions/hooks/useSeeWorkflowVersionsHistoryWorkflowVersionSingleRecordAction';
|
||||||
|
import { useUseAsDraftWorkflowVersionSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-version-actions/hooks/useUseAsDraftWorkflowVersionSingleRecordAction';
|
||||||
|
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||||
|
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||||
|
import { isDefined } from 'twenty-ui';
|
||||||
|
|
||||||
|
export const useWorkflowVersionsSingleRecordActions = () => {
|
||||||
|
const contextStoreTargetedRecordsRule = useRecoilComponentValueV2(
|
||||||
|
contextStoreTargetedRecordsRuleComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
|
const selectedRecordId =
|
||||||
|
contextStoreTargetedRecordsRule.mode === 'selection'
|
||||||
|
? contextStoreTargetedRecordsRule.selectedRecordIds?.[0]
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
if (!isDefined(selectedRecordId)) {
|
||||||
|
throw new Error('Selected record ID is required');
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
registerSeeWorkflowVersionsHistoryWorkflowVersionSingleRecordAction,
|
||||||
|
unregisterSeeWorkflowVersionsHistoryWorkflowVersionSingleRecordAction,
|
||||||
|
} = useSeeWorkflowVersionsHistoryWorkflowVersionSingleRecordAction({
|
||||||
|
workflowVersionId: selectedRecordId,
|
||||||
|
});
|
||||||
|
|
||||||
|
const {
|
||||||
|
registerUseAsDraftWorkflowVersionSingleRecordAction,
|
||||||
|
unregisterUseAsDraftWorkflowVersionSingleRecordAction,
|
||||||
|
} = useUseAsDraftWorkflowVersionSingleRecordAction({
|
||||||
|
workflowVersionId: selectedRecordId,
|
||||||
|
});
|
||||||
|
|
||||||
|
const {
|
||||||
|
registerSeeWorkflowExecutionsWorkflowVersionSingleRecordAction,
|
||||||
|
unregisterSeeWorkflowExecutionsWorkflowVersionSingleRecordAction,
|
||||||
|
} = useSeeWorkflowExecutionsWorkflowVersionSingleRecordAction({
|
||||||
|
workflowVersionId: selectedRecordId,
|
||||||
|
});
|
||||||
|
|
||||||
|
const registerSingleRecordActions = ({
|
||||||
|
startPosition,
|
||||||
|
}: {
|
||||||
|
startPosition: number;
|
||||||
|
}) => {
|
||||||
|
registerUseAsDraftWorkflowVersionSingleRecordAction({
|
||||||
|
position: startPosition,
|
||||||
|
});
|
||||||
|
registerSeeWorkflowVersionsHistoryWorkflowVersionSingleRecordAction({
|
||||||
|
position: startPosition + 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
registerSeeWorkflowExecutionsWorkflowVersionSingleRecordAction({
|
||||||
|
position: startPosition + 2,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const unregisterSingleRecordActions = () => {
|
||||||
|
unregisterUseAsDraftWorkflowVersionSingleRecordAction();
|
||||||
|
unregisterSeeWorkflowVersionsHistoryWorkflowVersionSingleRecordAction();
|
||||||
|
unregisterSeeWorkflowExecutionsWorkflowVersionSingleRecordAction();
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
registerSingleRecordActions,
|
||||||
|
unregisterSingleRecordActions,
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -6,13 +6,10 @@ import {
|
|||||||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
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 { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||||
import { useAllActiveWorkflowVersions } from '@/workflow/hooks/useAllActiveWorkflowVersions';
|
import { useAllActiveWorkflowVersions } from '@/workflow/hooks/useAllActiveWorkflowVersions';
|
||||||
import { useRunWorkflowVersion } from '@/workflow/hooks/useRunWorkflowVersion';
|
import { useRunWorkflowVersion } from '@/workflow/hooks/useRunWorkflowVersion';
|
||||||
|
|
||||||
import { useTheme } from '@emotion/react';
|
|
||||||
import { useRecoilValue } from 'recoil';
|
import { useRecoilValue } from 'recoil';
|
||||||
import { IconSettingsAutomation, isDefined } from 'twenty-ui';
|
import { IconSettingsAutomation, isDefined } from 'twenty-ui';
|
||||||
import { capitalize } from '~/utils/string/capitalize';
|
import { capitalize } from '~/utils/string/capitalize';
|
||||||
@ -33,8 +30,12 @@ export const useWorkflowRunRecordActions = ({
|
|||||||
? contextStoreTargetedRecordsRule.selectedRecordIds[0]
|
? contextStoreTargetedRecordsRule.selectedRecordIds[0]
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
|
if (!isDefined(selectedRecordId)) {
|
||||||
|
throw new Error('Selected record ID is required');
|
||||||
|
}
|
||||||
|
|
||||||
const selectedRecord = useRecoilValue(
|
const selectedRecord = useRecoilValue(
|
||||||
recordStoreFamilyState(selectedRecordId ?? ''),
|
recordStoreFamilyState(selectedRecordId),
|
||||||
);
|
);
|
||||||
|
|
||||||
const { records: activeWorkflowVersions } = useAllActiveWorkflowVersions({
|
const { records: activeWorkflowVersions } = useAllActiveWorkflowVersions({
|
||||||
@ -44,10 +45,6 @@ export const useWorkflowRunRecordActions = ({
|
|||||||
|
|
||||||
const { runWorkflowVersion } = useRunWorkflowVersion();
|
const { runWorkflowVersion } = useRunWorkflowVersion();
|
||||||
|
|
||||||
const { enqueueSnackBar } = useSnackBar();
|
|
||||||
|
|
||||||
const theme = useTheme();
|
|
||||||
|
|
||||||
const registerWorkflowRunRecordActions = () => {
|
const registerWorkflowRunRecordActions = () => {
|
||||||
if (!isDefined(objectMetadataItem) || objectMetadataItem.isRemote) {
|
if (!isDefined(objectMetadataItem) || objectMetadataItem.isRemote) {
|
||||||
return;
|
return;
|
||||||
@ -57,6 +54,9 @@ export const useWorkflowRunRecordActions = ({
|
|||||||
index,
|
index,
|
||||||
activeWorkflowVersion,
|
activeWorkflowVersion,
|
||||||
] of activeWorkflowVersions.entries()) {
|
] of activeWorkflowVersions.entries()) {
|
||||||
|
if (!isDefined(activeWorkflowVersion.workflow)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
const name = capitalize(activeWorkflowVersion.workflow.name);
|
const name = capitalize(activeWorkflowVersion.workflow.name);
|
||||||
addActionMenuEntry({
|
addActionMenuEntry({
|
||||||
type: ActionMenuEntryType.WorkflowRun,
|
type: ActionMenuEntryType.WorkflowRun,
|
||||||
@ -72,19 +72,9 @@ export const useWorkflowRunRecordActions = ({
|
|||||||
|
|
||||||
await runWorkflowVersion({
|
await runWorkflowVersion({
|
||||||
workflowVersionId: activeWorkflowVersion.id,
|
workflowVersionId: activeWorkflowVersion.id,
|
||||||
|
workflowName: name,
|
||||||
payload: selectedRecord,
|
payload: selectedRecord,
|
||||||
});
|
});
|
||||||
|
|
||||||
enqueueSnackBar('', {
|
|
||||||
variant: SnackBarVariant.Success,
|
|
||||||
title: `${name} starting...`,
|
|
||||||
icon: (
|
|
||||||
<IconSettingsAutomation
|
|
||||||
size={16}
|
|
||||||
color={theme.snackBar.success.color}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,14 +3,11 @@ import {
|
|||||||
ActionMenuEntryScope,
|
ActionMenuEntryScope,
|
||||||
ActionMenuEntryType,
|
ActionMenuEntryType,
|
||||||
} from '@/action-menu/types/ActionMenuEntry';
|
} from '@/action-menu/types/ActionMenuEntry';
|
||||||
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
|
||||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
|
||||||
import { useAllActiveWorkflowVersions } from '@/workflow/hooks/useAllActiveWorkflowVersions';
|
import { useAllActiveWorkflowVersions } from '@/workflow/hooks/useAllActiveWorkflowVersions';
|
||||||
import { useRunWorkflowVersion } from '@/workflow/hooks/useRunWorkflowVersion';
|
import { useRunWorkflowVersion } from '@/workflow/hooks/useRunWorkflowVersion';
|
||||||
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
||||||
|
|
||||||
import { useTheme } from '@emotion/react';
|
import { IconSettingsAutomation, isDefined } from 'twenty-ui';
|
||||||
import { IconSettingsAutomation } from 'twenty-ui';
|
|
||||||
import { capitalize } from '~/utils/string/capitalize';
|
import { capitalize } from '~/utils/string/capitalize';
|
||||||
|
|
||||||
export const useWorkflowRunActions = () => {
|
export const useWorkflowRunActions = () => {
|
||||||
@ -24,10 +21,6 @@ export const useWorkflowRunActions = () => {
|
|||||||
|
|
||||||
const { runWorkflowVersion } = useRunWorkflowVersion();
|
const { runWorkflowVersion } = useRunWorkflowVersion();
|
||||||
|
|
||||||
const { enqueueSnackBar } = useSnackBar();
|
|
||||||
|
|
||||||
const theme = useTheme();
|
|
||||||
|
|
||||||
const addWorkflowRunActions = () => {
|
const addWorkflowRunActions = () => {
|
||||||
if (!isWorkflowEnabled) {
|
if (!isWorkflowEnabled) {
|
||||||
return;
|
return;
|
||||||
@ -37,7 +30,12 @@ export const useWorkflowRunActions = () => {
|
|||||||
index,
|
index,
|
||||||
activeWorkflowVersion,
|
activeWorkflowVersion,
|
||||||
] of activeWorkflowVersions.entries()) {
|
] of activeWorkflowVersions.entries()) {
|
||||||
|
if (!isDefined(activeWorkflowVersion.workflow)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
const name = capitalize(activeWorkflowVersion.workflow.name);
|
const name = capitalize(activeWorkflowVersion.workflow.name);
|
||||||
|
|
||||||
addActionMenuEntry({
|
addActionMenuEntry({
|
||||||
type: ActionMenuEntryType.WorkflowRun,
|
type: ActionMenuEntryType.WorkflowRun,
|
||||||
key: `workflow-run-${activeWorkflowVersion.id}`,
|
key: `workflow-run-${activeWorkflowVersion.id}`,
|
||||||
@ -48,17 +46,7 @@ export const useWorkflowRunActions = () => {
|
|||||||
onClick: async () => {
|
onClick: async () => {
|
||||||
await runWorkflowVersion({
|
await runWorkflowVersion({
|
||||||
workflowVersionId: activeWorkflowVersion.id,
|
workflowVersionId: activeWorkflowVersion.id,
|
||||||
});
|
workflowName: name,
|
||||||
|
|
||||||
enqueueSnackBar('', {
|
|
||||||
variant: SnackBarVariant.Success,
|
|
||||||
title: `${name} starting...`,
|
|
||||||
icon: (
|
|
||||||
<IconSettingsAutomation
|
|
||||||
size={16}
|
|
||||||
color={theme.snackBar.success.color}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@ -7,14 +7,17 @@ import {
|
|||||||
} from '@/ui/layout/modal/components/ConfirmationModal';
|
} from '@/ui/layout/modal/components/ConfirmationModal';
|
||||||
import { openOverrideWorkflowDraftConfirmationModalState } from '@/workflow/states/openOverrideWorkflowDraftConfirmationModalState';
|
import { openOverrideWorkflowDraftConfirmationModalState } from '@/workflow/states/openOverrideWorkflowDraftConfirmationModalState';
|
||||||
import { WorkflowVersion } from '@/workflow/types/Workflow';
|
import { WorkflowVersion } from '@/workflow/types/Workflow';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { useRecoilState } from 'recoil';
|
import { useRecoilState } from 'recoil';
|
||||||
|
|
||||||
export const OverrideWorkflowDraftConfirmationModal = ({
|
export const OverrideWorkflowDraftConfirmationModal = ({
|
||||||
draftWorkflowVersionId,
|
draftWorkflowVersionId,
|
||||||
workflowVersionUpdateInput,
|
workflowVersionUpdateInput,
|
||||||
|
workflowId,
|
||||||
}: {
|
}: {
|
||||||
draftWorkflowVersionId: string;
|
draftWorkflowVersionId: string;
|
||||||
workflowVersionUpdateInput: Pick<WorkflowVersion, 'trigger' | 'steps'>;
|
workflowVersionUpdateInput: Pick<WorkflowVersion, 'trigger' | 'steps'>;
|
||||||
|
workflowId: string;
|
||||||
}) => {
|
}) => {
|
||||||
const [
|
const [
|
||||||
openOverrideWorkflowDraftConfirmationModal,
|
openOverrideWorkflowDraftConfirmationModal,
|
||||||
@ -26,11 +29,15 @@ export const OverrideWorkflowDraftConfirmationModal = ({
|
|||||||
objectNameSingular: CoreObjectNameSingular.WorkflowVersion,
|
objectNameSingular: CoreObjectNameSingular.WorkflowVersion,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const handleOverrideDraft = async () => {
|
const handleOverrideDraft = async () => {
|
||||||
await updateOneWorkflowVersion({
|
await updateOneWorkflowVersion({
|
||||||
idToUpdate: draftWorkflowVersionId,
|
idToUpdate: draftWorkflowVersionId,
|
||||||
updateOneRecordInput: workflowVersionUpdateInput,
|
updateOneRecordInput: workflowVersionUpdateInput,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
navigate(buildShowPageURL(CoreObjectNameSingular.Workflow, workflowId));
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -71,6 +71,7 @@ export const RecordShowPageWorkflowHeader = ({
|
|||||||
|
|
||||||
await runWorkflowVersion({
|
await runWorkflowVersion({
|
||||||
workflowVersionId: workflowWithCurrentVersion.currentVersion.id,
|
workflowVersionId: workflowWithCurrentVersion.currentVersion.id,
|
||||||
|
workflowName: workflowWithCurrentVersion.name,
|
||||||
});
|
});
|
||||||
|
|
||||||
enqueueSnackBar('', {
|
enqueueSnackBar('', {
|
||||||
|
|||||||
@ -131,6 +131,7 @@ export const RecordShowPageWorkflowVersionHeader = ({
|
|||||||
{isDefined(workflowVersion) && isDefined(draftWorkflowVersion) ? (
|
{isDefined(workflowVersion) && isDefined(draftWorkflowVersion) ? (
|
||||||
<OverrideWorkflowDraftConfirmationModal
|
<OverrideWorkflowDraftConfirmationModal
|
||||||
draftWorkflowVersionId={draftWorkflowVersion.id}
|
draftWorkflowVersionId={draftWorkflowVersion.id}
|
||||||
|
workflowId={workflowVersion.workflowId}
|
||||||
workflowVersionUpdateInput={{
|
workflowVersionUpdateInput={{
|
||||||
steps: workflowVersion.steps,
|
steps: workflowVersion.steps,
|
||||||
trigger: workflowVersion.trigger,
|
trigger: workflowVersion.trigger,
|
||||||
|
|||||||
@ -0,0 +1,43 @@
|
|||||||
|
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||||
|
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
|
||||||
|
import { Workflow, WorkflowVersion } from '@/workflow/types/Workflow';
|
||||||
|
|
||||||
|
export const useActiveWorkflowVersion = (workflowId: string) => {
|
||||||
|
const { records: workflowVersions } = useFindManyRecords<
|
||||||
|
WorkflowVersion & {
|
||||||
|
workflow: Omit<Workflow, 'versions'> & {
|
||||||
|
versions: Array<{ __typename: string }>;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
>({
|
||||||
|
objectNameSingular: CoreObjectNameSingular.WorkflowVersion,
|
||||||
|
filter: {
|
||||||
|
workflowId: {
|
||||||
|
eq: workflowId,
|
||||||
|
},
|
||||||
|
status: {
|
||||||
|
eq: 'ACTIVE',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
recordGqlFields: {
|
||||||
|
id: true,
|
||||||
|
name: true,
|
||||||
|
createdAt: true,
|
||||||
|
updatedAt: true,
|
||||||
|
workflowId: true,
|
||||||
|
trigger: true,
|
||||||
|
steps: true,
|
||||||
|
status: true,
|
||||||
|
workflow: {
|
||||||
|
id: true,
|
||||||
|
name: true,
|
||||||
|
statuses: true,
|
||||||
|
versions: {
|
||||||
|
totalCount: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return workflowVersions?.[0];
|
||||||
|
};
|
||||||
@ -1,10 +1,15 @@
|
|||||||
import { useApolloMetadataClient } from '@/object-metadata/hooks/useApolloMetadataClient';
|
import { useApolloMetadataClient } from '@/object-metadata/hooks/useApolloMetadataClient';
|
||||||
|
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
||||||
|
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||||
import { RUN_WORKFLOW_VERSION } from '@/workflow/graphql/mutations/runWorkflowVersion';
|
import { RUN_WORKFLOW_VERSION } from '@/workflow/graphql/mutations/runWorkflowVersion';
|
||||||
import { useMutation } from '@apollo/client';
|
import { useMutation } from '@apollo/client';
|
||||||
|
import { useTheme } from '@emotion/react';
|
||||||
|
import { IconSettingsAutomation } from 'twenty-ui';
|
||||||
import {
|
import {
|
||||||
RunWorkflowVersionMutation,
|
RunWorkflowVersionMutation,
|
||||||
RunWorkflowVersionMutationVariables,
|
RunWorkflowVersionMutationVariables,
|
||||||
} from '~/generated/graphql';
|
} from '~/generated/graphql';
|
||||||
|
import { capitalize } from '~/utils/string/capitalize';
|
||||||
|
|
||||||
export const useRunWorkflowVersion = () => {
|
export const useRunWorkflowVersion = () => {
|
||||||
const apolloMetadataClient = useApolloMetadataClient();
|
const apolloMetadataClient = useApolloMetadataClient();
|
||||||
@ -15,16 +20,33 @@ export const useRunWorkflowVersion = () => {
|
|||||||
client: apolloMetadataClient,
|
client: apolloMetadataClient,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const { enqueueSnackBar } = useSnackBar();
|
||||||
|
|
||||||
|
const theme = useTheme();
|
||||||
|
|
||||||
const runWorkflowVersion = async ({
|
const runWorkflowVersion = async ({
|
||||||
workflowVersionId,
|
workflowVersionId,
|
||||||
|
workflowName,
|
||||||
payload,
|
payload,
|
||||||
}: {
|
}: {
|
||||||
workflowVersionId: string;
|
workflowVersionId: string;
|
||||||
|
workflowName: string;
|
||||||
payload?: Record<string, any>;
|
payload?: Record<string, any>;
|
||||||
}) => {
|
}) => {
|
||||||
await mutate({
|
await mutate({
|
||||||
variables: { input: { workflowVersionId, payload } },
|
variables: { input: { workflowVersionId, payload } },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
enqueueSnackBar('', {
|
||||||
|
variant: SnackBarVariant.Success,
|
||||||
|
title: `${capitalize(workflowName)} starting...`,
|
||||||
|
icon: (
|
||||||
|
<IconSettingsAutomation
|
||||||
|
size={16}
|
||||||
|
color={theme.snackBar.success.color}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return { runWorkflowVersion };
|
return { runWorkflowVersion };
|
||||||
@ -17,6 +17,7 @@ export const useWorkflowWithCurrentVersion = (
|
|||||||
id: true,
|
id: true,
|
||||||
name: true,
|
name: true,
|
||||||
statuses: true,
|
statuses: true,
|
||||||
|
lastPublishedVersionId: true,
|
||||||
versions: true,
|
versions: true,
|
||||||
},
|
},
|
||||||
skip: !isDefined(workflowId),
|
skip: !isDefined(workflowId),
|
||||||
|
|||||||
@ -150,6 +150,8 @@ export {
|
|||||||
IconHeartOff,
|
IconHeartOff,
|
||||||
IconHelpCircle,
|
IconHelpCircle,
|
||||||
IconHierarchy2,
|
IconHierarchy2,
|
||||||
|
IconHistory,
|
||||||
|
IconHistoryToggle,
|
||||||
IconHome,
|
IconHome,
|
||||||
IconInbox,
|
IconInbox,
|
||||||
IconInfoCircle,
|
IconInfoCircle,
|
||||||
@ -192,6 +194,7 @@ export {
|
|||||||
IconPhoto,
|
IconPhoto,
|
||||||
IconPhotoUp,
|
IconPhotoUp,
|
||||||
IconPilcrow,
|
IconPilcrow,
|
||||||
|
IconPlayerPause,
|
||||||
IconPlayerPlay,
|
IconPlayerPlay,
|
||||||
IconPlayerStop,
|
IconPlayerStop,
|
||||||
IconPlaylistAdd,
|
IconPlaylistAdd,
|
||||||
|
|||||||
@ -10,20 +10,21 @@ import {
|
|||||||
Icon3dRotate,
|
Icon3dRotate,
|
||||||
IconAB,
|
IconAB,
|
||||||
IconAB2,
|
IconAB2,
|
||||||
|
IconABOff,
|
||||||
IconAbacus,
|
IconAbacus,
|
||||||
IconAbacusOff,
|
IconAbacusOff,
|
||||||
IconAbc,
|
IconAbc,
|
||||||
IconABOff,
|
|
||||||
IconAccessible,
|
|
||||||
IconAccessibleOff,
|
|
||||||
IconAccessPoint,
|
IconAccessPoint,
|
||||||
IconAccessPointOff,
|
IconAccessPointOff,
|
||||||
|
IconAccessible,
|
||||||
|
IconAccessibleOff,
|
||||||
IconActivity,
|
IconActivity,
|
||||||
IconActivityHeartbeat,
|
IconActivityHeartbeat,
|
||||||
IconAd,
|
IconAd,
|
||||||
IconAd2,
|
IconAd2,
|
||||||
IconAdCircle,
|
IconAdCircle,
|
||||||
IconAdCircleOff,
|
IconAdCircleOff,
|
||||||
|
IconAdOff,
|
||||||
IconAddressBookOff,
|
IconAddressBookOff,
|
||||||
IconAdjustments,
|
IconAdjustments,
|
||||||
IconAdjustmentsAlt,
|
IconAdjustmentsAlt,
|
||||||
@ -48,7 +49,6 @@ import {
|
|||||||
IconAdjustmentsStar,
|
IconAdjustmentsStar,
|
||||||
IconAdjustmentsUp,
|
IconAdjustmentsUp,
|
||||||
IconAdjustmentsX,
|
IconAdjustmentsX,
|
||||||
IconAdOff,
|
|
||||||
IconAerialLift,
|
IconAerialLift,
|
||||||
IconAffiliate,
|
IconAffiliate,
|
||||||
IconAirBalloon,
|
IconAirBalloon,
|
||||||
@ -118,10 +118,10 @@ import {
|
|||||||
IconApiApp,
|
IconApiApp,
|
||||||
IconApiAppOff,
|
IconApiAppOff,
|
||||||
IconApiOff,
|
IconApiOff,
|
||||||
|
IconAppWindow,
|
||||||
IconApple,
|
IconApple,
|
||||||
IconApps,
|
IconApps,
|
||||||
IconAppsOff,
|
IconAppsOff,
|
||||||
IconAppWindow,
|
|
||||||
IconArchive,
|
IconArchive,
|
||||||
IconArchiveOff,
|
IconArchiveOff,
|
||||||
IconArmchair,
|
IconArmchair,
|
||||||
@ -233,6 +233,23 @@ import {
|
|||||||
IconArrowRotaryStraight,
|
IconArrowRotaryStraight,
|
||||||
IconArrowRoundaboutLeft,
|
IconArrowRoundaboutLeft,
|
||||||
IconArrowRoundaboutRight,
|
IconArrowRoundaboutRight,
|
||||||
|
IconArrowSharpTurnLeft,
|
||||||
|
IconArrowSharpTurnRight,
|
||||||
|
IconArrowUp,
|
||||||
|
IconArrowUpBar,
|
||||||
|
IconArrowUpCircle,
|
||||||
|
IconArrowUpLeft,
|
||||||
|
IconArrowUpLeftCircle,
|
||||||
|
IconArrowUpRhombus,
|
||||||
|
IconArrowUpRight,
|
||||||
|
IconArrowUpRightCircle,
|
||||||
|
IconArrowUpSquare,
|
||||||
|
IconArrowUpTail,
|
||||||
|
IconArrowWaveLeftDown,
|
||||||
|
IconArrowWaveLeftUp,
|
||||||
|
IconArrowWaveRightDown,
|
||||||
|
IconArrowWaveRightUp,
|
||||||
|
IconArrowZigZag,
|
||||||
IconArrowsCross,
|
IconArrowsCross,
|
||||||
IconArrowsDiagonal,
|
IconArrowsDiagonal,
|
||||||
IconArrowsDiagonal2,
|
IconArrowsDiagonal2,
|
||||||
@ -247,8 +264,6 @@ import {
|
|||||||
IconArrowsDownUp,
|
IconArrowsDownUp,
|
||||||
IconArrowsExchange,
|
IconArrowsExchange,
|
||||||
IconArrowsExchange2,
|
IconArrowsExchange2,
|
||||||
IconArrowSharpTurnLeft,
|
|
||||||
IconArrowSharpTurnRight,
|
|
||||||
IconArrowsHorizontal,
|
IconArrowsHorizontal,
|
||||||
IconArrowsJoin,
|
IconArrowsJoin,
|
||||||
IconArrowsJoin2,
|
IconArrowsJoin2,
|
||||||
@ -276,21 +291,6 @@ import {
|
|||||||
IconArrowsUpLeft,
|
IconArrowsUpLeft,
|
||||||
IconArrowsUpRight,
|
IconArrowsUpRight,
|
||||||
IconArrowsVertical,
|
IconArrowsVertical,
|
||||||
IconArrowUp,
|
|
||||||
IconArrowUpBar,
|
|
||||||
IconArrowUpCircle,
|
|
||||||
IconArrowUpLeft,
|
|
||||||
IconArrowUpLeftCircle,
|
|
||||||
IconArrowUpRhombus,
|
|
||||||
IconArrowUpRight,
|
|
||||||
IconArrowUpRightCircle,
|
|
||||||
IconArrowUpSquare,
|
|
||||||
IconArrowUpTail,
|
|
||||||
IconArrowWaveLeftDown,
|
|
||||||
IconArrowWaveLeftUp,
|
|
||||||
IconArrowWaveRightDown,
|
|
||||||
IconArrowWaveRightUp,
|
|
||||||
IconArrowZigZag,
|
|
||||||
IconArtboard,
|
IconArtboard,
|
||||||
IconArtboardOff,
|
IconArtboardOff,
|
||||||
IconArticle,
|
IconArticle,
|
||||||
@ -331,13 +331,13 @@ import {
|
|||||||
IconBadgeCc,
|
IconBadgeCc,
|
||||||
IconBadgeHd,
|
IconBadgeHd,
|
||||||
IconBadgeOff,
|
IconBadgeOff,
|
||||||
IconBadges,
|
|
||||||
IconBadgeSd,
|
IconBadgeSd,
|
||||||
IconBadgesOff,
|
|
||||||
IconBadgeTm,
|
IconBadgeTm,
|
||||||
IconBadgeVo,
|
IconBadgeVo,
|
||||||
IconBadgeVr,
|
IconBadgeVr,
|
||||||
IconBadgeWc,
|
IconBadgeWc,
|
||||||
|
IconBadges,
|
||||||
|
IconBadgesOff,
|
||||||
IconBaguette,
|
IconBaguette,
|
||||||
IconBallAmericanFootball,
|
IconBallAmericanFootball,
|
||||||
IconBallAmericanFootballOff,
|
IconBallAmericanFootballOff,
|
||||||
@ -346,12 +346,12 @@ import {
|
|||||||
IconBallBowling,
|
IconBallBowling,
|
||||||
IconBallFootball,
|
IconBallFootball,
|
||||||
IconBallFootballOff,
|
IconBallFootballOff,
|
||||||
|
IconBallTennis,
|
||||||
|
IconBallVolleyball,
|
||||||
IconBalloon,
|
IconBalloon,
|
||||||
IconBalloonOff,
|
IconBalloonOff,
|
||||||
IconBallpen,
|
IconBallpen,
|
||||||
IconBallpenOff,
|
IconBallpenOff,
|
||||||
IconBallTennis,
|
|
||||||
IconBallVolleyball,
|
|
||||||
IconBan,
|
IconBan,
|
||||||
IconBandage,
|
IconBandage,
|
||||||
IconBandageOff,
|
IconBandageOff,
|
||||||
@ -468,6 +468,8 @@ import {
|
|||||||
IconBook,
|
IconBook,
|
||||||
IconBook2,
|
IconBook2,
|
||||||
IconBookDownload,
|
IconBookDownload,
|
||||||
|
IconBookOff,
|
||||||
|
IconBookUpload,
|
||||||
IconBookmark,
|
IconBookmark,
|
||||||
IconBookmarkEdit,
|
IconBookmarkEdit,
|
||||||
IconBookmarkMinus,
|
IconBookmarkMinus,
|
||||||
@ -476,10 +478,8 @@ import {
|
|||||||
IconBookmarkQuestion,
|
IconBookmarkQuestion,
|
||||||
IconBookmarks,
|
IconBookmarks,
|
||||||
IconBookmarksOff,
|
IconBookmarksOff,
|
||||||
IconBookOff,
|
|
||||||
IconBooks,
|
IconBooks,
|
||||||
IconBooksOff,
|
IconBooksOff,
|
||||||
IconBookUpload,
|
|
||||||
IconBorderAll,
|
IconBorderAll,
|
||||||
IconBorderBottom,
|
IconBorderBottom,
|
||||||
IconBorderCorners,
|
IconBorderCorners,
|
||||||
@ -582,6 +582,7 @@ import {
|
|||||||
IconBrandBulma,
|
IconBrandBulma,
|
||||||
IconBrandBumble,
|
IconBrandBumble,
|
||||||
IconBrandBunpo,
|
IconBrandBunpo,
|
||||||
|
IconBrandCSharp,
|
||||||
IconBrandCake,
|
IconBrandCake,
|
||||||
IconBrandCakephp,
|
IconBrandCakephp,
|
||||||
IconBrandCampaignmonitor,
|
IconBrandCampaignmonitor,
|
||||||
@ -603,7 +604,6 @@ import {
|
|||||||
IconBrandCpp,
|
IconBrandCpp,
|
||||||
IconBrandCraft,
|
IconBrandCraft,
|
||||||
IconBrandCrunchbase,
|
IconBrandCrunchbase,
|
||||||
IconBrandCSharp,
|
|
||||||
IconBrandCss3,
|
IconBrandCss3,
|
||||||
IconBrandCtemplar,
|
IconBrandCtemplar,
|
||||||
IconBrandCucumber,
|
IconBrandCucumber,
|
||||||
@ -739,8 +739,8 @@ import {
|
|||||||
IconBrandOkRu,
|
IconBrandOkRu,
|
||||||
IconBrandOnedrive,
|
IconBrandOnedrive,
|
||||||
IconBrandOnlyfans,
|
IconBrandOnlyfans,
|
||||||
IconBrandOpenai,
|
|
||||||
IconBrandOpenSource,
|
IconBrandOpenSource,
|
||||||
|
IconBrandOpenai,
|
||||||
IconBrandOpenvpn,
|
IconBrandOpenvpn,
|
||||||
IconBrandOpera,
|
IconBrandOpera,
|
||||||
IconBrandPagekit,
|
IconBrandPagekit,
|
||||||
@ -934,9 +934,9 @@ import {
|
|||||||
IconBulbOff,
|
IconBulbOff,
|
||||||
IconBulldozer,
|
IconBulldozer,
|
||||||
IconBus,
|
IconBus,
|
||||||
IconBusinessplan,
|
|
||||||
IconBusOff,
|
IconBusOff,
|
||||||
IconBusStop,
|
IconBusStop,
|
||||||
|
IconBusinessplan,
|
||||||
IconButterfly,
|
IconButterfly,
|
||||||
IconCactus,
|
IconCactus,
|
||||||
IconCactusOff,
|
IconCactusOff,
|
||||||
@ -1006,9 +1006,11 @@ import {
|
|||||||
IconCapture,
|
IconCapture,
|
||||||
IconCaptureOff,
|
IconCaptureOff,
|
||||||
IconCar,
|
IconCar,
|
||||||
IconCaravan,
|
|
||||||
IconCarCrane,
|
IconCarCrane,
|
||||||
IconCarCrash,
|
IconCarCrash,
|
||||||
|
IconCarOff,
|
||||||
|
IconCarTurbine,
|
||||||
|
IconCaravan,
|
||||||
IconCardboards,
|
IconCardboards,
|
||||||
IconCardboardsOff,
|
IconCardboardsOff,
|
||||||
IconCards,
|
IconCards,
|
||||||
@ -1016,12 +1018,10 @@ import {
|
|||||||
IconCaretLeft,
|
IconCaretLeft,
|
||||||
IconCaretRight,
|
IconCaretRight,
|
||||||
IconCaretUp,
|
IconCaretUp,
|
||||||
IconCarOff,
|
|
||||||
IconCarouselHorizontal,
|
IconCarouselHorizontal,
|
||||||
IconCarouselVertical,
|
IconCarouselVertical,
|
||||||
IconCarrot,
|
IconCarrot,
|
||||||
IconCarrotOff,
|
IconCarrotOff,
|
||||||
IconCarTurbine,
|
|
||||||
IconCash,
|
IconCash,
|
||||||
IconCashBanknote,
|
IconCashBanknote,
|
||||||
IconCashBanknoteOff,
|
IconCashBanknoteOff,
|
||||||
@ -1032,6 +1032,7 @@ import {
|
|||||||
IconCategory,
|
IconCategory,
|
||||||
IconCategory2,
|
IconCategory2,
|
||||||
IconCe,
|
IconCe,
|
||||||
|
IconCeOff,
|
||||||
IconCell,
|
IconCell,
|
||||||
IconCellSignal1,
|
IconCellSignal1,
|
||||||
IconCellSignal2,
|
IconCellSignal2,
|
||||||
@ -1039,7 +1040,6 @@ import {
|
|||||||
IconCellSignal4,
|
IconCellSignal4,
|
||||||
IconCellSignal5,
|
IconCellSignal5,
|
||||||
IconCellSignalOff,
|
IconCellSignalOff,
|
||||||
IconCeOff,
|
|
||||||
IconCertificate,
|
IconCertificate,
|
||||||
IconCertificate2,
|
IconCertificate2,
|
||||||
IconCertificate2Off,
|
IconCertificate2Off,
|
||||||
@ -1105,6 +1105,9 @@ import {
|
|||||||
IconChevronLeftPipe,
|
IconChevronLeftPipe,
|
||||||
IconChevronRight,
|
IconChevronRight,
|
||||||
IconChevronRightPipe,
|
IconChevronRightPipe,
|
||||||
|
IconChevronUp,
|
||||||
|
IconChevronUpLeft,
|
||||||
|
IconChevronUpRight,
|
||||||
IconChevronsDown,
|
IconChevronsDown,
|
||||||
IconChevronsDownLeft,
|
IconChevronsDownLeft,
|
||||||
IconChevronsDownRight,
|
IconChevronsDownRight,
|
||||||
@ -1113,9 +1116,6 @@ import {
|
|||||||
IconChevronsUp,
|
IconChevronsUp,
|
||||||
IconChevronsUpLeft,
|
IconChevronsUpLeft,
|
||||||
IconChevronsUpRight,
|
IconChevronsUpRight,
|
||||||
IconChevronUp,
|
|
||||||
IconChevronUpLeft,
|
|
||||||
IconChevronUpRight,
|
|
||||||
IconChisel,
|
IconChisel,
|
||||||
IconChristmasTree,
|
IconChristmasTree,
|
||||||
IconChristmasTreeOff,
|
IconChristmasTreeOff,
|
||||||
@ -1136,11 +1136,11 @@ import {
|
|||||||
IconCircleChevronDown,
|
IconCircleChevronDown,
|
||||||
IconCircleChevronLeft,
|
IconCircleChevronLeft,
|
||||||
IconCircleChevronRight,
|
IconCircleChevronRight,
|
||||||
|
IconCircleChevronUp,
|
||||||
IconCircleChevronsDown,
|
IconCircleChevronsDown,
|
||||||
IconCircleChevronsLeft,
|
IconCircleChevronsLeft,
|
||||||
IconCircleChevronsRight,
|
IconCircleChevronsRight,
|
||||||
IconCircleChevronsUp,
|
IconCircleChevronsUp,
|
||||||
IconCircleChevronUp,
|
|
||||||
IconCircleDashed,
|
IconCircleDashed,
|
||||||
IconCircleDot,
|
IconCircleDot,
|
||||||
IconCircleDotted,
|
IconCircleDotted,
|
||||||
@ -1189,11 +1189,11 @@ import {
|
|||||||
IconCirclePlus,
|
IconCirclePlus,
|
||||||
IconCircleRectangle,
|
IconCircleRectangle,
|
||||||
IconCircleRectangleOff,
|
IconCircleRectangleOff,
|
||||||
IconCircles,
|
|
||||||
IconCircleSquare,
|
IconCircleSquare,
|
||||||
IconCirclesRelation,
|
|
||||||
IconCircleTriangle,
|
IconCircleTriangle,
|
||||||
IconCircleX,
|
IconCircleX,
|
||||||
|
IconCircles,
|
||||||
|
IconCirclesRelation,
|
||||||
IconCircuitAmmeter,
|
IconCircuitAmmeter,
|
||||||
IconCircuitBattery,
|
IconCircuitBattery,
|
||||||
IconCircuitBulb,
|
IconCircuitBulb,
|
||||||
@ -1320,9 +1320,9 @@ import {
|
|||||||
IconCoinOff,
|
IconCoinOff,
|
||||||
IconCoinPound,
|
IconCoinPound,
|
||||||
IconCoinRupee,
|
IconCoinRupee,
|
||||||
IconCoins,
|
|
||||||
IconCoinYen,
|
IconCoinYen,
|
||||||
IconCoinYuan,
|
IconCoinYuan,
|
||||||
|
IconCoins,
|
||||||
IconColorFilter,
|
IconColorFilter,
|
||||||
IconColorPicker,
|
IconColorPicker,
|
||||||
IconColorPickerOff,
|
IconColorPickerOff,
|
||||||
@ -1361,9 +1361,9 @@ import {
|
|||||||
IconCookieMan,
|
IconCookieMan,
|
||||||
IconCookieOff,
|
IconCookieOff,
|
||||||
IconCopy,
|
IconCopy,
|
||||||
|
IconCopyOff,
|
||||||
IconCopyleft,
|
IconCopyleft,
|
||||||
IconCopyleftOff,
|
IconCopyleftOff,
|
||||||
IconCopyOff,
|
|
||||||
IconCopyright,
|
IconCopyright,
|
||||||
IconCopyrightOff,
|
IconCopyrightOff,
|
||||||
IconCornerDownLeft,
|
IconCornerDownLeft,
|
||||||
@ -1399,8 +1399,8 @@ import {
|
|||||||
IconCricket,
|
IconCricket,
|
||||||
IconCrop,
|
IconCrop,
|
||||||
IconCross,
|
IconCross,
|
||||||
IconCrosshair,
|
|
||||||
IconCrossOff,
|
IconCrossOff,
|
||||||
|
IconCrosshair,
|
||||||
IconCrown,
|
IconCrown,
|
||||||
IconCrownOff,
|
IconCrownOff,
|
||||||
IconCrutches,
|
IconCrutches,
|
||||||
@ -1648,37 +1648,13 @@ import {
|
|||||||
IconDeviceNintendoOff,
|
IconDeviceNintendoOff,
|
||||||
IconDeviceProjector,
|
IconDeviceProjector,
|
||||||
IconDeviceRemote,
|
IconDeviceRemote,
|
||||||
IconDevices,
|
|
||||||
IconDevices2,
|
|
||||||
IconDevicesBolt,
|
|
||||||
IconDevicesCancel,
|
|
||||||
IconDevicesCheck,
|
|
||||||
IconDevicesCode,
|
|
||||||
IconDevicesCog,
|
|
||||||
IconDeviceSdCard,
|
IconDeviceSdCard,
|
||||||
IconDevicesDollar,
|
|
||||||
IconDevicesDown,
|
|
||||||
IconDevicesExclamation,
|
|
||||||
IconDevicesHeart,
|
|
||||||
IconDeviceSim,
|
IconDeviceSim,
|
||||||
IconDeviceSim1,
|
IconDeviceSim1,
|
||||||
IconDeviceSim2,
|
IconDeviceSim2,
|
||||||
IconDeviceSim3,
|
IconDeviceSim3,
|
||||||
IconDevicesMinus,
|
|
||||||
IconDevicesOff,
|
|
||||||
IconDevicesPause,
|
|
||||||
IconDevicesPc,
|
|
||||||
IconDevicesPcOff,
|
|
||||||
IconDeviceSpeaker,
|
IconDeviceSpeaker,
|
||||||
IconDeviceSpeakerOff,
|
IconDeviceSpeakerOff,
|
||||||
IconDevicesPin,
|
|
||||||
IconDevicesPlus,
|
|
||||||
IconDevicesQuestion,
|
|
||||||
IconDevicesSearch,
|
|
||||||
IconDevicesShare,
|
|
||||||
IconDevicesStar,
|
|
||||||
IconDevicesUp,
|
|
||||||
IconDevicesX,
|
|
||||||
IconDeviceTablet,
|
IconDeviceTablet,
|
||||||
IconDeviceTabletBolt,
|
IconDeviceTabletBolt,
|
||||||
IconDeviceTabletCancel,
|
IconDeviceTabletCancel,
|
||||||
@ -1727,6 +1703,30 @@ import {
|
|||||||
IconDeviceWatchStats2,
|
IconDeviceWatchStats2,
|
||||||
IconDeviceWatchUp,
|
IconDeviceWatchUp,
|
||||||
IconDeviceWatchX,
|
IconDeviceWatchX,
|
||||||
|
IconDevices,
|
||||||
|
IconDevices2,
|
||||||
|
IconDevicesBolt,
|
||||||
|
IconDevicesCancel,
|
||||||
|
IconDevicesCheck,
|
||||||
|
IconDevicesCode,
|
||||||
|
IconDevicesCog,
|
||||||
|
IconDevicesDollar,
|
||||||
|
IconDevicesDown,
|
||||||
|
IconDevicesExclamation,
|
||||||
|
IconDevicesHeart,
|
||||||
|
IconDevicesMinus,
|
||||||
|
IconDevicesOff,
|
||||||
|
IconDevicesPause,
|
||||||
|
IconDevicesPc,
|
||||||
|
IconDevicesPcOff,
|
||||||
|
IconDevicesPin,
|
||||||
|
IconDevicesPlus,
|
||||||
|
IconDevicesQuestion,
|
||||||
|
IconDevicesSearch,
|
||||||
|
IconDevicesShare,
|
||||||
|
IconDevicesStar,
|
||||||
|
IconDevicesUp,
|
||||||
|
IconDevicesX,
|
||||||
IconDiabolo,
|
IconDiabolo,
|
||||||
IconDiaboloOff,
|
IconDiaboloOff,
|
||||||
IconDiaboloPlus,
|
IconDiaboloPlus,
|
||||||
@ -1745,9 +1745,9 @@ import {
|
|||||||
IconDimensions,
|
IconDimensions,
|
||||||
IconDirection,
|
IconDirection,
|
||||||
IconDirectionHorizontal,
|
IconDirectionHorizontal,
|
||||||
IconDirections,
|
|
||||||
IconDirectionSign,
|
IconDirectionSign,
|
||||||
IconDirectionSignOff,
|
IconDirectionSignOff,
|
||||||
|
IconDirections,
|
||||||
IconDirectionsOff,
|
IconDirectionsOff,
|
||||||
IconDisabled,
|
IconDisabled,
|
||||||
IconDisabled2,
|
IconDisabled2,
|
||||||
@ -1801,13 +1801,14 @@ import {
|
|||||||
IconDropletPin,
|
IconDropletPin,
|
||||||
IconDropletPlus,
|
IconDropletPlus,
|
||||||
IconDropletQuestion,
|
IconDropletQuestion,
|
||||||
IconDroplets,
|
|
||||||
IconDropletSearch,
|
IconDropletSearch,
|
||||||
IconDropletShare,
|
IconDropletShare,
|
||||||
IconDropletStar,
|
IconDropletStar,
|
||||||
IconDropletUp,
|
IconDropletUp,
|
||||||
IconDropletX,
|
IconDropletX,
|
||||||
|
IconDroplets,
|
||||||
IconDualScreen,
|
IconDualScreen,
|
||||||
|
IconEPassport,
|
||||||
IconEar,
|
IconEar,
|
||||||
IconEarOff,
|
IconEarOff,
|
||||||
IconEaseIn,
|
IconEaseIn,
|
||||||
@ -1833,7 +1834,6 @@ import {
|
|||||||
IconEmphasis,
|
IconEmphasis,
|
||||||
IconEngine,
|
IconEngine,
|
||||||
IconEngineOff,
|
IconEngineOff,
|
||||||
IconEPassport,
|
|
||||||
IconEqual,
|
IconEqual,
|
||||||
IconEqualDouble,
|
IconEqualDouble,
|
||||||
IconEqualNot,
|
IconEqualNot,
|
||||||
@ -1872,9 +1872,6 @@ import {
|
|||||||
IconEyeDown,
|
IconEyeDown,
|
||||||
IconEyeEdit,
|
IconEyeEdit,
|
||||||
IconEyeExclamation,
|
IconEyeExclamation,
|
||||||
IconEyeglass,
|
|
||||||
IconEyeglass2,
|
|
||||||
IconEyeglassOff,
|
|
||||||
IconEyeHeart,
|
IconEyeHeart,
|
||||||
IconEyeMinus,
|
IconEyeMinus,
|
||||||
IconEyeOff,
|
IconEyeOff,
|
||||||
@ -1888,6 +1885,9 @@ import {
|
|||||||
IconEyeTable,
|
IconEyeTable,
|
||||||
IconEyeUp,
|
IconEyeUp,
|
||||||
IconEyeX,
|
IconEyeX,
|
||||||
|
IconEyeglass,
|
||||||
|
IconEyeglass2,
|
||||||
|
IconEyeglassOff,
|
||||||
IconFaceId,
|
IconFaceId,
|
||||||
IconFaceIdError,
|
IconFaceIdError,
|
||||||
IconFaceMask,
|
IconFaceMask,
|
||||||
@ -1942,13 +1942,11 @@ import {
|
|||||||
IconFilePower,
|
IconFilePower,
|
||||||
IconFileReport,
|
IconFileReport,
|
||||||
IconFileRss,
|
IconFileRss,
|
||||||
IconFiles,
|
|
||||||
IconFileScissors,
|
IconFileScissors,
|
||||||
IconFileSearch,
|
IconFileSearch,
|
||||||
IconFileSettings,
|
IconFileSettings,
|
||||||
IconFileShredder,
|
IconFileShredder,
|
||||||
IconFileSignal,
|
IconFileSignal,
|
||||||
IconFilesOff,
|
|
||||||
IconFileSpreadsheet,
|
IconFileSpreadsheet,
|
||||||
IconFileStack,
|
IconFileStack,
|
||||||
IconFileStar,
|
IconFileStar,
|
||||||
@ -1985,6 +1983,8 @@ import {
|
|||||||
IconFileVector,
|
IconFileVector,
|
||||||
IconFileX,
|
IconFileX,
|
||||||
IconFileZip,
|
IconFileZip,
|
||||||
|
IconFiles,
|
||||||
|
IconFilesOff,
|
||||||
IconFilter,
|
IconFilter,
|
||||||
IconFilterBolt,
|
IconFilterBolt,
|
||||||
IconFilterCancel,
|
IconFilterCancel,
|
||||||
@ -2003,12 +2003,12 @@ import {
|
|||||||
IconFilterPin,
|
IconFilterPin,
|
||||||
IconFilterPlus,
|
IconFilterPlus,
|
||||||
IconFilterQuestion,
|
IconFilterQuestion,
|
||||||
IconFilters,
|
|
||||||
IconFilterSearch,
|
IconFilterSearch,
|
||||||
IconFilterShare,
|
IconFilterShare,
|
||||||
IconFilterStar,
|
IconFilterStar,
|
||||||
IconFilterUp,
|
IconFilterUp,
|
||||||
IconFilterX,
|
IconFilterX,
|
||||||
|
IconFilters,
|
||||||
IconFingerprint,
|
IconFingerprint,
|
||||||
IconFingerprintOff,
|
IconFingerprintOff,
|
||||||
IconFireExtinguisher,
|
IconFireExtinguisher,
|
||||||
@ -2070,6 +2070,7 @@ import {
|
|||||||
IconFocusCentered,
|
IconFocusCentered,
|
||||||
IconFold,
|
IconFold,
|
||||||
IconFoldDown,
|
IconFoldDown,
|
||||||
|
IconFoldUp,
|
||||||
IconFolder,
|
IconFolder,
|
||||||
IconFolderBolt,
|
IconFolderBolt,
|
||||||
IconFolderCancel,
|
IconFolderCancel,
|
||||||
@ -2087,15 +2088,14 @@ import {
|
|||||||
IconFolderPin,
|
IconFolderPin,
|
||||||
IconFolderPlus,
|
IconFolderPlus,
|
||||||
IconFolderQuestion,
|
IconFolderQuestion,
|
||||||
IconFolders,
|
|
||||||
IconFolderSearch,
|
IconFolderSearch,
|
||||||
IconFolderShare,
|
IconFolderShare,
|
||||||
IconFoldersOff,
|
|
||||||
IconFolderStar,
|
IconFolderStar,
|
||||||
IconFolderSymlink,
|
IconFolderSymlink,
|
||||||
IconFolderUp,
|
IconFolderUp,
|
||||||
IconFolderX,
|
IconFolderX,
|
||||||
IconFoldUp,
|
IconFolders,
|
||||||
|
IconFoldersOff,
|
||||||
IconForbid,
|
IconForbid,
|
||||||
IconForbid2,
|
IconForbid2,
|
||||||
IconForklift,
|
IconForklift,
|
||||||
@ -2224,7 +2224,6 @@ import {
|
|||||||
IconHeadsetOff,
|
IconHeadsetOff,
|
||||||
IconHealthRecognition,
|
IconHealthRecognition,
|
||||||
IconHeart,
|
IconHeart,
|
||||||
IconHeartbeat,
|
|
||||||
IconHeartBolt,
|
IconHeartBolt,
|
||||||
IconHeartBroken,
|
IconHeartBroken,
|
||||||
IconHeartCancel,
|
IconHeartCancel,
|
||||||
@ -2243,13 +2242,14 @@ import {
|
|||||||
IconHeartPlus,
|
IconHeartPlus,
|
||||||
IconHeartQuestion,
|
IconHeartQuestion,
|
||||||
IconHeartRateMonitor,
|
IconHeartRateMonitor,
|
||||||
IconHearts,
|
|
||||||
IconHeartSearch,
|
IconHeartSearch,
|
||||||
IconHeartShare,
|
IconHeartShare,
|
||||||
IconHeartsOff,
|
|
||||||
IconHeartStar,
|
IconHeartStar,
|
||||||
IconHeartUp,
|
IconHeartUp,
|
||||||
IconHeartX,
|
IconHeartX,
|
||||||
|
IconHeartbeat,
|
||||||
|
IconHearts,
|
||||||
|
IconHeartsOff,
|
||||||
IconHelicopter,
|
IconHelicopter,
|
||||||
IconHelicopterLanding,
|
IconHelicopterLanding,
|
||||||
IconHelmet,
|
IconHelmet,
|
||||||
@ -2268,12 +2268,6 @@ import {
|
|||||||
IconHemispherePlus,
|
IconHemispherePlus,
|
||||||
IconHexagon,
|
IconHexagon,
|
||||||
IconHexagon3d,
|
IconHexagon3d,
|
||||||
IconHexagonalPrism,
|
|
||||||
IconHexagonalPrismOff,
|
|
||||||
IconHexagonalPrismPlus,
|
|
||||||
IconHexagonalPyramid,
|
|
||||||
IconHexagonalPyramidOff,
|
|
||||||
IconHexagonalPyramidPlus,
|
|
||||||
IconHexagonLetterA,
|
IconHexagonLetterA,
|
||||||
IconHexagonLetterB,
|
IconHexagonLetterB,
|
||||||
IconHexagonLetterC,
|
IconHexagonLetterC,
|
||||||
@ -2311,6 +2305,12 @@ import {
|
|||||||
IconHexagonNumber8,
|
IconHexagonNumber8,
|
||||||
IconHexagonNumber9,
|
IconHexagonNumber9,
|
||||||
IconHexagonOff,
|
IconHexagonOff,
|
||||||
|
IconHexagonalPrism,
|
||||||
|
IconHexagonalPrismOff,
|
||||||
|
IconHexagonalPrismPlus,
|
||||||
|
IconHexagonalPyramid,
|
||||||
|
IconHexagonalPyramidOff,
|
||||||
|
IconHexagonalPyramidPlus,
|
||||||
IconHexagons,
|
IconHexagons,
|
||||||
IconHexagonsOff,
|
IconHexagonsOff,
|
||||||
IconHierarchy,
|
IconHierarchy,
|
||||||
@ -2424,6 +2424,7 @@ import {
|
|||||||
IconKayak,
|
IconKayak,
|
||||||
IconKering,
|
IconKering,
|
||||||
IconKey,
|
IconKey,
|
||||||
|
IconKeyOff,
|
||||||
IconKeyboard,
|
IconKeyboard,
|
||||||
IconKeyboardHide,
|
IconKeyboardHide,
|
||||||
IconKeyboardOff,
|
IconKeyboardOff,
|
||||||
@ -2433,7 +2434,6 @@ import {
|
|||||||
IconKeyframeAlignHorizontal,
|
IconKeyframeAlignHorizontal,
|
||||||
IconKeyframeAlignVertical,
|
IconKeyframeAlignVertical,
|
||||||
IconKeyframes,
|
IconKeyframes,
|
||||||
IconKeyOff,
|
|
||||||
IconLadder,
|
IconLadder,
|
||||||
IconLadderOff,
|
IconLadderOff,
|
||||||
IconLadle,
|
IconLadle,
|
||||||
@ -2627,8 +2627,6 @@ import {
|
|||||||
IconMail,
|
IconMail,
|
||||||
IconMailAi,
|
IconMailAi,
|
||||||
IconMailBolt,
|
IconMailBolt,
|
||||||
IconMailbox,
|
|
||||||
IconMailboxOff,
|
|
||||||
IconMailCancel,
|
IconMailCancel,
|
||||||
IconMailCheck,
|
IconMailCheck,
|
||||||
IconMailCode,
|
IconMailCode,
|
||||||
@ -2651,6 +2649,8 @@ import {
|
|||||||
IconMailStar,
|
IconMailStar,
|
||||||
IconMailUp,
|
IconMailUp,
|
||||||
IconMailX,
|
IconMailX,
|
||||||
|
IconMailbox,
|
||||||
|
IconMailboxOff,
|
||||||
IconMan,
|
IconMan,
|
||||||
IconManualGearbox,
|
IconManualGearbox,
|
||||||
IconMap,
|
IconMap,
|
||||||
@ -2684,12 +2684,12 @@ import {
|
|||||||
IconMapPinPin,
|
IconMapPinPin,
|
||||||
IconMapPinPlus,
|
IconMapPinPlus,
|
||||||
IconMapPinQuestion,
|
IconMapPinQuestion,
|
||||||
IconMapPins,
|
|
||||||
IconMapPinSearch,
|
IconMapPinSearch,
|
||||||
IconMapPinShare,
|
IconMapPinShare,
|
||||||
IconMapPinStar,
|
IconMapPinStar,
|
||||||
IconMapPinUp,
|
IconMapPinUp,
|
||||||
IconMapPinX,
|
IconMapPinX,
|
||||||
|
IconMapPins,
|
||||||
IconMapPlus,
|
IconMapPlus,
|
||||||
IconMapQuestion,
|
IconMapQuestion,
|
||||||
IconMapSearch,
|
IconMapSearch,
|
||||||
@ -2720,8 +2720,8 @@ import {
|
|||||||
IconMathFunctionY,
|
IconMathFunctionY,
|
||||||
IconMathGreater,
|
IconMathGreater,
|
||||||
IconMathIntegral,
|
IconMathIntegral,
|
||||||
IconMathIntegrals,
|
|
||||||
IconMathIntegralX,
|
IconMathIntegralX,
|
||||||
|
IconMathIntegrals,
|
||||||
IconMathLower,
|
IconMathLower,
|
||||||
IconMathMax,
|
IconMathMax,
|
||||||
IconMathMin,
|
IconMathMin,
|
||||||
@ -2820,13 +2820,13 @@ import {
|
|||||||
IconMessagePlus,
|
IconMessagePlus,
|
||||||
IconMessageQuestion,
|
IconMessageQuestion,
|
||||||
IconMessageReport,
|
IconMessageReport,
|
||||||
IconMessages,
|
|
||||||
IconMessageSearch,
|
IconMessageSearch,
|
||||||
IconMessageShare,
|
IconMessageShare,
|
||||||
IconMessagesOff,
|
|
||||||
IconMessageStar,
|
IconMessageStar,
|
||||||
IconMessageUp,
|
IconMessageUp,
|
||||||
IconMessageX,
|
IconMessageX,
|
||||||
|
IconMessages,
|
||||||
|
IconMessagesOff,
|
||||||
IconMeteor,
|
IconMeteor,
|
||||||
IconMeteorOff,
|
IconMeteorOff,
|
||||||
IconMichelinBibGourmand,
|
IconMichelinBibGourmand,
|
||||||
@ -2972,8 +2972,8 @@ import {
|
|||||||
IconNeedleThread,
|
IconNeedleThread,
|
||||||
IconNetwork,
|
IconNetwork,
|
||||||
IconNetworkOff,
|
IconNetworkOff,
|
||||||
IconNews,
|
|
||||||
IconNewSection,
|
IconNewSection,
|
||||||
|
IconNews,
|
||||||
IconNewsOff,
|
IconNewsOff,
|
||||||
IconNfc,
|
IconNfc,
|
||||||
IconNfcOff,
|
IconNfcOff,
|
||||||
@ -2982,9 +2982,9 @@ import {
|
|||||||
IconNoDerivatives,
|
IconNoDerivatives,
|
||||||
IconNorthStar,
|
IconNorthStar,
|
||||||
IconNote,
|
IconNote,
|
||||||
|
IconNoteOff,
|
||||||
IconNotebook,
|
IconNotebook,
|
||||||
IconNotebookOff,
|
IconNotebookOff,
|
||||||
IconNoteOff,
|
|
||||||
IconNotes,
|
IconNotes,
|
||||||
IconNotesOff,
|
IconNotesOff,
|
||||||
IconNotification,
|
IconNotification,
|
||||||
@ -3143,8 +3143,8 @@ import {
|
|||||||
IconPlaneDeparture,
|
IconPlaneDeparture,
|
||||||
IconPlaneInflight,
|
IconPlaneInflight,
|
||||||
IconPlaneOff,
|
IconPlaneOff,
|
||||||
IconPlanet,
|
|
||||||
IconPlaneTilt,
|
IconPlaneTilt,
|
||||||
|
IconPlanet,
|
||||||
IconPlanetOff,
|
IconPlanetOff,
|
||||||
IconPlant,
|
IconPlant,
|
||||||
IconPlant2,
|
IconPlant2,
|
||||||
@ -3153,6 +3153,9 @@ import {
|
|||||||
IconPlayBasketball,
|
IconPlayBasketball,
|
||||||
IconPlayCard,
|
IconPlayCard,
|
||||||
IconPlayCardOff,
|
IconPlayCardOff,
|
||||||
|
IconPlayFootball,
|
||||||
|
IconPlayHandball,
|
||||||
|
IconPlayVolleyball,
|
||||||
IconPlayerEject,
|
IconPlayerEject,
|
||||||
IconPlayerPause,
|
IconPlayerPause,
|
||||||
IconPlayerPlay,
|
IconPlayerPlay,
|
||||||
@ -3162,8 +3165,6 @@ import {
|
|||||||
IconPlayerStop,
|
IconPlayerStop,
|
||||||
IconPlayerTrackNext,
|
IconPlayerTrackNext,
|
||||||
IconPlayerTrackPrev,
|
IconPlayerTrackPrev,
|
||||||
IconPlayFootball,
|
|
||||||
IconPlayHandball,
|
|
||||||
IconPlaylist,
|
IconPlaylist,
|
||||||
IconPlaylistAdd,
|
IconPlaylistAdd,
|
||||||
IconPlaylistOff,
|
IconPlaylistOff,
|
||||||
@ -3172,7 +3173,6 @@ import {
|
|||||||
IconPlaystationSquare,
|
IconPlaystationSquare,
|
||||||
IconPlaystationTriangle,
|
IconPlaystationTriangle,
|
||||||
IconPlaystationX,
|
IconPlaystationX,
|
||||||
IconPlayVolleyball,
|
|
||||||
IconPlug,
|
IconPlug,
|
||||||
IconPlugConnected,
|
IconPlugConnected,
|
||||||
IconPlugConnectedX,
|
IconPlugConnectedX,
|
||||||
@ -3185,6 +3185,7 @@ import {
|
|||||||
IconPodium,
|
IconPodium,
|
||||||
IconPodiumOff,
|
IconPodiumOff,
|
||||||
IconPoint,
|
IconPoint,
|
||||||
|
IconPointOff,
|
||||||
IconPointer,
|
IconPointer,
|
||||||
IconPointerBolt,
|
IconPointerBolt,
|
||||||
IconPointerCancel,
|
IconPointerCancel,
|
||||||
@ -3206,7 +3207,6 @@ import {
|
|||||||
IconPointerStar,
|
IconPointerStar,
|
||||||
IconPointerUp,
|
IconPointerUp,
|
||||||
IconPointerX,
|
IconPointerX,
|
||||||
IconPointOff,
|
|
||||||
IconPokeball,
|
IconPokeball,
|
||||||
IconPokeballOff,
|
IconPokeballOff,
|
||||||
IconPokerChip,
|
IconPokerChip,
|
||||||
@ -3256,9 +3256,9 @@ import {
|
|||||||
IconRadar2,
|
IconRadar2,
|
||||||
IconRadarOff,
|
IconRadarOff,
|
||||||
IconRadio,
|
IconRadio,
|
||||||
|
IconRadioOff,
|
||||||
IconRadioactive,
|
IconRadioactive,
|
||||||
IconRadioactiveOff,
|
IconRadioactiveOff,
|
||||||
IconRadioOff,
|
|
||||||
IconRadiusBottomLeft,
|
IconRadiusBottomLeft,
|
||||||
IconRadiusBottomRight,
|
IconRadiusBottomRight,
|
||||||
IconRadiusTopLeft,
|
IconRadiusTopLeft,
|
||||||
@ -3342,9 +3342,9 @@ import {
|
|||||||
IconRobotOff,
|
IconRobotOff,
|
||||||
IconRocket,
|
IconRocket,
|
||||||
IconRocketOff,
|
IconRocketOff,
|
||||||
|
IconRollerSkating,
|
||||||
IconRollercoaster,
|
IconRollercoaster,
|
||||||
IconRollercoasterOff,
|
IconRollercoasterOff,
|
||||||
IconRollerSkating,
|
|
||||||
IconRosette,
|
IconRosette,
|
||||||
IconRosetteNumber0,
|
IconRosetteNumber0,
|
||||||
IconRosetteNumber1,
|
IconRosetteNumber1,
|
||||||
@ -3381,6 +3381,10 @@ import {
|
|||||||
IconRulerMeasure,
|
IconRulerMeasure,
|
||||||
IconRulerOff,
|
IconRulerOff,
|
||||||
IconRun,
|
IconRun,
|
||||||
|
IconSTurnDown,
|
||||||
|
IconSTurnLeft,
|
||||||
|
IconSTurnRight,
|
||||||
|
IconSTurnUp,
|
||||||
IconSailboat,
|
IconSailboat,
|
||||||
IconSailboat2,
|
IconSailboat2,
|
||||||
IconSailboatOff,
|
IconSailboatOff,
|
||||||
@ -3471,6 +3475,7 @@ import {
|
|||||||
IconShare2,
|
IconShare2,
|
||||||
IconShare3,
|
IconShare3,
|
||||||
IconShareOff,
|
IconShareOff,
|
||||||
|
IconShiJumping,
|
||||||
IconShield,
|
IconShield,
|
||||||
IconShieldBolt,
|
IconShieldBolt,
|
||||||
IconShieldCancel,
|
IconShieldCancel,
|
||||||
@ -3496,7 +3501,6 @@ import {
|
|||||||
IconShieldStar,
|
IconShieldStar,
|
||||||
IconShieldUp,
|
IconShieldUp,
|
||||||
IconShieldX,
|
IconShieldX,
|
||||||
IconShiJumping,
|
|
||||||
IconShip,
|
IconShip,
|
||||||
IconShipOff,
|
IconShipOff,
|
||||||
IconShirt,
|
IconShirt,
|
||||||
@ -3538,6 +3542,8 @@ import {
|
|||||||
IconShoppingCartX,
|
IconShoppingCartX,
|
||||||
IconShovel,
|
IconShovel,
|
||||||
IconShredder,
|
IconShredder,
|
||||||
|
IconSignLeft,
|
||||||
|
IconSignRight,
|
||||||
IconSignal2g,
|
IconSignal2g,
|
||||||
IconSignal3g,
|
IconSignal3g,
|
||||||
IconSignal4g,
|
IconSignal4g,
|
||||||
@ -3551,13 +3557,11 @@ import {
|
|||||||
IconSignalLte,
|
IconSignalLte,
|
||||||
IconSignature,
|
IconSignature,
|
||||||
IconSignatureOff,
|
IconSignatureOff,
|
||||||
IconSignLeft,
|
|
||||||
IconSignRight,
|
|
||||||
IconSitemap,
|
IconSitemap,
|
||||||
IconSitemapOff,
|
IconSitemapOff,
|
||||||
IconSkateboard,
|
IconSkateboard,
|
||||||
IconSkateboarding,
|
|
||||||
IconSkateboardOff,
|
IconSkateboardOff,
|
||||||
|
IconSkateboarding,
|
||||||
IconSkull,
|
IconSkull,
|
||||||
IconSlash,
|
IconSlash,
|
||||||
IconSlashes,
|
IconSlashes,
|
||||||
@ -3581,11 +3585,11 @@ import {
|
|||||||
IconSolarPanel2,
|
IconSolarPanel2,
|
||||||
IconSort09,
|
IconSort09,
|
||||||
IconSort90,
|
IconSort90,
|
||||||
|
IconSortAZ,
|
||||||
IconSortAscending,
|
IconSortAscending,
|
||||||
IconSortAscending2,
|
IconSortAscending2,
|
||||||
IconSortAscendingLetters,
|
IconSortAscendingLetters,
|
||||||
IconSortAscendingNumbers,
|
IconSortAscendingNumbers,
|
||||||
IconSortAZ,
|
|
||||||
IconSortDescending,
|
IconSortDescending,
|
||||||
IconSortDescending2,
|
IconSortDescending2,
|
||||||
IconSortDescendingLetters,
|
IconSortDescendingLetters,
|
||||||
@ -3624,11 +3628,11 @@ import {
|
|||||||
IconSquareChevronDown,
|
IconSquareChevronDown,
|
||||||
IconSquareChevronLeft,
|
IconSquareChevronLeft,
|
||||||
IconSquareChevronRight,
|
IconSquareChevronRight,
|
||||||
|
IconSquareChevronUp,
|
||||||
IconSquareChevronsDown,
|
IconSquareChevronsDown,
|
||||||
IconSquareChevronsLeft,
|
IconSquareChevronsLeft,
|
||||||
IconSquareChevronsRight,
|
IconSquareChevronsRight,
|
||||||
IconSquareChevronsUp,
|
IconSquareChevronsUp,
|
||||||
IconSquareChevronUp,
|
|
||||||
IconSquareDot,
|
IconSquareDot,
|
||||||
IconSquareF0,
|
IconSquareF0,
|
||||||
IconSquareF1,
|
IconSquareF1,
|
||||||
@ -3698,11 +3702,11 @@ import {
|
|||||||
IconSquareRoundedChevronDown,
|
IconSquareRoundedChevronDown,
|
||||||
IconSquareRoundedChevronLeft,
|
IconSquareRoundedChevronLeft,
|
||||||
IconSquareRoundedChevronRight,
|
IconSquareRoundedChevronRight,
|
||||||
|
IconSquareRoundedChevronUp,
|
||||||
IconSquareRoundedChevronsDown,
|
IconSquareRoundedChevronsDown,
|
||||||
IconSquareRoundedChevronsLeft,
|
IconSquareRoundedChevronsLeft,
|
||||||
IconSquareRoundedChevronsRight,
|
IconSquareRoundedChevronsRight,
|
||||||
IconSquareRoundedChevronsUp,
|
IconSquareRoundedChevronsUp,
|
||||||
IconSquareRoundedChevronUp,
|
|
||||||
IconSquareRoundedLetterA,
|
IconSquareRoundedLetterA,
|
||||||
IconSquareRoundedLetterB,
|
IconSquareRoundedLetterB,
|
||||||
IconSquareRoundedLetterC,
|
IconSquareRoundedLetterC,
|
||||||
@ -3742,10 +3746,10 @@ import {
|
|||||||
IconSquareRoundedNumber9,
|
IconSquareRoundedNumber9,
|
||||||
IconSquareRoundedPlus,
|
IconSquareRoundedPlus,
|
||||||
IconSquareRoundedX,
|
IconSquareRoundedX,
|
||||||
IconSquaresDiagonal,
|
|
||||||
IconSquareToggle,
|
IconSquareToggle,
|
||||||
IconSquareToggleHorizontal,
|
IconSquareToggleHorizontal,
|
||||||
IconSquareX,
|
IconSquareX,
|
||||||
|
IconSquaresDiagonal,
|
||||||
IconStack,
|
IconStack,
|
||||||
IconStack2,
|
IconStack2,
|
||||||
IconStack3,
|
IconStack3,
|
||||||
@ -3774,25 +3778,21 @@ import {
|
|||||||
IconStretching,
|
IconStretching,
|
||||||
IconStretching2,
|
IconStretching2,
|
||||||
IconStrikethrough,
|
IconStrikethrough,
|
||||||
IconSTurnDown,
|
|
||||||
IconSTurnLeft,
|
|
||||||
IconSTurnRight,
|
|
||||||
IconSTurnUp,
|
|
||||||
IconSubmarine,
|
IconSubmarine,
|
||||||
IconSubscript,
|
IconSubscript,
|
||||||
IconSubtask,
|
IconSubtask,
|
||||||
IconSum,
|
IconSum,
|
||||||
IconSumOff,
|
IconSumOff,
|
||||||
IconSun,
|
IconSun,
|
||||||
IconSunglasses,
|
|
||||||
IconSunHigh,
|
IconSunHigh,
|
||||||
IconSunLow,
|
IconSunLow,
|
||||||
IconSunMoon,
|
IconSunMoon,
|
||||||
IconSunOff,
|
IconSunOff,
|
||||||
|
IconSunWind,
|
||||||
|
IconSunglasses,
|
||||||
IconSunrise,
|
IconSunrise,
|
||||||
IconSunset,
|
IconSunset,
|
||||||
IconSunset2,
|
IconSunset2,
|
||||||
IconSunWind,
|
|
||||||
IconSuperscript,
|
IconSuperscript,
|
||||||
IconSvg,
|
IconSvg,
|
||||||
IconSwimming,
|
IconSwimming,
|
||||||
@ -3863,18 +3863,18 @@ import {
|
|||||||
IconTextResize,
|
IconTextResize,
|
||||||
IconTextSize,
|
IconTextSize,
|
||||||
IconTextSpellcheck,
|
IconTextSpellcheck,
|
||||||
IconTexture,
|
|
||||||
IconTextWrap,
|
IconTextWrap,
|
||||||
IconTextWrapDisabled,
|
IconTextWrapDisabled,
|
||||||
|
IconTexture,
|
||||||
IconTheater,
|
IconTheater,
|
||||||
IconThermometer,
|
IconThermometer,
|
||||||
IconThumbDown,
|
IconThumbDown,
|
||||||
IconThumbDownOff,
|
IconThumbDownOff,
|
||||||
IconThumbUp,
|
IconThumbUp,
|
||||||
IconThumbUpOff,
|
IconThumbUpOff,
|
||||||
|
IconTicTac,
|
||||||
IconTicket,
|
IconTicket,
|
||||||
IconTicketOff,
|
IconTicketOff,
|
||||||
IconTicTac,
|
|
||||||
IconTie,
|
IconTie,
|
||||||
IconTilde,
|
IconTilde,
|
||||||
IconTiltShift,
|
IconTiltShift,
|
||||||
@ -3960,8 +3960,8 @@ import {
|
|||||||
IconTriangle,
|
IconTriangle,
|
||||||
IconTriangleInverted,
|
IconTriangleInverted,
|
||||||
IconTriangleOff,
|
IconTriangleOff,
|
||||||
IconTriangles,
|
|
||||||
IconTriangleSquareCircle,
|
IconTriangleSquareCircle,
|
||||||
|
IconTriangles,
|
||||||
IconTrident,
|
IconTrident,
|
||||||
IconTrolley,
|
IconTrolley,
|
||||||
IconTrophy,
|
IconTrophy,
|
||||||
@ -4002,16 +4002,16 @@ import {
|
|||||||
IconUserPin,
|
IconUserPin,
|
||||||
IconUserPlus,
|
IconUserPlus,
|
||||||
IconUserQuestion,
|
IconUserQuestion,
|
||||||
IconUsers,
|
|
||||||
IconUserSearch,
|
IconUserSearch,
|
||||||
IconUsersGroup,
|
|
||||||
IconUserShare,
|
IconUserShare,
|
||||||
IconUserShield,
|
IconUserShield,
|
||||||
IconUsersMinus,
|
|
||||||
IconUsersPlus,
|
|
||||||
IconUserStar,
|
IconUserStar,
|
||||||
IconUserUp,
|
IconUserUp,
|
||||||
IconUserX,
|
IconUserX,
|
||||||
|
IconUsers,
|
||||||
|
IconUsersGroup,
|
||||||
|
IconUsersMinus,
|
||||||
|
IconUsersPlus,
|
||||||
IconUvIndex,
|
IconUvIndex,
|
||||||
IconUxCircle,
|
IconUxCircle,
|
||||||
IconVaccine,
|
IconVaccine,
|
||||||
@ -4060,9 +4060,9 @@ import {
|
|||||||
IconVolumeOff,
|
IconVolumeOff,
|
||||||
IconWalk,
|
IconWalk,
|
||||||
IconWall,
|
IconWall,
|
||||||
|
IconWallOff,
|
||||||
IconWallet,
|
IconWallet,
|
||||||
IconWalletOff,
|
IconWalletOff,
|
||||||
IconWallOff,
|
|
||||||
IconWallpaper,
|
IconWallpaper,
|
||||||
IconWallpaperOff,
|
IconWallpaperOff,
|
||||||
IconWand,
|
IconWand,
|
||||||
@ -4073,8 +4073,6 @@ import {
|
|||||||
IconWashDry2,
|
IconWashDry2,
|
||||||
IconWashDry3,
|
IconWashDry3,
|
||||||
IconWashDryA,
|
IconWashDryA,
|
||||||
IconWashDryclean,
|
|
||||||
IconWashDrycleanOff,
|
|
||||||
IconWashDryDip,
|
IconWashDryDip,
|
||||||
IconWashDryF,
|
IconWashDryF,
|
||||||
IconWashDryFlat,
|
IconWashDryFlat,
|
||||||
@ -4083,6 +4081,8 @@ import {
|
|||||||
IconWashDryP,
|
IconWashDryP,
|
||||||
IconWashDryShade,
|
IconWashDryShade,
|
||||||
IconWashDryW,
|
IconWashDryW,
|
||||||
|
IconWashDryclean,
|
||||||
|
IconWashDrycleanOff,
|
||||||
IconWashEco,
|
IconWashEco,
|
||||||
IconWashGentle,
|
IconWashGentle,
|
||||||
IconWashHand,
|
IconWashHand,
|
||||||
@ -4113,9 +4113,9 @@ import {
|
|||||||
IconWifi2,
|
IconWifi2,
|
||||||
IconWifiOff,
|
IconWifiOff,
|
||||||
IconWind,
|
IconWind,
|
||||||
|
IconWindOff,
|
||||||
IconWindmill,
|
IconWindmill,
|
||||||
IconWindmillOff,
|
IconWindmillOff,
|
||||||
IconWindOff,
|
|
||||||
IconWindow,
|
IconWindow,
|
||||||
IconWindowMaximize,
|
IconWindowMaximize,
|
||||||
IconWindowMinimize,
|
IconWindowMinimize,
|
||||||
|
|||||||
Reference in New Issue
Block a user