Make record name editable on show page (#9172)
When fields are not displayed in show page, title should be editable https://github.com/user-attachments/assets/049c2998-6944-46c7-9e9e-f6d29da06c6c
This commit is contained in:
@ -0,0 +1,100 @@
|
|||||||
|
import { useFindOneRecord } from '@/object-record/hooks/useFindOneRecord';
|
||||||
|
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
|
||||||
|
import { NavigationDrawerInput } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerInput';
|
||||||
|
import { NavigationDrawerItem } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItem';
|
||||||
|
import styled from '@emotion/styled';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import { capitalize } from '~/utils/string/capitalize';
|
||||||
|
|
||||||
|
const StyledEditableTitleContainer = styled.div`
|
||||||
|
align-items: flex-start;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledEditableTitlePrefix = styled.div`
|
||||||
|
color: ${({ theme }) => theme.font.color.tertiary};
|
||||||
|
line-height: 24px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
padding: ${({ theme }) => theme.spacing(0.75)};
|
||||||
|
gap: ${({ theme }) => theme.spacing(1)};
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const RecordEditableName = ({
|
||||||
|
objectNameSingular,
|
||||||
|
objectRecordId,
|
||||||
|
objectLabelPlural,
|
||||||
|
}: {
|
||||||
|
objectNameSingular: string;
|
||||||
|
objectRecordId: string;
|
||||||
|
objectLabelPlural: string;
|
||||||
|
}) => {
|
||||||
|
const [isRenaming, setIsRenaming] = useState(false);
|
||||||
|
const { record, loading } = useFindOneRecord({
|
||||||
|
objectNameSingular,
|
||||||
|
objectRecordId,
|
||||||
|
recordGqlFields: {
|
||||||
|
name: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const [recordName, setRecordName] = useState(record?.name);
|
||||||
|
|
||||||
|
const { updateOneRecord } = useUpdateOneRecord({
|
||||||
|
objectNameSingular,
|
||||||
|
recordGqlFields: {
|
||||||
|
name: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleSubmit = (value: string) => {
|
||||||
|
updateOneRecord({
|
||||||
|
idToUpdate: objectRecordId,
|
||||||
|
updateOneRecordInput: {
|
||||||
|
name: value,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
setIsRenaming(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCancel = () => {
|
||||||
|
setRecordName(record?.name);
|
||||||
|
setIsRenaming(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setRecordName(record?.name);
|
||||||
|
}, [record?.name]);
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<StyledEditableTitleContainer>
|
||||||
|
<StyledEditableTitlePrefix>
|
||||||
|
{capitalize(objectLabelPlural)}
|
||||||
|
<span>{' / '}</span>
|
||||||
|
</StyledEditableTitlePrefix>
|
||||||
|
{isRenaming ? (
|
||||||
|
<NavigationDrawerInput
|
||||||
|
value={recordName}
|
||||||
|
onChange={setRecordName}
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
onCancel={handleCancel}
|
||||||
|
onClickOutside={handleCancel}
|
||||||
|
hotkeyScope="favorites-folder-input"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<NavigationDrawerItem
|
||||||
|
label={recordName}
|
||||||
|
onClick={() => setIsRenaming(true)}
|
||||||
|
rightOptions={undefined}
|
||||||
|
className="navigation-drawer-item"
|
||||||
|
active
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</StyledEditableTitleContainer>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -204,5 +204,6 @@ export const useRecordShowPagePagination = (
|
|||||||
navigateToIndexView,
|
navigateToIndexView,
|
||||||
canNavigateToNextRecord,
|
canNavigateToNextRecord,
|
||||||
canNavigateToPreviousRecord,
|
canNavigateToPreviousRecord,
|
||||||
|
objectMetadataItem,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@ -17,7 +17,7 @@ import { useHotkeyScopeOnMount } from '~/hooks/useHotkeyScopeOnMount';
|
|||||||
|
|
||||||
type NavigationDrawerInputProps = {
|
type NavigationDrawerInputProps = {
|
||||||
className?: string;
|
className?: string;
|
||||||
Icon: IconComponent | ((props: TablerIconsProps) => JSX.Element);
|
Icon?: IconComponent | ((props: TablerIconsProps) => JSX.Element);
|
||||||
value: string;
|
value: string;
|
||||||
onChange: (value: string) => void;
|
onChange: (value: string) => void;
|
||||||
onSubmit: (value: string) => void;
|
onSubmit: (value: string) => void;
|
||||||
|
|||||||
@ -32,7 +32,7 @@ export type NavigationDrawerItemProps = {
|
|||||||
subItemState?: NavigationDrawerSubItemState;
|
subItemState?: NavigationDrawerSubItemState;
|
||||||
to?: string;
|
to?: string;
|
||||||
onClick?: () => void;
|
onClick?: () => void;
|
||||||
Icon: IconComponent | ((props: TablerIconsProps) => JSX.Element);
|
Icon?: IconComponent | ((props: TablerIconsProps) => JSX.Element);
|
||||||
active?: boolean;
|
active?: boolean;
|
||||||
danger?: boolean;
|
danger?: boolean;
|
||||||
soon?: boolean;
|
soon?: boolean;
|
||||||
@ -257,6 +257,7 @@ export const NavigationDrawerItem = ({
|
|||||||
const [isNavigationDrawerExpanded, setIsNavigationDrawerExpanded] =
|
const [isNavigationDrawerExpanded, setIsNavigationDrawerExpanded] =
|
||||||
useRecoilState(isNavigationDrawerExpandedState);
|
useRecoilState(isNavigationDrawerExpandedState);
|
||||||
const showBreadcrumb = indentationLevel === 2;
|
const showBreadcrumb = indentationLevel === 2;
|
||||||
|
const showStyledSpacer = !!soon || !!count || !!keyboard || !!rightOptions;
|
||||||
|
|
||||||
const handleItemClick = () => {
|
const handleItemClick = () => {
|
||||||
if (isMobile) {
|
if (isMobile) {
|
||||||
@ -320,7 +321,7 @@ export const NavigationDrawerItem = ({
|
|||||||
</StyledEllipsisContainer>
|
</StyledEllipsisContainer>
|
||||||
</StyledLabelParent>
|
</StyledLabelParent>
|
||||||
|
|
||||||
<StyledSpacer />
|
{showStyledSpacer && <StyledSpacer />}
|
||||||
|
|
||||||
{soon && (
|
{soon && (
|
||||||
<NavigationDrawerAnimatedCollapseWrapper>
|
<NavigationDrawerAnimatedCollapseWrapper>
|
||||||
@ -341,8 +342,9 @@ export const NavigationDrawerItem = ({
|
|||||||
</StyledKeyBoardShortcut>
|
</StyledKeyBoardShortcut>
|
||||||
</NavigationDrawerAnimatedCollapseWrapper>
|
</NavigationDrawerAnimatedCollapseWrapper>
|
||||||
)}
|
)}
|
||||||
<NavigationDrawerAnimatedCollapseWrapper>
|
|
||||||
{rightOptions && (
|
{rightOptions && (
|
||||||
|
<NavigationDrawerAnimatedCollapseWrapper>
|
||||||
<StyledRightOptionsContainer
|
<StyledRightOptionsContainer
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
@ -358,8 +360,8 @@ export const NavigationDrawerItem = ({
|
|||||||
{rightOptions}
|
{rightOptions}
|
||||||
</StyledRightOptionsVisbility>
|
</StyledRightOptionsVisbility>
|
||||||
</StyledRightOptionsContainer>
|
</StyledRightOptionsContainer>
|
||||||
)}
|
</NavigationDrawerAnimatedCollapseWrapper>
|
||||||
</NavigationDrawerAnimatedCollapseWrapper>
|
)}
|
||||||
</StyledItemElementsContainer>
|
</StyledItemElementsContainer>
|
||||||
</StyledItem>
|
</StyledItem>
|
||||||
</StyledNavigationDrawerItemContainer>
|
</StyledNavigationDrawerItemContainer>
|
||||||
|
|||||||
@ -1,7 +1,5 @@
|
|||||||
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
|
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
|
||||||
import { Select, SelectOption } from '@/ui/input/components/Select';
|
import { Select, SelectOption } from '@/ui/input/components/Select';
|
||||||
import { WorkflowSingleRecordPicker } from '@/workflow/components/WorkflowSingleRecordPicker';
|
|
||||||
import { WorkflowStepHeader } from '@/workflow/components/WorkflowStepHeader';
|
|
||||||
import { WorkflowUpdateRecordAction } from '@/workflow/types/Workflow';
|
import { WorkflowUpdateRecordAction } from '@/workflow/types/Workflow';
|
||||||
import { useTheme } from '@emotion/react';
|
import { useTheme } from '@emotion/react';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
@ -15,7 +13,9 @@ import {
|
|||||||
import { formatFieldMetadataItemAsFieldDefinition } from '@/object-metadata/utils/formatFieldMetadataItemAsFieldDefinition';
|
import { formatFieldMetadataItemAsFieldDefinition } from '@/object-metadata/utils/formatFieldMetadataItemAsFieldDefinition';
|
||||||
import { FormFieldInput } from '@/object-record/record-field/components/FormFieldInput';
|
import { FormFieldInput } from '@/object-record/record-field/components/FormFieldInput';
|
||||||
import { FormMultiSelectFieldInput } from '@/object-record/record-field/form-types/components/FormMultiSelectFieldInput';
|
import { FormMultiSelectFieldInput } from '@/object-record/record-field/form-types/components/FormMultiSelectFieldInput';
|
||||||
|
import { WorkflowSingleRecordPicker } from '@/workflow/components/WorkflowSingleRecordPicker';
|
||||||
import { WorkflowStepBody } from '@/workflow/components/WorkflowStepBody';
|
import { WorkflowStepBody } from '@/workflow/components/WorkflowStepBody';
|
||||||
|
import { WorkflowStepHeader } from '@/workflow/components/WorkflowStepHeader';
|
||||||
import { WorkflowVariablePicker } from '@/workflow/components/WorkflowVariablePicker';
|
import { WorkflowVariablePicker } from '@/workflow/components/WorkflowVariablePicker';
|
||||||
import { JsonValue } from 'type-fest';
|
import { JsonValue } from 'type-fest';
|
||||||
import { useDebouncedCallback } from 'use-debounce';
|
import { useDebouncedCallback } from 'use-debounce';
|
||||||
@ -176,6 +176,7 @@ export const WorkflowEditActionFormUpdateRecord = ({
|
|||||||
initialTitle={headerTitle}
|
initialTitle={headerTitle}
|
||||||
headerType="Action"
|
headerType="Action"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<WorkflowStepBody>
|
<WorkflowStepBody>
|
||||||
<Select
|
<Select
|
||||||
dropdownId="workflow-edit-action-record-update-object-name"
|
dropdownId="workflow-edit-action-record-update-object-name"
|
||||||
|
|||||||
@ -1,3 +1,6 @@
|
|||||||
|
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||||
|
import { RecordEditableName } from '@/object-record/components/RecordEditableName';
|
||||||
|
import { useRecordShowContainerTabs } from '@/object-record/record-show/hooks/useRecordShowContainerTabs';
|
||||||
import { useRecordShowPage } from '@/object-record/record-show/hooks/useRecordShowPage';
|
import { useRecordShowPage } from '@/object-record/record-show/hooks/useRecordShowPage';
|
||||||
import { useRecordShowPagePagination } from '@/object-record/record-show/hooks/useRecordShowPagePagination';
|
import { useRecordShowPagePagination } from '@/object-record/record-show/hooks/useRecordShowPagePagination';
|
||||||
import { PageHeader } from '@/ui/layout/page/components/PageHeader';
|
import { PageHeader } from '@/ui/layout/page/components/PageHeader';
|
||||||
@ -17,13 +20,33 @@ export const RecordShowPageHeader = ({
|
|||||||
navigateToPreviousRecord,
|
navigateToPreviousRecord,
|
||||||
navigateToNextRecord,
|
navigateToNextRecord,
|
||||||
navigateToIndexView,
|
navigateToIndexView,
|
||||||
|
objectMetadataItem,
|
||||||
} = useRecordShowPagePagination(objectNameSingular, objectRecordId);
|
} = useRecordShowPagePagination(objectNameSingular, objectRecordId);
|
||||||
|
|
||||||
const { headerIcon } = useRecordShowPage(objectNameSingular, objectRecordId);
|
const { headerIcon } = useRecordShowPage(objectNameSingular, objectRecordId);
|
||||||
|
|
||||||
|
const { layout } = useRecordShowContainerTabs(
|
||||||
|
false,
|
||||||
|
objectNameSingular as CoreObjectNameSingular,
|
||||||
|
false,
|
||||||
|
objectMetadataItem,
|
||||||
|
);
|
||||||
|
|
||||||
|
const hasEditableName = layout.hideSummaryAndFields === true;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageHeader
|
<PageHeader
|
||||||
title={viewName}
|
title={
|
||||||
|
hasEditableName ? (
|
||||||
|
<RecordEditableName
|
||||||
|
objectNameSingular={objectNameSingular}
|
||||||
|
objectRecordId={objectRecordId}
|
||||||
|
objectLabelPlural={objectMetadataItem.labelPlural}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
viewName
|
||||||
|
)
|
||||||
|
}
|
||||||
hasPaginationButtons
|
hasPaginationButtons
|
||||||
hasClosePageButton
|
hasClosePageButton
|
||||||
onClosePage={navigateToIndexView}
|
onClosePage={navigateToIndexView}
|
||||||
|
|||||||
Reference in New Issue
Block a user