Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions .changeset/editable-highlights.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
---
"@object-ui/plugin-detail": minor
"@object-ui/app-shell": minor
---

feat(detail): editable record highlights on the shared inline-edit draft (objectui#2407 P2)

The highlights strip is now editable in place and shares ONE draft + ONE atomic
Save with the details body (building on the P1 `InlineEditContext` / `#2529`
`InlineFieldInput`).

- **`HeaderHighlight`** consumes `useInlineEdit()`: hovering a highlight shows a
pencil and double-click enters the shared record edit session; each editable
highlight renders the same `<InlineFieldInput>` the body uses (value =
`draft[name] ?? data[name]`, write via `setField`). Computed
(`formula`/`summary`/`rollup`/`auto_number`), `readonly`, and system fields
expose no editor. Empty highlights are kept while editing so they can be
filled. Compact-layout UX: an actively-edited column widens and renders the
editor full-width (Salesforce-style expand-on-edit).
- **`RecordDetailView`** (app-shell) hosts ONE `<InlineEditProvider>` (with the
object-lifecycle `canEdit` gate) spanning both `record:highlights` and
`record:details`, plus the single record-level `<InlineEditSaveBar>` — so a
highlight edit and a body edit commit together in ONE
`update(obj, id, draft, { ifMatch })`.
- **`record:details`** drops its P1-local provider/save bar (it would otherwise
split the draft from the highlights) and just consumes the shared context;
**`record:highlights`** threads the DataSource through for lookup/user editors.

Guardrails preserved: computed/readonly/system highlights non-editable; `canEdit`
gate; OCC (`ifMatch` + `ConcurrentUpdateDialog`); only user-edited keys are sent.
22 changes: 20 additions & 2 deletions packages/app-shell/src/views/RecordDetailView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@

import { useState, useEffect, useCallback, useMemo, useRef } from 'react';
import { useParams, useNavigate, useLocation, useSearchParams, Link } from 'react-router-dom';
import { RecordChatterPanel, buildDefaultPageSchema, deriveFieldGroupDetailSections, extractMentions } from '@object-ui/plugin-detail';
import { RecordChatterPanel, InlineEditSaveBar, buildDefaultPageSchema, deriveFieldGroupDetailSections, extractMentions } from '@object-ui/plugin-detail';
import { Empty, EmptyTitle, EmptyDescription } from '@object-ui/components';
import { useAuth, createAuthenticatedFetch } from '@object-ui/auth';
import { ActionProvider, useObjectTranslation, useObjectLabel, usePageAssignment, RecordContextProvider, SchemaRenderer, DiscussionContextProvider, HighlightFieldsProvider, useGlobalUndo, useDataInvalidation, notifyDataChanged } from '@object-ui/react';
import { ActionProvider, useObjectTranslation, useObjectLabel, usePageAssignment, RecordContextProvider, SchemaRenderer, DiscussionContextProvider, HighlightFieldsProvider, InlineEditProvider, useGlobalUndo, useDataInvalidation, notifyDataChanged } from '@object-ui/react';
import { buildExpandFields } from '@object-ui/core';
import { toast } from 'sonner';
import { useRecordPresence, PresenceAvatars } from '@object-ui/collaboration';
Expand Down Expand Up @@ -1737,6 +1737,11 @@ export function RecordDetailView({ dataSource, objects, onEdit, objectNameOverri
isFavorite={isRecordFavorite}
onToggleFavorite={favoriteRecord ? handleToggleRecordFavorite : undefined}
>
{/* objectui#2407 P2 — ONE record-level inline-edit session spanning
the highlights strip AND the details body (both descend from this
provider), committed together by the single <InlineEditSaveBar>
below. `canEdit` carries the object-lifecycle / permission gate. */}
<InlineEditProvider canEdit={resolveCrudAffordances(objectDef as any).edit}>
<HighlightFieldsProvider>
<DiscussionContextProvider
items={feedItems as any}
Expand Down Expand Up @@ -1824,6 +1829,18 @@ export function RecordDetailView({ dataSource, objects, onEdit, objectNameOverri
/>
</div>
)}
{/* Record-level inline-edit Save/Cancel bar (objectui#2407 P2) —
commits the whole draft (highlights + body) in ONE atomic OCC
update; renders (sticky) only while editing. */}
<InlineEditSaveBar
dataSource={dataSource}
objectName={objectName}
recordId={pureRecordId}
data={pageRecord}
refresh={notifyRecordChanged}
fieldLabelFor={(name: string) => (objectDef as any)?.fields?.[name]?.label || fieldLabel(objectName || '', name, name)}
locked={(pageRecord as any)?.approval_status === 'pending' || (pageRecord as any)?.approval_status === 'in_approval'}
/>
</div>
<MetadataPanel
open={showDebug}
Expand All @@ -1834,6 +1851,7 @@ export function RecordDetailView({ dataSource, objects, onEdit, objectNameOverri
</ActionProvider>
</DiscussionContextProvider>
</HighlightFieldsProvider>
</InlineEditProvider>
</RecordContextProvider>

{/* Action Confirm Dialog */}
Expand Down
Loading
Loading