diff --git a/packages/twenty-client-sdk/src/metadata/generated/schema.graphql b/packages/twenty-client-sdk/src/metadata/generated/schema.graphql index 691054d149..69e5ca36a2 100644 --- a/packages/twenty-client-sdk/src/metadata/generated/schema.graphql +++ b/packages/twenty-client-sdk/src/metadata/generated/schema.graphql @@ -3287,6 +3287,7 @@ type Mutation { updateOneLogicFunction(input: UpdateLogicFunctionFromSourceInput!): Boolean! createCommandMenuItem(input: CreateCommandMenuItemInput!): CommandMenuItem! updateCommandMenuItem(input: UpdateCommandMenuItemInput!): CommandMenuItem! + resetCommandMenuItem(id: UUID!): CommandMenuItem! deleteCommandMenuItem(id: UUID!): CommandMenuItem! createFrontComponent(input: CreateFrontComponentInput!): FrontComponent! updateFrontComponent(input: UpdateFrontComponentInput!): FrontComponent! diff --git a/packages/twenty-client-sdk/src/metadata/generated/schema.ts b/packages/twenty-client-sdk/src/metadata/generated/schema.ts index 62adcd6131..d74f5db993 100644 --- a/packages/twenty-client-sdk/src/metadata/generated/schema.ts +++ b/packages/twenty-client-sdk/src/metadata/generated/schema.ts @@ -2809,6 +2809,7 @@ export interface Mutation { updateOneLogicFunction: Scalars['Boolean'] createCommandMenuItem: CommandMenuItem updateCommandMenuItem: CommandMenuItem + resetCommandMenuItem: CommandMenuItem deleteCommandMenuItem: CommandMenuItem createFrontComponent: FrontComponent updateFrontComponent: FrontComponent @@ -5952,6 +5953,7 @@ export interface MutationGenqlSelection{ updateOneLogicFunction?: { __args: {input: UpdateLogicFunctionFromSourceInput} } createCommandMenuItem?: (CommandMenuItemGenqlSelection & { __args: {input: CreateCommandMenuItemInput} }) updateCommandMenuItem?: (CommandMenuItemGenqlSelection & { __args: {input: UpdateCommandMenuItemInput} }) + resetCommandMenuItem?: (CommandMenuItemGenqlSelection & { __args: {id: Scalars['UUID']} }) deleteCommandMenuItem?: (CommandMenuItemGenqlSelection & { __args: {id: Scalars['UUID']} }) createFrontComponent?: (FrontComponentGenqlSelection & { __args: {input: CreateFrontComponentInput} }) updateFrontComponent?: (FrontComponentGenqlSelection & { __args: {input: UpdateFrontComponentInput} }) diff --git a/packages/twenty-client-sdk/src/metadata/generated/types.ts b/packages/twenty-client-sdk/src/metadata/generated/types.ts index 9c5faf4403..5a8138972e 100644 --- a/packages/twenty-client-sdk/src/metadata/generated/types.ts +++ b/packages/twenty-client-sdk/src/metadata/generated/types.ts @@ -7585,6 +7585,15 @@ export default { ] } ], + "resetCommandMenuItem": [ + 35, + { + "id": [ + 3, + "UUID!" + ] + } + ], "deleteCommandMenuItem": [ 35, { diff --git a/packages/twenty-front/src/generated-metadata/graphql.ts b/packages/twenty-front/src/generated-metadata/graphql.ts index b4fb17a2f2..d876fbeb76 100644 --- a/packages/twenty-front/src/generated-metadata/graphql.ts +++ b/packages/twenty-front/src/generated-metadata/graphql.ts @@ -2549,6 +2549,7 @@ export type Mutation = { renewToken: AuthTokens; resendEmailVerificationToken: ResendEmailVerificationToken; resendWorkspaceInvitation: SendInvitations; + resetCommandMenuItem: CommandMenuItem; resetPageLayoutTabToDefault: PageLayoutTab; resetPageLayoutToDefault: PageLayout; resetPageLayoutWidgetToDefault: PageLayoutWidget; @@ -3240,6 +3241,11 @@ export type MutationResendWorkspaceInvitationArgs = { }; +export type MutationResetCommandMenuItemArgs = { + id: Scalars['UUID']['input']; +}; + + export type MutationResetPageLayoutTabToDefaultArgs = { id: Scalars['String']['input']; }; @@ -6750,6 +6756,16 @@ export type CommandMenuItemFieldsFragment = { __typename?: 'CommandMenuItem', id | { __typename?: 'PathCommandMenuItemPayload', path: string } | null }; +export type ResetCommandMenuItemMutationVariables = Exact<{ + id: Scalars['UUID']['input']; +}>; + + +export type ResetCommandMenuItemMutation = { __typename?: 'Mutation', resetCommandMenuItem: { __typename?: 'CommandMenuItem', id: string, applicationId?: string | null, workflowVersionId?: string | null, frontComponentId?: string | null, engineComponentKey: EngineComponentKey, label: string, icon?: string | null, shortLabel?: string | null, position: number, isPinned: boolean, hotKeys?: Array | null, conditionalAvailabilityExpression?: string | null, availabilityType: CommandMenuItemAvailabilityType, availabilityObjectMetadataId?: string | null, pageLayoutId?: string | null, isActive: boolean, frontComponent?: { __typename?: 'FrontComponent', id: string, name: string, isHeadless: boolean } | null, payload?: + | { __typename?: 'ObjectMetadataCommandMenuItemPayload', objectMetadataItemId: string } + | { __typename?: 'PathCommandMenuItemPayload', path: string } + | null } }; + export type UpdateCommandMenuItemMutationVariables = Exact<{ input: UpdateCommandMenuItemInput; }>; @@ -8616,6 +8632,7 @@ export const CheckUserExistsDocument = {"kind":"Document","definitions":[{"kind" export const FindApplicationRegistrationByClientIdDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"FindApplicationRegistrationByClientId"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"clientId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"findApplicationRegistrationByClientId"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"clientId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"clientId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"logoUrl"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"oAuthScopes"}},{"kind":"Field","name":{"kind":"Name","value":"websiteUrl"}}]}}]}}]} as unknown as DocumentNode; export const GetPublicWorkspaceDataByDomainDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetPublicWorkspaceDataByDomain"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"origin"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"getPublicWorkspaceDataByDomain"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"origin"},"value":{"kind":"Variable","name":{"kind":"Name","value":"origin"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}},{"kind":"Field","name":{"kind":"Name","value":"displayName"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceUrls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceUrlsFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"authProviders"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"sso"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"issuer"}}]}},{"kind":"Field","name":{"kind":"Name","value":"google"}},{"kind":"Field","name":{"kind":"Name","value":"magicLink"}},{"kind":"Field","name":{"kind":"Name","value":"password"}},{"kind":"Field","name":{"kind":"Name","value":"microsoft"}}]}},{"kind":"Field","name":{"kind":"Name","value":"authBypassProviders"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"google"}},{"kind":"Field","name":{"kind":"Name","value":"password"}},{"kind":"Field","name":{"kind":"Name","value":"microsoft"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceUrlsFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkspaceUrls"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"subdomainUrl"}},{"kind":"Field","name":{"kind":"Name","value":"customUrl"}}]}}]} as unknown as DocumentNode; export const ValidatePasswordResetTokenDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ValidatePasswordResetToken"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"token"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"validatePasswordResetToken"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"passwordResetToken"},"value":{"kind":"Variable","name":{"kind":"Name","value":"token"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"hasPassword"}}]}}]}}]} as unknown as DocumentNode; +export const ResetCommandMenuItemDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"ResetCommandMenuItem"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"resetCommandMenuItem"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CommandMenuItemFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CommandMenuItemFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"CommandMenuItem"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"applicationId"}},{"kind":"Field","name":{"kind":"Name","value":"workflowVersionId"}},{"kind":"Field","name":{"kind":"Name","value":"frontComponentId"}},{"kind":"Field","name":{"kind":"Name","value":"frontComponent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"isHeadless"}}]}},{"kind":"Field","name":{"kind":"Name","value":"engineComponentKey"}},{"kind":"Field","name":{"kind":"Name","value":"label"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"shortLabel"}},{"kind":"Field","name":{"kind":"Name","value":"position"}},{"kind":"Field","name":{"kind":"Name","value":"isPinned"}},{"kind":"Field","name":{"kind":"Name","value":"payload"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PathCommandMenuItemPayload"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"path"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ObjectMetadataCommandMenuItemPayload"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"objectMetadataItemId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"hotKeys"}},{"kind":"Field","name":{"kind":"Name","value":"conditionalAvailabilityExpression"}},{"kind":"Field","name":{"kind":"Name","value":"availabilityType"}},{"kind":"Field","name":{"kind":"Name","value":"availabilityObjectMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"pageLayoutId"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}}]}}]} as unknown as DocumentNode; export const UpdateCommandMenuItemDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateCommandMenuItem"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UpdateCommandMenuItemInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateCommandMenuItem"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CommandMenuItemFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CommandMenuItemFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"CommandMenuItem"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"applicationId"}},{"kind":"Field","name":{"kind":"Name","value":"workflowVersionId"}},{"kind":"Field","name":{"kind":"Name","value":"frontComponentId"}},{"kind":"Field","name":{"kind":"Name","value":"frontComponent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"isHeadless"}}]}},{"kind":"Field","name":{"kind":"Name","value":"engineComponentKey"}},{"kind":"Field","name":{"kind":"Name","value":"label"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"shortLabel"}},{"kind":"Field","name":{"kind":"Name","value":"position"}},{"kind":"Field","name":{"kind":"Name","value":"isPinned"}},{"kind":"Field","name":{"kind":"Name","value":"payload"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PathCommandMenuItemPayload"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"path"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ObjectMetadataCommandMenuItemPayload"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"objectMetadataItemId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"hotKeys"}},{"kind":"Field","name":{"kind":"Name","value":"conditionalAvailabilityExpression"}},{"kind":"Field","name":{"kind":"Name","value":"availabilityType"}},{"kind":"Field","name":{"kind":"Name","value":"availabilityObjectMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"pageLayoutId"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}}]}}]} as unknown as DocumentNode; export const FindManyCommandMenuItemsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"FindManyCommandMenuItems"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"commandMenuItems"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CommandMenuItemFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CommandMenuItemFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"CommandMenuItem"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"applicationId"}},{"kind":"Field","name":{"kind":"Name","value":"workflowVersionId"}},{"kind":"Field","name":{"kind":"Name","value":"frontComponentId"}},{"kind":"Field","name":{"kind":"Name","value":"frontComponent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"isHeadless"}}]}},{"kind":"Field","name":{"kind":"Name","value":"engineComponentKey"}},{"kind":"Field","name":{"kind":"Name","value":"label"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"shortLabel"}},{"kind":"Field","name":{"kind":"Name","value":"position"}},{"kind":"Field","name":{"kind":"Name","value":"isPinned"}},{"kind":"Field","name":{"kind":"Name","value":"payload"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PathCommandMenuItemPayload"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"path"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ObjectMetadataCommandMenuItemPayload"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"objectMetadataItemId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"hotKeys"}},{"kind":"Field","name":{"kind":"Name","value":"conditionalAvailabilityExpression"}},{"kind":"Field","name":{"kind":"Name","value":"availabilityType"}},{"kind":"Field","name":{"kind":"Name","value":"availabilityObjectMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"pageLayoutId"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}}]}}]} as unknown as DocumentNode; export const DuplicateDashboardDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DuplicateDashboard"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"duplicateDashboard"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"pageLayoutId"}},{"kind":"Field","name":{"kind":"Name","value":"position"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]}}]} as unknown as DocumentNode; diff --git a/packages/twenty-front/src/modules/command-menu-item/edit/components/CommandMenuItemOptionsDropdown.tsx b/packages/twenty-front/src/modules/command-menu-item/edit/components/CommandMenuItemOptionsDropdown.tsx index 39002fbbfa..61a0814571 100644 --- a/packages/twenty-front/src/modules/command-menu-item/edit/components/CommandMenuItemOptionsDropdown.tsx +++ b/packages/twenty-front/src/modules/command-menu-item/edit/components/CommandMenuItemOptionsDropdown.tsx @@ -1,3 +1,4 @@ +import { useResetCommandMenuItemToDefault } from '@/command-menu-item/edit/hooks/useResetCommandMenuItemToDefault'; import { useUpdateCommandMenuItemInDraft } from '@/command-menu-item/edit/hooks/useUpdateCommandMenuItemInDraft'; import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent'; @@ -34,14 +35,13 @@ export const CommandMenuItemOptionsDropdown = ({ const dropdownId = getCommandMenuItemOptionsDropdownId(itemId); const { closeDropdown } = useCloseDropdown(); const { updateCommandMenuItemInDraft } = useUpdateCommandMenuItemInDraft(); + const { resetCommandMenuItemToDefault } = useResetCommandMenuItemToDefault(); const normalizedServerShortLabel = serverShortLabel ?? null; const normalizedShortLabel = shortLabel ?? null; const hasNoShortLabel = normalizedServerShortLabel === null; const isLabelHidden = normalizedShortLabel === null && isDefined(normalizedServerShortLabel); - const hasShortLabelOverride = - normalizedShortLabel !== normalizedServerShortLabel; const handleToggleHideLabel = (toggled: boolean) => { updateCommandMenuItemInDraft(itemId, { @@ -49,11 +49,9 @@ export const CommandMenuItemOptionsDropdown = ({ }); }; - const handleResetLabelToDefault = () => { - updateCommandMenuItemInDraft(itemId, { - shortLabel: normalizedServerShortLabel, - }); + const handleResetToDefault = async () => { closeDropdown(dropdownId); + await resetCommandMenuItemToDefault(itemId); }; return ( @@ -74,10 +72,9 @@ export const CommandMenuItemOptionsDropdown = ({ /> diff --git a/packages/twenty-front/src/modules/command-menu-item/edit/components/SidePanelCommandMenuItemEditPage.tsx b/packages/twenty-front/src/modules/command-menu-item/edit/components/SidePanelCommandMenuItemEditPage.tsx index a091b6150e..5f733c6c25 100644 --- a/packages/twenty-front/src/modules/command-menu-item/edit/components/SidePanelCommandMenuItemEditPage.tsx +++ b/packages/twenty-front/src/modules/command-menu-item/edit/components/SidePanelCommandMenuItemEditPage.tsx @@ -2,7 +2,6 @@ import { CommandMenuItemEditRecordSelectionDropdown } from '@/command-menu-item/ import { CommandMenuItemOptionsDropdown } from '@/command-menu-item/edit/components/CommandMenuItemOptionsDropdown'; import { useEditableCommandMenuItems } from '@/command-menu-item/edit/hooks/useEditableCommandMenuItems'; import { useReorderCommandMenuItemsInDraft } from '@/command-menu-item/edit/hooks/useReorderCommandMenuItemsInDraft'; -import { useResetCommandMenuItemsDraft } from '@/command-menu-item/edit/hooks/useResetCommandMenuItemsDraft'; import { useUpdateCommandMenuItemInDraft } from '@/command-menu-item/edit/hooks/useUpdateCommandMenuItemInDraft'; import { useCurrentCommandMenuContextApi } from '@/command-menu-item/hooks/useCurrentCommandMenuContextApi'; import { commandMenuItemsSelector } from '@/command-menu-item/states/commandMenuItemsSelector'; @@ -14,7 +13,6 @@ import { sidePanelSearchState } from '@/side-panel/states/sidePanelSearchState'; import { DraggableItem } from '@/ui/layout/draggable-list/components/DraggableItem'; import { DraggableList } from '@/ui/layout/draggable-list/components/DraggableList'; import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; -import { SidePanelFooter } from '@/ui/layout/side-panel/components/SidePanelFooter'; import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue'; import { type DropResult } from '@hello-pangea/dnd'; import { styled } from '@linaria/react'; @@ -28,10 +26,8 @@ import { IconDotsVertical, IconPin, IconPinnedOff, - IconRefresh, useIcons, } from 'twenty-ui-deprecated/display'; -import { Button } from 'twenty-ui-deprecated/input'; import { MenuItem, MenuItemDraggable } from 'twenty-ui-deprecated/navigation'; import { themeCssVariables } from 'twenty-ui-deprecated/theme-constants'; import { type CommandMenuItemFieldsFragment } from '~/generated-metadata/graphql'; @@ -79,7 +75,6 @@ export const SidePanelCommandMenuItemEditPage = () => { ); const { updateCommandMenuItemInDraft } = useUpdateCommandMenuItemInDraft(); const { reorderCommandMenuItemInDraft } = useReorderCommandMenuItemsInDraft(); - const { resetCommandMenuItemsDraft } = useResetCommandMenuItemsDraft(); const editableCommandMenuItems = useEditableCommandMenuItems(); @@ -298,19 +293,6 @@ export const SidePanelCommandMenuItemEditPage = () => { - , - ]} - /> ); }; diff --git a/packages/twenty-front/src/modules/command-menu-item/edit/hooks/useResetCommandMenuItemToDefault.ts b/packages/twenty-front/src/modules/command-menu-item/edit/hooks/useResetCommandMenuItemToDefault.ts new file mode 100644 index 0000000000..1e52ef86d8 --- /dev/null +++ b/packages/twenty-front/src/modules/command-menu-item/edit/hooks/useResetCommandMenuItemToDefault.ts @@ -0,0 +1,75 @@ +import { CombinedGraphQLErrors } from '@apollo/client/errors'; +import { useMutation } from '@apollo/client/react'; +import { t } from '@lingui/core/macro'; +import { useStore } from 'jotai'; +import { useCallback } from 'react'; +import { CrudOperationType } from 'twenty-shared/types'; +import { isDefined } from 'twenty-shared/utils'; + +import { commandMenuItemsDraftState } from '@/command-menu-item/edit/states/commandMenuItemsDraftState'; +import { RESET_COMMAND_MENU_ITEM } from '@/command-menu-item/graphql/mutations/resetCommandMenuItem'; +import { useMetadataErrorHandler } from '@/metadata-error-handler/hooks/useMetadataErrorHandler'; +import { useUpdateMetadataStoreDraft } from '@/metadata-store/hooks/useUpdateMetadataStoreDraft'; +import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; +import { + type ResetCommandMenuItemMutation, + type ResetCommandMenuItemMutationVariables, +} from '~/generated-metadata/graphql'; + +export const useResetCommandMenuItemToDefault = () => { + const store = useStore(); + const [resetCommandMenuItem] = useMutation< + ResetCommandMenuItemMutation, + ResetCommandMenuItemMutationVariables + >(RESET_COMMAND_MENU_ITEM); + const { updateInDraft, applyChanges } = useUpdateMetadataStoreDraft(); + const { handleMetadataError } = useMetadataErrorHandler(); + const { enqueueErrorSnackBar } = useSnackBar(); + + const resetCommandMenuItemToDefault = useCallback( + async (id: string) => { + try { + const result = await resetCommandMenuItem({ variables: { id } }); + + const resetItem = result.data?.resetCommandMenuItem; + + if (!isDefined(resetItem)) { + return; + } + + updateInDraft('commandMenuItems', [resetItem]); + applyChanges(); + + const draft = store.get(commandMenuItemsDraftState.atom); + + if (isDefined(draft)) { + store.set( + commandMenuItemsDraftState.atom, + draft.map((item) => + item.id === id ? { ...item, ...resetItem } : item, + ), + ); + } + } catch (error) { + if (CombinedGraphQLErrors.is(error)) { + handleMetadataError(error, { + primaryMetadataName: 'commandMenuItem', + operationType: CrudOperationType.UPDATE, + }); + } else { + enqueueErrorSnackBar({ message: t`An error occurred.` }); + } + } + }, + [ + resetCommandMenuItem, + updateInDraft, + applyChanges, + store, + handleMetadataError, + enqueueErrorSnackBar, + ], + ); + + return { resetCommandMenuItemToDefault }; +}; diff --git a/packages/twenty-front/src/modules/command-menu-item/edit/hooks/useResetCommandMenuItemsDraft.ts b/packages/twenty-front/src/modules/command-menu-item/edit/hooks/useResetCommandMenuItemsDraft.ts deleted file mode 100644 index f92da8b35f..0000000000 --- a/packages/twenty-front/src/modules/command-menu-item/edit/hooks/useResetCommandMenuItemsDraft.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { useCallback } from 'react'; -import { useStore } from 'jotai'; - -import { commandMenuItemsDraftState } from '@/command-menu-item/edit/states/commandMenuItemsDraftState'; -import { commandMenuItemsSelector } from '@/command-menu-item/states/commandMenuItemsSelector'; - -// Resets the draft to the current server state, discarding all user edits. -export const useResetCommandMenuItemsDraft = () => { - const store = useStore(); - - const resetCommandMenuItemsDraft = useCallback(() => { - const serverItems = store.get(commandMenuItemsSelector.atom); - - store.set( - commandMenuItemsDraftState.atom, - serverItems.map((item) => ({ ...item })), - ); - }, [store]); - - return { resetCommandMenuItemsDraft }; -}; diff --git a/packages/twenty-front/src/modules/command-menu-item/edit/hooks/useSaveCommandMenuItemsDraft.ts b/packages/twenty-front/src/modules/command-menu-item/edit/hooks/useSaveCommandMenuItemsDraft.ts index 107642c230..857585366e 100644 --- a/packages/twenty-front/src/modules/command-menu-item/edit/hooks/useSaveCommandMenuItemsDraft.ts +++ b/packages/twenty-front/src/modules/command-menu-item/edit/hooks/useSaveCommandMenuItemsDraft.ts @@ -6,13 +6,22 @@ import { isDefined } from 'twenty-shared/utils'; import { commandMenuItemsDraftState } from '@/command-menu-item/edit/states/commandMenuItemsDraftState'; import { UPDATE_COMMAND_MENU_ITEM } from '@/command-menu-item/graphql/mutations/updateCommandMenuItem'; import { commandMenuItemsSelector } from '@/command-menu-item/states/commandMenuItemsSelector'; +import { useUpdateMetadataStoreDraft } from '@/metadata-store/hooks/useUpdateMetadataStoreDraft'; import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue'; -import { type UpdateCommandMenuItemInput } from '~/generated-metadata/graphql'; +import { + type UpdateCommandMenuItemInput, + type UpdateCommandMenuItemMutation, + type UpdateCommandMenuItemMutationVariables, +} from '~/generated-metadata/graphql'; export const useSaveCommandMenuItemsDraft = () => { const store = useStore(); - const [updateCommandMenuItem] = useMutation(UPDATE_COMMAND_MENU_ITEM); + const [updateCommandMenuItem] = useMutation< + UpdateCommandMenuItemMutation, + UpdateCommandMenuItemMutationVariables + >(UPDATE_COMMAND_MENU_ITEM); const commandMenuItems = useAtomStateValue(commandMenuItemsSelector); + const { updateInDraft, applyChanges } = useUpdateMetadataStoreDraft(); const saveCommandMenuItemsDraft = useCallback(async () => { const draft = store.get(commandMenuItemsDraftState.atom); @@ -39,6 +48,10 @@ export const useSaveCommandMenuItemsDraft = () => { ); }); + if (changedItems.length === 0) { + return; + } + await Promise.all( changedItems.map((item) => { const input: UpdateCommandMenuItemInput = { @@ -51,7 +64,16 @@ export const useSaveCommandMenuItemsDraft = () => { return updateCommandMenuItem({ variables: { input } }); }), ); - }, [store, commandMenuItems, updateCommandMenuItem]); + + updateInDraft('commandMenuItems', changedItems); + applyChanges(); + }, [ + store, + commandMenuItems, + updateCommandMenuItem, + updateInDraft, + applyChanges, + ]); return { saveCommandMenuItemsDraft }; }; diff --git a/packages/twenty-front/src/modules/command-menu-item/graphql/mutations/resetCommandMenuItem.ts b/packages/twenty-front/src/modules/command-menu-item/graphql/mutations/resetCommandMenuItem.ts new file mode 100644 index 0000000000..d47e785f8f --- /dev/null +++ b/packages/twenty-front/src/modules/command-menu-item/graphql/mutations/resetCommandMenuItem.ts @@ -0,0 +1,11 @@ +import { COMMAND_MENU_ITEM_FRAGMENT } from '@/command-menu-item/graphql/fragments/commandMenuItemFragment'; +import { gql } from '@apollo/client'; + +export const RESET_COMMAND_MENU_ITEM = gql` + ${COMMAND_MENU_ITEM_FRAGMENT} + mutation ResetCommandMenuItem($id: UUID!) { + resetCommandMenuItem(id: $id) { + ...CommandMenuItemFields + } + } +`; diff --git a/packages/twenty-server/src/engine/metadata-modules/command-menu-item/command-menu-item.exception.ts b/packages/twenty-server/src/engine/metadata-modules/command-menu-item/command-menu-item.exception.ts index c5b833f60a..1a74d7b383 100644 --- a/packages/twenty-server/src/engine/metadata-modules/command-menu-item/command-menu-item.exception.ts +++ b/packages/twenty-server/src/engine/metadata-modules/command-menu-item/command-menu-item.exception.ts @@ -8,6 +8,7 @@ export enum CommandMenuItemExceptionCode { COMMAND_MENU_ITEM_NOT_FOUND = 'COMMAND_MENU_ITEM_NOT_FOUND', INVALID_COMMAND_MENU_ITEM_INPUT = 'INVALID_COMMAND_MENU_ITEM_INPUT', WORKFLOW_OR_FRONT_COMPONENT_REQUIRED = 'WORKFLOW_OR_FRONT_COMPONENT_REQUIRED', + COMMAND_MENU_ITEM_CANNOT_BE_RESET = 'COMMAND_MENU_ITEM_CANNOT_BE_RESET', } const getCommandMenuItemExceptionUserFriendlyMessage = ( @@ -20,6 +21,8 @@ const getCommandMenuItemExceptionUserFriendlyMessage = ( return msg`Invalid command menu item input.`; case CommandMenuItemExceptionCode.WORKFLOW_OR_FRONT_COMPONENT_REQUIRED: return msg`Either workflow version or front component is required.`; + case CommandMenuItemExceptionCode.COMMAND_MENU_ITEM_CANNOT_BE_RESET: + return msg`Custom command menu item cannot be reset to default.`; default: assertUnreachable(code); } diff --git a/packages/twenty-server/src/engine/metadata-modules/command-menu-item/command-menu-item.resolver.ts b/packages/twenty-server/src/engine/metadata-modules/command-menu-item/command-menu-item.resolver.ts index 4d39e9a9bc..034561f7db 100644 --- a/packages/twenty-server/src/engine/metadata-modules/command-menu-item/command-menu-item.resolver.ts +++ b/packages/twenty-server/src/engine/metadata-modules/command-menu-item/command-menu-item.resolver.ts @@ -136,6 +136,15 @@ export class CommandMenuItemResolver { return await this.commandMenuItemService.update(input, workspace.id); } + @Mutation(() => CommandMenuItemDTO) + @UseGuards(NoPermissionGuard) + async resetCommandMenuItem( + @Args('id', { type: () => UUIDScalarType }) id: string, + @AuthWorkspace() workspace: WorkspaceEntity, + ): Promise { + return await this.commandMenuItemService.reset(id, workspace.id); + } + @Mutation(() => CommandMenuItemDTO) @UseGuards(NoPermissionGuard) async deleteCommandMenuItem( diff --git a/packages/twenty-server/src/engine/metadata-modules/command-menu-item/command-menu-item.service.ts b/packages/twenty-server/src/engine/metadata-modules/command-menu-item/command-menu-item.service.ts index 283401f7da..01ab54e29a 100644 --- a/packages/twenty-server/src/engine/metadata-modules/command-menu-item/command-menu-item.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/command-menu-item/command-menu-item.service.ts @@ -245,6 +245,90 @@ export class CommandMenuItemService { ); } + async reset(id: string, workspaceId: string): Promise { + const { workspaceCustomFlatApplication } = + await this.applicationService.findWorkspaceTwentyStandardAndCustomApplicationOrThrow( + { workspaceId }, + ); + + const { flatCommandMenuItemMaps: existingFlatCommandMenuItemMaps } = + await this.workspaceManyOrAllFlatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps( + { + workspaceId, + flatMapsKeys: ['flatCommandMenuItemMaps'], + }, + ); + + const existingFlatCommandMenuItem = findFlatEntityByIdInFlatEntityMaps({ + flatEntityId: id, + flatEntityMaps: existingFlatCommandMenuItemMaps, + }); + + if (!isDefined(existingFlatCommandMenuItem)) { + throw new CommandMenuItemException( + 'Command menu item not found', + CommandMenuItemExceptionCode.COMMAND_MENU_ITEM_NOT_FOUND, + ); + } + + if ( + existingFlatCommandMenuItem.applicationUniversalIdentifier === + workspaceCustomFlatApplication.universalIdentifier + ) { + throw new CommandMenuItemException( + 'Custom command menu item cannot be reset to default', + CommandMenuItemExceptionCode.COMMAND_MENU_ITEM_CANNOT_BE_RESET, + ); + } + + const flatCommandMenuItemToUpdate: FlatCommandMenuItem = { + ...existingFlatCommandMenuItem, + isActive: true, + overrides: null, + universalOverrides: null, + updatedAt: new Date().toISOString(), + }; + + const validateAndBuildResult = + await this.workspaceMigrationValidateBuildAndRunService.validateBuildAndRunWorkspaceMigration( + { + allFlatEntityOperationByMetadataName: { + commandMenuItem: { + flatEntityToCreate: [], + flatEntityToDelete: [], + flatEntityToUpdate: [flatCommandMenuItemToUpdate], + }, + }, + workspaceId, + isSystemBuild: false, + applicationUniversalIdentifier: + workspaceCustomFlatApplication.universalIdentifier, + }, + ); + + if (validateAndBuildResult.status === 'fail') { + throw new WorkspaceMigrationBuilderException( + validateAndBuildResult, + 'Multiple validation errors occurred while resetting command menu item to default', + ); + } + + const { flatCommandMenuItemMaps: recomputedFlatCommandMenuItemMaps } = + await this.workspaceManyOrAllFlatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps( + { + workspaceId, + flatMapsKeys: ['flatCommandMenuItemMaps'], + }, + ); + + return fromFlatCommandMenuItemToCommandMenuItemDto( + findFlatEntityByIdInFlatEntityMapsOrThrow({ + flatEntityId: id, + flatEntityMaps: recomputedFlatCommandMenuItemMaps, + }), + ); + } + async delete(id: string, workspaceId: string): Promise { const { workspaceCustomFlatApplication } = await this.applicationService.findWorkspaceTwentyStandardAndCustomApplicationOrThrow( diff --git a/packages/twenty-server/src/engine/metadata-modules/command-menu-item/utils/command-menu-item-graphql-api-exception-handler.util.ts b/packages/twenty-server/src/engine/metadata-modules/command-menu-item/utils/command-menu-item-graphql-api-exception-handler.util.ts index 6e6b55c458..2c215c6fd2 100644 --- a/packages/twenty-server/src/engine/metadata-modules/command-menu-item/utils/command-menu-item-graphql-api-exception-handler.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/command-menu-item/utils/command-menu-item-graphql-api-exception-handler.util.ts @@ -16,6 +16,7 @@ export const commandMenuItemGraphqlApiExceptionHandler = (error: Error) => { throw new NotFoundError(error); case CommandMenuItemExceptionCode.INVALID_COMMAND_MENU_ITEM_INPUT: case CommandMenuItemExceptionCode.WORKFLOW_OR_FRONT_COMPONENT_REQUIRED: + case CommandMenuItemExceptionCode.COMMAND_MENU_ITEM_CANNOT_BE_RESET: throw new UserInputError(error); default: { return assertUnreachable(error.code);