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,
|
||||
canNavigateToNextRecord,
|
||||
canNavigateToPreviousRecord,
|
||||
objectMetadataItem,
|
||||
};
|
||||
};
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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}
|
||||
|
||||
Reference in New Issue
Block a user