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:
Thomas Trompette
2024-12-26 10:59:17 +01:00
committed by GitHub
parent 29d364fa7b
commit f63b0a235a
6 changed files with 137 additions and 10 deletions

View File

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

View File

@ -204,5 +204,6 @@ export const useRecordShowPagePagination = (
navigateToIndexView,
canNavigateToNextRecord,
canNavigateToPreviousRecord,
objectMetadataItem,
};
};

View File

@ -17,7 +17,7 @@ import { useHotkeyScopeOnMount } from '~/hooks/useHotkeyScopeOnMount';
type NavigationDrawerInputProps = {
className?: string;
Icon: IconComponent | ((props: TablerIconsProps) => JSX.Element);
Icon?: IconComponent | ((props: TablerIconsProps) => JSX.Element);
value: string;
onChange: (value: string) => void;
onSubmit: (value: string) => void;

View File

@ -32,7 +32,7 @@ export type NavigationDrawerItemProps = {
subItemState?: NavigationDrawerSubItemState;
to?: string;
onClick?: () => void;
Icon: IconComponent | ((props: TablerIconsProps) => JSX.Element);
Icon?: IconComponent | ((props: TablerIconsProps) => JSX.Element);
active?: boolean;
danger?: boolean;
soon?: boolean;
@ -257,6 +257,7 @@ export const NavigationDrawerItem = ({
const [isNavigationDrawerExpanded, setIsNavigationDrawerExpanded] =
useRecoilState(isNavigationDrawerExpandedState);
const showBreadcrumb = indentationLevel === 2;
const showStyledSpacer = !!soon || !!count || !!keyboard || !!rightOptions;
const handleItemClick = () => {
if (isMobile) {
@ -320,7 +321,7 @@ export const NavigationDrawerItem = ({
</StyledEllipsisContainer>
</StyledLabelParent>
<StyledSpacer />
{showStyledSpacer && <StyledSpacer />}
{soon && (
<NavigationDrawerAnimatedCollapseWrapper>
@ -341,8 +342,9 @@ export const NavigationDrawerItem = ({
</StyledKeyBoardShortcut>
</NavigationDrawerAnimatedCollapseWrapper>
)}
<NavigationDrawerAnimatedCollapseWrapper>
{rightOptions && (
{rightOptions && (
<NavigationDrawerAnimatedCollapseWrapper>
<StyledRightOptionsContainer
onClick={(e) => {
e.stopPropagation();
@ -358,8 +360,8 @@ export const NavigationDrawerItem = ({
{rightOptions}
</StyledRightOptionsVisbility>
</StyledRightOptionsContainer>
)}
</NavigationDrawerAnimatedCollapseWrapper>
</NavigationDrawerAnimatedCollapseWrapper>
)}
</StyledItemElementsContainer>
</StyledItem>
</StyledNavigationDrawerItemContainer>

View File

@ -1,7 +1,5 @@
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
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 { useTheme } from '@emotion/react';
import { useEffect, useState } from 'react';
@ -15,7 +13,9 @@ import {
import { formatFieldMetadataItemAsFieldDefinition } from '@/object-metadata/utils/formatFieldMetadataItemAsFieldDefinition';
import { FormFieldInput } from '@/object-record/record-field/components/FormFieldInput';
import { FormMultiSelectFieldInput } from '@/object-record/record-field/form-types/components/FormMultiSelectFieldInput';
import { WorkflowSingleRecordPicker } from '@/workflow/components/WorkflowSingleRecordPicker';
import { WorkflowStepBody } from '@/workflow/components/WorkflowStepBody';
import { WorkflowStepHeader } from '@/workflow/components/WorkflowStepHeader';
import { WorkflowVariablePicker } from '@/workflow/components/WorkflowVariablePicker';
import { JsonValue } from 'type-fest';
import { useDebouncedCallback } from 'use-debounce';
@ -176,6 +176,7 @@ export const WorkflowEditActionFormUpdateRecord = ({
initialTitle={headerTitle}
headerType="Action"
/>
<WorkflowStepBody>
<Select
dropdownId="workflow-edit-action-record-update-object-name"

View File

@ -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 { useRecordShowPagePagination } from '@/object-record/record-show/hooks/useRecordShowPagePagination';
import { PageHeader } from '@/ui/layout/page/components/PageHeader';
@ -17,13 +20,33 @@ export const RecordShowPageHeader = ({
navigateToPreviousRecord,
navigateToNextRecord,
navigateToIndexView,
objectMetadataItem,
} = useRecordShowPagePagination(objectNameSingular, objectRecordId);
const { headerIcon } = useRecordShowPage(objectNameSingular, objectRecordId);
const { layout } = useRecordShowContainerTabs(
false,
objectNameSingular as CoreObjectNameSingular,
false,
objectMetadataItem,
);
const hasEditableName = layout.hideSummaryAndFields === true;
return (
<PageHeader
title={viewName}
title={
hasEditableName ? (
<RecordEditableName
objectNameSingular={objectNameSingular}
objectRecordId={objectRecordId}
objectLabelPlural={objectMetadataItem.labelPlural}
/>
) : (
viewName
)
}
hasPaginationButtons
hasClosePageButton
onClosePage={navigateToIndexView}