Action menu refactoring (#11454)
# Description Closes [#696](https://github.com/twentyhq/core-team-issues/issues/696) - `useAction` hooks have been removed for all actions - Every action can now declare a react component - Some standard action components have been introduced: `Action`, `ActionLink` and `ActionModal` - The `ActionDisplay` component uses the new `displayType` prop of the `ActionMenuContext` to render the right component for the action according to its container: `ActionButton`, `ActionDropdownItem` or `ActionListItem` - The `ActionDisplayer` wraps the action component inside a context which gives it all the information about the action -`actionMenuEntriesComponenState` has been removed and now all actions are computed directly using `useRegisteredAction` - This computation is done inside `ActionMenuContextProvider` and the actions are passed inside a context - `actionMenuType` gives information about the container of the action, so the action can know wether or not to close this container upon execution
This commit is contained in:
@ -0,0 +1,27 @@
|
||||
import { ActionDisplay } from '@/action-menu/actions/display/components/ActionDisplay';
|
||||
import { ActionConfigContext } from '@/action-menu/contexts/ActionConfigContext';
|
||||
import { useCloseActionMenu } from '@/action-menu/hooks/useCloseActionMenu';
|
||||
import { useContext } from 'react';
|
||||
|
||||
export const Action = ({
|
||||
onClick,
|
||||
preventCommandMenuClosing,
|
||||
}: {
|
||||
onClick: () => void;
|
||||
preventCommandMenuClosing?: boolean;
|
||||
}) => {
|
||||
const actionConfig = useContext(ActionConfigContext);
|
||||
|
||||
const { closeActionMenu } = useCloseActionMenu(preventCommandMenuClosing);
|
||||
|
||||
if (!actionConfig) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const handleClick = () => {
|
||||
closeActionMenu();
|
||||
onClick();
|
||||
};
|
||||
|
||||
return <ActionDisplay action={actionConfig} onClick={handleClick} />;
|
||||
};
|
||||
@ -0,0 +1,35 @@
|
||||
import { ActionDisplay } from '@/action-menu/actions/display/components/ActionDisplay';
|
||||
import { ActionConfigContext } from '@/action-menu/contexts/ActionConfigContext';
|
||||
import { useCloseActionMenu } from '@/action-menu/hooks/useCloseActionMenu';
|
||||
import { AppPath } from '@/types/AppPath';
|
||||
import { useContext } from 'react';
|
||||
import { PathParam } from 'react-router-dom';
|
||||
import { getAppPath } from '~/utils/navigation/getAppPath';
|
||||
|
||||
export const ActionLink = <T extends AppPath>({
|
||||
to,
|
||||
params,
|
||||
queryParams,
|
||||
}: {
|
||||
to: T;
|
||||
params?: { [key in PathParam<T>]: string | null };
|
||||
queryParams?: Record<string, any>;
|
||||
}) => {
|
||||
const actionConfig = useContext(ActionConfigContext);
|
||||
|
||||
const { closeActionMenu } = useCloseActionMenu();
|
||||
|
||||
if (!actionConfig) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const path = getAppPath(to, params, queryParams);
|
||||
|
||||
return (
|
||||
<ActionDisplay
|
||||
action={{ ...actionConfig }}
|
||||
onClick={closeActionMenu}
|
||||
to={path}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,66 @@
|
||||
import { ReactNode, useCallback, useContext, useState } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
|
||||
import { ActionDisplay } from '@/action-menu/actions/display/components/ActionDisplay';
|
||||
import { ActionConfigContext } from '@/action-menu/contexts/ActionConfigContext';
|
||||
import { useCloseActionMenu } from '@/action-menu/hooks/useCloseActionMenu';
|
||||
import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal';
|
||||
import { ButtonAccent } from 'twenty-ui/input';
|
||||
|
||||
export type ActionModalProps = {
|
||||
title: string;
|
||||
subtitle: ReactNode;
|
||||
onConfirmClick: () => void;
|
||||
confirmButtonText?: string;
|
||||
confirmButtonAccent?: ButtonAccent;
|
||||
isLoading?: boolean;
|
||||
};
|
||||
|
||||
export const ActionModal = ({
|
||||
title,
|
||||
subtitle,
|
||||
onConfirmClick,
|
||||
confirmButtonText = 'Confirm',
|
||||
confirmButtonAccent = 'danger',
|
||||
isLoading = false,
|
||||
}: ActionModalProps) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
const handleOpen = useCallback(() => {
|
||||
setIsOpen(true);
|
||||
}, []);
|
||||
|
||||
const { closeActionMenu } = useCloseActionMenu();
|
||||
|
||||
const handleConfirmClick = () => {
|
||||
closeActionMenu();
|
||||
onConfirmClick();
|
||||
setIsOpen(false);
|
||||
};
|
||||
|
||||
const actionConfig = useContext(ActionConfigContext);
|
||||
|
||||
if (!actionConfig) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<ActionDisplay action={actionConfig} onClick={handleOpen} />
|
||||
{isOpen &&
|
||||
createPortal(
|
||||
<ConfirmationModal
|
||||
isOpen={isOpen}
|
||||
setIsOpen={setIsOpen}
|
||||
title={title}
|
||||
subtitle={subtitle}
|
||||
onConfirmClick={handleConfirmClick}
|
||||
confirmButtonText={confirmButtonText}
|
||||
confirmButtonAccent={confirmButtonAccent}
|
||||
loading={isLoading}
|
||||
/>,
|
||||
document.body,
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,66 @@
|
||||
import { ActionDisplayProps } from '@/action-menu/actions/display/components/ActionDisplay';
|
||||
import { getActionLabel } from '@/action-menu/utils/getActionLabel';
|
||||
import styled from '@emotion/styled';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { AppTooltip, TooltipDelay, TooltipPosition } from 'twenty-ui/display';
|
||||
import { Button, IconButton } from 'twenty-ui/input';
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
font-size: ${({ theme }) => theme.font.size.md};
|
||||
`;
|
||||
|
||||
export const ActionButton = ({
|
||||
action,
|
||||
onClick,
|
||||
to,
|
||||
}: {
|
||||
action: ActionDisplayProps;
|
||||
onClick?: (event?: React.MouseEvent<HTMLElement>) => void;
|
||||
to?: string;
|
||||
}) => {
|
||||
const label = getActionLabel(action.label);
|
||||
|
||||
const shortLabel = isDefined(action.shortLabel)
|
||||
? getActionLabel(action.shortLabel)
|
||||
: undefined;
|
||||
|
||||
return (
|
||||
<>
|
||||
{action.shortLabel ? (
|
||||
<Button
|
||||
Icon={action.Icon}
|
||||
size="small"
|
||||
variant="secondary"
|
||||
accent="default"
|
||||
to={to}
|
||||
onClick={onClick}
|
||||
title={shortLabel}
|
||||
ariaLabel={label}
|
||||
/>
|
||||
) : (
|
||||
<div id={`action-menu-entry-${action.key}`} key={action.key}>
|
||||
<IconButton
|
||||
Icon={action.Icon}
|
||||
size="small"
|
||||
variant="secondary"
|
||||
accent="default"
|
||||
to={to}
|
||||
onClick={onClick}
|
||||
ariaLabel={label}
|
||||
/>
|
||||
<StyledWrapper>
|
||||
<AppTooltip
|
||||
// eslint-disable-next-line
|
||||
anchorSelect={`#action-menu-entry-${action.key}`}
|
||||
content={label}
|
||||
delay={TooltipDelay.longDelay}
|
||||
place={TooltipPosition.Bottom}
|
||||
offset={5}
|
||||
noArrow
|
||||
/>
|
||||
</StyledWrapper>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,10 @@
|
||||
import { ActionConfig } from '@/action-menu/actions/types/ActionConfig';
|
||||
import { ActionConfigContext } from '@/action-menu/contexts/ActionConfigContext';
|
||||
|
||||
export const ActionComponent = ({ action }: { action: ActionConfig }) => {
|
||||
return (
|
||||
<ActionConfigContext.Provider value={action}>
|
||||
{action.component}
|
||||
</ActionConfigContext.Provider>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,52 @@
|
||||
import { ActionButton } from '@/action-menu/actions/display/components/ActionButton';
|
||||
import { ActionDropdownItem } from '@/action-menu/actions/display/components/ActionDropdownItem';
|
||||
import { ActionListItem } from '@/action-menu/actions/display/components/ActionListItem';
|
||||
import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext';
|
||||
import { MessageDescriptor } from '@lingui/core';
|
||||
import { useContext } from 'react';
|
||||
import { assertUnreachable } from 'twenty-shared/utils';
|
||||
import { IconComponent } from 'twenty-ui/display';
|
||||
import { MenuItemAccent } from 'twenty-ui/navigation';
|
||||
|
||||
export type ActionDisplayProps = {
|
||||
key: string;
|
||||
label: MessageDescriptor | string;
|
||||
shortLabel?: MessageDescriptor | string;
|
||||
description?: MessageDescriptor | string;
|
||||
Icon: IconComponent;
|
||||
accent?: MenuItemAccent;
|
||||
hotKeys?: string[];
|
||||
};
|
||||
|
||||
export const ActionDisplay = ({
|
||||
action,
|
||||
onClick,
|
||||
to,
|
||||
}: {
|
||||
action: ActionDisplayProps;
|
||||
onClick?: (event?: React.MouseEvent<HTMLElement>) => void;
|
||||
to?: string;
|
||||
}) => {
|
||||
const { displayType } = useContext(ActionMenuContext);
|
||||
|
||||
if (!action) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (displayType === 'button') {
|
||||
return <ActionButton action={action} onClick={onClick} to={to} />;
|
||||
}
|
||||
|
||||
if (displayType === 'listItem') {
|
||||
return <ActionListItem action={action} onClick={onClick} to={to} />;
|
||||
}
|
||||
|
||||
if (displayType === 'dropdownItem') {
|
||||
return <ActionDropdownItem action={action} onClick={onClick} to={to} />;
|
||||
}
|
||||
|
||||
return assertUnreachable(
|
||||
displayType,
|
||||
`Unsupported display type: ${displayType}`,
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,33 @@
|
||||
import { ActionDisplayProps } from '@/action-menu/actions/display/components/ActionDisplay';
|
||||
import { getActionLabel } from '@/action-menu/utils/getActionLabel';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { MenuItem } from 'twenty-ui/navigation';
|
||||
|
||||
export const ActionDropdownItem = ({
|
||||
action,
|
||||
onClick,
|
||||
to,
|
||||
}: {
|
||||
action: ActionDisplayProps;
|
||||
onClick?: () => void;
|
||||
to?: string;
|
||||
}) => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleClick = () => {
|
||||
onClick?.();
|
||||
if (isDefined(to)) {
|
||||
navigate(to);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<MenuItem
|
||||
key={action.key}
|
||||
LeftIcon={action.Icon}
|
||||
onClick={handleClick}
|
||||
text={getActionLabel(action.label)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,47 @@
|
||||
import { ActionDisplayProps } from '@/action-menu/actions/display/components/ActionDisplay';
|
||||
import { getActionLabel } from '@/action-menu/utils/getActionLabel';
|
||||
import { CommandMenuItem } from '@/command-menu/components/CommandMenuItem';
|
||||
import { SelectableItem } from '@/ui/layout/selectable-list/components/SelectableItem';
|
||||
import { useListenToEnterHotkeyOnListItem } from '@/ui/layout/selectable-list/hooks/useListenToEnterHotkeyOnListItem';
|
||||
import { AppHotkeyScope } from '@/ui/utilities/hotkey/types/AppHotkeyScope';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const ActionListItem = ({
|
||||
action,
|
||||
onClick,
|
||||
to,
|
||||
}: {
|
||||
action: ActionDisplayProps;
|
||||
onClick?: () => void;
|
||||
to?: string;
|
||||
}) => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleClick = () => {
|
||||
onClick?.();
|
||||
if (isDefined(to)) {
|
||||
navigate(to);
|
||||
}
|
||||
};
|
||||
|
||||
useListenToEnterHotkeyOnListItem({
|
||||
hotkeyScope: AppHotkeyScope.CommandMenuOpen,
|
||||
itemId: action.key,
|
||||
onEnter: handleClick,
|
||||
});
|
||||
|
||||
return (
|
||||
<SelectableItem itemId={action.key}>
|
||||
<CommandMenuItem
|
||||
id={action.key}
|
||||
Icon={action.Icon}
|
||||
label={getActionLabel(action.label)}
|
||||
description={getActionLabel(action.description ?? '')}
|
||||
to={to}
|
||||
onClick={onClick}
|
||||
hotKeys={action.hotKeys}
|
||||
/>
|
||||
</SelectableItem>
|
||||
);
|
||||
};
|
||||
@ -1,77 +0,0 @@
|
||||
import { RegisterRecordActionEffects } from '@/action-menu/actions/record-actions/components/RegisterRecordActionEffects';
|
||||
import { RegisterWorkflowRecordActionEffects } from '@/action-menu/actions/record-actions/components/RegisterWorkflowRecordActionEffects';
|
||||
import { WorkflowRunRecordActionMenuEntrySetterEffect } from '@/action-menu/actions/record-actions/workflow-run-record-actions/components/WorkflowRunRecordActionMenuEntrySetter';
|
||||
import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainContextStoreInstanceId';
|
||||
import { contextStoreCurrentObjectMetadataItemIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataItemIdComponentState';
|
||||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { FeatureFlagKey } from '~/generated/graphql';
|
||||
|
||||
export const RecordActionMenuEntriesSetter = () => {
|
||||
const localContextStoreCurrentObjectMetadataItemId =
|
||||
useRecoilComponentValueV2(
|
||||
contextStoreCurrentObjectMetadataItemIdComponentState,
|
||||
);
|
||||
|
||||
const mainContextStoreCurrentObjectMetadataItemId = useRecoilComponentValueV2(
|
||||
contextStoreCurrentObjectMetadataItemIdComponentState,
|
||||
MAIN_CONTEXT_STORE_INSTANCE_ID,
|
||||
);
|
||||
|
||||
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
|
||||
|
||||
const localContextStoreObjectMetadataItem = objectMetadataItems.find(
|
||||
(objectMetadataItem) =>
|
||||
objectMetadataItem.id === localContextStoreCurrentObjectMetadataItemId,
|
||||
);
|
||||
|
||||
const mainContextStoreObjectMetadataItem = objectMetadataItems.find(
|
||||
(objectMetadataItem) =>
|
||||
objectMetadataItem.id === mainContextStoreCurrentObjectMetadataItemId,
|
||||
);
|
||||
|
||||
const objectMetadataItem =
|
||||
localContextStoreObjectMetadataItem ?? mainContextStoreObjectMetadataItem;
|
||||
|
||||
const contextStoreTargetedRecordsRule = useRecoilComponentValueV2(
|
||||
contextStoreTargetedRecordsRuleComponentState,
|
||||
);
|
||||
|
||||
const isWorkflowEnabled = useIsFeatureEnabled(
|
||||
FeatureFlagKey.IsWorkflowEnabled,
|
||||
);
|
||||
|
||||
if (!isDefined(objectMetadataItem)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const isWorkflowObject =
|
||||
objectMetadataItem.nameSingular === CoreObjectNameSingular.Workflow ||
|
||||
objectMetadataItem.nameSingular === CoreObjectNameSingular.WorkflowRun ||
|
||||
objectMetadataItem.nameSingular === CoreObjectNameSingular.WorkflowVersion;
|
||||
|
||||
return (
|
||||
<>
|
||||
{isWorkflowObject ? (
|
||||
<RegisterWorkflowRecordActionEffects
|
||||
objectMetadataItem={objectMetadataItem}
|
||||
/>
|
||||
) : (
|
||||
<RegisterRecordActionEffects objectMetadataItem={objectMetadataItem} />
|
||||
)}
|
||||
|
||||
{isWorkflowEnabled &&
|
||||
contextStoreTargetedRecordsRule?.mode === 'selection' &&
|
||||
contextStoreTargetedRecordsRule?.selectedRecordIds.length === 1 && (
|
||||
<WorkflowRunRecordActionMenuEntrySetterEffect
|
||||
objectMetadataItem={objectMetadataItem}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
@ -1,45 +0,0 @@
|
||||
import { RecordConfigAction } from '@/action-menu/actions/types/RecordConfigAction';
|
||||
import { wrapActionInCallbacks } from '@/action-menu/actions/utils/wrapActionInCallbacks';
|
||||
import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext';
|
||||
import { useActionMenuEntries } from '@/action-menu/hooks/useActionMenuEntries';
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { useContext, useEffect } from 'react';
|
||||
|
||||
type RegisterRecordActionEffectProps = {
|
||||
action: RecordConfigAction;
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
};
|
||||
|
||||
export const RegisterRecordActionEffect = ({
|
||||
action,
|
||||
objectMetadataItem,
|
||||
}: RegisterRecordActionEffectProps) => {
|
||||
const { onClick, ConfirmationModal } = action.useAction({
|
||||
objectMetadataItem,
|
||||
});
|
||||
|
||||
const { onActionStartedCallback, onActionExecutedCallback } =
|
||||
useContext(ActionMenuContext);
|
||||
|
||||
const { addActionMenuEntry, removeActionMenuEntry } = useActionMenuEntries();
|
||||
|
||||
const wrappedAction = wrapActionInCallbacks({
|
||||
action: {
|
||||
...action,
|
||||
onClick,
|
||||
ConfirmationModal,
|
||||
},
|
||||
onActionStartedCallback,
|
||||
onActionExecutedCallback,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
addActionMenuEntry(wrappedAction);
|
||||
|
||||
return () => {
|
||||
removeActionMenuEntry(wrappedAction.key);
|
||||
};
|
||||
}, [addActionMenuEntry, removeActionMenuEntry, wrappedAction]);
|
||||
|
||||
return null;
|
||||
};
|
||||
@ -1,33 +0,0 @@
|
||||
import { RegisterRecordActionEffect } from '@/action-menu/actions/record-actions/components/RegisterRecordActionEffect';
|
||||
import { useRegisteredRecordActions } from '@/action-menu/hooks/useRegisteredRecordActions';
|
||||
import { useShouldActionBeRegisteredParams } from '@/action-menu/hooks/useShouldActionBeRegisteredParams';
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
|
||||
type RegisterRecordActionEffectsProps = {
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
};
|
||||
|
||||
export const RegisterRecordActionEffects = ({
|
||||
objectMetadataItem,
|
||||
}: RegisterRecordActionEffectsProps) => {
|
||||
const shouldBeRegisteredParams = useShouldActionBeRegisteredParams({
|
||||
objectMetadataItem,
|
||||
});
|
||||
|
||||
const actionsToRegister = useRegisteredRecordActions({
|
||||
objectMetadataItem,
|
||||
shouldBeRegisteredParams,
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
{actionsToRegister.map((action) => (
|
||||
<RegisterRecordActionEffect
|
||||
key={action.key}
|
||||
action={action}
|
||||
objectMetadataItem={objectMetadataItem}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
};
|
||||
@ -1,50 +0,0 @@
|
||||
import { RegisterRecordActionEffect } from '@/action-menu/actions/record-actions/components/RegisterRecordActionEffect';
|
||||
import { useRegisteredRecordActions } from '@/action-menu/hooks/useRegisteredRecordActions';
|
||||
import { useShouldActionBeRegisteredParams } from '@/action-menu/hooks/useShouldActionBeRegisteredParams';
|
||||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||
|
||||
type RegisterWorkflowRecordActionEffectsProps = {
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
};
|
||||
|
||||
export const RegisterWorkflowRecordActionEffects = ({
|
||||
objectMetadataItem,
|
||||
}: RegisterWorkflowRecordActionEffectsProps) => {
|
||||
const shouldBeRegisteredParams = useShouldActionBeRegisteredParams({
|
||||
objectMetadataItem,
|
||||
});
|
||||
|
||||
const contextStoreTargetedRecordsRule = useRecoilComponentValueV2(
|
||||
contextStoreTargetedRecordsRuleComponentState,
|
||||
);
|
||||
|
||||
const recordId =
|
||||
contextStoreTargetedRecordsRule.mode === 'selection'
|
||||
? contextStoreTargetedRecordsRule.selectedRecordIds[0]
|
||||
: undefined;
|
||||
|
||||
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(recordId);
|
||||
|
||||
const actionsToRegister = useRegisteredRecordActions({
|
||||
objectMetadataItem,
|
||||
shouldBeRegisteredParams: {
|
||||
...shouldBeRegisteredParams,
|
||||
workflowWithCurrentVersion,
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
{actionsToRegister.map((action) => (
|
||||
<RegisterRecordActionEffect
|
||||
key={action.key}
|
||||
action={action}
|
||||
objectMetadataItem={objectMetadataItem}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
};
|
||||
@ -1,35 +1,32 @@
|
||||
import { useDeleteMultipleRecordsAction } from '@/action-menu/actions/record-actions/multiple-records/hooks/useDeleteMultipleRecordsAction';
|
||||
import { useDestroyMultipleRecordsAction } from '@/action-menu/actions/record-actions/multiple-records/hooks/useDestroyMultipleRecordsAction';
|
||||
import { useExportMultipleRecordsAction } from '@/action-menu/actions/record-actions/multiple-records/hooks/useExportMultipleRecordsAction';
|
||||
import { useRestoreMultipleRecordsAction } from '@/action-menu/actions/record-actions/multiple-records/hooks/useRestoreMultipleRecordsAction';
|
||||
import { ActionLink } from '@/action-menu/actions/components/ActionLink';
|
||||
import { DeleteMultipleRecordsAction } from '@/action-menu/actions/record-actions/multiple-records/components/DeleteMultipleRecordsAction';
|
||||
import { DestroyMultipleRecordsAction } from '@/action-menu/actions/record-actions/multiple-records/components/DestroyMultipleRecordsAction';
|
||||
import { ExportMultipleRecordsAction } from '@/action-menu/actions/record-actions/multiple-records/components/ExportMultipleRecordsAction';
|
||||
import { RestoreMultipleRecordsAction } from '@/action-menu/actions/record-actions/multiple-records/components/RestoreMultipleRecordsAction';
|
||||
import { MultipleRecordsActionKeys } from '@/action-menu/actions/record-actions/multiple-records/types/MultipleRecordsActionKeys';
|
||||
import { useCreateNewTableRecordNoSelectionRecordAction } from '@/action-menu/actions/record-actions/no-selection/hooks/useCreateNewTableRecordNoSelectionRecordAction';
|
||||
import { useGoToCompaniesNoSelectionRecordAction } from '@/action-menu/actions/record-actions/no-selection/hooks/useGoToCompaniesNoSelectionRecordAction';
|
||||
import { useGoToOpportunitiesNoSelectionRecordAction } from '@/action-menu/actions/record-actions/no-selection/hooks/useGoToOpportunitiesNoSelectionRecordAction';
|
||||
import { useGoToPeopleNoSelectionRecordAction } from '@/action-menu/actions/record-actions/no-selection/hooks/useGoToPeopleNoSelectionRecordAction';
|
||||
import { useGoToSettingsNoSelectionRecordAction } from '@/action-menu/actions/record-actions/no-selection/hooks/useGoToSettingsNoSelectionRecordAction';
|
||||
import { useGoToTasksNoSelectionRecordAction } from '@/action-menu/actions/record-actions/no-selection/hooks/useGoToTasksNoSelectionRecordAction';
|
||||
import { useHideDeletedRecordsNoSelectionRecordAction } from '@/action-menu/actions/record-actions/no-selection/hooks/useHideDeletedRecordsNoSelectionRecordAction';
|
||||
import { useImportRecordsNoSelectionRecordAction } from '@/action-menu/actions/record-actions/no-selection/hooks/useImportRecordsNoSelectionRecordAction';
|
||||
import { useSeeDeletedRecordsNoSelectionRecordAction } from '@/action-menu/actions/record-actions/no-selection/hooks/useSeeDeletedRecordsNoSelectionRecordAction';
|
||||
import { useSeeWorkflowsNoSelectionRecordAction } from '@/action-menu/actions/record-actions/no-selection/hooks/useSeeWorkflowsNoSelectionRecordAction';
|
||||
import { CreateNewTableRecordNoSelectionRecordAction } from '@/action-menu/actions/record-actions/no-selection/components/CreateNewTableRecordNoSelectionRecordAction';
|
||||
import { HideDeletedRecordsNoSelectionRecordAction } from '@/action-menu/actions/record-actions/no-selection/components/HideDeletedRecordsNoSelectionRecordAction';
|
||||
import { ImportRecordsNoSelectionRecordAction } from '@/action-menu/actions/record-actions/no-selection/components/ImportRecordsNoSelectionRecordAction';
|
||||
import { SeeDeletedRecordsNoSelectionRecordAction } from '@/action-menu/actions/record-actions/no-selection/components/SeeDeletedRecordsNoSelectionRecordAction';
|
||||
import { NoSelectionRecordActionKeys } from '@/action-menu/actions/record-actions/no-selection/types/NoSelectionRecordActionsKeys';
|
||||
import { useAddToFavoritesSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useAddToFavoritesSingleRecordAction';
|
||||
import { useDeleteSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useDeleteSingleRecordAction';
|
||||
import { useDestroySingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useDestroySingleRecordAction';
|
||||
import { useExportNoteAction } from '@/action-menu/actions/record-actions/single-record/hooks/useExportNoteAction';
|
||||
import { useNavigateToNextRecordSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useNavigateToNextRecordSingleRecordAction';
|
||||
import { useNavigateToPreviousRecordSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useNavigateToPreviousRecordSingleRecordAction';
|
||||
import { useRemoveFromFavoritesSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useRemoveFromFavoritesSingleRecordAction';
|
||||
import { useRestoreSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useRestoreSingleRecordAction';
|
||||
import { AddToFavoritesSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/components/AddToFavoritesSingleRecordAction';
|
||||
import { DeleteSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/components/DeleteSingleRecordAction';
|
||||
import { DestroySingleRecordAction } from '@/action-menu/actions/record-actions/single-record/components/DestroySingleRecordAction';
|
||||
import { ExportNoteActionSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/components/ExportNoteActionSingleRecordAction';
|
||||
import { NavigateToNextRecordSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/components/NavigateToNextRecordSingleRecordAction';
|
||||
import { NavigateToPreviousRecordSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/components/NavigateToPreviousRecordSingleRecordAction';
|
||||
import { RemoveFromFavoritesSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/components/RemoveFromFavoritesSingleRecordAction';
|
||||
import { RestoreSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/components/RestoreSingleRecordAction';
|
||||
import { SingleRecordActionKeys } from '@/action-menu/actions/record-actions/single-record/types/SingleRecordActionsKey';
|
||||
import { ActionConfig } from '@/action-menu/actions/types/ActionConfig';
|
||||
import { ActionScope } from '@/action-menu/actions/types/ActionScope';
|
||||
import { ActionType } from '@/action-menu/actions/types/ActionType';
|
||||
import { ActionViewType } from '@/action-menu/actions/types/ActionViewType';
|
||||
import { RecordConfigAction } from '@/action-menu/actions/types/RecordConfigAction';
|
||||
import {
|
||||
ActionMenuEntryScope,
|
||||
ActionMenuEntryType,
|
||||
} from '@/action-menu/types/ActionMenuEntry';
|
||||
import { CoreObjectNamePlural } from '@/object-metadata/types/CoreObjectNamePlural';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { BACKEND_BATCH_REQUEST_MAX_COUNT } from '@/object-record/constants/BackendBatchRequestMaxCount';
|
||||
import { AppPath } from '@/types/AppPath';
|
||||
import { SettingsPath } from '@/types/SettingsPath';
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
@ -58,11 +55,11 @@ export const DEFAULT_RECORD_ACTIONS_CONFIG: Record<
|
||||
| NoSelectionRecordActionKeys
|
||||
| SingleRecordActionKeys
|
||||
| MultipleRecordsActionKeys,
|
||||
RecordConfigAction
|
||||
ActionConfig
|
||||
> = {
|
||||
[NoSelectionRecordActionKeys.CREATE_NEW_RECORD]: {
|
||||
type: ActionMenuEntryType.Standard,
|
||||
scope: ActionMenuEntryScope.Object,
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.Object,
|
||||
key: NoSelectionRecordActionKeys.CREATE_NEW_RECORD,
|
||||
label: msg`Create new record`,
|
||||
shortLabel: msg`New record`,
|
||||
@ -72,11 +69,11 @@ export const DEFAULT_RECORD_ACTIONS_CONFIG: Record<
|
||||
shouldBeRegistered: ({ hasObjectReadOnlyPermission }) =>
|
||||
!hasObjectReadOnlyPermission,
|
||||
availableOn: [ActionViewType.INDEX_PAGE_NO_SELECTION],
|
||||
useAction: useCreateNewTableRecordNoSelectionRecordAction,
|
||||
component: <CreateNewTableRecordNoSelectionRecordAction />,
|
||||
},
|
||||
[SingleRecordActionKeys.EXPORT_NOTE_TO_PDF]: {
|
||||
type: ActionMenuEntryType.Standard,
|
||||
scope: ActionMenuEntryScope.RecordSelection,
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
key: SingleRecordActionKeys.EXPORT_NOTE_TO_PDF,
|
||||
label: msg`Export to PDF`,
|
||||
shortLabel: msg`Export`,
|
||||
@ -88,11 +85,11 @@ export const DEFAULT_RECORD_ACTIONS_CONFIG: Record<
|
||||
isNoteOrTask &&
|
||||
isNonEmptyString(selectedRecord?.bodyV2?.blocknote),
|
||||
availableOn: [ActionViewType.SHOW_PAGE],
|
||||
useAction: useExportNoteAction,
|
||||
component: <ExportNoteActionSingleRecordAction />,
|
||||
},
|
||||
[SingleRecordActionKeys.ADD_TO_FAVORITES]: {
|
||||
type: ActionMenuEntryType.Standard,
|
||||
scope: ActionMenuEntryScope.RecordSelection,
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
key: SingleRecordActionKeys.ADD_TO_FAVORITES,
|
||||
label: msg`Add to favorites`,
|
||||
shortLabel: msg`Add to favorites`,
|
||||
@ -105,11 +102,11 @@ export const DEFAULT_RECORD_ACTIONS_CONFIG: Record<
|
||||
ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
ActionViewType.SHOW_PAGE,
|
||||
],
|
||||
useAction: useAddToFavoritesSingleRecordAction,
|
||||
component: <AddToFavoritesSingleRecordAction />,
|
||||
},
|
||||
[SingleRecordActionKeys.REMOVE_FROM_FAVORITES]: {
|
||||
type: ActionMenuEntryType.Standard,
|
||||
scope: ActionMenuEntryScope.RecordSelection,
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
key: SingleRecordActionKeys.REMOVE_FROM_FAVORITES,
|
||||
label: msg`Remove from favorites`,
|
||||
shortLabel: msg`Remove from favorites`,
|
||||
@ -125,11 +122,11 @@ export const DEFAULT_RECORD_ACTIONS_CONFIG: Record<
|
||||
ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
ActionViewType.SHOW_PAGE,
|
||||
],
|
||||
useAction: useRemoveFromFavoritesSingleRecordAction,
|
||||
component: <RemoveFromFavoritesSingleRecordAction />,
|
||||
},
|
||||
[SingleRecordActionKeys.EXPORT]: {
|
||||
type: ActionMenuEntryType.Standard,
|
||||
scope: ActionMenuEntryScope.RecordSelection,
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
key: SingleRecordActionKeys.EXPORT,
|
||||
label: msg`Export`,
|
||||
shortLabel: msg`Export`,
|
||||
@ -143,11 +140,11 @@ export const DEFAULT_RECORD_ACTIONS_CONFIG: Record<
|
||||
ActionViewType.SHOW_PAGE,
|
||||
ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
],
|
||||
useAction: useExportMultipleRecordsAction,
|
||||
component: <ExportMultipleRecordsAction />,
|
||||
},
|
||||
[MultipleRecordsActionKeys.EXPORT]: {
|
||||
type: ActionMenuEntryType.Standard,
|
||||
scope: ActionMenuEntryScope.RecordSelection,
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
key: MultipleRecordsActionKeys.EXPORT,
|
||||
label: msg`Export records`,
|
||||
shortLabel: msg`Export`,
|
||||
@ -157,11 +154,11 @@ export const DEFAULT_RECORD_ACTIONS_CONFIG: Record<
|
||||
isPinned: false,
|
||||
shouldBeRegistered: () => true,
|
||||
availableOn: [ActionViewType.INDEX_PAGE_BULK_SELECTION],
|
||||
useAction: useExportMultipleRecordsAction,
|
||||
component: <ExportMultipleRecordsAction />,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.EXPORT_VIEW]: {
|
||||
type: ActionMenuEntryType.Standard,
|
||||
scope: ActionMenuEntryScope.Object,
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.Object,
|
||||
key: NoSelectionRecordActionKeys.EXPORT_VIEW,
|
||||
label: msg`Export view`,
|
||||
shortLabel: msg`Export`,
|
||||
@ -171,11 +168,11 @@ export const DEFAULT_RECORD_ACTIONS_CONFIG: Record<
|
||||
isPinned: false,
|
||||
shouldBeRegistered: () => true,
|
||||
availableOn: [ActionViewType.INDEX_PAGE_NO_SELECTION],
|
||||
useAction: useExportMultipleRecordsAction,
|
||||
component: <ExportMultipleRecordsAction />,
|
||||
},
|
||||
[SingleRecordActionKeys.DELETE]: {
|
||||
type: ActionMenuEntryType.Standard,
|
||||
scope: ActionMenuEntryScope.RecordSelection,
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
key: SingleRecordActionKeys.DELETE,
|
||||
label: msg`Delete`,
|
||||
shortLabel: msg`Delete`,
|
||||
@ -189,11 +186,11 @@ export const DEFAULT_RECORD_ACTIONS_CONFIG: Record<
|
||||
ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
ActionViewType.SHOW_PAGE,
|
||||
],
|
||||
useAction: useDeleteSingleRecordAction,
|
||||
component: <DeleteSingleRecordAction />,
|
||||
},
|
||||
[MultipleRecordsActionKeys.DELETE]: {
|
||||
type: ActionMenuEntryType.Standard,
|
||||
scope: ActionMenuEntryScope.RecordSelection,
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
key: MultipleRecordsActionKeys.DELETE,
|
||||
label: msg`Delete records`,
|
||||
shortLabel: msg`Delete`,
|
||||
@ -213,11 +210,11 @@ export const DEFAULT_RECORD_ACTIONS_CONFIG: Record<
|
||||
isDefined(numberOfSelectedRecords) &&
|
||||
numberOfSelectedRecords < BACKEND_BATCH_REQUEST_MAX_COUNT,
|
||||
availableOn: [ActionViewType.INDEX_PAGE_BULK_SELECTION],
|
||||
useAction: useDeleteMultipleRecordsAction,
|
||||
component: <DeleteMultipleRecordsAction />,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.SEE_DELETED_RECORDS]: {
|
||||
type: ActionMenuEntryType.Standard,
|
||||
scope: ActionMenuEntryScope.Object,
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.Object,
|
||||
key: NoSelectionRecordActionKeys.SEE_DELETED_RECORDS,
|
||||
label: msg`See deleted records`,
|
||||
shortLabel: msg`Deleted records`,
|
||||
@ -228,11 +225,11 @@ export const DEFAULT_RECORD_ACTIONS_CONFIG: Record<
|
||||
shouldBeRegistered: ({ isSoftDeleteFilterActive }) =>
|
||||
!isSoftDeleteFilterActive,
|
||||
availableOn: [ActionViewType.INDEX_PAGE_NO_SELECTION],
|
||||
useAction: useSeeDeletedRecordsNoSelectionRecordAction,
|
||||
component: <SeeDeletedRecordsNoSelectionRecordAction />,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.HIDE_DELETED_RECORDS]: {
|
||||
type: ActionMenuEntryType.Standard,
|
||||
scope: ActionMenuEntryScope.Object,
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.Object,
|
||||
key: NoSelectionRecordActionKeys.HIDE_DELETED_RECORDS,
|
||||
label: msg`Hide deleted records`,
|
||||
shortLabel: msg`Hide deleted`,
|
||||
@ -243,11 +240,11 @@ export const DEFAULT_RECORD_ACTIONS_CONFIG: Record<
|
||||
shouldBeRegistered: ({ isSoftDeleteFilterActive }) =>
|
||||
isDefined(isSoftDeleteFilterActive) && isSoftDeleteFilterActive,
|
||||
availableOn: [ActionViewType.INDEX_PAGE_NO_SELECTION],
|
||||
useAction: useHideDeletedRecordsNoSelectionRecordAction,
|
||||
component: <HideDeletedRecordsNoSelectionRecordAction />,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.IMPORT_RECORDS]: {
|
||||
type: ActionMenuEntryType.Standard,
|
||||
scope: ActionMenuEntryScope.Object,
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.Object,
|
||||
key: NoSelectionRecordActionKeys.IMPORT_RECORDS,
|
||||
label: msg`Import records`,
|
||||
shortLabel: msg`Import`,
|
||||
@ -257,11 +254,11 @@ export const DEFAULT_RECORD_ACTIONS_CONFIG: Record<
|
||||
isPinned: false,
|
||||
shouldBeRegistered: () => true,
|
||||
availableOn: [ActionViewType.INDEX_PAGE_NO_SELECTION],
|
||||
useAction: useImportRecordsNoSelectionRecordAction,
|
||||
component: <ImportRecordsNoSelectionRecordAction />,
|
||||
},
|
||||
[SingleRecordActionKeys.DESTROY]: {
|
||||
type: ActionMenuEntryType.Standard,
|
||||
scope: ActionMenuEntryScope.RecordSelection,
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
key: SingleRecordActionKeys.DESTROY,
|
||||
label: msg`Permanently destroy record`,
|
||||
shortLabel: msg`Destroy`,
|
||||
@ -281,11 +278,11 @@ export const DEFAULT_RECORD_ACTIONS_CONFIG: Record<
|
||||
ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
ActionViewType.SHOW_PAGE,
|
||||
],
|
||||
useAction: useDestroySingleRecordAction,
|
||||
component: <DestroySingleRecordAction />,
|
||||
},
|
||||
[SingleRecordActionKeys.NAVIGATE_TO_PREVIOUS_RECORD]: {
|
||||
type: ActionMenuEntryType.Standard,
|
||||
scope: ActionMenuEntryScope.RecordSelection,
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
key: SingleRecordActionKeys.NAVIGATE_TO_PREVIOUS_RECORD,
|
||||
label: msg`Navigate to previous record`,
|
||||
position: 13,
|
||||
@ -293,11 +290,11 @@ export const DEFAULT_RECORD_ACTIONS_CONFIG: Record<
|
||||
Icon: IconChevronUp,
|
||||
shouldBeRegistered: ({ isInRightDrawer }) => !isInRightDrawer,
|
||||
availableOn: [ActionViewType.SHOW_PAGE],
|
||||
useAction: useNavigateToPreviousRecordSingleRecordAction,
|
||||
component: <NavigateToPreviousRecordSingleRecordAction />,
|
||||
},
|
||||
[SingleRecordActionKeys.NAVIGATE_TO_NEXT_RECORD]: {
|
||||
type: ActionMenuEntryType.Standard,
|
||||
scope: ActionMenuEntryScope.RecordSelection,
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
key: SingleRecordActionKeys.NAVIGATE_TO_NEXT_RECORD,
|
||||
label: msg`Navigate to next record`,
|
||||
position: 14,
|
||||
@ -305,11 +302,11 @@ export const DEFAULT_RECORD_ACTIONS_CONFIG: Record<
|
||||
Icon: IconChevronDown,
|
||||
shouldBeRegistered: ({ isInRightDrawer }) => !isInRightDrawer,
|
||||
availableOn: [ActionViewType.SHOW_PAGE],
|
||||
useAction: useNavigateToNextRecordSingleRecordAction,
|
||||
component: <NavigateToNextRecordSingleRecordAction />,
|
||||
},
|
||||
[MultipleRecordsActionKeys.DESTROY]: {
|
||||
type: ActionMenuEntryType.Standard,
|
||||
scope: ActionMenuEntryScope.RecordSelection,
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
key: MultipleRecordsActionKeys.DESTROY,
|
||||
label: msg`Permanently destroy records`,
|
||||
shortLabel: msg`Destroy`,
|
||||
@ -330,11 +327,11 @@ export const DEFAULT_RECORD_ACTIONS_CONFIG: Record<
|
||||
isDefined(numberOfSelectedRecords) &&
|
||||
numberOfSelectedRecords < BACKEND_BATCH_REQUEST_MAX_COUNT,
|
||||
availableOn: [ActionViewType.INDEX_PAGE_BULK_SELECTION],
|
||||
useAction: useDestroyMultipleRecordsAction,
|
||||
component: <DestroyMultipleRecordsAction />,
|
||||
},
|
||||
[SingleRecordActionKeys.RESTORE]: {
|
||||
type: ActionMenuEntryType.Standard,
|
||||
scope: ActionMenuEntryScope.RecordSelection,
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
key: SingleRecordActionKeys.RESTORE,
|
||||
label: msg`Restore record`,
|
||||
shortLabel: msg`Restore`,
|
||||
@ -358,11 +355,11 @@ export const DEFAULT_RECORD_ACTIONS_CONFIG: Record<
|
||||
ActionViewType.SHOW_PAGE,
|
||||
ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
],
|
||||
useAction: useRestoreSingleRecordAction,
|
||||
component: <RestoreSingleRecordAction />,
|
||||
},
|
||||
[MultipleRecordsActionKeys.RESTORE]: {
|
||||
type: ActionMenuEntryType.Standard,
|
||||
scope: ActionMenuEntryScope.RecordSelection,
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
key: MultipleRecordsActionKeys.RESTORE,
|
||||
label: msg`Restore records`,
|
||||
shortLabel: msg`Restore`,
|
||||
@ -383,11 +380,11 @@ export const DEFAULT_RECORD_ACTIONS_CONFIG: Record<
|
||||
isDefined(numberOfSelectedRecords) &&
|
||||
numberOfSelectedRecords < BACKEND_BATCH_REQUEST_MAX_COUNT,
|
||||
availableOn: [ActionViewType.INDEX_PAGE_BULK_SELECTION],
|
||||
useAction: useRestoreMultipleRecordsAction,
|
||||
component: <RestoreMultipleRecordsAction />,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_WORKFLOWS]: {
|
||||
type: ActionMenuEntryType.Navigation,
|
||||
scope: ActionMenuEntryScope.Global,
|
||||
type: ActionType.Navigation,
|
||||
scope: ActionScope.Global,
|
||||
key: NoSelectionRecordActionKeys.GO_TO_WORKFLOWS,
|
||||
label: msg`Go to workflows`,
|
||||
shortLabel: msg`See workflows`,
|
||||
@ -395,14 +392,27 @@ export const DEFAULT_RECORD_ACTIONS_CONFIG: Record<
|
||||
Icon: IconSettingsAutomation,
|
||||
accent: 'default',
|
||||
isPinned: false,
|
||||
shouldBeRegistered: () => true,
|
||||
availableOn: [ActionViewType.INDEX_PAGE_NO_SELECTION],
|
||||
useAction: useSeeWorkflowsNoSelectionRecordAction,
|
||||
shouldBeRegistered: ({ objectMetadataItem, viewType, isWorkflowEnabled }) =>
|
||||
(objectMetadataItem?.nameSingular !== CoreObjectNameSingular.Workflow ||
|
||||
viewType === ActionViewType.SHOW_PAGE) &&
|
||||
isWorkflowEnabled,
|
||||
availableOn: [
|
||||
ActionViewType.INDEX_PAGE_NO_SELECTION,
|
||||
ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
ActionViewType.INDEX_PAGE_BULK_SELECTION,
|
||||
ActionViewType.SHOW_PAGE,
|
||||
],
|
||||
component: (
|
||||
<ActionLink
|
||||
to={AppPath.RecordIndexPage}
|
||||
params={{ objectNamePlural: CoreObjectNamePlural.Workflow }}
|
||||
/>
|
||||
),
|
||||
hotKeys: ['G', 'W'],
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_PEOPLE]: {
|
||||
type: ActionMenuEntryType.Navigation,
|
||||
scope: ActionMenuEntryScope.Global,
|
||||
type: ActionType.Navigation,
|
||||
scope: ActionScope.Global,
|
||||
key: NoSelectionRecordActionKeys.GO_TO_PEOPLE,
|
||||
label: msg`Go to People`,
|
||||
shortLabel: msg`People`,
|
||||
@ -415,13 +425,20 @@ export const DEFAULT_RECORD_ACTIONS_CONFIG: Record<
|
||||
ActionViewType.INDEX_PAGE_BULK_SELECTION,
|
||||
ActionViewType.SHOW_PAGE,
|
||||
],
|
||||
shouldBeRegistered: () => true,
|
||||
useAction: useGoToPeopleNoSelectionRecordAction,
|
||||
shouldBeRegistered: ({ objectMetadataItem, viewType }) =>
|
||||
objectMetadataItem?.nameSingular !== CoreObjectNameSingular.Person ||
|
||||
viewType === ActionViewType.SHOW_PAGE,
|
||||
component: (
|
||||
<ActionLink
|
||||
to={AppPath.RecordIndexPage}
|
||||
params={{ objectNamePlural: CoreObjectNamePlural.Person }}
|
||||
/>
|
||||
),
|
||||
hotKeys: ['G', 'P'],
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_COMPANIES]: {
|
||||
type: ActionMenuEntryType.Navigation,
|
||||
scope: ActionMenuEntryScope.Global,
|
||||
type: ActionType.Navigation,
|
||||
scope: ActionScope.Global,
|
||||
key: NoSelectionRecordActionKeys.GO_TO_COMPANIES,
|
||||
label: msg`Go to Companies`,
|
||||
shortLabel: msg`Companies`,
|
||||
@ -434,13 +451,20 @@ export const DEFAULT_RECORD_ACTIONS_CONFIG: Record<
|
||||
ActionViewType.INDEX_PAGE_BULK_SELECTION,
|
||||
ActionViewType.SHOW_PAGE,
|
||||
],
|
||||
shouldBeRegistered: () => true,
|
||||
useAction: useGoToCompaniesNoSelectionRecordAction,
|
||||
shouldBeRegistered: ({ objectMetadataItem, viewType }) =>
|
||||
objectMetadataItem?.nameSingular !== CoreObjectNameSingular.Company ||
|
||||
viewType === ActionViewType.SHOW_PAGE,
|
||||
component: (
|
||||
<ActionLink
|
||||
to={AppPath.RecordIndexPage}
|
||||
params={{ objectNamePlural: CoreObjectNamePlural.Company }}
|
||||
/>
|
||||
),
|
||||
hotKeys: ['G', 'C'],
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_OPPORTUNITIES]: {
|
||||
type: ActionMenuEntryType.Navigation,
|
||||
scope: ActionMenuEntryScope.Global,
|
||||
type: ActionType.Navigation,
|
||||
scope: ActionScope.Global,
|
||||
key: NoSelectionRecordActionKeys.GO_TO_OPPORTUNITIES,
|
||||
label: msg`Go to Opportunities`,
|
||||
shortLabel: msg`Opportunities`,
|
||||
@ -453,13 +477,20 @@ export const DEFAULT_RECORD_ACTIONS_CONFIG: Record<
|
||||
ActionViewType.INDEX_PAGE_BULK_SELECTION,
|
||||
ActionViewType.SHOW_PAGE,
|
||||
],
|
||||
shouldBeRegistered: () => true,
|
||||
useAction: useGoToOpportunitiesNoSelectionRecordAction,
|
||||
shouldBeRegistered: ({ objectMetadataItem, viewType }) =>
|
||||
objectMetadataItem?.nameSingular !== CoreObjectNameSingular.Opportunity ||
|
||||
viewType === ActionViewType.SHOW_PAGE,
|
||||
component: (
|
||||
<ActionLink
|
||||
to={AppPath.RecordIndexPage}
|
||||
params={{ objectNamePlural: CoreObjectNamePlural.Opportunity }}
|
||||
/>
|
||||
),
|
||||
hotKeys: ['G', 'O'],
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_SETTINGS]: {
|
||||
type: ActionMenuEntryType.Navigation,
|
||||
scope: ActionMenuEntryScope.Global,
|
||||
type: ActionType.Navigation,
|
||||
scope: ActionScope.Global,
|
||||
key: NoSelectionRecordActionKeys.GO_TO_SETTINGS,
|
||||
label: msg`Go to Settings`,
|
||||
shortLabel: msg`Settings`,
|
||||
@ -473,12 +504,19 @@ export const DEFAULT_RECORD_ACTIONS_CONFIG: Record<
|
||||
ActionViewType.SHOW_PAGE,
|
||||
],
|
||||
shouldBeRegistered: () => true,
|
||||
useAction: useGoToSettingsNoSelectionRecordAction,
|
||||
component: (
|
||||
<ActionLink
|
||||
to={AppPath.SettingsCatchAll}
|
||||
params={{
|
||||
'*': SettingsPath.ProfilePage,
|
||||
}}
|
||||
/>
|
||||
),
|
||||
hotKeys: ['G', 'S'],
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_TASKS]: {
|
||||
type: ActionMenuEntryType.Navigation,
|
||||
scope: ActionMenuEntryScope.Global,
|
||||
type: ActionType.Navigation,
|
||||
scope: ActionScope.Global,
|
||||
key: NoSelectionRecordActionKeys.GO_TO_TASKS,
|
||||
label: msg`Go to Tasks`,
|
||||
shortLabel: msg`Tasks`,
|
||||
@ -491,8 +529,41 @@ export const DEFAULT_RECORD_ACTIONS_CONFIG: Record<
|
||||
ActionViewType.INDEX_PAGE_BULK_SELECTION,
|
||||
ActionViewType.SHOW_PAGE,
|
||||
],
|
||||
shouldBeRegistered: () => true,
|
||||
useAction: useGoToTasksNoSelectionRecordAction,
|
||||
shouldBeRegistered: ({ objectMetadataItem, viewType }) =>
|
||||
objectMetadataItem?.nameSingular !== CoreObjectNameSingular.Task ||
|
||||
viewType === ActionViewType.SHOW_PAGE,
|
||||
component: (
|
||||
<ActionLink
|
||||
to={AppPath.RecordIndexPage}
|
||||
params={{ objectNamePlural: CoreObjectNamePlural.Task }}
|
||||
/>
|
||||
),
|
||||
hotKeys: ['G', 'T'],
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_NOTES]: {
|
||||
type: ActionType.Navigation,
|
||||
scope: ActionScope.Global,
|
||||
key: NoSelectionRecordActionKeys.GO_TO_NOTES,
|
||||
label: msg`Go to Notes`,
|
||||
shortLabel: msg`Notes`,
|
||||
position: 24,
|
||||
Icon: IconCheckbox,
|
||||
isPinned: false,
|
||||
availableOn: [
|
||||
ActionViewType.INDEX_PAGE_NO_SELECTION,
|
||||
ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
ActionViewType.INDEX_PAGE_BULK_SELECTION,
|
||||
ActionViewType.SHOW_PAGE,
|
||||
],
|
||||
shouldBeRegistered: ({ objectMetadataItem, viewType }) =>
|
||||
objectMetadataItem?.nameSingular !== CoreObjectNameSingular.Note ||
|
||||
viewType === ActionViewType.SHOW_PAGE,
|
||||
component: (
|
||||
<ActionLink
|
||||
to={AppPath.RecordIndexPage}
|
||||
params={{ objectNamePlural: CoreObjectNamePlural.Note }}
|
||||
/>
|
||||
),
|
||||
hotKeys: ['G', 'N'],
|
||||
},
|
||||
};
|
||||
@ -1,22 +1,22 @@
|
||||
import { ActionLink } from '@/action-menu/actions/components/ActionLink';
|
||||
import { MultipleRecordsActionKeys } from '@/action-menu/actions/record-actions/multiple-records/types/MultipleRecordsActionKeys';
|
||||
import { useSeeRunsNoSelectionRecordAction } from '@/action-menu/actions/record-actions/no-selection/hooks/useSeeRunsNoSelectionRecordAction';
|
||||
import { NoSelectionRecordActionKeys } from '@/action-menu/actions/record-actions/no-selection/types/NoSelectionRecordActionsKeys';
|
||||
import { NoSelectionWorkflowRecordActionKeys } from '@/action-menu/actions/record-actions/no-selection/workflow-actions/types/NoSelectionWorkflowRecordActionsKeys';
|
||||
import { SingleRecordActionKeys } from '@/action-menu/actions/record-actions/single-record/types/SingleRecordActionsKey';
|
||||
import { useActivateWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useActivateWorkflowSingleRecordAction';
|
||||
import { useDeactivateWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useDeactivateWorkflowSingleRecordAction';
|
||||
import { useDiscardDraftWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useDiscardDraftWorkflowSingleRecordAction';
|
||||
import { useSeeActiveVersionWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useSeeActiveVersionWorkflowSingleRecordAction';
|
||||
import { useSeeRunsWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useSeeRunsWorkflowSingleRecordAction';
|
||||
import { useSeeVersionsWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useSeeVersionsWorkflowSingleRecordAction';
|
||||
import { useTestWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useTestWorkflowSingleRecordAction';
|
||||
import { ActivateWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/components/ActivateWorkflowSingleRecordAction';
|
||||
import { DeactivateWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/components/DeactivateWorkflowSingleRecordAction';
|
||||
import { DiscardDraftWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/components/DiscardDraftWorkflowSingleRecordAction';
|
||||
import { SeeActiveVersionWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/components/SeeActiveVersionWorkflowSingleRecordAction';
|
||||
import { SeeRunsWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/components/SeeRunsWorkflowSingleRecordAction';
|
||||
import { SeeVersionsWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/components/SeeVersionsWorkflowSingleRecordAction';
|
||||
import { TestWorkflowSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-actions/components/TestWorkflowSingleRecordAction';
|
||||
import { WorkflowSingleRecordActionKeys } from '@/action-menu/actions/record-actions/single-record/workflow-actions/types/WorkflowSingleRecordActionsKeys';
|
||||
import { inheritActionsFromDefaultConfig } from '@/action-menu/actions/record-actions/utils/inheritActionsFromDefaultConfig';
|
||||
import { ActionScope } from '@/action-menu/actions/types/ActionScope';
|
||||
import { ActionType } from '@/action-menu/actions/types/ActionType';
|
||||
import { ActionViewType } from '@/action-menu/actions/types/ActionViewType';
|
||||
import {
|
||||
ActionMenuEntryScope,
|
||||
ActionMenuEntryType,
|
||||
} from '@/action-menu/types/ActionMenuEntry';
|
||||
import { CoreObjectNamePlural } from '@/object-metadata/types/CoreObjectNamePlural';
|
||||
import { AppPath } from '@/types/AppPath';
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import {
|
||||
@ -37,8 +37,8 @@ export const WORKFLOW_ACTIONS_CONFIG = inheritActionsFromDefaultConfig({
|
||||
isPinned: true,
|
||||
position: 1,
|
||||
Icon: IconPower,
|
||||
type: ActionMenuEntryType.Standard,
|
||||
scope: ActionMenuEntryScope.RecordSelection,
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
shouldBeRegistered: ({ workflowWithCurrentVersion }) =>
|
||||
isDefined(workflowWithCurrentVersion?.currentVersion?.trigger) &&
|
||||
isDefined(workflowWithCurrentVersion.currentVersion?.steps) &&
|
||||
@ -51,7 +51,7 @@ export const WORKFLOW_ACTIONS_CONFIG = inheritActionsFromDefaultConfig({
|
||||
ActionViewType.SHOW_PAGE,
|
||||
ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
],
|
||||
useAction: useActivateWorkflowSingleRecordAction,
|
||||
component: <ActivateWorkflowSingleRecordAction />,
|
||||
},
|
||||
[WorkflowSingleRecordActionKeys.DEACTIVATE]: {
|
||||
key: WorkflowSingleRecordActionKeys.DEACTIVATE,
|
||||
@ -60,8 +60,8 @@ export const WORKFLOW_ACTIONS_CONFIG = inheritActionsFromDefaultConfig({
|
||||
isPinned: true,
|
||||
position: 2,
|
||||
Icon: IconPlayerPause,
|
||||
type: ActionMenuEntryType.Standard,
|
||||
scope: ActionMenuEntryScope.RecordSelection,
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
shouldBeRegistered: ({ workflowWithCurrentVersion }) =>
|
||||
isDefined(workflowWithCurrentVersion) &&
|
||||
workflowWithCurrentVersion.currentVersion.status === 'ACTIVE',
|
||||
@ -69,7 +69,7 @@ export const WORKFLOW_ACTIONS_CONFIG = inheritActionsFromDefaultConfig({
|
||||
ActionViewType.SHOW_PAGE,
|
||||
ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
],
|
||||
useAction: useDeactivateWorkflowSingleRecordAction,
|
||||
component: <DeactivateWorkflowSingleRecordAction />,
|
||||
},
|
||||
[WorkflowSingleRecordActionKeys.DISCARD_DRAFT]: {
|
||||
key: WorkflowSingleRecordActionKeys.DISCARD_DRAFT,
|
||||
@ -78,8 +78,8 @@ export const WORKFLOW_ACTIONS_CONFIG = inheritActionsFromDefaultConfig({
|
||||
isPinned: true,
|
||||
position: 3,
|
||||
Icon: IconNoteOff,
|
||||
type: ActionMenuEntryType.Standard,
|
||||
scope: ActionMenuEntryScope.RecordSelection,
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
shouldBeRegistered: ({ workflowWithCurrentVersion }) =>
|
||||
isDefined(workflowWithCurrentVersion) &&
|
||||
workflowWithCurrentVersion.versions.length > 1 &&
|
||||
@ -88,7 +88,7 @@ export const WORKFLOW_ACTIONS_CONFIG = inheritActionsFromDefaultConfig({
|
||||
ActionViewType.SHOW_PAGE,
|
||||
ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
],
|
||||
useAction: useDiscardDraftWorkflowSingleRecordAction,
|
||||
component: <DiscardDraftWorkflowSingleRecordAction />,
|
||||
},
|
||||
[WorkflowSingleRecordActionKeys.SEE_ACTIVE_VERSION]: {
|
||||
key: WorkflowSingleRecordActionKeys.SEE_ACTIVE_VERSION,
|
||||
@ -97,8 +97,8 @@ export const WORKFLOW_ACTIONS_CONFIG = inheritActionsFromDefaultConfig({
|
||||
isPinned: false,
|
||||
position: 4,
|
||||
Icon: IconVersions,
|
||||
type: ActionMenuEntryType.Standard,
|
||||
scope: ActionMenuEntryScope.RecordSelection,
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
shouldBeRegistered: ({ workflowWithCurrentVersion }) =>
|
||||
(workflowWithCurrentVersion?.statuses?.includes('ACTIVE') || false) &&
|
||||
(workflowWithCurrentVersion?.statuses?.includes('DRAFT') || false),
|
||||
@ -106,7 +106,7 @@ export const WORKFLOW_ACTIONS_CONFIG = inheritActionsFromDefaultConfig({
|
||||
ActionViewType.SHOW_PAGE,
|
||||
ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
],
|
||||
useAction: useSeeActiveVersionWorkflowSingleRecordAction,
|
||||
component: <SeeActiveVersionWorkflowSingleRecordAction />,
|
||||
},
|
||||
[WorkflowSingleRecordActionKeys.SEE_RUNS]: {
|
||||
key: WorkflowSingleRecordActionKeys.SEE_RUNS,
|
||||
@ -115,15 +115,15 @@ export const WORKFLOW_ACTIONS_CONFIG = inheritActionsFromDefaultConfig({
|
||||
isPinned: true,
|
||||
position: 5,
|
||||
Icon: IconHistoryToggle,
|
||||
type: ActionMenuEntryType.Standard,
|
||||
scope: ActionMenuEntryScope.RecordSelection,
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
shouldBeRegistered: ({ workflowWithCurrentVersion }) =>
|
||||
isDefined(workflowWithCurrentVersion),
|
||||
availableOn: [
|
||||
ActionViewType.SHOW_PAGE,
|
||||
ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
],
|
||||
useAction: useSeeRunsWorkflowSingleRecordAction,
|
||||
component: <SeeRunsWorkflowSingleRecordAction />,
|
||||
},
|
||||
[WorkflowSingleRecordActionKeys.SEE_VERSIONS]: {
|
||||
key: WorkflowSingleRecordActionKeys.SEE_VERSIONS,
|
||||
@ -132,15 +132,15 @@ export const WORKFLOW_ACTIONS_CONFIG = inheritActionsFromDefaultConfig({
|
||||
isPinned: false,
|
||||
position: 6,
|
||||
Icon: IconVersions,
|
||||
type: ActionMenuEntryType.Standard,
|
||||
scope: ActionMenuEntryScope.RecordSelection,
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
shouldBeRegistered: ({ workflowWithCurrentVersion }) =>
|
||||
isDefined(workflowWithCurrentVersion),
|
||||
availableOn: [
|
||||
ActionViewType.SHOW_PAGE,
|
||||
ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
],
|
||||
useAction: useSeeVersionsWorkflowSingleRecordAction,
|
||||
component: <SeeVersionsWorkflowSingleRecordAction />,
|
||||
},
|
||||
[WorkflowSingleRecordActionKeys.TEST]: {
|
||||
key: WorkflowSingleRecordActionKeys.TEST,
|
||||
@ -149,8 +149,8 @@ export const WORKFLOW_ACTIONS_CONFIG = inheritActionsFromDefaultConfig({
|
||||
isPinned: true,
|
||||
position: 7,
|
||||
Icon: IconPlayerPlay,
|
||||
type: ActionMenuEntryType.Standard,
|
||||
scope: ActionMenuEntryScope.RecordSelection,
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
shouldBeRegistered: ({ workflowWithCurrentVersion }) =>
|
||||
isDefined(workflowWithCurrentVersion?.currentVersion?.trigger) &&
|
||||
((workflowWithCurrentVersion.currentVersion.trigger.type === 'MANUAL' &&
|
||||
@ -163,11 +163,11 @@ export const WORKFLOW_ACTIONS_CONFIG = inheritActionsFromDefaultConfig({
|
||||
ActionViewType.SHOW_PAGE,
|
||||
ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
],
|
||||
useAction: useTestWorkflowSingleRecordAction,
|
||||
component: <TestWorkflowSingleRecordAction />,
|
||||
},
|
||||
[NoSelectionWorkflowRecordActionKeys.GO_TO_RUNS]: {
|
||||
type: ActionMenuEntryType.Navigation,
|
||||
scope: ActionMenuEntryScope.Global,
|
||||
type: ActionType.Navigation,
|
||||
scope: ActionScope.Global,
|
||||
key: NoSelectionWorkflowRecordActionKeys.GO_TO_RUNS,
|
||||
label: msg`Go to runs`,
|
||||
shortLabel: msg`See runs`,
|
||||
@ -177,7 +177,12 @@ export const WORKFLOW_ACTIONS_CONFIG = inheritActionsFromDefaultConfig({
|
||||
isPinned: true,
|
||||
shouldBeRegistered: () => true,
|
||||
availableOn: [ActionViewType.INDEX_PAGE_NO_SELECTION],
|
||||
useAction: useSeeRunsNoSelectionRecordAction,
|
||||
component: (
|
||||
<ActionLink
|
||||
to={AppPath.RecordIndexPage}
|
||||
params={{ objectNamePlural: CoreObjectNamePlural.WorkflowRun }}
|
||||
/>
|
||||
),
|
||||
},
|
||||
},
|
||||
actionKeys: [
|
||||
@ -199,6 +204,7 @@ export const WORKFLOW_ACTIONS_CONFIG = inheritActionsFromDefaultConfig({
|
||||
NoSelectionRecordActionKeys.GO_TO_OPPORTUNITIES,
|
||||
NoSelectionRecordActionKeys.GO_TO_SETTINGS,
|
||||
NoSelectionRecordActionKeys.GO_TO_TASKS,
|
||||
NoSelectionRecordActionKeys.GO_TO_NOTES,
|
||||
],
|
||||
propertiesToOverwrite: {
|
||||
[NoSelectionRecordActionKeys.CREATE_NEW_RECORD]: {
|
||||
@ -260,18 +266,21 @@ export const WORKFLOW_ACTIONS_CONFIG = inheritActionsFromDefaultConfig({
|
||||
label: msg`Import workflows`,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_PEOPLE]: {
|
||||
position: 23,
|
||||
position: 22,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_COMPANIES]: {
|
||||
position: 24,
|
||||
position: 23,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_OPPORTUNITIES]: {
|
||||
position: 25,
|
||||
position: 24,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_SETTINGS]: {
|
||||
position: 26,
|
||||
position: 25,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_TASKS]: {
|
||||
position: 26,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_NOTES]: {
|
||||
position: 27,
|
||||
},
|
||||
},
|
||||
@ -1,16 +1,13 @@
|
||||
import { MultipleRecordsActionKeys } from '@/action-menu/actions/record-actions/multiple-records/types/MultipleRecordsActionKeys';
|
||||
import { useSeeWorkflowsNoSelectionRecordAction } from '@/action-menu/actions/record-actions/no-selection/hooks/useSeeWorkflowsNoSelectionRecordAction';
|
||||
import { NoSelectionRecordActionKeys } from '@/action-menu/actions/record-actions/no-selection/types/NoSelectionRecordActionsKeys';
|
||||
import { SingleRecordActionKeys } from '@/action-menu/actions/record-actions/single-record/types/SingleRecordActionsKey';
|
||||
import { useSeeVersionWorkflowRunSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-run-actions/hooks/useSeeVersionWorkflowRunSingleRecordAction';
|
||||
import { useSeeWorkflowWorkflowRunSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-run-actions/hooks/useSeeWorkflowWorkflowRunSingleRecordAction';
|
||||
import { SeeVersionWorkflowRunSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-run-actions/components/SeeVersionWorkflowRunSingleRecordAction';
|
||||
import { SeeWorkflowWorkflowRunSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-run-actions/components/SeeWorkflowWorkflowRunSingleRecordAction';
|
||||
import { WorkflowRunSingleRecordActionKeys } from '@/action-menu/actions/record-actions/single-record/workflow-run-actions/types/WorkflowRunSingleRecordActionsKeys';
|
||||
import { inheritActionsFromDefaultConfig } from '@/action-menu/actions/record-actions/utils/inheritActionsFromDefaultConfig';
|
||||
import { ActionScope } from '@/action-menu/actions/types/ActionScope';
|
||||
import { ActionType } from '@/action-menu/actions/types/ActionType';
|
||||
import { ActionViewType } from '@/action-menu/actions/types/ActionViewType';
|
||||
import {
|
||||
ActionMenuEntryScope,
|
||||
ActionMenuEntryType,
|
||||
} from '@/action-menu/types/ActionMenuEntry';
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { IconSettingsAutomation, IconVersions } from 'twenty-ui/display';
|
||||
|
||||
@ -22,15 +19,15 @@ export const WORKFLOW_RUNS_ACTIONS_CONFIG = inheritActionsFromDefaultConfig({
|
||||
shortLabel: msg`See workflow`,
|
||||
position: 0,
|
||||
isPinned: true,
|
||||
type: ActionMenuEntryType.Standard,
|
||||
scope: ActionMenuEntryScope.RecordSelection,
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
Icon: IconSettingsAutomation,
|
||||
shouldBeRegistered: () => true,
|
||||
availableOn: [
|
||||
ActionViewType.SHOW_PAGE,
|
||||
ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
],
|
||||
useAction: useSeeWorkflowWorkflowRunSingleRecordAction,
|
||||
component: <SeeWorkflowWorkflowRunSingleRecordAction />,
|
||||
},
|
||||
[WorkflowRunSingleRecordActionKeys.SEE_VERSION]: {
|
||||
key: WorkflowRunSingleRecordActionKeys.SEE_VERSION,
|
||||
@ -38,30 +35,15 @@ export const WORKFLOW_RUNS_ACTIONS_CONFIG = inheritActionsFromDefaultConfig({
|
||||
shortLabel: msg`See version`,
|
||||
position: 1,
|
||||
isPinned: true,
|
||||
type: ActionMenuEntryType.Standard,
|
||||
scope: ActionMenuEntryScope.RecordSelection,
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
Icon: IconVersions,
|
||||
shouldBeRegistered: () => true,
|
||||
availableOn: [
|
||||
ActionViewType.SHOW_PAGE,
|
||||
ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
],
|
||||
useAction: useSeeVersionWorkflowRunSingleRecordAction,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_WORKFLOWS]: {
|
||||
type: ActionMenuEntryType.Navigation,
|
||||
scope: ActionMenuEntryScope.Global,
|
||||
key: NoSelectionRecordActionKeys.GO_TO_WORKFLOWS,
|
||||
label: msg`Go to workflows`,
|
||||
shortLabel: msg`See workflows`,
|
||||
position: 11,
|
||||
Icon: IconSettingsAutomation,
|
||||
accent: 'default',
|
||||
isPinned: true,
|
||||
shouldBeRegistered: () => true,
|
||||
availableOn: [ActionViewType.INDEX_PAGE_NO_SELECTION],
|
||||
useAction: useSeeWorkflowsNoSelectionRecordAction,
|
||||
hotKeys: ['G', 'W'],
|
||||
component: <SeeVersionWorkflowRunSingleRecordAction />,
|
||||
},
|
||||
},
|
||||
actionKeys: [
|
||||
@ -74,11 +56,13 @@ export const WORKFLOW_RUNS_ACTIONS_CONFIG = inheritActionsFromDefaultConfig({
|
||||
NoSelectionRecordActionKeys.EXPORT_VIEW,
|
||||
NoSelectionRecordActionKeys.SEE_DELETED_RECORDS,
|
||||
NoSelectionRecordActionKeys.HIDE_DELETED_RECORDS,
|
||||
NoSelectionRecordActionKeys.GO_TO_WORKFLOWS,
|
||||
NoSelectionRecordActionKeys.GO_TO_PEOPLE,
|
||||
NoSelectionRecordActionKeys.GO_TO_COMPANIES,
|
||||
NoSelectionRecordActionKeys.GO_TO_OPPORTUNITIES,
|
||||
NoSelectionRecordActionKeys.GO_TO_SETTINGS,
|
||||
NoSelectionRecordActionKeys.GO_TO_TASKS,
|
||||
NoSelectionRecordActionKeys.GO_TO_NOTES,
|
||||
],
|
||||
propertiesToOverwrite: {
|
||||
[SingleRecordActionKeys.ADD_TO_FAVORITES]: {
|
||||
@ -115,6 +99,10 @@ export const WORKFLOW_RUNS_ACTIONS_CONFIG = inheritActionsFromDefaultConfig({
|
||||
[SingleRecordActionKeys.NAVIGATE_TO_NEXT_RECORD]: {
|
||||
position: 10,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_WORKFLOWS]: {
|
||||
position: 11,
|
||||
isPinned: true,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_PEOPLE]: {
|
||||
position: 12,
|
||||
},
|
||||
@ -130,5 +118,8 @@ export const WORKFLOW_RUNS_ACTIONS_CONFIG = inheritActionsFromDefaultConfig({
|
||||
[NoSelectionRecordActionKeys.GO_TO_TASKS]: {
|
||||
position: 16,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_NOTES]: {
|
||||
position: 17,
|
||||
},
|
||||
},
|
||||
});
|
||||
@ -1,20 +1,19 @@
|
||||
import { ActionLink } from '@/action-menu/actions/components/ActionLink';
|
||||
import { MultipleRecordsActionKeys } from '@/action-menu/actions/record-actions/multiple-records/types/MultipleRecordsActionKeys';
|
||||
import { useSeeRunsNoSelectionRecordAction } from '@/action-menu/actions/record-actions/no-selection/hooks/useSeeRunsNoSelectionRecordAction';
|
||||
import { useSeeWorkflowsNoSelectionRecordAction } from '@/action-menu/actions/record-actions/no-selection/hooks/useSeeWorkflowsNoSelectionRecordAction';
|
||||
import { NoSelectionRecordActionKeys } from '@/action-menu/actions/record-actions/no-selection/types/NoSelectionRecordActionsKeys';
|
||||
import { NoSelectionWorkflowRecordActionKeys } from '@/action-menu/actions/record-actions/no-selection/workflow-actions/types/NoSelectionWorkflowRecordActionsKeys';
|
||||
import { SingleRecordActionKeys } from '@/action-menu/actions/record-actions/single-record/types/SingleRecordActionsKey';
|
||||
import { useSeeRunsWorkflowVersionSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-version-actions/hooks/useSeeRunsWorkflowVersionSingleRecordAction';
|
||||
import { useSeeVersionsWorkflowVersionSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-version-actions/hooks/useSeeVersionsWorkflowVersionSingleRecordAction';
|
||||
import { useSeeWorkflowWorkflowVersionSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-version-actions/hooks/useSeeWorkflowWorkflowVersionSingleRecordAction';
|
||||
import { useUseAsDraftWorkflowVersionSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-version-actions/hooks/useUseAsDraftWorkflowVersionSingleRecordAction';
|
||||
import { SeeRunsWorkflowVersionSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-version-actions/components/SeeRunsWorkflowVersionSingleRecordAction';
|
||||
import { SeeVersionsWorkflowVersionSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-version-actions/components/SeeVersionsWorkflowVersionSingleRecordAction';
|
||||
import { SeeWorkflowWorkflowVersionSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-version-actions/components/SeeWorkflowWorkflowVersionSingleRecordAction';
|
||||
import { UseAsDraftWorkflowVersionSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/workflow-version-actions/components/UseAsDraftWorkflowVersionSingleRecordAction';
|
||||
import { WorkflowVersionSingleRecordActionKeys } from '@/action-menu/actions/record-actions/single-record/workflow-version-actions/types/WorkflowVersionSingleRecordActionsKeys';
|
||||
import { inheritActionsFromDefaultConfig } from '@/action-menu/actions/record-actions/utils/inheritActionsFromDefaultConfig';
|
||||
import { ActionScope } from '@/action-menu/actions/types/ActionScope';
|
||||
import { ActionType } from '@/action-menu/actions/types/ActionType';
|
||||
import { ActionViewType } from '@/action-menu/actions/types/ActionViewType';
|
||||
import {
|
||||
ActionMenuEntryScope,
|
||||
ActionMenuEntryType,
|
||||
} from '@/action-menu/types/ActionMenuEntry';
|
||||
import { CoreObjectNamePlural } from '@/object-metadata/types/CoreObjectNamePlural';
|
||||
import { AppPath } from '@/types/AppPath';
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import {
|
||||
@ -33,8 +32,8 @@ export const WORKFLOW_VERSIONS_ACTIONS_CONFIG = inheritActionsFromDefaultConfig(
|
||||
shortLabel: msg`Use as draft`,
|
||||
position: 1,
|
||||
isPinned: true,
|
||||
type: ActionMenuEntryType.Standard,
|
||||
scope: ActionMenuEntryScope.RecordSelection,
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
Icon: IconPencil,
|
||||
shouldBeRegistered: ({ selectedRecord }) =>
|
||||
isDefined(selectedRecord) && selectedRecord.status !== 'DRAFT',
|
||||
@ -42,7 +41,7 @@ export const WORKFLOW_VERSIONS_ACTIONS_CONFIG = inheritActionsFromDefaultConfig(
|
||||
ActionViewType.SHOW_PAGE,
|
||||
ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
],
|
||||
useAction: useUseAsDraftWorkflowVersionSingleRecordAction,
|
||||
component: <UseAsDraftWorkflowVersionSingleRecordAction />,
|
||||
},
|
||||
[WorkflowVersionSingleRecordActionKeys.SEE_WORKFLOW]: {
|
||||
key: WorkflowVersionSingleRecordActionKeys.SEE_WORKFLOW,
|
||||
@ -50,8 +49,8 @@ export const WORKFLOW_VERSIONS_ACTIONS_CONFIG = inheritActionsFromDefaultConfig(
|
||||
shortLabel: msg`See workflow`,
|
||||
position: 2,
|
||||
isPinned: true,
|
||||
type: ActionMenuEntryType.Standard,
|
||||
scope: ActionMenuEntryScope.RecordSelection,
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
Icon: IconSettingsAutomation,
|
||||
shouldBeRegistered: ({ selectedRecord }) =>
|
||||
isDefined(selectedRecord?.workflow?.id),
|
||||
@ -59,7 +58,7 @@ export const WORKFLOW_VERSIONS_ACTIONS_CONFIG = inheritActionsFromDefaultConfig(
|
||||
ActionViewType.SHOW_PAGE,
|
||||
ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
],
|
||||
useAction: useSeeWorkflowWorkflowVersionSingleRecordAction,
|
||||
component: <SeeWorkflowWorkflowVersionSingleRecordAction />,
|
||||
},
|
||||
[WorkflowVersionSingleRecordActionKeys.SEE_RUNS]: {
|
||||
key: WorkflowVersionSingleRecordActionKeys.SEE_RUNS,
|
||||
@ -67,8 +66,8 @@ export const WORKFLOW_VERSIONS_ACTIONS_CONFIG = inheritActionsFromDefaultConfig(
|
||||
shortLabel: msg`See runs`,
|
||||
position: 3,
|
||||
isPinned: true,
|
||||
type: ActionMenuEntryType.Standard,
|
||||
scope: ActionMenuEntryScope.RecordSelection,
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
Icon: IconHistoryToggle,
|
||||
shouldBeRegistered: ({ workflowWithCurrentVersion }) =>
|
||||
isDefined(workflowWithCurrentVersion),
|
||||
@ -76,15 +75,15 @@ export const WORKFLOW_VERSIONS_ACTIONS_CONFIG = inheritActionsFromDefaultConfig(
|
||||
ActionViewType.SHOW_PAGE,
|
||||
ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
],
|
||||
useAction: useSeeRunsWorkflowVersionSingleRecordAction,
|
||||
component: <SeeRunsWorkflowVersionSingleRecordAction />,
|
||||
},
|
||||
[WorkflowVersionSingleRecordActionKeys.SEE_VERSIONS]: {
|
||||
key: WorkflowVersionSingleRecordActionKeys.SEE_VERSIONS,
|
||||
label: msg`See versions history`,
|
||||
shortLabel: msg`See versions`,
|
||||
position: 4,
|
||||
type: ActionMenuEntryType.Standard,
|
||||
scope: ActionMenuEntryScope.RecordSelection,
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.RecordSelection,
|
||||
Icon: IconVersions,
|
||||
shouldBeRegistered: ({ workflowWithCurrentVersion }) =>
|
||||
isDefined(workflowWithCurrentVersion),
|
||||
@ -92,26 +91,11 @@ export const WORKFLOW_VERSIONS_ACTIONS_CONFIG = inheritActionsFromDefaultConfig(
|
||||
ActionViewType.SHOW_PAGE,
|
||||
ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
],
|
||||
useAction: useSeeVersionsWorkflowVersionSingleRecordAction,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_WORKFLOWS]: {
|
||||
type: ActionMenuEntryType.Navigation,
|
||||
scope: ActionMenuEntryScope.Global,
|
||||
key: NoSelectionRecordActionKeys.GO_TO_WORKFLOWS,
|
||||
label: msg`Go to workflows`,
|
||||
shortLabel: msg`See workflows`,
|
||||
position: 14,
|
||||
Icon: IconSettingsAutomation,
|
||||
accent: 'default',
|
||||
isPinned: true,
|
||||
shouldBeRegistered: () => true,
|
||||
availableOn: [ActionViewType.INDEX_PAGE_NO_SELECTION],
|
||||
useAction: useSeeWorkflowsNoSelectionRecordAction,
|
||||
hotKeys: ['G', 'W'],
|
||||
component: <SeeVersionsWorkflowVersionSingleRecordAction />,
|
||||
},
|
||||
[NoSelectionWorkflowRecordActionKeys.GO_TO_RUNS]: {
|
||||
type: ActionMenuEntryType.Navigation,
|
||||
scope: ActionMenuEntryScope.Global,
|
||||
type: ActionType.Navigation,
|
||||
scope: ActionScope.Global,
|
||||
key: NoSelectionWorkflowRecordActionKeys.GO_TO_RUNS,
|
||||
label: msg`Go to runs`,
|
||||
shortLabel: msg`See runs`,
|
||||
@ -121,7 +105,12 @@ export const WORKFLOW_VERSIONS_ACTIONS_CONFIG = inheritActionsFromDefaultConfig(
|
||||
isPinned: true,
|
||||
shouldBeRegistered: () => true,
|
||||
availableOn: [ActionViewType.INDEX_PAGE_NO_SELECTION],
|
||||
useAction: useSeeRunsNoSelectionRecordAction,
|
||||
component: (
|
||||
<ActionLink
|
||||
to={AppPath.RecordIndexPage}
|
||||
params={{ objectNamePlural: CoreObjectNamePlural.WorkflowRun }}
|
||||
/>
|
||||
),
|
||||
},
|
||||
},
|
||||
actionKeys: [
|
||||
@ -134,11 +123,13 @@ export const WORKFLOW_VERSIONS_ACTIONS_CONFIG = inheritActionsFromDefaultConfig(
|
||||
NoSelectionRecordActionKeys.EXPORT_VIEW,
|
||||
NoSelectionRecordActionKeys.SEE_DELETED_RECORDS,
|
||||
NoSelectionRecordActionKeys.HIDE_DELETED_RECORDS,
|
||||
NoSelectionRecordActionKeys.GO_TO_WORKFLOWS,
|
||||
NoSelectionRecordActionKeys.GO_TO_PEOPLE,
|
||||
NoSelectionRecordActionKeys.GO_TO_COMPANIES,
|
||||
NoSelectionRecordActionKeys.GO_TO_OPPORTUNITIES,
|
||||
NoSelectionRecordActionKeys.GO_TO_SETTINGS,
|
||||
NoSelectionRecordActionKeys.GO_TO_TASKS,
|
||||
NoSelectionRecordActionKeys.GO_TO_NOTES,
|
||||
],
|
||||
propertiesToOverwrite: {
|
||||
[SingleRecordActionKeys.ADD_TO_FAVORITES]: {
|
||||
@ -174,19 +165,26 @@ export const WORKFLOW_VERSIONS_ACTIONS_CONFIG = inheritActionsFromDefaultConfig(
|
||||
position: 13,
|
||||
label: msg`Navigate to next version`,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_WORKFLOWS]: {
|
||||
position: 14,
|
||||
isPinned: true,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_PEOPLE]: {
|
||||
position: 16,
|
||||
position: 15,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_COMPANIES]: {
|
||||
position: 17,
|
||||
position: 16,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_OPPORTUNITIES]: {
|
||||
position: 18,
|
||||
position: 17,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_SETTINGS]: {
|
||||
position: 19,
|
||||
position: 18,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_TASKS]: {
|
||||
position: 19,
|
||||
},
|
||||
[NoSelectionRecordActionKeys.GO_TO_NOTES]: {
|
||||
position: 20,
|
||||
},
|
||||
},
|
||||
@ -0,0 +1,81 @@
|
||||
import { ActionModal } from '@/action-menu/actions/components/ActionModal';
|
||||
import { useContextStoreObjectMetadataItemOrThrow } from '@/context-store/hooks/useContextStoreObjectMetadataItemOrThrow';
|
||||
import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/contextStoreCurrentViewIdComponentState';
|
||||
import { contextStoreFiltersComponentState } from '@/context-store/states/contextStoreFiltersComponentState';
|
||||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||
import { computeContextStoreFilters } from '@/context-store/utils/computeContextStoreFilters';
|
||||
import { DEFAULT_QUERY_PAGE_SIZE } from '@/object-record/constants/DefaultQueryPageSize';
|
||||
import { useDeleteManyRecords } from '@/object-record/hooks/useDeleteManyRecords';
|
||||
import { useLazyFetchAllRecords } from '@/object-record/hooks/useLazyFetchAllRecords';
|
||||
import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies';
|
||||
import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable';
|
||||
import { getRecordIndexIdFromObjectNamePluralAndViewId } from '@/object-record/utils/getRecordIndexIdFromObjectNamePluralAndViewId';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { t } from '@lingui/core/macro';
|
||||
|
||||
export const DeleteMultipleRecordsAction = () => {
|
||||
const { objectMetadataItem } = useContextStoreObjectMetadataItemOrThrow();
|
||||
|
||||
const contextStoreCurrentViewId = useRecoilComponentValueV2(
|
||||
contextStoreCurrentViewIdComponentState,
|
||||
);
|
||||
|
||||
if (!contextStoreCurrentViewId) {
|
||||
throw new Error('Current view ID is not defined');
|
||||
}
|
||||
|
||||
const { resetTableRowSelection } = useRecordTable({
|
||||
recordTableId: getRecordIndexIdFromObjectNamePluralAndViewId(
|
||||
objectMetadataItem.namePlural,
|
||||
contextStoreCurrentViewId,
|
||||
),
|
||||
});
|
||||
|
||||
const { deleteManyRecords } = useDeleteManyRecords({
|
||||
objectNameSingular: objectMetadataItem.nameSingular,
|
||||
});
|
||||
|
||||
const contextStoreTargetedRecordsRule = useRecoilComponentValueV2(
|
||||
contextStoreTargetedRecordsRuleComponentState,
|
||||
);
|
||||
|
||||
const contextStoreFilters = useRecoilComponentValueV2(
|
||||
contextStoreFiltersComponentState,
|
||||
);
|
||||
|
||||
const { filterValueDependencies } = useFilterValueDependencies();
|
||||
|
||||
const graphqlFilter = computeContextStoreFilters(
|
||||
contextStoreTargetedRecordsRule,
|
||||
contextStoreFilters,
|
||||
objectMetadataItem,
|
||||
filterValueDependencies,
|
||||
);
|
||||
|
||||
const { fetchAllRecords: fetchAllRecordIds } = useLazyFetchAllRecords({
|
||||
objectNameSingular: objectMetadataItem.nameSingular,
|
||||
filter: graphqlFilter,
|
||||
limit: DEFAULT_QUERY_PAGE_SIZE,
|
||||
recordGqlFields: { id: true },
|
||||
});
|
||||
|
||||
const handleDeleteClick = async () => {
|
||||
const recordsToDelete = await fetchAllRecordIds();
|
||||
const recordIdsToDelete = recordsToDelete.map((record) => record.id);
|
||||
|
||||
resetTableRowSelection();
|
||||
|
||||
await deleteManyRecords({
|
||||
recordIdsToDelete,
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<ActionModal
|
||||
title="Delete Records"
|
||||
subtitle={t`Are you sure you want to delete these records? They can be recovered from the Command menu.`}
|
||||
onConfirmClick={handleDeleteClick}
|
||||
confirmButtonText="Delete Records"
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,85 @@
|
||||
import { ActionModal } from '@/action-menu/actions/components/ActionModal';
|
||||
import { useContextStoreObjectMetadataItemOrThrow } from '@/context-store/hooks/useContextStoreObjectMetadataItemOrThrow';
|
||||
import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/contextStoreCurrentViewIdComponentState';
|
||||
import { contextStoreFiltersComponentState } from '@/context-store/states/contextStoreFiltersComponentState';
|
||||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||
import { computeContextStoreFilters } from '@/context-store/utils/computeContextStoreFilters';
|
||||
import { DEFAULT_QUERY_PAGE_SIZE } from '@/object-record/constants/DefaultQueryPageSize';
|
||||
import { RecordGqlOperationFilter } from '@/object-record/graphql/types/RecordGqlOperationFilter';
|
||||
import { useDestroyManyRecords } from '@/object-record/hooks/useDestroyManyRecords';
|
||||
import { useLazyFetchAllRecords } from '@/object-record/hooks/useLazyFetchAllRecords';
|
||||
import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies';
|
||||
import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable';
|
||||
import { getRecordIndexIdFromObjectNamePluralAndViewId } from '@/object-record/utils/getRecordIndexIdFromObjectNamePluralAndViewId';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
|
||||
export const DestroyMultipleRecordsAction = () => {
|
||||
const { objectMetadataItem } = useContextStoreObjectMetadataItemOrThrow();
|
||||
|
||||
const contextStoreCurrentViewId = useRecoilComponentValueV2(
|
||||
contextStoreCurrentViewIdComponentState,
|
||||
);
|
||||
|
||||
if (!contextStoreCurrentViewId) {
|
||||
throw new Error('Current view ID is not defined');
|
||||
}
|
||||
|
||||
const { resetTableRowSelection } = useRecordTable({
|
||||
recordTableId: getRecordIndexIdFromObjectNamePluralAndViewId(
|
||||
objectMetadataItem.namePlural,
|
||||
contextStoreCurrentViewId,
|
||||
),
|
||||
});
|
||||
|
||||
const { destroyManyRecords } = useDestroyManyRecords({
|
||||
objectNameSingular: objectMetadataItem.nameSingular,
|
||||
});
|
||||
|
||||
const contextStoreTargetedRecordsRule = useRecoilComponentValueV2(
|
||||
contextStoreTargetedRecordsRuleComponentState,
|
||||
);
|
||||
|
||||
const contextStoreFilters = useRecoilComponentValueV2(
|
||||
contextStoreFiltersComponentState,
|
||||
);
|
||||
|
||||
const { filterValueDependencies } = useFilterValueDependencies();
|
||||
|
||||
const deletedAtFilter: RecordGqlOperationFilter = {
|
||||
deletedAt: { is: 'NOT_NULL' },
|
||||
};
|
||||
const graphqlFilter = {
|
||||
...computeContextStoreFilters(
|
||||
contextStoreTargetedRecordsRule,
|
||||
contextStoreFilters,
|
||||
objectMetadataItem,
|
||||
filterValueDependencies,
|
||||
),
|
||||
...deletedAtFilter,
|
||||
};
|
||||
|
||||
const { fetchAllRecords: fetchAllRecordIds } = useLazyFetchAllRecords({
|
||||
objectNameSingular: objectMetadataItem.nameSingular,
|
||||
filter: graphqlFilter,
|
||||
limit: DEFAULT_QUERY_PAGE_SIZE,
|
||||
recordGqlFields: { id: true },
|
||||
});
|
||||
|
||||
const handleDestroyClick = async () => {
|
||||
const recordsToDestroy = await fetchAllRecordIds();
|
||||
const recordIdsToDestroy = recordsToDestroy.map((record) => record.id);
|
||||
|
||||
resetTableRowSelection();
|
||||
|
||||
await destroyManyRecords({ recordIdsToDestroy });
|
||||
};
|
||||
|
||||
return (
|
||||
<ActionModal
|
||||
title="Permanently Destroy Records"
|
||||
subtitle="Are you sure you want to destroy these records? They won't be recoverable anymore."
|
||||
onConfirmClick={handleDestroyClick}
|
||||
confirmButtonText="Destroy Records"
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -1,15 +1,13 @@
|
||||
import { Action } from '@/action-menu/actions/components/Action';
|
||||
import { useContextStoreObjectMetadataItemOrThrow } from '@/context-store/hooks/useContextStoreObjectMetadataItemOrThrow';
|
||||
import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/contextStoreCurrentViewIdComponentState';
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
|
||||
import { useExportRecords } from '@/object-record/record-index/export/hooks/useExportRecords';
|
||||
import { getRecordIndexIdFromObjectNamePluralAndViewId } from '@/object-record/utils/getRecordIndexIdFromObjectNamePluralAndViewId';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
|
||||
export const useExportMultipleRecordsAction = ({
|
||||
objectMetadataItem,
|
||||
}: {
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
}) => {
|
||||
export const ExportMultipleRecordsAction = () => {
|
||||
const { objectMetadataItem } = useContextStoreObjectMetadataItemOrThrow();
|
||||
|
||||
const contextStoreCurrentViewId = useRecoilComponentValueV2(
|
||||
contextStoreCurrentViewIdComponentState,
|
||||
);
|
||||
@ -28,11 +26,5 @@ export const useExportMultipleRecordsAction = ({
|
||||
filename: `${objectMetadataItem.nameSingular}.csv`,
|
||||
});
|
||||
|
||||
const onClick = async () => {
|
||||
await download();
|
||||
};
|
||||
|
||||
return {
|
||||
onClick,
|
||||
};
|
||||
return <Action onClick={download} />;
|
||||
};
|
||||
@ -0,0 +1,89 @@
|
||||
import { ActionModal } from '@/action-menu/actions/components/ActionModal';
|
||||
import { useContextStoreObjectMetadataItemOrThrow } from '@/context-store/hooks/useContextStoreObjectMetadataItemOrThrow';
|
||||
import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/contextStoreCurrentViewIdComponentState';
|
||||
import { contextStoreFiltersComponentState } from '@/context-store/states/contextStoreFiltersComponentState';
|
||||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||
import { computeContextStoreFilters } from '@/context-store/utils/computeContextStoreFilters';
|
||||
import { DEFAULT_QUERY_PAGE_SIZE } from '@/object-record/constants/DefaultQueryPageSize';
|
||||
import { RecordGqlOperationFilter } from '@/object-record/graphql/types/RecordGqlOperationFilter';
|
||||
import { useLazyFetchAllRecords } from '@/object-record/hooks/useLazyFetchAllRecords';
|
||||
import { useRestoreManyRecords } from '@/object-record/hooks/useRestoreManyRecords';
|
||||
import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies';
|
||||
import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable';
|
||||
import { getRecordIndexIdFromObjectNamePluralAndViewId } from '@/object-record/utils/getRecordIndexIdFromObjectNamePluralAndViewId';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
|
||||
export const RestoreMultipleRecordsAction = () => {
|
||||
const { objectMetadataItem } = useContextStoreObjectMetadataItemOrThrow();
|
||||
|
||||
const contextStoreCurrentViewId = useRecoilComponentValueV2(
|
||||
contextStoreCurrentViewIdComponentState,
|
||||
);
|
||||
|
||||
if (!contextStoreCurrentViewId) {
|
||||
throw new Error('Current view ID is not defined');
|
||||
}
|
||||
|
||||
const { resetTableRowSelection } = useRecordTable({
|
||||
recordTableId: getRecordIndexIdFromObjectNamePluralAndViewId(
|
||||
objectMetadataItem.namePlural,
|
||||
contextStoreCurrentViewId,
|
||||
),
|
||||
});
|
||||
|
||||
const { restoreManyRecords } = useRestoreManyRecords({
|
||||
objectNameSingular: objectMetadataItem.nameSingular,
|
||||
});
|
||||
|
||||
const contextStoreTargetedRecordsRule = useRecoilComponentValueV2(
|
||||
contextStoreTargetedRecordsRuleComponentState,
|
||||
);
|
||||
|
||||
const contextStoreFilters = useRecoilComponentValueV2(
|
||||
contextStoreFiltersComponentState,
|
||||
);
|
||||
|
||||
const { filterValueDependencies } = useFilterValueDependencies();
|
||||
|
||||
const deletedAtFilter: RecordGqlOperationFilter = {
|
||||
deletedAt: { is: 'NOT_NULL' },
|
||||
};
|
||||
|
||||
const graphqlFilter = {
|
||||
...computeContextStoreFilters(
|
||||
contextStoreTargetedRecordsRule,
|
||||
contextStoreFilters,
|
||||
objectMetadataItem,
|
||||
filterValueDependencies,
|
||||
),
|
||||
...deletedAtFilter,
|
||||
};
|
||||
|
||||
const { fetchAllRecords: fetchAllRecordIds } = useLazyFetchAllRecords({
|
||||
objectNameSingular: objectMetadataItem.nameSingular,
|
||||
filter: graphqlFilter,
|
||||
limit: DEFAULT_QUERY_PAGE_SIZE,
|
||||
recordGqlFields: { id: true },
|
||||
});
|
||||
|
||||
const handleRestoreClick = async () => {
|
||||
const recordsToRestore = await fetchAllRecordIds();
|
||||
const recordIdsToRestore = recordsToRestore.map((record) => record.id);
|
||||
|
||||
resetTableRowSelection();
|
||||
|
||||
await restoreManyRecords({
|
||||
idsToRestore: recordIdsToRestore,
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<ActionModal
|
||||
title="Restore Records"
|
||||
subtitle="Are you sure you want to restore these records?"
|
||||
onConfirmClick={handleRestoreClick}
|
||||
confirmButtonText="Restore Records"
|
||||
confirmButtonAccent="default"
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -1,88 +0,0 @@
|
||||
import { DeleteManyRecordsProps } from '@/object-record/hooks/useDeleteManyRecords';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { renderHook, waitFor } from '@testing-library/react';
|
||||
import { act } from 'react';
|
||||
import { getJestMetadataAndApolloMocksAndActionMenuWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksAndActionMenuWrapper';
|
||||
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
|
||||
import { getPeopleRecordConnectionMock } from '~/testing/mock-data/people';
|
||||
import { useDeleteMultipleRecordsAction } from '../useDeleteMultipleRecordsAction';
|
||||
|
||||
const personMockObjectMetadataItem = generatedMockObjectMetadataItems.find(
|
||||
(item) => item.nameSingular === 'person',
|
||||
)!;
|
||||
|
||||
const peopleMock = getPeopleRecordConnectionMock();
|
||||
|
||||
const deleteManyRecordsMock = jest.fn();
|
||||
const resetTableRowSelectionMock = jest.fn();
|
||||
|
||||
jest.mock('@/object-record/hooks/useDeleteManyRecords', () => ({
|
||||
useDeleteManyRecords: () => ({
|
||||
deleteManyRecords: deleteManyRecordsMock,
|
||||
}),
|
||||
}));
|
||||
|
||||
jest.mock('@/object-record/hooks/useLazyFetchAllRecords', () => ({
|
||||
useLazyFetchAllRecords: () => {
|
||||
return {
|
||||
fetchAllRecords: () => [peopleMock[0], peopleMock[1]],
|
||||
};
|
||||
},
|
||||
}));
|
||||
|
||||
jest.mock('@/object-record/record-table/hooks/useRecordTable', () => ({
|
||||
useRecordTable: () => ({
|
||||
resetTableRowSelection: resetTableRowSelectionMock,
|
||||
}),
|
||||
}));
|
||||
|
||||
const wrapper = getJestMetadataAndApolloMocksAndActionMenuWrapper({
|
||||
apolloMocks: [],
|
||||
componentInstanceId: '1',
|
||||
contextStoreCurrentObjectMetadataNameSingular:
|
||||
personMockObjectMetadataItem.nameSingular,
|
||||
contextStoreCurrentViewId: 'my-view-id',
|
||||
contextStoreTargetedRecordsRule: {
|
||||
mode: 'selection',
|
||||
selectedRecordIds: [peopleMock[0].id, peopleMock[1].id],
|
||||
},
|
||||
contextStoreNumberOfSelectedRecords: 2,
|
||||
onInitializeRecoilSnapshot: (snapshot) => {
|
||||
snapshot.set(recordStoreFamilyState(peopleMock[0].id), peopleMock[0]);
|
||||
snapshot.set(recordStoreFamilyState(peopleMock[1].id), peopleMock[1]);
|
||||
},
|
||||
});
|
||||
|
||||
describe('useDeleteMultipleRecordsAction', () => {
|
||||
it('should call deleteManyRecords on click', async () => {
|
||||
const { result } = renderHook(
|
||||
() =>
|
||||
useDeleteMultipleRecordsAction({
|
||||
objectMetadataItem: personMockObjectMetadataItem,
|
||||
}),
|
||||
{
|
||||
wrapper,
|
||||
},
|
||||
);
|
||||
|
||||
expect(result.current.ConfirmationModal?.props?.isOpen).toBe(false);
|
||||
|
||||
act(() => {
|
||||
result.current.onClick();
|
||||
});
|
||||
|
||||
expect(result.current.ConfirmationModal?.props?.isOpen).toBe(true);
|
||||
|
||||
act(() => {
|
||||
result.current.ConfirmationModal?.props?.onConfirmClick();
|
||||
});
|
||||
|
||||
const expectedParams: DeleteManyRecordsProps = {
|
||||
recordIdsToDelete: [peopleMock[0].id, peopleMock[1].id],
|
||||
};
|
||||
await waitFor(() => {
|
||||
expect(resetTableRowSelectionMock).toHaveBeenCalled();
|
||||
expect(deleteManyRecordsMock).toHaveBeenCalledWith(expectedParams);
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1,122 +0,0 @@
|
||||
import { DestroyManyRecordsProps } from '@/object-record/hooks/useDestroyManyRecords';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
|
||||
import { expect } from '@storybook/test';
|
||||
import { renderHook, waitFor } from '@testing-library/react';
|
||||
import { act } from 'react';
|
||||
import {
|
||||
GetJestMetadataAndApolloMocksAndActionMenuWrapperProps,
|
||||
getJestMetadataAndApolloMocksAndActionMenuWrapper,
|
||||
} from '~/testing/jest/getJestMetadataAndApolloMocksAndActionMenuWrapper';
|
||||
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
|
||||
import { getPeopleRecordConnectionMock } from '~/testing/mock-data/people';
|
||||
import { useDestroyMultipleRecordsAction } from '../useDestroyMultipleRecordsAction';
|
||||
|
||||
const personMockObjectMetadataItem = generatedMockObjectMetadataItems.find(
|
||||
(item) => item.nameSingular === 'person',
|
||||
)!;
|
||||
const personMockObjectMetadataItemDeletedAtField =
|
||||
personMockObjectMetadataItem.fields.find((el) => el.name === 'deletedAt');
|
||||
if (personMockObjectMetadataItemDeletedAtField === undefined)
|
||||
throw new Error('Should never occur');
|
||||
|
||||
const [firstPeopleMock, secondPeopleMock] = getPeopleRecordConnectionMock().map(
|
||||
(record) => ({
|
||||
...record,
|
||||
deletedAt: new Date().toISOString(),
|
||||
}),
|
||||
);
|
||||
|
||||
const destroyManyRecordsMock = jest.fn();
|
||||
const resetTableRowSelectionMock = jest.fn();
|
||||
|
||||
jest.mock('@/object-record/hooks/useDestroyManyRecords', () => ({
|
||||
useDestroyManyRecords: () => ({
|
||||
destroyManyRecords: destroyManyRecordsMock,
|
||||
}),
|
||||
}));
|
||||
|
||||
jest.mock('@/object-record/hooks/useLazyFetchAllRecords', () => ({
|
||||
useLazyFetchAllRecords: () => {
|
||||
return {
|
||||
fetchAllRecords: () => [firstPeopleMock, secondPeopleMock],
|
||||
};
|
||||
},
|
||||
}));
|
||||
|
||||
jest.mock('@/object-record/record-table/hooks/useRecordTable', () => ({
|
||||
useRecordTable: () => ({
|
||||
resetTableRowSelection: resetTableRowSelectionMock,
|
||||
}),
|
||||
}));
|
||||
|
||||
const getWrapper = (
|
||||
overrides?: Partial<GetJestMetadataAndApolloMocksAndActionMenuWrapperProps>,
|
||||
) =>
|
||||
getJestMetadataAndApolloMocksAndActionMenuWrapper({
|
||||
apolloMocks: [],
|
||||
componentInstanceId: '1',
|
||||
contextStoreCurrentObjectMetadataNameSingular:
|
||||
personMockObjectMetadataItem.nameSingular,
|
||||
contextStoreCurrentViewId: 'my-view-id',
|
||||
contextStoreTargetedRecordsRule: {
|
||||
mode: 'selection',
|
||||
selectedRecordIds: [firstPeopleMock.id, secondPeopleMock.id],
|
||||
},
|
||||
contextStoreFilters: [],
|
||||
contextStoreNumberOfSelectedRecords: 2,
|
||||
onInitializeRecoilSnapshot: (snapshot) => {
|
||||
snapshot.set(recordStoreFamilyState(firstPeopleMock.id), firstPeopleMock);
|
||||
snapshot.set(
|
||||
recordStoreFamilyState(secondPeopleMock.id),
|
||||
secondPeopleMock,
|
||||
);
|
||||
},
|
||||
...overrides,
|
||||
});
|
||||
|
||||
describe('useDestroyMultipleRecordsAction', () => {
|
||||
it('should call destroyManyRecords on click if records are filtered by deletedAt', async () => {
|
||||
const { result } = renderHook(
|
||||
() =>
|
||||
useDestroyMultipleRecordsAction({
|
||||
objectMetadataItem: personMockObjectMetadataItem,
|
||||
}),
|
||||
{
|
||||
wrapper: getWrapper({
|
||||
contextStoreFilters: [
|
||||
{
|
||||
id: '1553cda7-893d-4d89-b7ab-04969a4c2927',
|
||||
fieldMetadataId: personMockObjectMetadataItemDeletedAtField.id,
|
||||
value: '',
|
||||
displayValue: '',
|
||||
operand: ViewFilterOperand.IsNotEmpty,
|
||||
type: 'DATE_TIME',
|
||||
label: 'Deleted',
|
||||
},
|
||||
],
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
expect(result.current.ConfirmationModal?.props?.isOpen).toBeFalsy();
|
||||
|
||||
act(() => {
|
||||
result.current.onClick();
|
||||
});
|
||||
|
||||
expect(result.current.ConfirmationModal?.props?.isOpen).toBe(true);
|
||||
|
||||
act(() => {
|
||||
result.current.ConfirmationModal?.props?.onConfirmClick();
|
||||
});
|
||||
|
||||
const expectedParams: DestroyManyRecordsProps = {
|
||||
recordIdsToDestroy: [firstPeopleMock.id, secondPeopleMock.id],
|
||||
};
|
||||
await waitFor(() => {
|
||||
expect(resetTableRowSelectionMock).toHaveBeenCalled();
|
||||
expect(destroyManyRecordsMock).toHaveBeenCalledWith(expectedParams);
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1,60 +0,0 @@
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { renderHook, waitFor } from '@testing-library/react';
|
||||
import { act } from 'react';
|
||||
import { getJestMetadataAndApolloMocksAndActionMenuWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksAndActionMenuWrapper';
|
||||
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
|
||||
import { getPeopleRecordConnectionMock } from '~/testing/mock-data/people';
|
||||
import { useExportMultipleRecordsAction } from '../useExportMultipleRecordsAction';
|
||||
|
||||
const personMockObjectMetadataItem = generatedMockObjectMetadataItems.find(
|
||||
(item) => item.nameSingular === 'person',
|
||||
)!;
|
||||
|
||||
const peopleMock = getPeopleRecordConnectionMock();
|
||||
|
||||
const downloadMock = jest.fn();
|
||||
|
||||
jest.mock('@/object-record/record-index/export/hooks/useExportRecords', () => ({
|
||||
useExportRecords: () => ({
|
||||
download: downloadMock,
|
||||
}),
|
||||
}));
|
||||
|
||||
const wrapper = getJestMetadataAndApolloMocksAndActionMenuWrapper({
|
||||
apolloMocks: [],
|
||||
componentInstanceId: '1',
|
||||
contextStoreCurrentViewId: 'my-view-id',
|
||||
contextStoreCurrentObjectMetadataNameSingular:
|
||||
personMockObjectMetadataItem.nameSingular,
|
||||
contextStoreTargetedRecordsRule: {
|
||||
mode: 'selection',
|
||||
selectedRecordIds: [peopleMock[0].id, peopleMock[1].id],
|
||||
},
|
||||
contextStoreNumberOfSelectedRecords: 2,
|
||||
onInitializeRecoilSnapshot: (snapshot) => {
|
||||
snapshot.set(recordStoreFamilyState(peopleMock[0].id), peopleMock[0]);
|
||||
snapshot.set(recordStoreFamilyState(peopleMock[1].id), peopleMock[1]);
|
||||
},
|
||||
});
|
||||
|
||||
describe('useExportMultipleRecordsAction', () => {
|
||||
it('should call exportManyRecords on click', async () => {
|
||||
const { result } = renderHook(
|
||||
() =>
|
||||
useExportMultipleRecordsAction({
|
||||
objectMetadataItem: personMockObjectMetadataItem,
|
||||
}),
|
||||
{
|
||||
wrapper,
|
||||
},
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.onClick();
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(downloadMock).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1,96 +0,0 @@
|
||||
import { ActionHookWithObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
|
||||
import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/contextStoreCurrentViewIdComponentState';
|
||||
|
||||
import { contextStoreFiltersComponentState } from '@/context-store/states/contextStoreFiltersComponentState';
|
||||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||
import { computeContextStoreFilters } from '@/context-store/utils/computeContextStoreFilters';
|
||||
import { DEFAULT_QUERY_PAGE_SIZE } from '@/object-record/constants/DefaultQueryPageSize';
|
||||
import { useDeleteManyRecords } from '@/object-record/hooks/useDeleteManyRecords';
|
||||
import { useLazyFetchAllRecords } from '@/object-record/hooks/useLazyFetchAllRecords';
|
||||
import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies';
|
||||
import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable';
|
||||
import { getRecordIndexIdFromObjectNamePluralAndViewId } from '@/object-record/utils/getRecordIndexIdFromObjectNamePluralAndViewId';
|
||||
import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { useCallback, useState } from 'react';
|
||||
|
||||
export const useDeleteMultipleRecordsAction: ActionHookWithObjectMetadataItem =
|
||||
({ objectMetadataItem }) => {
|
||||
const [isDeleteRecordsModalOpen, setIsDeleteRecordsModalOpen] =
|
||||
useState(false);
|
||||
|
||||
const contextStoreCurrentViewId = useRecoilComponentValueV2(
|
||||
contextStoreCurrentViewIdComponentState,
|
||||
);
|
||||
|
||||
if (!contextStoreCurrentViewId) {
|
||||
throw new Error('Current view ID is not defined');
|
||||
}
|
||||
|
||||
const { resetTableRowSelection } = useRecordTable({
|
||||
recordTableId: getRecordIndexIdFromObjectNamePluralAndViewId(
|
||||
objectMetadataItem.namePlural,
|
||||
contextStoreCurrentViewId,
|
||||
),
|
||||
});
|
||||
|
||||
const { deleteManyRecords } = useDeleteManyRecords({
|
||||
objectNameSingular: objectMetadataItem.nameSingular,
|
||||
});
|
||||
|
||||
const contextStoreTargetedRecordsRule = useRecoilComponentValueV2(
|
||||
contextStoreTargetedRecordsRuleComponentState,
|
||||
);
|
||||
|
||||
const contextStoreFilters = useRecoilComponentValueV2(
|
||||
contextStoreFiltersComponentState,
|
||||
);
|
||||
|
||||
const { filterValueDependencies } = useFilterValueDependencies();
|
||||
|
||||
const graphqlFilter = computeContextStoreFilters(
|
||||
contextStoreTargetedRecordsRule,
|
||||
contextStoreFilters,
|
||||
objectMetadataItem,
|
||||
filterValueDependencies,
|
||||
);
|
||||
|
||||
const { fetchAllRecords: fetchAllRecordIds } = useLazyFetchAllRecords({
|
||||
objectNameSingular: objectMetadataItem.nameSingular,
|
||||
filter: graphqlFilter,
|
||||
limit: DEFAULT_QUERY_PAGE_SIZE,
|
||||
recordGqlFields: { id: true },
|
||||
});
|
||||
|
||||
const handleDeleteClick = useCallback(async () => {
|
||||
const recordsToDelete = await fetchAllRecordIds();
|
||||
const recordIdsToDelete = recordsToDelete.map((record) => record.id);
|
||||
|
||||
resetTableRowSelection();
|
||||
|
||||
await deleteManyRecords({
|
||||
recordIdsToDelete,
|
||||
});
|
||||
}, [deleteManyRecords, fetchAllRecordIds, resetTableRowSelection]);
|
||||
|
||||
const onClick = () => {
|
||||
setIsDeleteRecordsModalOpen(true);
|
||||
};
|
||||
|
||||
const confirmationModal = (
|
||||
<ConfirmationModal
|
||||
isOpen={isDeleteRecordsModalOpen}
|
||||
setIsOpen={setIsDeleteRecordsModalOpen}
|
||||
title={'Delete Records'}
|
||||
subtitle={t`Are you sure you want to delete these records? They can be recovered from the Command menu.`}
|
||||
onConfirmClick={handleDeleteClick}
|
||||
confirmButtonText={'Delete Records'}
|
||||
/>
|
||||
);
|
||||
|
||||
return {
|
||||
onClick,
|
||||
ConfirmationModal: confirmationModal,
|
||||
};
|
||||
};
|
||||
@ -1,102 +0,0 @@
|
||||
import { ActionHookWithObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
|
||||
import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/contextStoreCurrentViewIdComponentState';
|
||||
|
||||
import { contextStoreFiltersComponentState } from '@/context-store/states/contextStoreFiltersComponentState';
|
||||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||
import { computeContextStoreFilters } from '@/context-store/utils/computeContextStoreFilters';
|
||||
import { DEFAULT_QUERY_PAGE_SIZE } from '@/object-record/constants/DefaultQueryPageSize';
|
||||
import { RecordGqlOperationFilter } from '@/object-record/graphql/types/RecordGqlOperationFilter';
|
||||
import { useDestroyManyRecords } from '@/object-record/hooks/useDestroyManyRecords';
|
||||
import { useLazyFetchAllRecords } from '@/object-record/hooks/useLazyFetchAllRecords';
|
||||
import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies';
|
||||
import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable';
|
||||
import { getRecordIndexIdFromObjectNamePluralAndViewId } from '@/object-record/utils/getRecordIndexIdFromObjectNamePluralAndViewId';
|
||||
import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { useCallback, useState } from 'react';
|
||||
|
||||
export const useDestroyMultipleRecordsAction: ActionHookWithObjectMetadataItem =
|
||||
({ objectMetadataItem }) => {
|
||||
const [isDestroyRecordsModalOpen, setIsDestroyRecordsModalOpen] =
|
||||
useState(false);
|
||||
|
||||
const contextStoreCurrentViewId = useRecoilComponentValueV2(
|
||||
contextStoreCurrentViewIdComponentState,
|
||||
);
|
||||
|
||||
if (!contextStoreCurrentViewId) {
|
||||
throw new Error('Current view ID is not defined');
|
||||
}
|
||||
|
||||
const { resetTableRowSelection } = useRecordTable({
|
||||
recordTableId: getRecordIndexIdFromObjectNamePluralAndViewId(
|
||||
objectMetadataItem.namePlural,
|
||||
contextStoreCurrentViewId,
|
||||
),
|
||||
});
|
||||
|
||||
const { destroyManyRecords } = useDestroyManyRecords({
|
||||
objectNameSingular: objectMetadataItem.nameSingular,
|
||||
});
|
||||
|
||||
const contextStoreTargetedRecordsRule = useRecoilComponentValueV2(
|
||||
contextStoreTargetedRecordsRuleComponentState,
|
||||
);
|
||||
|
||||
const contextStoreFilters = useRecoilComponentValueV2(
|
||||
contextStoreFiltersComponentState,
|
||||
);
|
||||
|
||||
const { filterValueDependencies } = useFilterValueDependencies();
|
||||
|
||||
const deletedAtFilter: RecordGqlOperationFilter = {
|
||||
deletedAt: { is: 'NOT_NULL' },
|
||||
};
|
||||
const graphqlFilter = {
|
||||
...computeContextStoreFilters(
|
||||
contextStoreTargetedRecordsRule,
|
||||
contextStoreFilters,
|
||||
objectMetadataItem,
|
||||
filterValueDependencies,
|
||||
),
|
||||
...deletedAtFilter,
|
||||
};
|
||||
|
||||
const { fetchAllRecords: fetchAllRecordIds } = useLazyFetchAllRecords({
|
||||
objectNameSingular: objectMetadataItem.nameSingular,
|
||||
filter: graphqlFilter,
|
||||
limit: DEFAULT_QUERY_PAGE_SIZE,
|
||||
recordGqlFields: { id: true },
|
||||
});
|
||||
|
||||
const handleDestroyClick = useCallback(async () => {
|
||||
const recordsToDestroy = await fetchAllRecordIds();
|
||||
const recordIdsToDestroy = recordsToDestroy.map((record) => record.id);
|
||||
|
||||
resetTableRowSelection();
|
||||
|
||||
await destroyManyRecords({ recordIdsToDestroy });
|
||||
}, [destroyManyRecords, fetchAllRecordIds, resetTableRowSelection]);
|
||||
|
||||
const onClick = () => {
|
||||
setIsDestroyRecordsModalOpen(true);
|
||||
};
|
||||
|
||||
const confirmationModal = (
|
||||
<ConfirmationModal
|
||||
isOpen={isDestroyRecordsModalOpen}
|
||||
setIsOpen={setIsDestroyRecordsModalOpen}
|
||||
title={'Permanently Destroy Records'}
|
||||
subtitle={
|
||||
"Are you sure you want to destroy these records? They won't be recoverable anymore."
|
||||
}
|
||||
onConfirmClick={handleDestroyClick}
|
||||
confirmButtonText={'Destroy Records'}
|
||||
/>
|
||||
);
|
||||
|
||||
return {
|
||||
onClick,
|
||||
ConfirmationModal: confirmationModal,
|
||||
};
|
||||
};
|
||||
@ -1,103 +0,0 @@
|
||||
import { useCallback, useState } from 'react';
|
||||
|
||||
import { ActionHookWithObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
|
||||
import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/contextStoreCurrentViewIdComponentState';
|
||||
import { contextStoreFiltersComponentState } from '@/context-store/states/contextStoreFiltersComponentState';
|
||||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||
import { computeContextStoreFilters } from '@/context-store/utils/computeContextStoreFilters';
|
||||
import { DEFAULT_QUERY_PAGE_SIZE } from '@/object-record/constants/DefaultQueryPageSize';
|
||||
import { RecordGqlOperationFilter } from '@/object-record/graphql/types/RecordGqlOperationFilter';
|
||||
import { useLazyFetchAllRecords } from '@/object-record/hooks/useLazyFetchAllRecords';
|
||||
import { useRestoreManyRecords } from '@/object-record/hooks/useRestoreManyRecords';
|
||||
import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies';
|
||||
import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable';
|
||||
import { getRecordIndexIdFromObjectNamePluralAndViewId } from '@/object-record/utils/getRecordIndexIdFromObjectNamePluralAndViewId';
|
||||
import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
|
||||
export const useRestoreMultipleRecordsAction: ActionHookWithObjectMetadataItem =
|
||||
({ objectMetadataItem }) => {
|
||||
const [isRestoreRecordsModalOpen, setIsRestoreRecordsModalOpen] =
|
||||
useState(false);
|
||||
|
||||
const contextStoreCurrentViewId = useRecoilComponentValueV2(
|
||||
contextStoreCurrentViewIdComponentState,
|
||||
);
|
||||
|
||||
if (!contextStoreCurrentViewId) {
|
||||
throw new Error('Current view ID is not defined');
|
||||
}
|
||||
|
||||
const { resetTableRowSelection } = useRecordTable({
|
||||
recordTableId: getRecordIndexIdFromObjectNamePluralAndViewId(
|
||||
objectMetadataItem.namePlural,
|
||||
contextStoreCurrentViewId,
|
||||
),
|
||||
});
|
||||
|
||||
const { restoreManyRecords } = useRestoreManyRecords({
|
||||
objectNameSingular: objectMetadataItem.nameSingular,
|
||||
});
|
||||
|
||||
const contextStoreTargetedRecordsRule = useRecoilComponentValueV2(
|
||||
contextStoreTargetedRecordsRuleComponentState,
|
||||
);
|
||||
|
||||
const contextStoreFilters = useRecoilComponentValueV2(
|
||||
contextStoreFiltersComponentState,
|
||||
);
|
||||
|
||||
const { filterValueDependencies } = useFilterValueDependencies();
|
||||
|
||||
const deletedAtFilter: RecordGqlOperationFilter = {
|
||||
deletedAt: { is: 'NOT_NULL' },
|
||||
};
|
||||
|
||||
const graphqlFilter = {
|
||||
...computeContextStoreFilters(
|
||||
contextStoreTargetedRecordsRule,
|
||||
contextStoreFilters,
|
||||
objectMetadataItem,
|
||||
filterValueDependencies,
|
||||
),
|
||||
...deletedAtFilter,
|
||||
};
|
||||
|
||||
const { fetchAllRecords: fetchAllRecordIds } = useLazyFetchAllRecords({
|
||||
objectNameSingular: objectMetadataItem.nameSingular,
|
||||
filter: graphqlFilter,
|
||||
limit: DEFAULT_QUERY_PAGE_SIZE,
|
||||
recordGqlFields: { id: true },
|
||||
});
|
||||
|
||||
const handleRestoreClick = useCallback(async () => {
|
||||
const recordsToRestore = await fetchAllRecordIds();
|
||||
const recordIdsToRestore = recordsToRestore.map((record) => record.id);
|
||||
|
||||
resetTableRowSelection();
|
||||
|
||||
await restoreManyRecords({
|
||||
idsToRestore: recordIdsToRestore,
|
||||
});
|
||||
}, [restoreManyRecords, fetchAllRecordIds, resetTableRowSelection]);
|
||||
|
||||
const onClick = () => {
|
||||
setIsRestoreRecordsModalOpen(true);
|
||||
};
|
||||
|
||||
const confirmationModal = (
|
||||
<ConfirmationModal
|
||||
isOpen={isRestoreRecordsModalOpen}
|
||||
setIsOpen={setIsRestoreRecordsModalOpen}
|
||||
title={'Restore Records'}
|
||||
subtitle={`Are you sure you want to restore these records?`}
|
||||
onConfirmClick={handleRestoreClick}
|
||||
confirmButtonText={'Restore Records'}
|
||||
/>
|
||||
);
|
||||
|
||||
return {
|
||||
onClick,
|
||||
ConfirmationModal: confirmationModal,
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,15 @@
|
||||
import { Action } from '@/action-menu/actions/components/Action';
|
||||
import { useContextStoreObjectMetadataItemOrThrow } from '@/context-store/hooks/useContextStoreObjectMetadataItemOrThrow';
|
||||
import { useCreateNewIndexRecord } from '@/object-record/record-table/hooks/useCreateNewIndexRecord';
|
||||
|
||||
export const CreateNewTableRecordNoSelectionRecordAction = () => {
|
||||
const { objectMetadataItem } = useContextStoreObjectMetadataItemOrThrow();
|
||||
|
||||
const { createNewIndexRecord } = useCreateNewIndexRecord({
|
||||
objectMetadataItem,
|
||||
});
|
||||
|
||||
return (
|
||||
<Action onClick={() => createNewIndexRecord()} preventCommandMenuClosing />
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,54 @@
|
||||
import { Action } from '@/action-menu/actions/components/Action';
|
||||
import { useContextStoreObjectMetadataItemOrThrow } from '@/context-store/hooks/useContextStoreObjectMetadataItemOrThrow';
|
||||
import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/contextStoreCurrentViewIdComponentState';
|
||||
import { useCheckIsSoftDeleteFilter } from '@/object-record/record-filter/hooks/useCheckIsSoftDeleteFilter';
|
||||
import { useRemoveRecordFilter } from '@/object-record/record-filter/hooks/useRemoveRecordFilter';
|
||||
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
|
||||
import { useHandleToggleTrashColumnFilter } from '@/object-record/record-index/hooks/useHandleToggleTrashColumnFilter';
|
||||
import { getRecordIndexIdFromObjectNamePluralAndViewId } from '@/object-record/utils/getRecordIndexIdFromObjectNamePluralAndViewId';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const HideDeletedRecordsNoSelectionRecordAction = () => {
|
||||
const { objectMetadataItem } = useContextStoreObjectMetadataItemOrThrow();
|
||||
|
||||
const currentViewId = useRecoilComponentValueV2(
|
||||
contextStoreCurrentViewIdComponentState,
|
||||
);
|
||||
|
||||
if (!currentViewId) {
|
||||
throw new Error('Current view ID is not defined');
|
||||
}
|
||||
|
||||
const recordIndexId = getRecordIndexIdFromObjectNamePluralAndViewId(
|
||||
objectMetadataItem.namePlural,
|
||||
currentViewId,
|
||||
);
|
||||
|
||||
const { toggleSoftDeleteFilterState } = useHandleToggleTrashColumnFilter({
|
||||
objectNameSingular: objectMetadataItem.nameSingular,
|
||||
viewBarId: recordIndexId,
|
||||
});
|
||||
|
||||
const { checkIsSoftDeleteFilter } = useCheckIsSoftDeleteFilter();
|
||||
|
||||
const currentRecordFilters = useRecoilComponentValueV2(
|
||||
currentRecordFiltersComponentState,
|
||||
recordIndexId,
|
||||
);
|
||||
|
||||
const deletedFilter = currentRecordFilters.find(checkIsSoftDeleteFilter);
|
||||
|
||||
const { removeRecordFilter } = useRemoveRecordFilter();
|
||||
|
||||
const handleClick = () => {
|
||||
if (!isDefined(deletedFilter)) {
|
||||
return;
|
||||
}
|
||||
|
||||
removeRecordFilter({ recordFilterId: deletedFilter.id });
|
||||
toggleSoftDeleteFilterState(false);
|
||||
};
|
||||
|
||||
return <Action onClick={handleClick} />;
|
||||
};
|
||||
@ -0,0 +1,14 @@
|
||||
import { Action } from '@/action-menu/actions/components/Action';
|
||||
import { useContextStoreObjectMetadataItemOrThrow } from '@/context-store/hooks/useContextStoreObjectMetadataItemOrThrow';
|
||||
import { useOpenObjectRecordsSpreadsheetImportDialog } from '@/object-record/spreadsheet-import/hooks/useOpenObjectRecordsSpreadsheetImportDialog';
|
||||
|
||||
export const ImportRecordsNoSelectionRecordAction = () => {
|
||||
const { objectMetadataItem } = useContextStoreObjectMetadataItemOrThrow();
|
||||
|
||||
const { openObjectRecordsSpreadsheetImportDialog } =
|
||||
useOpenObjectRecordsSpreadsheetImportDialog(
|
||||
objectMetadataItem.nameSingular,
|
||||
);
|
||||
|
||||
return <Action onClick={openObjectRecordsSpreadsheetImportDialog} />;
|
||||
};
|
||||
@ -0,0 +1,38 @@
|
||||
import { Action } from '@/action-menu/actions/components/Action';
|
||||
import { useContextStoreObjectMetadataItemOrThrow } from '@/context-store/hooks/useContextStoreObjectMetadataItemOrThrow';
|
||||
import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/contextStoreCurrentViewIdComponentState';
|
||||
import { useHandleToggleTrashColumnFilter } from '@/object-record/record-index/hooks/useHandleToggleTrashColumnFilter';
|
||||
import { getRecordIndexIdFromObjectNamePluralAndViewId } from '@/object-record/utils/getRecordIndexIdFromObjectNamePluralAndViewId';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
|
||||
export const SeeDeletedRecordsNoSelectionRecordAction = () => {
|
||||
const { objectMetadataItem } = useContextStoreObjectMetadataItemOrThrow();
|
||||
|
||||
const currentViewId = useRecoilComponentValueV2(
|
||||
contextStoreCurrentViewIdComponentState,
|
||||
);
|
||||
|
||||
if (!currentViewId) {
|
||||
throw new Error('Current view ID is not defined');
|
||||
}
|
||||
|
||||
const recordIndexId = getRecordIndexIdFromObjectNamePluralAndViewId(
|
||||
objectMetadataItem.namePlural,
|
||||
currentViewId,
|
||||
);
|
||||
|
||||
const { handleToggleTrashColumnFilter, toggleSoftDeleteFilterState } =
|
||||
useHandleToggleTrashColumnFilter({
|
||||
objectNameSingular: objectMetadataItem.nameSingular,
|
||||
viewBarId: recordIndexId,
|
||||
});
|
||||
|
||||
return (
|
||||
<Action
|
||||
onClick={() => {
|
||||
handleToggleTrashColumnFilter();
|
||||
toggleSoftDeleteFilterState(true);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -1,13 +0,0 @@
|
||||
import { ActionHookWithObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
|
||||
import { useCreateNewIndexRecord } from '@/object-record/record-table/hooks/useCreateNewIndexRecord';
|
||||
|
||||
export const useCreateNewTableRecordNoSelectionRecordAction: ActionHookWithObjectMetadataItem =
|
||||
({ objectMetadataItem }) => {
|
||||
const { createNewIndexRecord } = useCreateNewIndexRecord({
|
||||
objectMetadataItem,
|
||||
});
|
||||
|
||||
return {
|
||||
onClick: createNewIndexRecord,
|
||||
};
|
||||
};
|
||||
@ -1,19 +0,0 @@
|
||||
import { ActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
|
||||
import { CoreObjectNamePlural } from '@/object-metadata/types/CoreObjectNamePlural';
|
||||
import { AppPath } from '@/types/AppPath';
|
||||
import { useNavigateApp } from '~/hooks/useNavigateApp';
|
||||
|
||||
export const useGoToCompaniesNoSelectionRecordAction: ActionHookWithoutObjectMetadataItem =
|
||||
() => {
|
||||
const navigateApp = useNavigateApp();
|
||||
|
||||
const onClick = () => {
|
||||
navigateApp(AppPath.RecordIndexPage, {
|
||||
objectNamePlural: CoreObjectNamePlural.Company,
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
onClick,
|
||||
};
|
||||
};
|
||||
@ -1,19 +0,0 @@
|
||||
import { ActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
|
||||
import { CoreObjectNamePlural } from '@/object-metadata/types/CoreObjectNamePlural';
|
||||
import { AppPath } from '@/types/AppPath';
|
||||
import { useNavigateApp } from '~/hooks/useNavigateApp';
|
||||
|
||||
export const useGoToOpportunitiesNoSelectionRecordAction: ActionHookWithoutObjectMetadataItem =
|
||||
() => {
|
||||
const navigateApp = useNavigateApp();
|
||||
|
||||
const onClick = () => {
|
||||
navigateApp(AppPath.RecordIndexPage, {
|
||||
objectNamePlural: CoreObjectNamePlural.Opportunity,
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
onClick,
|
||||
};
|
||||
};
|
||||
@ -1,19 +0,0 @@
|
||||
import { ActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
|
||||
import { CoreObjectNamePlural } from '@/object-metadata/types/CoreObjectNamePlural';
|
||||
import { AppPath } from '@/types/AppPath';
|
||||
import { useNavigateApp } from '~/hooks/useNavigateApp';
|
||||
|
||||
export const useGoToPeopleNoSelectionRecordAction: ActionHookWithoutObjectMetadataItem =
|
||||
() => {
|
||||
const navigateApp = useNavigateApp();
|
||||
|
||||
const onClick = () => {
|
||||
navigateApp(AppPath.RecordIndexPage, {
|
||||
objectNamePlural: CoreObjectNamePlural.Person,
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
onClick,
|
||||
};
|
||||
};
|
||||
@ -1,16 +0,0 @@
|
||||
import { ActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
|
||||
import { SettingsPath } from '@/types/SettingsPath';
|
||||
import { useNavigateSettings } from '~/hooks/useNavigateSettings';
|
||||
|
||||
export const useGoToSettingsNoSelectionRecordAction: ActionHookWithoutObjectMetadataItem =
|
||||
() => {
|
||||
const navigateSettings = useNavigateSettings();
|
||||
|
||||
const onClick = () => {
|
||||
navigateSettings(SettingsPath.ProfilePage);
|
||||
};
|
||||
|
||||
return {
|
||||
onClick,
|
||||
};
|
||||
};
|
||||
@ -1,19 +0,0 @@
|
||||
import { ActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
|
||||
import { CoreObjectNamePlural } from '@/object-metadata/types/CoreObjectNamePlural';
|
||||
import { AppPath } from '@/types/AppPath';
|
||||
import { useNavigateApp } from '~/hooks/useNavigateApp';
|
||||
|
||||
export const useGoToTasksNoSelectionRecordAction: ActionHookWithoutObjectMetadataItem =
|
||||
() => {
|
||||
const navigateApp = useNavigateApp();
|
||||
|
||||
const onClick = () => {
|
||||
navigateApp(AppPath.RecordIndexPage, {
|
||||
objectNamePlural: CoreObjectNamePlural.Task,
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
onClick,
|
||||
};
|
||||
};
|
||||
@ -1,57 +0,0 @@
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { ActionHookWithObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
|
||||
import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/contextStoreCurrentViewIdComponentState';
|
||||
import { useCheckIsSoftDeleteFilter } from '@/object-record/record-filter/hooks/useCheckIsSoftDeleteFilter';
|
||||
import { useRemoveRecordFilter } from '@/object-record/record-filter/hooks/useRemoveRecordFilter';
|
||||
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
|
||||
import { useHandleToggleTrashColumnFilter } from '@/object-record/record-index/hooks/useHandleToggleTrashColumnFilter';
|
||||
import { getRecordIndexIdFromObjectNamePluralAndViewId } from '@/object-record/utils/getRecordIndexIdFromObjectNamePluralAndViewId';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const useHideDeletedRecordsNoSelectionRecordAction: ActionHookWithObjectMetadataItem =
|
||||
({ objectMetadataItem }) => {
|
||||
const currentViewId = useRecoilComponentValueV2(
|
||||
contextStoreCurrentViewIdComponentState,
|
||||
);
|
||||
|
||||
if (!currentViewId) {
|
||||
throw new Error('Current view ID is not defined');
|
||||
}
|
||||
|
||||
const recordIndexId = getRecordIndexIdFromObjectNamePluralAndViewId(
|
||||
objectMetadataItem.namePlural,
|
||||
currentViewId,
|
||||
);
|
||||
|
||||
const { toggleSoftDeleteFilterState } = useHandleToggleTrashColumnFilter({
|
||||
objectNameSingular: objectMetadataItem.nameSingular,
|
||||
viewBarId: recordIndexId,
|
||||
});
|
||||
|
||||
const { checkIsSoftDeleteFilter } = useCheckIsSoftDeleteFilter();
|
||||
|
||||
const currentRecordFilters = useRecoilComponentValueV2(
|
||||
currentRecordFiltersComponentState,
|
||||
recordIndexId,
|
||||
);
|
||||
|
||||
const deletedFilter = currentRecordFilters.find(checkIsSoftDeleteFilter);
|
||||
|
||||
const { removeRecordFilter } = useRemoveRecordFilter();
|
||||
|
||||
const onClick = useCallback(() => {
|
||||
if (!isDefined(deletedFilter)) {
|
||||
return;
|
||||
}
|
||||
|
||||
removeRecordFilter({ recordFilterId: deletedFilter.id });
|
||||
|
||||
toggleSoftDeleteFilterState(false);
|
||||
}, [deletedFilter, removeRecordFilter, toggleSoftDeleteFilterState]);
|
||||
|
||||
return {
|
||||
onClick,
|
||||
};
|
||||
};
|
||||
@ -1,14 +0,0 @@
|
||||
import { ActionHookWithObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
|
||||
import { useOpenObjectRecordsSpreadsheetImportDialog } from '@/object-record/spreadsheet-import/hooks/useOpenObjectRecordsSpreadsheetImportDialog';
|
||||
|
||||
export const useImportRecordsNoSelectionRecordAction: ActionHookWithObjectMetadataItem =
|
||||
({ objectMetadataItem }) => {
|
||||
const { openObjectRecordsSpreadsheetImportDialog } =
|
||||
useOpenObjectRecordsSpreadsheetImportDialog(
|
||||
objectMetadataItem.nameSingular,
|
||||
);
|
||||
|
||||
return {
|
||||
onClick: openObjectRecordsSpreadsheetImportDialog,
|
||||
};
|
||||
};
|
||||
@ -1,38 +0,0 @@
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { ActionHookWithObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
|
||||
import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/contextStoreCurrentViewIdComponentState';
|
||||
import { useHandleToggleTrashColumnFilter } from '@/object-record/record-index/hooks/useHandleToggleTrashColumnFilter';
|
||||
import { getRecordIndexIdFromObjectNamePluralAndViewId } from '@/object-record/utils/getRecordIndexIdFromObjectNamePluralAndViewId';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
|
||||
export const useSeeDeletedRecordsNoSelectionRecordAction: ActionHookWithObjectMetadataItem =
|
||||
({ objectMetadataItem }) => {
|
||||
const currentViewId = useRecoilComponentValueV2(
|
||||
contextStoreCurrentViewIdComponentState,
|
||||
);
|
||||
|
||||
if (!currentViewId) {
|
||||
throw new Error('Current view ID is not defined');
|
||||
}
|
||||
|
||||
const recordIndexId = getRecordIndexIdFromObjectNamePluralAndViewId(
|
||||
objectMetadataItem.namePlural,
|
||||
currentViewId,
|
||||
);
|
||||
|
||||
const { handleToggleTrashColumnFilter, toggleSoftDeleteFilterState } =
|
||||
useHandleToggleTrashColumnFilter({
|
||||
objectNameSingular: objectMetadataItem.nameSingular,
|
||||
viewBarId: recordIndexId,
|
||||
});
|
||||
|
||||
const onClick = useCallback(() => {
|
||||
handleToggleTrashColumnFilter();
|
||||
toggleSoftDeleteFilterState(true);
|
||||
}, [handleToggleTrashColumnFilter, toggleSoftDeleteFilterState]);
|
||||
|
||||
return {
|
||||
onClick,
|
||||
};
|
||||
};
|
||||
@ -1,19 +0,0 @@
|
||||
import { ActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
|
||||
import { CoreObjectNamePlural } from '@/object-metadata/types/CoreObjectNamePlural';
|
||||
import { AppPath } from '@/types/AppPath';
|
||||
import { useNavigateApp } from '~/hooks/useNavigateApp';
|
||||
|
||||
export const useSeeRunsNoSelectionRecordAction: ActionHookWithoutObjectMetadataItem =
|
||||
() => {
|
||||
const navigateApp = useNavigateApp();
|
||||
|
||||
const onClick = () => {
|
||||
navigateApp(AppPath.RecordIndexPage, {
|
||||
objectNamePlural: CoreObjectNamePlural.WorkflowRun,
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
onClick,
|
||||
};
|
||||
};
|
||||
@ -1,19 +0,0 @@
|
||||
import { ActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
|
||||
import { CoreObjectNamePlural } from '@/object-metadata/types/CoreObjectNamePlural';
|
||||
import { AppPath } from '@/types/AppPath';
|
||||
import { useNavigateApp } from '~/hooks/useNavigateApp';
|
||||
|
||||
export const useSeeWorkflowsNoSelectionRecordAction: ActionHookWithoutObjectMetadataItem =
|
||||
() => {
|
||||
const navigateApp = useNavigateApp();
|
||||
|
||||
const onClick = () => {
|
||||
navigateApp(AppPath.RecordIndexPage, {
|
||||
objectNamePlural: CoreObjectNamePlural.Workflow,
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
onClick,
|
||||
};
|
||||
};
|
||||
@ -10,4 +10,5 @@ export enum NoSelectionRecordActionKeys {
|
||||
GO_TO_OPPORTUNITIES = 'go-to-opportunities',
|
||||
GO_TO_SETTINGS = 'go-to-settings',
|
||||
GO_TO_TASKS = 'go-to-tasks',
|
||||
GO_TO_NOTES = 'go-to-notes',
|
||||
}
|
||||
|
||||
@ -0,0 +1,27 @@
|
||||
import { Action } from '@/action-menu/actions/components/Action';
|
||||
import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
|
||||
import { useContextStoreObjectMetadataItemOrThrow } from '@/context-store/hooks/useContextStoreObjectMetadataItemOrThrow';
|
||||
import { useCreateFavorite } from '@/favorites/hooks/useCreateFavorite';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const AddToFavoritesSingleRecordAction = () => {
|
||||
const { objectMetadataItem } = useContextStoreObjectMetadataItemOrThrow();
|
||||
|
||||
const recordId = useSelectedRecordIdOrThrow();
|
||||
|
||||
const { createFavorite } = useCreateFavorite();
|
||||
|
||||
const selectedRecord = useRecoilValue(recordStoreFamilyState(recordId));
|
||||
|
||||
const handleClick = () => {
|
||||
if (!isDefined(selectedRecord)) {
|
||||
return;
|
||||
}
|
||||
|
||||
createFavorite(selectedRecord, objectMetadataItem.nameSingular);
|
||||
};
|
||||
|
||||
return <Action onClick={handleClick} />;
|
||||
};
|
||||
@ -1,21 +1,17 @@
|
||||
import { ActionModal } from '@/action-menu/actions/components/ActionModal';
|
||||
import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
|
||||
import { ActionHookWithObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
|
||||
import { useContextStoreObjectMetadataItemOrThrow } from '@/context-store/hooks/useContextStoreObjectMetadataItemOrThrow';
|
||||
import { useDeleteFavorite } from '@/favorites/hooks/useDeleteFavorite';
|
||||
import { useFavorites } from '@/favorites/hooks/useFavorites';
|
||||
import { useDeleteOneRecord } from '@/object-record/hooks/useDeleteOneRecord';
|
||||
import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable';
|
||||
import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { useCallback, useState } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const useDeleteSingleRecordAction: ActionHookWithObjectMetadataItem = ({
|
||||
objectMetadataItem,
|
||||
}) => {
|
||||
const recordId = useSelectedRecordIdOrThrow();
|
||||
export const DeleteSingleRecordAction = () => {
|
||||
const { objectMetadataItem } = useContextStoreObjectMetadataItemOrThrow();
|
||||
|
||||
const [isDeleteRecordsModalOpen, setIsDeleteRecordsModalOpen] =
|
||||
useState(false);
|
||||
const recordId = useSelectedRecordIdOrThrow();
|
||||
|
||||
const { resetTableRowSelection } = useRecordTable({
|
||||
recordTableId: objectMetadataItem.namePlural,
|
||||
@ -28,7 +24,7 @@ export const useDeleteSingleRecordAction: ActionHookWithObjectMetadataItem = ({
|
||||
const { sortedFavorites: favorites } = useFavorites();
|
||||
const { deleteFavorite } = useDeleteFavorite();
|
||||
|
||||
const handleDeleteClick = useCallback(async () => {
|
||||
const handleDeleteClick = async () => {
|
||||
resetTableRowSelection();
|
||||
|
||||
const foundFavorite = favorites?.find(
|
||||
@ -40,31 +36,14 @@ export const useDeleteSingleRecordAction: ActionHookWithObjectMetadataItem = ({
|
||||
}
|
||||
|
||||
await deleteOneRecord(recordId);
|
||||
}, [
|
||||
deleteFavorite,
|
||||
deleteOneRecord,
|
||||
favorites,
|
||||
resetTableRowSelection,
|
||||
recordId,
|
||||
]);
|
||||
|
||||
const onClick = () => {
|
||||
setIsDeleteRecordsModalOpen(true);
|
||||
};
|
||||
|
||||
return {
|
||||
onClick,
|
||||
ConfirmationModal: (
|
||||
<ConfirmationModal
|
||||
isOpen={isDeleteRecordsModalOpen}
|
||||
setIsOpen={setIsDeleteRecordsModalOpen}
|
||||
title={'Delete Record'}
|
||||
subtitle={t`Are you sure you want to delete this record? It can be recovered from the Command menu.`}
|
||||
onConfirmClick={() => {
|
||||
handleDeleteClick();
|
||||
}}
|
||||
confirmButtonText={'Delete Record'}
|
||||
/>
|
||||
),
|
||||
};
|
||||
return (
|
||||
<ActionModal
|
||||
title="Delete Record"
|
||||
subtitle={t`Are you sure you want to delete this record? It can be recovered from the Command menu.`}
|
||||
onConfirmClick={handleDeleteClick}
|
||||
confirmButtonText="Delete Record"
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,41 @@
|
||||
import { ActionModal } from '@/action-menu/actions/components/ActionModal';
|
||||
import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
|
||||
import { useContextStoreObjectMetadataItemOrThrow } from '@/context-store/hooks/useContextStoreObjectMetadataItemOrThrow';
|
||||
import { useDestroyOneRecord } from '@/object-record/hooks/useDestroyOneRecord';
|
||||
import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable';
|
||||
import { AppPath } from '@/types/AppPath';
|
||||
import { useNavigateApp } from '~/hooks/useNavigateApp';
|
||||
|
||||
export const DestroySingleRecordAction = () => {
|
||||
const { objectMetadataItem } = useContextStoreObjectMetadataItemOrThrow();
|
||||
|
||||
const recordId = useSelectedRecordIdOrThrow();
|
||||
|
||||
const navigateApp = useNavigateApp();
|
||||
|
||||
const { resetTableRowSelection } = useRecordTable({
|
||||
recordTableId: objectMetadataItem.namePlural,
|
||||
});
|
||||
|
||||
const { destroyOneRecord } = useDestroyOneRecord({
|
||||
objectNameSingular: objectMetadataItem.nameSingular,
|
||||
});
|
||||
|
||||
const handleDeleteClick = async () => {
|
||||
resetTableRowSelection();
|
||||
|
||||
await destroyOneRecord(recordId);
|
||||
navigateApp(AppPath.RecordIndexPage, {
|
||||
objectNamePlural: objectMetadataItem.namePlural,
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<ActionModal
|
||||
title="Permanently Destroy Record"
|
||||
subtitle="Are you sure you want to destroy this record? It cannot be recovered anymore."
|
||||
onConfirmClick={handleDeleteClick}
|
||||
confirmButtonText="Permanently Destroy Record"
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -1,18 +1,18 @@
|
||||
import { Action } from '@/action-menu/actions/components/Action';
|
||||
import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
|
||||
import { ActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { BlockNoteEditor } from '@blocknote/core';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const useExportNoteAction: ActionHookWithoutObjectMetadataItem = () => {
|
||||
export const ExportNoteActionSingleRecordAction = () => {
|
||||
const recordId = useSelectedRecordIdOrThrow();
|
||||
|
||||
const selectedRecord = useRecoilValue(recordStoreFamilyState(recordId));
|
||||
|
||||
const filename = `${(selectedRecord?.title || 'Untitled Note').replace(/[<>:"/\\|?*]/g, '-')}`;
|
||||
|
||||
const onClick = async () => {
|
||||
const handleClick = async () => {
|
||||
if (!isDefined(selectedRecord)) {
|
||||
return;
|
||||
}
|
||||
@ -50,7 +50,5 @@ export const useExportNoteAction: ActionHookWithoutObjectMetadataItem = () => {
|
||||
// await exportBlockNoteEditorToDocx(editor, filename);
|
||||
};
|
||||
|
||||
return {
|
||||
onClick,
|
||||
};
|
||||
return <Action onClick={handleClick} />;
|
||||
};
|
||||
@ -0,0 +1,17 @@
|
||||
import { Action } from '@/action-menu/actions/components/Action';
|
||||
import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
|
||||
import { useContextStoreObjectMetadataItemOrThrow } from '@/context-store/hooks/useContextStoreObjectMetadataItemOrThrow';
|
||||
import { useRecordShowPagePagination } from '@/object-record/record-show/hooks/useRecordShowPagePagination';
|
||||
|
||||
export const NavigateToNextRecordSingleRecordAction = () => {
|
||||
const { objectMetadataItem } = useContextStoreObjectMetadataItemOrThrow();
|
||||
|
||||
const recordId = useSelectedRecordIdOrThrow();
|
||||
|
||||
const { navigateToNextRecord } = useRecordShowPagePagination(
|
||||
objectMetadataItem.nameSingular,
|
||||
recordId,
|
||||
);
|
||||
|
||||
return <Action onClick={navigateToNextRecord} />;
|
||||
};
|
||||
@ -0,0 +1,17 @@
|
||||
import { Action } from '@/action-menu/actions/components/Action';
|
||||
import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
|
||||
import { useContextStoreObjectMetadataItemOrThrow } from '@/context-store/hooks/useContextStoreObjectMetadataItemOrThrow';
|
||||
import { useRecordShowPagePagination } from '@/object-record/record-show/hooks/useRecordShowPagePagination';
|
||||
|
||||
export const NavigateToPreviousRecordSingleRecordAction = () => {
|
||||
const { objectMetadataItem } = useContextStoreObjectMetadataItemOrThrow();
|
||||
|
||||
const recordId = useSelectedRecordIdOrThrow();
|
||||
|
||||
const { navigateToPreviousRecord } = useRecordShowPagePagination(
|
||||
objectMetadataItem.nameSingular,
|
||||
recordId,
|
||||
);
|
||||
|
||||
return <Action onClick={navigateToPreviousRecord} />;
|
||||
};
|
||||
@ -0,0 +1,27 @@
|
||||
import { Action } from '@/action-menu/actions/components/Action';
|
||||
import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
|
||||
import { useDeleteFavorite } from '@/favorites/hooks/useDeleteFavorite';
|
||||
import { useFavorites } from '@/favorites/hooks/useFavorites';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const RemoveFromFavoritesSingleRecordAction = () => {
|
||||
const recordId = useSelectedRecordIdOrThrow();
|
||||
|
||||
const { sortedFavorites: favorites } = useFavorites();
|
||||
|
||||
const { deleteFavorite } = useDeleteFavorite();
|
||||
|
||||
const foundFavorite = favorites?.find(
|
||||
(favorite) => favorite.recordId === recordId,
|
||||
);
|
||||
|
||||
const handleClick = () => {
|
||||
if (!isDefined(foundFavorite)) {
|
||||
return;
|
||||
}
|
||||
|
||||
deleteFavorite(foundFavorite.id);
|
||||
};
|
||||
|
||||
return <Action onClick={handleClick} />;
|
||||
};
|
||||
@ -0,0 +1,37 @@
|
||||
import { ActionModal } from '@/action-menu/actions/components/ActionModal';
|
||||
import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
|
||||
import { useContextStoreObjectMetadataItemOrThrow } from '@/context-store/hooks/useContextStoreObjectMetadataItemOrThrow';
|
||||
import { useRestoreManyRecords } from '@/object-record/hooks/useRestoreManyRecords';
|
||||
import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable';
|
||||
|
||||
export const RestoreSingleRecordAction = () => {
|
||||
const { objectMetadataItem } = useContextStoreObjectMetadataItemOrThrow();
|
||||
|
||||
const recordId = useSelectedRecordIdOrThrow();
|
||||
|
||||
const { resetTableRowSelection } = useRecordTable({
|
||||
recordTableId: objectMetadataItem.namePlural,
|
||||
});
|
||||
|
||||
const { restoreManyRecords } = useRestoreManyRecords({
|
||||
objectNameSingular: objectMetadataItem.nameSingular,
|
||||
});
|
||||
|
||||
const handleRestoreClick = async () => {
|
||||
resetTableRowSelection();
|
||||
|
||||
await restoreManyRecords({
|
||||
idsToRestore: [recordId],
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<ActionModal
|
||||
title="Restore Record"
|
||||
subtitle="Are you sure you want to restore this record?"
|
||||
onConfirmClick={handleRestoreClick}
|
||||
confirmButtonText="Restore Record"
|
||||
confirmButtonAccent="default"
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -1,99 +0,0 @@
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { act } from 'react';
|
||||
import {
|
||||
GetJestMetadataAndApolloMocksAndActionMenuWrapperProps,
|
||||
getJestMetadataAndApolloMocksAndActionMenuWrapper,
|
||||
} from '~/testing/jest/getJestMetadataAndApolloMocksAndActionMenuWrapper';
|
||||
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
|
||||
import { getPeopleRecordConnectionMock } from '~/testing/mock-data/people';
|
||||
import { useAddToFavoritesSingleRecordAction } from '../useAddToFavoritesSingleRecordAction';
|
||||
|
||||
const personMockObjectMetadataItem = generatedMockObjectMetadataItems.find(
|
||||
(item) => item.nameSingular === 'person',
|
||||
)!;
|
||||
|
||||
const peopleMock = getPeopleRecordConnectionMock();
|
||||
|
||||
const favoritesMock = [
|
||||
{
|
||||
id: '1',
|
||||
recordId: peopleMock[0].id,
|
||||
position: 0,
|
||||
avatarType: 'rounded',
|
||||
avatarUrl: '',
|
||||
labelIdentifier: ' ',
|
||||
link: `/object/${personMockObjectMetadataItem.nameSingular}/${peopleMock[0].id}`,
|
||||
objectNameSingular: personMockObjectMetadataItem.nameSingular,
|
||||
workspaceMemberId: '1',
|
||||
favoriteFolderId: undefined,
|
||||
},
|
||||
];
|
||||
|
||||
jest.mock('@/favorites/hooks/useFavorites', () => ({
|
||||
useFavorites: () => ({
|
||||
favorites: favoritesMock,
|
||||
sortedFavorites: favoritesMock,
|
||||
}),
|
||||
}));
|
||||
|
||||
const createFavoriteMock = jest.fn();
|
||||
|
||||
jest.mock('@/favorites/hooks/useCreateFavorite', () => ({
|
||||
useCreateFavorite: () => ({
|
||||
createFavorite: createFavoriteMock,
|
||||
}),
|
||||
}));
|
||||
|
||||
const wrapperConfigWithSelectedRecordAsFavorite: GetJestMetadataAndApolloMocksAndActionMenuWrapperProps =
|
||||
{
|
||||
apolloMocks: [],
|
||||
componentInstanceId: '1',
|
||||
contextStoreCurrentObjectMetadataNameSingular:
|
||||
personMockObjectMetadataItem.nameSingular,
|
||||
contextStoreTargetedRecordsRule: {
|
||||
mode: 'selection',
|
||||
selectedRecordIds: [peopleMock[0].id],
|
||||
},
|
||||
onInitializeRecoilSnapshot: (snapshot) => {
|
||||
snapshot.set(recordStoreFamilyState(peopleMock[0].id), peopleMock[0]);
|
||||
snapshot.set(recordStoreFamilyState(peopleMock[1].id), peopleMock[1]);
|
||||
},
|
||||
};
|
||||
|
||||
const wrapperConfigWithSelectedRecordNotAsFavorite: GetJestMetadataAndApolloMocksAndActionMenuWrapperProps =
|
||||
{
|
||||
...wrapperConfigWithSelectedRecordAsFavorite,
|
||||
contextStoreTargetedRecordsRule: {
|
||||
mode: 'selection',
|
||||
selectedRecordIds: [peopleMock[1].id],
|
||||
},
|
||||
};
|
||||
|
||||
const wrapperWithSelectedRecordNotAsFavorite =
|
||||
getJestMetadataAndApolloMocksAndActionMenuWrapper(
|
||||
wrapperConfigWithSelectedRecordNotAsFavorite,
|
||||
);
|
||||
|
||||
describe('useAddToFavoritesSingleRecordAction', () => {
|
||||
it('should call createFavorite on click', () => {
|
||||
const { result } = renderHook(
|
||||
() =>
|
||||
useAddToFavoritesSingleRecordAction({
|
||||
objectMetadataItem: personMockObjectMetadataItem,
|
||||
}),
|
||||
{
|
||||
wrapper: wrapperWithSelectedRecordNotAsFavorite,
|
||||
},
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.onClick();
|
||||
});
|
||||
|
||||
expect(createFavoriteMock).toHaveBeenCalledWith(
|
||||
peopleMock[1],
|
||||
personMockObjectMetadataItem.nameSingular,
|
||||
);
|
||||
});
|
||||
});
|
||||
@ -1,63 +0,0 @@
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { act } from 'react';
|
||||
import { getJestMetadataAndApolloMocksAndActionMenuWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksAndActionMenuWrapper';
|
||||
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
|
||||
import { getPeopleRecordConnectionMock } from '~/testing/mock-data/people';
|
||||
import { useDeleteSingleRecordAction } from '../useDeleteSingleRecordAction';
|
||||
|
||||
const personMockObjectMetadataItem = generatedMockObjectMetadataItems.find(
|
||||
(item) => item.nameSingular === 'person',
|
||||
)!;
|
||||
|
||||
const peopleMock = getPeopleRecordConnectionMock();
|
||||
|
||||
const deleteOneRecordMock = jest.fn();
|
||||
|
||||
jest.mock('@/object-record/hooks/useDeleteOneRecord', () => ({
|
||||
useDeleteOneRecord: () => ({
|
||||
deleteOneRecord: deleteOneRecordMock,
|
||||
}),
|
||||
}));
|
||||
|
||||
const wrapper = getJestMetadataAndApolloMocksAndActionMenuWrapper({
|
||||
apolloMocks: [],
|
||||
componentInstanceId: '1',
|
||||
contextStoreCurrentObjectMetadataNameSingular:
|
||||
personMockObjectMetadataItem.nameSingular,
|
||||
contextStoreTargetedRecordsRule: {
|
||||
mode: 'selection',
|
||||
selectedRecordIds: [peopleMock[0].id],
|
||||
},
|
||||
onInitializeRecoilSnapshot: (snapshot) => {
|
||||
snapshot.set(recordStoreFamilyState(peopleMock[0].id), peopleMock[0]);
|
||||
},
|
||||
});
|
||||
|
||||
describe('useDeleteSingleRecordAction', () => {
|
||||
it('should call deleteOneRecord on click', () => {
|
||||
const { result } = renderHook(
|
||||
() =>
|
||||
useDeleteSingleRecordAction({
|
||||
objectMetadataItem: personMockObjectMetadataItem,
|
||||
}),
|
||||
{
|
||||
wrapper,
|
||||
},
|
||||
);
|
||||
|
||||
expect(result.current.ConfirmationModal?.props?.isOpen).toBe(false);
|
||||
|
||||
act(() => {
|
||||
result.current.onClick();
|
||||
});
|
||||
|
||||
expect(result.current.ConfirmationModal?.props?.isOpen).toBe(true);
|
||||
|
||||
act(() => {
|
||||
result.current.ConfirmationModal?.props?.onConfirmClick();
|
||||
});
|
||||
|
||||
expect(deleteOneRecordMock).toHaveBeenCalledWith(peopleMock[0].id);
|
||||
});
|
||||
});
|
||||
@ -1,84 +0,0 @@
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { act } from 'react';
|
||||
import {
|
||||
GetJestMetadataAndApolloMocksAndActionMenuWrapperProps,
|
||||
getJestMetadataAndApolloMocksAndActionMenuWrapper,
|
||||
} from '~/testing/jest/getJestMetadataAndApolloMocksAndActionMenuWrapper';
|
||||
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
|
||||
import { getPeopleRecordConnectionMock } from '~/testing/mock-data/people';
|
||||
import { useRemoveFromFavoritesSingleRecordAction } from '../useRemoveFromFavoritesSingleRecordAction';
|
||||
|
||||
const personMockObjectMetadataItem = generatedMockObjectMetadataItems.find(
|
||||
(item) => item.nameSingular === 'person',
|
||||
)!;
|
||||
|
||||
const peopleMock = getPeopleRecordConnectionMock();
|
||||
|
||||
const favoritesMock = [
|
||||
{
|
||||
id: '1',
|
||||
recordId: peopleMock[0].id,
|
||||
position: 0,
|
||||
avatarType: 'rounded',
|
||||
avatarUrl: '',
|
||||
labelIdentifier: ' ',
|
||||
link: `/object/${personMockObjectMetadataItem.nameSingular}/${peopleMock[0].id}`,
|
||||
objectNameSingular: personMockObjectMetadataItem.nameSingular,
|
||||
workspaceMemberId: '1',
|
||||
favoriteFolderId: undefined,
|
||||
},
|
||||
];
|
||||
|
||||
jest.mock('@/favorites/hooks/useFavorites', () => ({
|
||||
useFavorites: () => ({
|
||||
favorites: favoritesMock,
|
||||
sortedFavorites: favoritesMock,
|
||||
}),
|
||||
}));
|
||||
|
||||
const deleteFavoriteMock = jest.fn();
|
||||
|
||||
jest.mock('@/favorites/hooks/useDeleteFavorite', () => ({
|
||||
useDeleteFavorite: () => ({
|
||||
deleteFavorite: deleteFavoriteMock,
|
||||
}),
|
||||
}));
|
||||
|
||||
const wrapperConfigWithSelectedRecordAsFavorite: GetJestMetadataAndApolloMocksAndActionMenuWrapperProps =
|
||||
{
|
||||
apolloMocks: [],
|
||||
componentInstanceId: '1',
|
||||
contextStoreCurrentObjectMetadataNameSingular:
|
||||
personMockObjectMetadataItem.nameSingular,
|
||||
contextStoreTargetedRecordsRule: {
|
||||
mode: 'selection',
|
||||
selectedRecordIds: [peopleMock[0].id],
|
||||
},
|
||||
onInitializeRecoilSnapshot: (snapshot) => {
|
||||
snapshot.set(recordStoreFamilyState(peopleMock[0].id), peopleMock[0]);
|
||||
snapshot.set(recordStoreFamilyState(peopleMock[1].id), peopleMock[1]);
|
||||
},
|
||||
};
|
||||
|
||||
const wrapperWithSelectedRecordAsFavorite =
|
||||
getJestMetadataAndApolloMocksAndActionMenuWrapper(
|
||||
wrapperConfigWithSelectedRecordAsFavorite,
|
||||
);
|
||||
|
||||
describe('useRemoveFromFavoritesSingleRecordAction', () => {
|
||||
it('should call deleteFavorite on click', () => {
|
||||
const { result } = renderHook(
|
||||
() => useRemoveFromFavoritesSingleRecordAction(),
|
||||
{
|
||||
wrapper: wrapperWithSelectedRecordAsFavorite,
|
||||
},
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.onClick();
|
||||
});
|
||||
|
||||
expect(deleteFavoriteMock).toHaveBeenCalledWith(favoritesMock[0].id);
|
||||
});
|
||||
});
|
||||
@ -1,27 +0,0 @@
|
||||
import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
|
||||
import { ActionHookWithObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
|
||||
import { useCreateFavorite } from '@/favorites/hooks/useCreateFavorite';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const useAddToFavoritesSingleRecordAction: ActionHookWithObjectMetadataItem =
|
||||
({ objectMetadataItem }) => {
|
||||
const recordId = useSelectedRecordIdOrThrow();
|
||||
|
||||
const { createFavorite } = useCreateFavorite();
|
||||
|
||||
const selectedRecord = useRecoilValue(recordStoreFamilyState(recordId));
|
||||
|
||||
const onClick = () => {
|
||||
if (!isDefined(selectedRecord)) {
|
||||
return;
|
||||
}
|
||||
|
||||
createFavorite(selectedRecord, objectMetadataItem.nameSingular);
|
||||
};
|
||||
|
||||
return {
|
||||
onClick,
|
||||
};
|
||||
};
|
||||
@ -1,64 +0,0 @@
|
||||
import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
|
||||
import { ActionHookWithObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
|
||||
import { useDestroyOneRecord } from '@/object-record/hooks/useDestroyOneRecord';
|
||||
import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable';
|
||||
import { AppPath } from '@/types/AppPath';
|
||||
import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal';
|
||||
import { useCallback, useState } from 'react';
|
||||
import { useNavigateApp } from '~/hooks/useNavigateApp';
|
||||
|
||||
export const useDestroySingleRecordAction: ActionHookWithObjectMetadataItem = ({
|
||||
objectMetadataItem,
|
||||
}) => {
|
||||
const recordId = useSelectedRecordIdOrThrow();
|
||||
|
||||
const [isDestroyRecordsModalOpen, setIsDestroyRecordsModalOpen] =
|
||||
useState(false);
|
||||
|
||||
const navigateApp = useNavigateApp();
|
||||
|
||||
const { resetTableRowSelection } = useRecordTable({
|
||||
recordTableId: objectMetadataItem.namePlural,
|
||||
});
|
||||
|
||||
const { destroyOneRecord } = useDestroyOneRecord({
|
||||
objectNameSingular: objectMetadataItem.nameSingular,
|
||||
});
|
||||
|
||||
const handleDeleteClick = useCallback(async () => {
|
||||
resetTableRowSelection();
|
||||
|
||||
await destroyOneRecord(recordId);
|
||||
navigateApp(AppPath.RecordIndexPage, {
|
||||
objectNamePlural: objectMetadataItem.namePlural,
|
||||
});
|
||||
}, [
|
||||
resetTableRowSelection,
|
||||
destroyOneRecord,
|
||||
recordId,
|
||||
navigateApp,
|
||||
objectMetadataItem.namePlural,
|
||||
]);
|
||||
|
||||
const onClick = () => {
|
||||
setIsDestroyRecordsModalOpen(true);
|
||||
};
|
||||
|
||||
return {
|
||||
onClick,
|
||||
ConfirmationModal: (
|
||||
<ConfirmationModal
|
||||
isOpen={isDestroyRecordsModalOpen}
|
||||
setIsOpen={setIsDestroyRecordsModalOpen}
|
||||
title={'Permanently Destroy Record'}
|
||||
subtitle={
|
||||
'Are you sure you want to destroy this record? It cannot be recovered anymore.'
|
||||
}
|
||||
onConfirmClick={async () => {
|
||||
await handleDeleteClick();
|
||||
}}
|
||||
confirmButtonText={'Permanently Destroy Record'}
|
||||
/>
|
||||
),
|
||||
};
|
||||
};
|
||||
@ -1,17 +0,0 @@
|
||||
import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
|
||||
import { ActionHookWithObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
|
||||
import { useRecordShowPagePagination } from '@/object-record/record-show/hooks/useRecordShowPagePagination';
|
||||
|
||||
export const useNavigateToNextRecordSingleRecordAction: ActionHookWithObjectMetadataItem =
|
||||
({ objectMetadataItem }) => {
|
||||
const recordId = useSelectedRecordIdOrThrow();
|
||||
|
||||
const { navigateToNextRecord } = useRecordShowPagePagination(
|
||||
objectMetadataItem.nameSingular,
|
||||
recordId,
|
||||
);
|
||||
|
||||
return {
|
||||
onClick: navigateToNextRecord,
|
||||
};
|
||||
};
|
||||
@ -1,17 +0,0 @@
|
||||
import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
|
||||
import { ActionHookWithObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
|
||||
import { useRecordShowPagePagination } from '@/object-record/record-show/hooks/useRecordShowPagePagination';
|
||||
|
||||
export const useNavigateToPreviousRecordSingleRecordAction: ActionHookWithObjectMetadataItem =
|
||||
({ objectMetadataItem }) => {
|
||||
const recordId = useSelectedRecordIdOrThrow();
|
||||
|
||||
const { navigateToPreviousRecord } = useRecordShowPagePagination(
|
||||
objectMetadataItem.nameSingular,
|
||||
recordId,
|
||||
);
|
||||
|
||||
return {
|
||||
onClick: navigateToPreviousRecord,
|
||||
};
|
||||
};
|
||||
@ -1,30 +0,0 @@
|
||||
import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
|
||||
import { ActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
|
||||
import { useDeleteFavorite } from '@/favorites/hooks/useDeleteFavorite';
|
||||
import { useFavorites } from '@/favorites/hooks/useFavorites';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const useRemoveFromFavoritesSingleRecordAction: ActionHookWithoutObjectMetadataItem =
|
||||
() => {
|
||||
const recordId = useSelectedRecordIdOrThrow();
|
||||
|
||||
const { sortedFavorites: favorites } = useFavorites();
|
||||
|
||||
const { deleteFavorite } = useDeleteFavorite();
|
||||
|
||||
const foundFavorite = favorites?.find(
|
||||
(favorite) => favorite.recordId === recordId,
|
||||
);
|
||||
|
||||
const onClick = () => {
|
||||
if (!isDefined(foundFavorite)) {
|
||||
return;
|
||||
}
|
||||
|
||||
deleteFavorite(foundFavorite.id);
|
||||
};
|
||||
|
||||
return {
|
||||
onClick,
|
||||
};
|
||||
};
|
||||
@ -1,53 +0,0 @@
|
||||
import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
|
||||
import { ActionHookWithObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
|
||||
import { useRestoreManyRecords } from '@/object-record/hooks/useRestoreManyRecords';
|
||||
import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable';
|
||||
import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal';
|
||||
import { useCallback, useState } from 'react';
|
||||
|
||||
export const useRestoreSingleRecordAction: ActionHookWithObjectMetadataItem = ({
|
||||
objectMetadataItem,
|
||||
}) => {
|
||||
const recordId = useSelectedRecordIdOrThrow();
|
||||
|
||||
const [isRestoreRecordModalOpen, setIsRestoreRecordModalOpen] =
|
||||
useState(false);
|
||||
|
||||
const { resetTableRowSelection } = useRecordTable({
|
||||
recordTableId: objectMetadataItem.namePlural,
|
||||
});
|
||||
|
||||
const { restoreManyRecords } = useRestoreManyRecords({
|
||||
objectNameSingular: objectMetadataItem.nameSingular,
|
||||
});
|
||||
|
||||
const handleRestoreClick = useCallback(async () => {
|
||||
resetTableRowSelection();
|
||||
|
||||
await restoreManyRecords({
|
||||
idsToRestore: [recordId],
|
||||
});
|
||||
}, [restoreManyRecords, resetTableRowSelection, recordId]);
|
||||
|
||||
const onClick = () => {
|
||||
setIsRestoreRecordModalOpen(true);
|
||||
};
|
||||
|
||||
const handleConfirmClick = () => {
|
||||
handleRestoreClick();
|
||||
};
|
||||
|
||||
return {
|
||||
onClick,
|
||||
ConfirmationModal: (
|
||||
<ConfirmationModal
|
||||
isOpen={isRestoreRecordModalOpen}
|
||||
setIsOpen={setIsRestoreRecordModalOpen}
|
||||
title={'Restore Record'}
|
||||
subtitle={'Are you sure you want to restore this record?'}
|
||||
onConfirmClick={handleConfirmClick}
|
||||
confirmButtonText={'Restore Record'}
|
||||
/>
|
||||
),
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,24 @@
|
||||
import { Action } from '@/action-menu/actions/components/Action';
|
||||
import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
|
||||
import { useActivateWorkflowVersion } from '@/workflow/hooks/useActivateWorkflowVersion';
|
||||
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const ActivateWorkflowSingleRecordAction = () => {
|
||||
const recordId = useSelectedRecordIdOrThrow();
|
||||
const { activateWorkflowVersion } = useActivateWorkflowVersion();
|
||||
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(recordId);
|
||||
|
||||
const onClick = () => {
|
||||
if (!isDefined(workflowWithCurrentVersion)) {
|
||||
return;
|
||||
}
|
||||
|
||||
activateWorkflowVersion({
|
||||
workflowVersionId: workflowWithCurrentVersion.currentVersion.id,
|
||||
workflowId: workflowWithCurrentVersion.id,
|
||||
});
|
||||
};
|
||||
|
||||
return <Action onClick={onClick} />;
|
||||
};
|
||||
@ -0,0 +1,23 @@
|
||||
import { Action } from '@/action-menu/actions/components/Action';
|
||||
import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
|
||||
import { useDeactivateWorkflowVersion } from '@/workflow/hooks/useDeactivateWorkflowVersion';
|
||||
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const DeactivateWorkflowSingleRecordAction = () => {
|
||||
const recordId = useSelectedRecordIdOrThrow();
|
||||
const { deactivateWorkflowVersion } = useDeactivateWorkflowVersion();
|
||||
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(recordId);
|
||||
|
||||
const onClick = () => {
|
||||
if (!isDefined(workflowWithCurrentVersion)) {
|
||||
return;
|
||||
}
|
||||
|
||||
deactivateWorkflowVersion({
|
||||
workflowVersionId: workflowWithCurrentVersion.currentVersion.id,
|
||||
});
|
||||
};
|
||||
|
||||
return <Action onClick={onClick} />;
|
||||
};
|
||||
@ -0,0 +1,23 @@
|
||||
import { Action } from '@/action-menu/actions/components/Action';
|
||||
import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
|
||||
import { useDeleteOneWorkflowVersion } from '@/workflow/hooks/useDeleteOneWorkflowVersion';
|
||||
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const DiscardDraftWorkflowSingleRecordAction = () => {
|
||||
const recordId = useSelectedRecordIdOrThrow();
|
||||
const { deleteOneWorkflowVersion } = useDeleteOneWorkflowVersion();
|
||||
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(recordId);
|
||||
|
||||
const onClick = () => {
|
||||
if (!isDefined(workflowWithCurrentVersion)) {
|
||||
return;
|
||||
}
|
||||
|
||||
deleteOneWorkflowVersion({
|
||||
workflowVersionId: workflowWithCurrentVersion.currentVersion.id,
|
||||
});
|
||||
};
|
||||
|
||||
return <Action onClick={onClick} />;
|
||||
};
|
||||
@ -0,0 +1,21 @@
|
||||
import { ActionLink } from '@/action-menu/actions/components/ActionLink';
|
||||
import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { AppPath } from '@/types/AppPath';
|
||||
import { useActiveWorkflowVersion } from '@/workflow/hooks/useActiveWorkflowVersion';
|
||||
|
||||
export const SeeActiveVersionWorkflowSingleRecordAction = () => {
|
||||
const recordId = useSelectedRecordIdOrThrow();
|
||||
|
||||
const workflowActiveVersion = useActiveWorkflowVersion(recordId);
|
||||
|
||||
return (
|
||||
<ActionLink
|
||||
to={AppPath.RecordShowPage}
|
||||
params={{
|
||||
objectNameSingular: CoreObjectNameSingular.WorkflowVersion,
|
||||
objectRecordId: workflowActiveVersion.id,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,27 @@
|
||||
import { ActionLink } from '@/action-menu/actions/components/ActionLink';
|
||||
import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
|
||||
import { CoreObjectNamePlural } from '@/object-metadata/types/CoreObjectNamePlural';
|
||||
import { AppPath } from '@/types/AppPath';
|
||||
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
|
||||
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||
|
||||
export const SeeRunsWorkflowSingleRecordAction = () => {
|
||||
const recordId = useSelectedRecordIdOrThrow();
|
||||
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(recordId);
|
||||
|
||||
return (
|
||||
<ActionLink
|
||||
to={AppPath.RecordIndexPage}
|
||||
params={{ objectNamePlural: CoreObjectNamePlural.WorkflowRun }}
|
||||
queryParams={{
|
||||
filter: {
|
||||
workflow: {
|
||||
[ViewFilterOperand.Is]: {
|
||||
selectedRecordIds: [workflowWithCurrentVersion?.id],
|
||||
},
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,27 @@
|
||||
import { ActionLink } from '@/action-menu/actions/components/ActionLink';
|
||||
import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
|
||||
import { CoreObjectNamePlural } from '@/object-metadata/types/CoreObjectNamePlural';
|
||||
import { AppPath } from '@/types/AppPath';
|
||||
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
|
||||
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||
|
||||
export const SeeVersionsWorkflowSingleRecordAction = () => {
|
||||
const recordId = useSelectedRecordIdOrThrow();
|
||||
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(recordId);
|
||||
|
||||
return (
|
||||
<ActionLink
|
||||
to={AppPath.RecordIndexPage}
|
||||
params={{ objectNamePlural: CoreObjectNamePlural.WorkflowVersion }}
|
||||
queryParams={{
|
||||
filter: {
|
||||
workflow: {
|
||||
[ViewFilterOperand.Is]: {
|
||||
selectedRecordIds: [workflowWithCurrentVersion?.id],
|
||||
},
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,23 @@
|
||||
import { Action } from '@/action-menu/actions/components/Action';
|
||||
import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
|
||||
import { useRunWorkflowVersion } from '@/workflow/hooks/useRunWorkflowVersion';
|
||||
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const TestWorkflowSingleRecordAction = () => {
|
||||
const recordId = useSelectedRecordIdOrThrow();
|
||||
const { runWorkflowVersion } = useRunWorkflowVersion();
|
||||
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(recordId);
|
||||
|
||||
const onClick = () => {
|
||||
if (!isDefined(workflowWithCurrentVersion)) {
|
||||
return;
|
||||
}
|
||||
|
||||
runWorkflowVersion({
|
||||
workflowVersionId: workflowWithCurrentVersion.currentVersion.id,
|
||||
});
|
||||
};
|
||||
|
||||
return <Action onClick={onClick} />;
|
||||
};
|
||||
@ -1,117 +0,0 @@
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { act } from 'react';
|
||||
import { FeatureFlagKey } from '~/generated-metadata/graphql';
|
||||
import { getJestMetadataAndApolloMocksAndActionMenuWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksAndActionMenuWrapper';
|
||||
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
|
||||
import { mockCurrentWorkspace } from '~/testing/mock-data/users';
|
||||
import { useActivateWorkflowSingleRecordAction } from '../useActivateWorkflowSingleRecordAction';
|
||||
|
||||
const workflowMockObjectMetadataItem = generatedMockObjectMetadataItems.find(
|
||||
(item) => item.nameSingular === 'workflow',
|
||||
)!;
|
||||
|
||||
const mockedWorkflowEnabledFeatureFlag = {
|
||||
id: '1',
|
||||
key: FeatureFlagKey.IsWorkflowEnabled,
|
||||
value: true,
|
||||
workspaceId: '1',
|
||||
};
|
||||
|
||||
const baseWorkflowMock = {
|
||||
__typename: 'Workflow',
|
||||
id: 'workflowId',
|
||||
currentVersion: {
|
||||
__typename: 'WorkflowVersion',
|
||||
id: 'currentVersionId',
|
||||
trigger: 'trigger',
|
||||
steps: [
|
||||
{
|
||||
__typename: 'WorkflowStep',
|
||||
id: 'stepId1',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
const draftWorkflowMock = {
|
||||
...baseWorkflowMock,
|
||||
currentVersion: {
|
||||
...baseWorkflowMock.currentVersion,
|
||||
status: 'DRAFT',
|
||||
},
|
||||
versions: [
|
||||
{
|
||||
__typename: 'WorkflowVersion',
|
||||
id: 'currentVersionId',
|
||||
status: 'DRAFT',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
jest.mock('@/workflow/hooks/useWorkflowWithCurrentVersion', () => ({
|
||||
useWorkflowWithCurrentVersion: jest.fn(),
|
||||
}));
|
||||
|
||||
const activateWorkflowVersionMock = jest.fn();
|
||||
|
||||
jest.mock('@/workflow/hooks/useActivateWorkflowVersion', () => ({
|
||||
useActivateWorkflowVersion: () => ({
|
||||
activateWorkflowVersion: activateWorkflowVersionMock,
|
||||
}),
|
||||
}));
|
||||
|
||||
const createWrapper = (workflow: {
|
||||
__typename: string;
|
||||
id: string;
|
||||
currentVersion: {
|
||||
id: string;
|
||||
};
|
||||
}) =>
|
||||
getJestMetadataAndApolloMocksAndActionMenuWrapper({
|
||||
apolloMocks: [],
|
||||
componentInstanceId: '1',
|
||||
contextStoreCurrentObjectMetadataNameSingular:
|
||||
workflowMockObjectMetadataItem.nameSingular,
|
||||
contextStoreTargetedRecordsRule: {
|
||||
mode: 'selection',
|
||||
selectedRecordIds: [workflow.id],
|
||||
},
|
||||
onInitializeRecoilSnapshot: (snapshot) => {
|
||||
snapshot.set(recordStoreFamilyState(workflow.id), workflow);
|
||||
snapshot.set(currentWorkspaceState, {
|
||||
...mockCurrentWorkspace,
|
||||
featureFlags: [mockedWorkflowEnabledFeatureFlag],
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
describe('useActivateWorkflowSingleRecordAction', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should call activateWorkflowVersion on click', () => {
|
||||
(useWorkflowWithCurrentVersion as jest.Mock).mockReturnValue(
|
||||
draftWorkflowMock,
|
||||
);
|
||||
|
||||
const { result } = renderHook(
|
||||
() => useActivateWorkflowSingleRecordAction(),
|
||||
{
|
||||
wrapper: createWrapper(draftWorkflowMock),
|
||||
},
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.onClick();
|
||||
});
|
||||
|
||||
expect(activateWorkflowVersionMock).toHaveBeenCalledWith({
|
||||
workflowId: draftWorkflowMock.id,
|
||||
workflowVersionId: draftWorkflowMock.currentVersion.id,
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1,99 +0,0 @@
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { act } from 'react';
|
||||
import { FeatureFlagKey } from '~/generated-metadata/graphql';
|
||||
import { getJestMetadataAndApolloMocksAndActionMenuWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksAndActionMenuWrapper';
|
||||
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
|
||||
import { mockCurrentWorkspace } from '~/testing/mock-data/users';
|
||||
import { useDeactivateWorkflowSingleRecordAction } from '../useDeactivateWorkflowSingleRecordAction';
|
||||
const workflowMockObjectMetadataItem = generatedMockObjectMetadataItems.find(
|
||||
(item) => item.nameSingular === 'workflow',
|
||||
)!;
|
||||
|
||||
const mockedWorkflowEnabledFeatureFlag = {
|
||||
id: '1',
|
||||
key: FeatureFlagKey.IsWorkflowEnabled,
|
||||
value: true,
|
||||
workspaceId: '1',
|
||||
};
|
||||
|
||||
const activeWorkflowMock = {
|
||||
__typename: 'Workflow',
|
||||
id: 'workflowId',
|
||||
lastPublishedVersionId: 'lastPublishedVersionId',
|
||||
currentVersion: {
|
||||
__typename: 'WorkflowVersion',
|
||||
id: 'currentVersionId',
|
||||
trigger: 'trigger',
|
||||
status: 'ACTIVE',
|
||||
steps: [
|
||||
{
|
||||
__typename: 'WorkflowStep',
|
||||
id: 'stepId1',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
jest.mock('@/workflow/hooks/useWorkflowWithCurrentVersion', () => ({
|
||||
useWorkflowWithCurrentVersion: jest.fn(),
|
||||
}));
|
||||
|
||||
const deactivateWorkflowVersionMock = jest.fn();
|
||||
|
||||
jest.mock('@/workflow/hooks/useDeactivateWorkflowVersion', () => ({
|
||||
useDeactivateWorkflowVersion: () => ({
|
||||
deactivateWorkflowVersion: deactivateWorkflowVersionMock,
|
||||
}),
|
||||
}));
|
||||
|
||||
const activeWorkflowWrapper = getJestMetadataAndApolloMocksAndActionMenuWrapper(
|
||||
{
|
||||
apolloMocks: [],
|
||||
componentInstanceId: '1',
|
||||
contextStoreCurrentObjectMetadataNameSingular:
|
||||
workflowMockObjectMetadataItem.nameSingular,
|
||||
contextStoreTargetedRecordsRule: {
|
||||
mode: 'selection',
|
||||
selectedRecordIds: [activeWorkflowMock.id],
|
||||
},
|
||||
onInitializeRecoilSnapshot: (snapshot) => {
|
||||
snapshot.set(
|
||||
recordStoreFamilyState(activeWorkflowMock.id),
|
||||
activeWorkflowMock,
|
||||
);
|
||||
snapshot.set(currentWorkspaceState, {
|
||||
...mockCurrentWorkspace,
|
||||
featureFlags: [mockedWorkflowEnabledFeatureFlag],
|
||||
});
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
describe('useDeactivateWorkflowSingleRecordAction', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should call deactivateWorkflowVersion on click', () => {
|
||||
(useWorkflowWithCurrentVersion as jest.Mock).mockImplementation(
|
||||
() => activeWorkflowMock,
|
||||
);
|
||||
const { result } = renderHook(
|
||||
() => useDeactivateWorkflowSingleRecordAction(),
|
||||
{
|
||||
wrapper: activeWorkflowWrapper,
|
||||
},
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.onClick();
|
||||
});
|
||||
|
||||
expect(deactivateWorkflowVersionMock).toHaveBeenCalledWith({
|
||||
workflowVersionId: activeWorkflowMock.currentVersion.id,
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1,124 +0,0 @@
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { act } from 'react';
|
||||
import { FeatureFlagKey } from '~/generated-metadata/graphql';
|
||||
import { getJestMetadataAndApolloMocksAndActionMenuWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksAndActionMenuWrapper';
|
||||
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
|
||||
import { mockCurrentWorkspace } from '~/testing/mock-data/users';
|
||||
import { useDiscardDraftWorkflowSingleRecordAction } from '../useDiscardDraftWorkflowSingleRecordAction';
|
||||
|
||||
const workflowMockObjectMetadataItem = generatedMockObjectMetadataItems.find(
|
||||
(item) => item.nameSingular === 'workflow',
|
||||
)!;
|
||||
|
||||
const mockedWorkflowEnabledFeatureFlag = {
|
||||
id: '1',
|
||||
key: FeatureFlagKey.IsWorkflowEnabled,
|
||||
value: true,
|
||||
workspaceId: '1',
|
||||
};
|
||||
|
||||
const draftWorkflowMock = {
|
||||
__typename: 'Workflow',
|
||||
id: 'workflowId',
|
||||
lastPublishedVersionId: 'lastPublishedVersionId',
|
||||
currentVersion: {
|
||||
__typename: 'WorkflowVersion',
|
||||
id: 'currentVersionId',
|
||||
trigger: 'trigger',
|
||||
status: 'DRAFT',
|
||||
steps: [
|
||||
{
|
||||
__typename: 'WorkflowStep',
|
||||
id: 'stepId1',
|
||||
},
|
||||
],
|
||||
},
|
||||
versions: [
|
||||
{
|
||||
__typename: 'WorkflowVersion',
|
||||
id: 'currentVersionId',
|
||||
trigger: 'trigger',
|
||||
status: 'DRAFT',
|
||||
steps: [
|
||||
{
|
||||
__typename: 'WorkflowStep',
|
||||
id: 'stepId1',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
__typename: 'WorkflowVersion',
|
||||
id: 'versionId2',
|
||||
trigger: 'trigger',
|
||||
status: 'ACTIVE',
|
||||
steps: [
|
||||
{
|
||||
__typename: 'WorkflowStep',
|
||||
id: 'stepId2',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
jest.mock('@/workflow/hooks/useWorkflowWithCurrentVersion', () => ({
|
||||
useWorkflowWithCurrentVersion: jest.fn(),
|
||||
}));
|
||||
|
||||
const deleteOneWorkflowVersionMock = jest.fn();
|
||||
|
||||
jest.mock('@/workflow/hooks/useDeleteOneWorkflowVersion', () => ({
|
||||
useDeleteOneWorkflowVersion: () => ({
|
||||
deleteOneWorkflowVersion: deleteOneWorkflowVersionMock,
|
||||
}),
|
||||
}));
|
||||
|
||||
const draftWorkflowWrapper = getJestMetadataAndApolloMocksAndActionMenuWrapper({
|
||||
apolloMocks: [],
|
||||
componentInstanceId: '1',
|
||||
contextStoreCurrentObjectMetadataNameSingular:
|
||||
workflowMockObjectMetadataItem.nameSingular,
|
||||
contextStoreTargetedRecordsRule: {
|
||||
mode: 'selection',
|
||||
selectedRecordIds: [draftWorkflowMock.id],
|
||||
},
|
||||
onInitializeRecoilSnapshot: (snapshot) => {
|
||||
snapshot.set(
|
||||
recordStoreFamilyState(draftWorkflowMock.id),
|
||||
draftWorkflowMock,
|
||||
);
|
||||
snapshot.set(currentWorkspaceState, {
|
||||
...mockCurrentWorkspace,
|
||||
featureFlags: [mockedWorkflowEnabledFeatureFlag],
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
describe('useDiscardDraftWorkflowSingleRecordAction', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should call deleteOneWorkflowVersion on click', () => {
|
||||
(useWorkflowWithCurrentVersion as jest.Mock).mockImplementation(
|
||||
() => draftWorkflowMock,
|
||||
);
|
||||
const { result } = renderHook(
|
||||
() => useDiscardDraftWorkflowSingleRecordAction(),
|
||||
{
|
||||
wrapper: draftWorkflowWrapper,
|
||||
},
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.onClick();
|
||||
});
|
||||
|
||||
expect(deleteOneWorkflowVersionMock).toHaveBeenCalledWith({
|
||||
workflowVersionId: draftWorkflowMock.currentVersion.id,
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1,29 +0,0 @@
|
||||
import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
|
||||
import { ActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
|
||||
import { useActivateWorkflowVersion } from '@/workflow/hooks/useActivateWorkflowVersion';
|
||||
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const useActivateWorkflowSingleRecordAction: ActionHookWithoutObjectMetadataItem =
|
||||
() => {
|
||||
const recordId = useSelectedRecordIdOrThrow();
|
||||
|
||||
const { activateWorkflowVersion } = useActivateWorkflowVersion();
|
||||
|
||||
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(recordId);
|
||||
|
||||
const onClick = () => {
|
||||
if (!isDefined(workflowWithCurrentVersion)) {
|
||||
return;
|
||||
}
|
||||
|
||||
activateWorkflowVersion({
|
||||
workflowVersionId: workflowWithCurrentVersion.currentVersion.id,
|
||||
workflowId: workflowWithCurrentVersion.id,
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
onClick,
|
||||
};
|
||||
};
|
||||
@ -1,28 +0,0 @@
|
||||
import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
|
||||
import { ActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
|
||||
import { useDeactivateWorkflowVersion } from '@/workflow/hooks/useDeactivateWorkflowVersion';
|
||||
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const useDeactivateWorkflowSingleRecordAction: ActionHookWithoutObjectMetadataItem =
|
||||
() => {
|
||||
const recordId = useSelectedRecordIdOrThrow();
|
||||
|
||||
const { deactivateWorkflowVersion } = useDeactivateWorkflowVersion();
|
||||
|
||||
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(recordId);
|
||||
|
||||
const onClick = () => {
|
||||
if (!isDefined(workflowWithCurrentVersion)) {
|
||||
return;
|
||||
}
|
||||
|
||||
deactivateWorkflowVersion({
|
||||
workflowVersionId: workflowWithCurrentVersion.currentVersion.id,
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
onClick,
|
||||
};
|
||||
};
|
||||
@ -1,28 +0,0 @@
|
||||
import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
|
||||
import { ActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
|
||||
import { useDeleteOneWorkflowVersion } from '@/workflow/hooks/useDeleteOneWorkflowVersion';
|
||||
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const useDiscardDraftWorkflowSingleRecordAction: ActionHookWithoutObjectMetadataItem =
|
||||
() => {
|
||||
const recordId = useSelectedRecordIdOrThrow();
|
||||
|
||||
const { deleteOneWorkflowVersion } = useDeleteOneWorkflowVersion();
|
||||
|
||||
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(recordId);
|
||||
|
||||
const onClick = () => {
|
||||
if (!isDefined(workflowWithCurrentVersion)) {
|
||||
return;
|
||||
}
|
||||
|
||||
deleteOneWorkflowVersion({
|
||||
workflowVersionId: workflowWithCurrentVersion.currentVersion.id,
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
onClick,
|
||||
};
|
||||
};
|
||||
@ -1,31 +0,0 @@
|
||||
import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
|
||||
import { ActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { AppPath } from '@/types/AppPath';
|
||||
import { useActiveWorkflowVersion } from '@/workflow/hooks/useActiveWorkflowVersion';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { useNavigateApp } from '~/hooks/useNavigateApp';
|
||||
|
||||
export const useSeeActiveVersionWorkflowSingleRecordAction: ActionHookWithoutObjectMetadataItem =
|
||||
() => {
|
||||
const recordId = useSelectedRecordIdOrThrow();
|
||||
|
||||
const workflowActiveVersion = useActiveWorkflowVersion(recordId);
|
||||
|
||||
const navigateApp = useNavigateApp();
|
||||
|
||||
const onClick = () => {
|
||||
if (!isDefined(workflowActiveVersion)) {
|
||||
return;
|
||||
}
|
||||
|
||||
navigateApp(AppPath.RecordShowPage, {
|
||||
objectNameSingular: CoreObjectNameSingular.WorkflowVersion,
|
||||
objectRecordId: workflowActiveVersion.id,
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
onClick,
|
||||
};
|
||||
};
|
||||
@ -1,43 +0,0 @@
|
||||
import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
|
||||
import { ActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
|
||||
import { CoreObjectNamePlural } from '@/object-metadata/types/CoreObjectNamePlural';
|
||||
import { AppPath } from '@/types/AppPath';
|
||||
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
|
||||
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { useNavigateApp } from '~/hooks/useNavigateApp';
|
||||
|
||||
export const useSeeRunsWorkflowSingleRecordAction: ActionHookWithoutObjectMetadataItem =
|
||||
() => {
|
||||
const recordId = useSelectedRecordIdOrThrow();
|
||||
|
||||
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(recordId);
|
||||
|
||||
const navigateApp = useNavigateApp();
|
||||
|
||||
const onClick = () => {
|
||||
if (!isDefined(workflowWithCurrentVersion)) {
|
||||
return;
|
||||
}
|
||||
|
||||
navigateApp(
|
||||
AppPath.RecordIndexPage,
|
||||
{
|
||||
objectNamePlural: CoreObjectNamePlural.WorkflowRun,
|
||||
},
|
||||
{
|
||||
filter: {
|
||||
workflow: {
|
||||
[ViewFilterOperand.Is]: {
|
||||
selectedRecordIds: [workflowWithCurrentVersion.id],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
return {
|
||||
onClick,
|
||||
};
|
||||
};
|
||||
@ -1,43 +0,0 @@
|
||||
import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
|
||||
import { ActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
|
||||
import { CoreObjectNamePlural } from '@/object-metadata/types/CoreObjectNamePlural';
|
||||
import { AppPath } from '@/types/AppPath';
|
||||
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
|
||||
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { useNavigateApp } from '~/hooks/useNavigateApp';
|
||||
|
||||
export const useSeeVersionsWorkflowSingleRecordAction: ActionHookWithoutObjectMetadataItem =
|
||||
() => {
|
||||
const recordId = useSelectedRecordIdOrThrow();
|
||||
|
||||
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(recordId);
|
||||
|
||||
const navigateApp = useNavigateApp();
|
||||
|
||||
const onClick = () => {
|
||||
if (!isDefined(workflowWithCurrentVersion)) {
|
||||
return;
|
||||
}
|
||||
|
||||
navigateApp(
|
||||
AppPath.RecordIndexPage,
|
||||
{
|
||||
objectNamePlural: CoreObjectNamePlural.WorkflowVersion,
|
||||
},
|
||||
{
|
||||
filter: {
|
||||
workflow: {
|
||||
[ViewFilterOperand.Is]: {
|
||||
selectedRecordIds: [workflowWithCurrentVersion.id],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
return {
|
||||
onClick,
|
||||
};
|
||||
};
|
||||
@ -1,28 +0,0 @@
|
||||
import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
|
||||
import { ActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
|
||||
import { useRunWorkflowVersion } from '@/workflow/hooks/useRunWorkflowVersion';
|
||||
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const useTestWorkflowSingleRecordAction: ActionHookWithoutObjectMetadataItem =
|
||||
() => {
|
||||
const recordId = useSelectedRecordIdOrThrow();
|
||||
|
||||
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(recordId);
|
||||
|
||||
const { runWorkflowVersion } = useRunWorkflowVersion();
|
||||
|
||||
const onClick = () => {
|
||||
if (!isDefined(workflowWithCurrentVersion)) {
|
||||
return;
|
||||
}
|
||||
|
||||
runWorkflowVersion({
|
||||
workflowVersionId: workflowWithCurrentVersion.currentVersion.id,
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
onClick,
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,26 @@
|
||||
import { ActionLink } from '@/action-menu/actions/components/ActionLink';
|
||||
import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { AppPath } from '@/types/AppPath';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const SeeVersionWorkflowRunSingleRecordAction = () => {
|
||||
const recordId = useSelectedRecordIdOrThrow();
|
||||
const workflowRun = useRecoilValue(recordStoreFamilyState(recordId));
|
||||
|
||||
if (!isDefined(workflowRun) || !isDefined(workflowRun?.workflowVersion?.id)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<ActionLink
|
||||
to={AppPath.RecordShowPage}
|
||||
params={{
|
||||
objectNameSingular: CoreObjectNameSingular.WorkflowVersion,
|
||||
objectRecordId: workflowRun.workflowVersion.id,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,26 @@
|
||||
import { ActionLink } from '@/action-menu/actions/components/ActionLink';
|
||||
import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { AppPath } from '@/types/AppPath';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const SeeWorkflowWorkflowRunSingleRecordAction = () => {
|
||||
const recordId = useSelectedRecordIdOrThrow();
|
||||
const workflowRun = useRecoilValue(recordStoreFamilyState(recordId));
|
||||
|
||||
if (!isDefined(workflowRun) || !isDefined(workflowRun?.workflow?.id)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<ActionLink
|
||||
to={AppPath.RecordShowPage}
|
||||
params={{
|
||||
objectNameSingular: CoreObjectNameSingular.Workflow,
|
||||
objectRecordId: workflowRun.workflow.id,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -1,35 +0,0 @@
|
||||
import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
|
||||
import { ActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { AppPath } from '@/types/AppPath';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { useNavigateApp } from '~/hooks/useNavigateApp';
|
||||
|
||||
export const useSeeVersionWorkflowRunSingleRecordAction: ActionHookWithoutObjectMetadataItem =
|
||||
() => {
|
||||
const recordId = useSelectedRecordIdOrThrow();
|
||||
|
||||
const workflowRun = useRecoilValue(recordStoreFamilyState(recordId));
|
||||
|
||||
const navigateApp = useNavigateApp();
|
||||
|
||||
const onClick = () => {
|
||||
if (
|
||||
!isDefined(workflowRun) ||
|
||||
!isDefined(workflowRun?.workflowVersion?.id)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
navigateApp(AppPath.RecordShowPage, {
|
||||
objectNameSingular: CoreObjectNameSingular.WorkflowVersion,
|
||||
objectRecordId: workflowRun.workflowVersion.id,
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
onClick,
|
||||
};
|
||||
};
|
||||
@ -1,32 +0,0 @@
|
||||
import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
|
||||
import { ActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { AppPath } from '@/types/AppPath';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { useNavigateApp } from '~/hooks/useNavigateApp';
|
||||
|
||||
export const useSeeWorkflowWorkflowRunSingleRecordAction: ActionHookWithoutObjectMetadataItem =
|
||||
() => {
|
||||
const recordId = useSelectedRecordIdOrThrow();
|
||||
|
||||
const workflowRun = useRecoilValue(recordStoreFamilyState(recordId));
|
||||
|
||||
const navigateApp = useNavigateApp();
|
||||
|
||||
const onClick = () => {
|
||||
if (!isDefined(workflowRun) || !isDefined(workflowRun?.workflow?.id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
navigateApp(AppPath.RecordShowPage, {
|
||||
objectNameSingular: CoreObjectNameSingular.Workflow,
|
||||
objectRecordId: workflowRun.workflow.id,
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
onClick,
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,37 @@
|
||||
import { ActionLink } from '@/action-menu/actions/components/ActionLink';
|
||||
import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
|
||||
import { CoreObjectNamePlural } from '@/object-metadata/types/CoreObjectNamePlural';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { AppPath } from '@/types/AppPath';
|
||||
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
|
||||
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
export const SeeRunsWorkflowVersionSingleRecordAction = () => {
|
||||
const recordId = useSelectedRecordIdOrThrow();
|
||||
const workflowVersion = useRecoilValue(recordStoreFamilyState(recordId));
|
||||
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(
|
||||
workflowVersion?.workflow.id,
|
||||
);
|
||||
|
||||
return (
|
||||
<ActionLink
|
||||
to={AppPath.RecordIndexPage}
|
||||
params={{ objectNamePlural: CoreObjectNamePlural.WorkflowRun }}
|
||||
queryParams={{
|
||||
filter: {
|
||||
workflow: {
|
||||
[ViewFilterOperand.Is]: {
|
||||
selectedRecordIds: [workflowWithCurrentVersion?.id],
|
||||
},
|
||||
},
|
||||
workflowVersion: {
|
||||
[ViewFilterOperand.Is]: {
|
||||
selectedRecordIds: [recordId],
|
||||
},
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,32 @@
|
||||
import { ActionLink } from '@/action-menu/actions/components/ActionLink';
|
||||
import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
|
||||
import { CoreObjectNamePlural } from '@/object-metadata/types/CoreObjectNamePlural';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { AppPath } from '@/types/AppPath';
|
||||
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
|
||||
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
export const SeeVersionsWorkflowVersionSingleRecordAction = () => {
|
||||
const recordId = useSelectedRecordIdOrThrow();
|
||||
const workflowVersion = useRecoilValue(recordStoreFamilyState(recordId));
|
||||
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(
|
||||
workflowVersion?.workflowId,
|
||||
);
|
||||
|
||||
return (
|
||||
<ActionLink
|
||||
to={AppPath.RecordIndexPage}
|
||||
params={{ objectNamePlural: CoreObjectNamePlural.WorkflowVersion }}
|
||||
queryParams={{
|
||||
filter: {
|
||||
workflow: {
|
||||
[ViewFilterOperand.Is]: {
|
||||
selectedRecordIds: [workflowWithCurrentVersion?.id],
|
||||
},
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,21 @@
|
||||
import { ActionLink } from '@/action-menu/actions/components/ActionLink';
|
||||
import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { AppPath } from '@/types/AppPath';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
export const SeeWorkflowWorkflowVersionSingleRecordAction = () => {
|
||||
const recordId = useSelectedRecordIdOrThrow();
|
||||
const workflowVersion = useRecoilValue(recordStoreFamilyState(recordId));
|
||||
|
||||
return (
|
||||
<ActionLink
|
||||
to={AppPath.RecordShowPage}
|
||||
params={{
|
||||
objectNameSingular: CoreObjectNameSingular.Workflow,
|
||||
objectRecordId: workflowVersion?.workflow?.id,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -1,5 +1,5 @@
|
||||
import { Action } from '@/action-menu/actions/components/Action';
|
||||
import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
|
||||
import { ActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { AppPath } from '@/types/AppPath';
|
||||
import { OverrideWorkflowDraftConfirmationModal } from '@/workflow/components/OverrideWorkflowDraftConfirmationModal';
|
||||
@ -7,60 +7,61 @@ import { useCreateDraftFromWorkflowVersion } from '@/workflow/hooks/useCreateDra
|
||||
import { useWorkflowVersion } from '@/workflow/hooks/useWorkflowVersion';
|
||||
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||
import { openOverrideWorkflowDraftConfirmationModalState } from '@/workflow/states/openOverrideWorkflowDraftConfirmationModalState';
|
||||
import { useState } from 'react';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { useNavigateApp } from '~/hooks/useNavigateApp';
|
||||
|
||||
export const useUseAsDraftWorkflowVersionSingleRecordAction: ActionHookWithoutObjectMetadataItem =
|
||||
() => {
|
||||
const recordId = useSelectedRecordIdOrThrow();
|
||||
export const UseAsDraftWorkflowVersionSingleRecordAction = () => {
|
||||
const recordId = useSelectedRecordIdOrThrow();
|
||||
const workflowVersion = useWorkflowVersion(recordId);
|
||||
const workflow = useWorkflowWithCurrentVersion(
|
||||
workflowVersion?.workflow?.id ?? '',
|
||||
);
|
||||
const { createDraftFromWorkflowVersion } =
|
||||
useCreateDraftFromWorkflowVersion();
|
||||
const setOpenOverrideWorkflowDraftConfirmationModal = useSetRecoilState(
|
||||
openOverrideWorkflowDraftConfirmationModalState,
|
||||
);
|
||||
const navigate = useNavigateApp();
|
||||
const [hasNavigated, setHasNavigated] = useState(false);
|
||||
|
||||
const workflowVersion = useWorkflowVersion(recordId);
|
||||
const hasAlreadyDraftVersion =
|
||||
workflow?.versions.some((version) => version.status === 'DRAFT') || false;
|
||||
|
||||
const workflow = useWorkflowWithCurrentVersion(
|
||||
workflowVersion?.workflow?.id ?? '',
|
||||
);
|
||||
const handleClick = () => {
|
||||
if (!isDefined(workflowVersion) || !isDefined(workflow) || hasNavigated) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { createDraftFromWorkflowVersion } =
|
||||
useCreateDraftFromWorkflowVersion();
|
||||
|
||||
const setOpenOverrideWorkflowDraftConfirmationModal = useSetRecoilState(
|
||||
openOverrideWorkflowDraftConfirmationModalState,
|
||||
);
|
||||
|
||||
const navigate = useNavigateApp();
|
||||
|
||||
const hasAlreadyDraftVersion =
|
||||
workflow?.versions.some((version) => version.status === 'DRAFT') || false;
|
||||
|
||||
const onClick = async () => {
|
||||
if (!isDefined(workflowVersion) || !isDefined(workflow)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (hasAlreadyDraftVersion) {
|
||||
setOpenOverrideWorkflowDraftConfirmationModal(true);
|
||||
} else {
|
||||
if (hasAlreadyDraftVersion) {
|
||||
setOpenOverrideWorkflowDraftConfirmationModal(true);
|
||||
} else {
|
||||
const executeActionWithoutWaiting = async () => {
|
||||
await createDraftFromWorkflowVersion({
|
||||
workflowId: workflowVersion.workflow.id,
|
||||
workflowVersionIdToCopy: workflowVersion.id,
|
||||
});
|
||||
|
||||
navigate(AppPath.RecordShowPage, {
|
||||
objectNameSingular: CoreObjectNameSingular.Workflow,
|
||||
objectRecordId: workflowVersion.workflow.id,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const ConfirmationModal = isDefined(workflowVersion) ? (
|
||||
setHasNavigated(true);
|
||||
};
|
||||
|
||||
executeActionWithoutWaiting();
|
||||
}
|
||||
};
|
||||
|
||||
return isDefined(workflowVersion) ? (
|
||||
<>
|
||||
<Action onClick={handleClick} />
|
||||
<OverrideWorkflowDraftConfirmationModal
|
||||
workflowId={workflowVersion.workflow.id}
|
||||
workflowVersionIdToCopy={workflowVersion.id}
|
||||
/>
|
||||
) : undefined;
|
||||
|
||||
return {
|
||||
onClick,
|
||||
ConfirmationModal,
|
||||
};
|
||||
};
|
||||
</>
|
||||
) : null;
|
||||
};
|
||||
@ -1,54 +0,0 @@
|
||||
import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
|
||||
import { ActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
|
||||
import { CoreObjectNamePlural } from '@/object-metadata/types/CoreObjectNamePlural';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { AppPath } from '@/types/AppPath';
|
||||
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
|
||||
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { useNavigateApp } from '~/hooks/useNavigateApp';
|
||||
|
||||
export const useSeeRunsWorkflowVersionSingleRecordAction: ActionHookWithoutObjectMetadataItem =
|
||||
() => {
|
||||
const recordId = useSelectedRecordIdOrThrow();
|
||||
|
||||
const workflowVersion = useRecoilValue(recordStoreFamilyState(recordId));
|
||||
|
||||
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(
|
||||
workflowVersion?.workflow.id,
|
||||
);
|
||||
|
||||
const navigateApp = useNavigateApp();
|
||||
|
||||
const onClick = () => {
|
||||
if (!isDefined(workflowWithCurrentVersion)) {
|
||||
return;
|
||||
}
|
||||
|
||||
navigateApp(
|
||||
AppPath.RecordIndexPage,
|
||||
{
|
||||
objectNamePlural: CoreObjectNamePlural.WorkflowRun,
|
||||
},
|
||||
{
|
||||
filter: {
|
||||
workflow: {
|
||||
[ViewFilterOperand.Is]: {
|
||||
selectedRecordIds: [workflowWithCurrentVersion.id],
|
||||
},
|
||||
},
|
||||
workflowVersion: {
|
||||
[ViewFilterOperand.Is]: {
|
||||
selectedRecordIds: [recordId],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
return {
|
||||
onClick,
|
||||
};
|
||||
};
|
||||
@ -1,49 +0,0 @@
|
||||
import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
|
||||
import { ActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
|
||||
import { CoreObjectNamePlural } from '@/object-metadata/types/CoreObjectNamePlural';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { AppPath } from '@/types/AppPath';
|
||||
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
|
||||
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { useNavigateApp } from '~/hooks/useNavigateApp';
|
||||
|
||||
export const useSeeVersionsWorkflowVersionSingleRecordAction: ActionHookWithoutObjectMetadataItem =
|
||||
() => {
|
||||
const recordId = useSelectedRecordIdOrThrow();
|
||||
|
||||
const workflowVersion = useRecoilValue(recordStoreFamilyState(recordId));
|
||||
|
||||
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(
|
||||
workflowVersion?.workflowId,
|
||||
);
|
||||
|
||||
const navigateApp = useNavigateApp();
|
||||
|
||||
const onClick = () => {
|
||||
if (!isDefined(workflowWithCurrentVersion)) {
|
||||
return;
|
||||
}
|
||||
|
||||
navigateApp(
|
||||
AppPath.RecordIndexPage,
|
||||
{
|
||||
objectNamePlural: CoreObjectNamePlural.WorkflowVersion,
|
||||
},
|
||||
{
|
||||
filter: {
|
||||
workflow: {
|
||||
[ViewFilterOperand.Is]: {
|
||||
selectedRecordIds: [workflowWithCurrentVersion.id],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
return {
|
||||
onClick,
|
||||
};
|
||||
};
|
||||
@ -1,35 +0,0 @@
|
||||
import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
|
||||
import { ActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/ActionHook';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { AppPath } from '@/types/AppPath';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { useNavigateApp } from '~/hooks/useNavigateApp';
|
||||
|
||||
export const useSeeWorkflowWorkflowVersionSingleRecordAction: ActionHookWithoutObjectMetadataItem =
|
||||
() => {
|
||||
const recordId = useSelectedRecordIdOrThrow();
|
||||
|
||||
const workflowVersion = useRecoilValue(recordStoreFamilyState(recordId));
|
||||
|
||||
const navigateApp = useNavigateApp();
|
||||
|
||||
const onClick = () => {
|
||||
if (
|
||||
!isDefined(workflowVersion) ||
|
||||
!isDefined(workflowVersion?.workflow?.id)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
navigateApp(AppPath.RecordShowPage, {
|
||||
objectNameSingular: CoreObjectNameSingular.Workflow,
|
||||
objectRecordId: workflowVersion.workflow.id,
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
onClick,
|
||||
};
|
||||
};
|
||||
@ -1,18 +1,18 @@
|
||||
import { DEFAULT_RECORD_ACTIONS_CONFIG } from '@/action-menu/actions/record-actions/constants/DefaultRecordActionsConfig';
|
||||
import { ActionConfig } from '@/action-menu/actions/types/ActionConfig';
|
||||
import { DefaultRecordActionConfigKeys } from '@/action-menu/actions/types/DefaultRecordActionConfigKeys';
|
||||
import { RecordConfigAction } from '@/action-menu/actions/types/RecordConfigAction';
|
||||
|
||||
export const inheritActionsFromDefaultConfig = ({
|
||||
config,
|
||||
actionKeys,
|
||||
propertiesToOverwrite,
|
||||
}: {
|
||||
config: Record<string, RecordConfigAction>;
|
||||
config: Record<string, ActionConfig>;
|
||||
actionKeys: DefaultRecordActionConfigKeys[];
|
||||
propertiesToOverwrite: Partial<
|
||||
Record<DefaultRecordActionConfigKeys, Partial<RecordConfigAction>>
|
||||
Record<DefaultRecordActionConfigKeys, Partial<ActionConfig>>
|
||||
>;
|
||||
}): Record<string, RecordConfigAction> => {
|
||||
}): Record<string, ActionConfig> => {
|
||||
const actionsFromDefaultConfig = actionKeys.reduce(
|
||||
(acc, key) => ({
|
||||
...acc,
|
||||
@ -21,7 +21,7 @@ export const inheritActionsFromDefaultConfig = ({
|
||||
...propertiesToOverwrite[key],
|
||||
},
|
||||
}),
|
||||
{} as Record<string, RecordConfigAction>,
|
||||
{} as Record<string, ActionConfig>,
|
||||
);
|
||||
|
||||
return {
|
||||
|
||||
@ -1,26 +0,0 @@
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { useEffect } from 'react';
|
||||
import { useWorkflowRunRecordActions } from '../hooks/useWorkflowRunRecordActions';
|
||||
|
||||
export const WorkflowRunRecordActionMenuEntrySetterEffect = ({
|
||||
objectMetadataItem,
|
||||
}: {
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
}) => {
|
||||
const {
|
||||
registerWorkflowRunRecordActions,
|
||||
unregisterWorkflowRunRecordActions,
|
||||
} = useWorkflowRunRecordActions({
|
||||
objectMetadataItem,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
registerWorkflowRunRecordActions();
|
||||
|
||||
return () => {
|
||||
unregisterWorkflowRunRecordActions();
|
||||
};
|
||||
}, [registerWorkflowRunRecordActions, unregisterWorkflowRunRecordActions]);
|
||||
|
||||
return null;
|
||||
};
|
||||
@ -1,8 +1,6 @@
|
||||
import { useActionMenuEntries } from '@/action-menu/hooks/useActionMenuEntries';
|
||||
import {
|
||||
ActionMenuEntryScope,
|
||||
ActionMenuEntryType,
|
||||
} from '@/action-menu/types/ActionMenuEntry';
|
||||
import { Action } from '@/action-menu/actions/components/Action';
|
||||
import { ActionScope } from '@/action-menu/actions/types/ActionScope';
|
||||
import { ActionType } from '@/action-menu/actions/types/ActionType';
|
||||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
@ -20,8 +18,6 @@ export const useWorkflowRunRecordActions = ({
|
||||
}: {
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
}) => {
|
||||
const { addActionMenuEntry, removeActionMenuEntry } = useActionMenuEntries();
|
||||
|
||||
const contextStoreTargetedRecordsRule = useRecoilComponentValueV2(
|
||||
contextStoreTargetedRecordsRuleComponentState,
|
||||
);
|
||||
@ -46,48 +42,35 @@ export const useWorkflowRunRecordActions = ({
|
||||
|
||||
const { runWorkflowVersion } = useRunWorkflowVersion();
|
||||
|
||||
const registerWorkflowRunRecordActions = () => {
|
||||
if (!isDefined(objectMetadataItem) || objectMetadataItem.isRemote) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const [
|
||||
index,
|
||||
activeWorkflowVersion,
|
||||
] of activeWorkflowVersions.entries()) {
|
||||
if (!isDefined(activeWorkflowVersion.workflow)) {
|
||||
continue;
|
||||
}
|
||||
return activeWorkflowVersions
|
||||
.filter((activeWorkflowVersion) =>
|
||||
isDefined(activeWorkflowVersion.workflow),
|
||||
)
|
||||
.map((activeWorkflowVersion, index) => {
|
||||
const name = capitalize(activeWorkflowVersion.workflow.name);
|
||||
addActionMenuEntry({
|
||||
type: ActionMenuEntryType.WorkflowRun,
|
||||
|
||||
return {
|
||||
type: ActionType.WorkflowRun,
|
||||
key: `workflow-run-${activeWorkflowVersion.id}`,
|
||||
scope: ActionMenuEntryScope.RecordSelection,
|
||||
scope: ActionScope.RecordSelection,
|
||||
label: msg`${name}`,
|
||||
position: index,
|
||||
Icon: IconSettingsAutomation,
|
||||
onClick: async () => {
|
||||
if (!isDefined(selectedRecord)) {
|
||||
return;
|
||||
}
|
||||
shouldBeRegistered: () => true,
|
||||
component: (
|
||||
<Action
|
||||
onClick={async () => {
|
||||
if (!isDefined(selectedRecord)) {
|
||||
return;
|
||||
}
|
||||
|
||||
await runWorkflowVersion({
|
||||
workflowVersionId: activeWorkflowVersion.id,
|
||||
payload: selectedRecord,
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const unregisterWorkflowRunRecordActions = () => {
|
||||
for (const activeWorkflowVersion of activeWorkflowVersions) {
|
||||
removeActionMenuEntry(`workflow-run-${activeWorkflowVersion.id}`);
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
registerWorkflowRunRecordActions,
|
||||
unregisterWorkflowRunRecordActions,
|
||||
};
|
||||
await runWorkflowVersion({
|
||||
workflowVersionId: activeWorkflowVersion.id,
|
||||
payload: selectedRecord,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
),
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
@ -1,14 +0,0 @@
|
||||
import { RegisterAgnosticActionEffect } from '@/action-menu/actions/record-agnostic-actions/components/RegisterAgnosticActionEffect';
|
||||
import { useRegisteredRecordAgnosticActions } from '@/action-menu/hooks/useRegisteredRecordAgnosticActions';
|
||||
|
||||
export const RecordAgnosticActionMenuEntriesSetter = () => {
|
||||
const actionsToRegister = useRegisteredRecordAgnosticActions();
|
||||
|
||||
return (
|
||||
<>
|
||||
{actionsToRegister.map((action) => (
|
||||
<RegisterAgnosticActionEffect key={action.key} action={action} />
|
||||
))}
|
||||
</>
|
||||
);
|
||||
};
|
||||
@ -1,40 +0,0 @@
|
||||
import { RecordAgnosticConfigAction } from '@/action-menu/actions/types/RecordAgnosticConfigAction';
|
||||
import { wrapActionInCallbacks } from '@/action-menu/actions/utils/wrapActionInCallbacks';
|
||||
import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext';
|
||||
import { useActionMenuEntries } from '@/action-menu/hooks/useActionMenuEntries';
|
||||
import { useContext, useEffect } from 'react';
|
||||
|
||||
type RegisterAgnosticActionEffectProps = {
|
||||
action: RecordAgnosticConfigAction;
|
||||
};
|
||||
|
||||
export const RegisterAgnosticActionEffect = ({
|
||||
action,
|
||||
}: RegisterAgnosticActionEffectProps) => {
|
||||
const { onClick, ConfirmationModal } = action.useAction();
|
||||
|
||||
const { onActionStartedCallback, onActionExecutedCallback } =
|
||||
useContext(ActionMenuContext);
|
||||
|
||||
const { addActionMenuEntry, removeActionMenuEntry } = useActionMenuEntries();
|
||||
|
||||
const wrappedAction = wrapActionInCallbacks({
|
||||
action: {
|
||||
...action,
|
||||
onClick,
|
||||
ConfirmationModal,
|
||||
},
|
||||
onActionStartedCallback,
|
||||
onActionExecutedCallback,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
addActionMenuEntry(wrappedAction);
|
||||
|
||||
return () => {
|
||||
removeActionMenuEntry(wrappedAction.key);
|
||||
};
|
||||
}, [addActionMenuEntry, removeActionMenuEntry, wrappedAction]);
|
||||
|
||||
return null;
|
||||
};
|
||||
@ -1,14 +0,0 @@
|
||||
import { RegisterAgnosticActionEffect } from '@/action-menu/actions/record-agnostic-actions/components/RegisterAgnosticActionEffect';
|
||||
import { useRunWorkflowActions } from '@/action-menu/actions/record-agnostic-actions/run-workflow-actions/hooks/useRunWorkflowActions';
|
||||
|
||||
export const RunWorkflowRecordAgnosticActionMenuEntriesSetter = () => {
|
||||
const { runWorkflowActions } = useRunWorkflowActions();
|
||||
|
||||
return (
|
||||
<>
|
||||
{runWorkflowActions.map((action) => (
|
||||
<RegisterAgnosticActionEffect key={action.key} action={action} />
|
||||
))}
|
||||
</>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,34 @@
|
||||
import { Action } from '@/action-menu/actions/components/Action';
|
||||
import { ActionType } from '@/action-menu/actions/types/ActionType';
|
||||
import { ActionConfigContext } from '@/action-menu/contexts/ActionConfigContext';
|
||||
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
|
||||
import { commandMenuSearchState } from '@/command-menu/states/commandMenuSearchState';
|
||||
import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages';
|
||||
import { useContext } from 'react';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
import { IconSearch } from 'twenty-ui/display';
|
||||
|
||||
export const SearchRecordsRecordAgnosticAction = () => {
|
||||
const { navigateCommandMenu } = useCommandMenu();
|
||||
|
||||
const setCommandMenuSearchState = useSetRecoilState(commandMenuSearchState);
|
||||
|
||||
const actionConfig = useContext(ActionConfigContext);
|
||||
|
||||
return (
|
||||
<Action
|
||||
onClick={() => {
|
||||
navigateCommandMenu({
|
||||
page: CommandMenuPages.SearchRecords,
|
||||
pageTitle: 'Search',
|
||||
pageIcon: IconSearch,
|
||||
});
|
||||
|
||||
if (actionConfig?.type !== ActionType.Fallback) {
|
||||
setCommandMenuSearchState('');
|
||||
}
|
||||
}}
|
||||
preventCommandMenuClosing
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -1,21 +1,16 @@
|
||||
import { useSearchRecordsRecordAgnosticAction } from '@/action-menu/actions/record-agnostic-actions/hooks/useSearchRecordsRecordAgnosticAction';
|
||||
import { SearchRecordsRecordAgnosticAction } from '@/action-menu/actions/record-agnostic-actions/components/SearchRecordsRecordAgnosticAction';
|
||||
import { RecordAgnosticActionsKeys } from '@/action-menu/actions/record-agnostic-actions/types/RecordAgnosticActionsKeys';
|
||||
import { ActionConfig } from '@/action-menu/actions/types/ActionConfig';
|
||||
import { ActionScope } from '@/action-menu/actions/types/ActionScope';
|
||||
import { ActionType } from '@/action-menu/actions/types/ActionType';
|
||||
import { ActionViewType } from '@/action-menu/actions/types/ActionViewType';
|
||||
import { RecordAgnosticConfigAction } from '@/action-menu/actions/types/RecordAgnosticConfigAction';
|
||||
import {
|
||||
ActionMenuEntryScope,
|
||||
ActionMenuEntryType,
|
||||
} from '@/action-menu/types/ActionMenuEntry';
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { IconSearch } from 'twenty-ui/display';
|
||||
|
||||
export const RECORD_AGNOSTIC_ACTIONS_CONFIG: Record<
|
||||
string,
|
||||
RecordAgnosticConfigAction
|
||||
> = {
|
||||
export const RECORD_AGNOSTIC_ACTIONS_CONFIG: Record<string, ActionConfig> = {
|
||||
[RecordAgnosticActionsKeys.SEARCH_RECORDS]: {
|
||||
type: ActionMenuEntryType.Standard,
|
||||
scope: ActionMenuEntryScope.Global,
|
||||
type: ActionType.Standard,
|
||||
scope: ActionScope.Global,
|
||||
key: RecordAgnosticActionsKeys.SEARCH_RECORDS,
|
||||
label: msg`Search records`,
|
||||
shortLabel: msg`Search`,
|
||||
@ -23,13 +18,13 @@ export const RECORD_AGNOSTIC_ACTIONS_CONFIG: Record<
|
||||
isPinned: false,
|
||||
Icon: IconSearch,
|
||||
availableOn: [ActionViewType.GLOBAL],
|
||||
useAction: useSearchRecordsRecordAgnosticAction,
|
||||
component: <SearchRecordsRecordAgnosticAction />,
|
||||
hotKeys: ['/'],
|
||||
shouldBeRegistered: () => true,
|
||||
},
|
||||
[RecordAgnosticActionsKeys.SEARCH_RECORDS_FALLBACK]: {
|
||||
type: ActionMenuEntryType.Fallback,
|
||||
scope: ActionMenuEntryScope.Global,
|
||||
type: ActionType.Fallback,
|
||||
scope: ActionScope.Global,
|
||||
key: RecordAgnosticActionsKeys.SEARCH_RECORDS_FALLBACK,
|
||||
label: msg`Search records`,
|
||||
shortLabel: msg`Search`,
|
||||
@ -37,7 +32,7 @@ export const RECORD_AGNOSTIC_ACTIONS_CONFIG: Record<
|
||||
isPinned: false,
|
||||
Icon: IconSearch,
|
||||
availableOn: [ActionViewType.GLOBAL],
|
||||
useAction: useSearchRecordsRecordAgnosticAction,
|
||||
component: <SearchRecordsRecordAgnosticAction />,
|
||||
hotKeys: ['/'],
|
||||
shouldBeRegistered: () => true,
|
||||
},
|
||||
@ -1,19 +0,0 @@
|
||||
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
|
||||
import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages';
|
||||
import { IconSearch } from 'twenty-ui/display';
|
||||
|
||||
export const useSearchRecordsRecordAgnosticAction = () => {
|
||||
const { navigateCommandMenu } = useCommandMenu();
|
||||
|
||||
const onClick = () => {
|
||||
navigateCommandMenu({
|
||||
page: CommandMenuPages.SearchRecords,
|
||||
pageTitle: 'Search',
|
||||
pageIcon: IconSearch,
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
onClick,
|
||||
};
|
||||
};
|
||||
@ -1,14 +1,12 @@
|
||||
import {
|
||||
ActionMenuEntryScope,
|
||||
ActionMenuEntryType,
|
||||
} from '@/action-menu/types/ActionMenuEntry';
|
||||
import { ActionScope } from '@/action-menu/actions/types/ActionScope';
|
||||
import { ActionType } from '@/action-menu/actions/types/ActionType';
|
||||
import { useActiveWorkflowVersionsWithManualTrigger } from '@/workflow/hooks/useActiveWorkflowVersionsWithManualTrigger';
|
||||
import { useRunWorkflowVersion } from '@/workflow/hooks/useRunWorkflowVersion';
|
||||
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { capitalize, isDefined } from 'twenty-shared/utils';
|
||||
import { FeatureFlagKey } from '~/generated/graphql';
|
||||
import { IconSettingsAutomation } from 'twenty-ui/display';
|
||||
import { FeatureFlagKey } from '~/generated/graphql';
|
||||
|
||||
export const useRunWorkflowActions = () => {
|
||||
const isWorkflowEnabled = useIsFeatureEnabled(
|
||||
@ -33,9 +31,9 @@ export const useRunWorkflowActions = () => {
|
||||
const name = capitalize(activeWorkflowVersion.workflow.name);
|
||||
|
||||
return {
|
||||
type: ActionMenuEntryType.WorkflowRun,
|
||||
type: ActionType.WorkflowRun,
|
||||
key: `workflow-run-${activeWorkflowVersion.id}`,
|
||||
scope: ActionMenuEntryScope.Global,
|
||||
scope: ActionScope.Global,
|
||||
label: msg`${name}`,
|
||||
position: index,
|
||||
Icon: IconSettingsAutomation,
|
||||
|
||||
@ -1,26 +1,24 @@
|
||||
import { ActionHook } from '@/action-menu/actions/types/ActionHook';
|
||||
import { ActionScope } from '@/action-menu/actions/types/ActionScope';
|
||||
import { ActionType } from '@/action-menu/actions/types/ActionType';
|
||||
import { ActionViewType } from '@/action-menu/actions/types/ActionViewType';
|
||||
import { ShouldBeRegisteredFunctionParams } from '@/action-menu/actions/types/ShouldBeRegisteredFunctionParams';
|
||||
import {
|
||||
ActionMenuEntryScope,
|
||||
ActionMenuEntryType,
|
||||
} from '@/action-menu/types/ActionMenuEntry';
|
||||
import { MessageDescriptor } from '@lingui/core';
|
||||
import { IconComponent } from 'twenty-ui/display';
|
||||
import { MenuItemAccent } from 'twenty-ui/navigation';
|
||||
|
||||
export type RecordConfigAction = {
|
||||
type: ActionMenuEntryType;
|
||||
scope: ActionMenuEntryScope;
|
||||
export type ActionConfig = {
|
||||
type: ActionType;
|
||||
scope: ActionScope;
|
||||
key: string;
|
||||
label: MessageDescriptor;
|
||||
shortLabel?: MessageDescriptor;
|
||||
label: MessageDescriptor | string;
|
||||
shortLabel?: MessageDescriptor | string;
|
||||
description?: MessageDescriptor | string;
|
||||
position: number;
|
||||
Icon: IconComponent;
|
||||
isPinned?: boolean;
|
||||
accent?: MenuItemAccent;
|
||||
availableOn?: ActionViewType[];
|
||||
shouldBeRegistered: (params: ShouldBeRegisteredFunctionParams) => boolean;
|
||||
useAction: ActionHook;
|
||||
component: React.ReactNode;
|
||||
hotKeys?: string[];
|
||||
};
|
||||
@ -1,15 +0,0 @@
|
||||
import { ActionHookResult } from '@/action-menu/actions/types/ActionHookResult';
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
|
||||
export type ActionHook =
|
||||
| ActionHookWithoutObjectMetadataItem
|
||||
| ActionHookWithObjectMetadataItem;
|
||||
|
||||
export type ActionHookWithoutObjectMetadataItem = () => ActionHookResult;
|
||||
|
||||
type ActionHookWithObjectMetadataItemParams = {
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
};
|
||||
export type ActionHookWithObjectMetadataItem = (
|
||||
params: ActionHookWithObjectMetadataItemParams,
|
||||
) => ActionHookResult;
|
||||
@ -1,6 +0,0 @@
|
||||
import { ConfirmationModalProps } from '@/ui/layout/modal/components/ConfirmationModal';
|
||||
|
||||
export type ActionHookResult = {
|
||||
onClick: () => Promise<void> | void;
|
||||
ConfirmationModal?: React.ReactElement<ConfirmationModalProps>;
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user