The Page d'accueil global now uses the same edit-in-place pattern as
products: hero (with image upload), collection header, contact, and
footer are all clickable-to-edit directly on a scoped replica of the
live page. Technical fields (WhatsApp number, Instagram URL, response
time, SEO) move to a "Réglages avancés" drawer.
- HomePreviewEditor + home-panel.css mirror the public layout
- HeroImageUploadSlot: single-image click-to-replace variant
- HomeSettingsDrawer for non-visual fields
- InlineEditable: add `separator` prop so stored `|` line-breaks render
as newlines in the editor and roundtrip on save (used for heroTitle,
contactTitle)
- Remove unused ProductPanelInfo + editor.css
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Calling dispatchFields({ type: 'ADD_ROW' }) directly bypasses
setModified(true), so the form never registered uploads as changes —
no autosave, no publish. Switch to addFieldRow/removeFieldRow helpers
from useForm, which dispatch and mark modified in one step.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Rewrite ImageUploadSlot to read the full images array via
useField({ path: 'images', hasRows: true }), resolve each row's
media id via /api/media/<id>, and render prev/next arrows on hover,
dot navigation, and a remove button. Uploading appends a new row via
dispatchFields ADD_ROW.
Dots are 14px black squares with a 1px yellow border and a 6px yellow
inner square when active.
Frontend already supports multi-image carousel (main.js) via
data-images JSON on product cards — no frontend changes needed.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Form state for the images array only holds the media ID, not the
populated doc. Read images.0.image via useField, then fetch
/api/media/<id> to resolve url+alt for display. On upload, add a new
row via dispatchFields if the array is empty, otherwise update in
place.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
React 19 does not reconcile children of contentEditable elements
after mount, so fields whose useField value arrives async (on most
paths under the tabs wrapper) stayed visually empty even though the
form state had the right value.
Drive textContent from a useLayoutEffect keyed on value, skip updates
while the element has focus (user typing), and let commit() write back
on blur. Same fix applied to PriceEditable.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Overriding admin.components.views.edit.default makes /admin/collections/
products/:id and /create render the product-detail panel directly —
text fields are contentEditable, the image is click-to-upload, and
price is inline-editable in the checkout-price-line. Fields that don't
fit the public template (slug, name, currency, availability, SEO,
isPublished, sortOrder, stripeID) live in a collapsible "Réglages
avancés" drawer below the panel.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
New "Édition visuelle" tab on the product edit view renders the
product panel with each text field wrapped in a contentEditable
InlineEditable that calls useField.setValue on blur. Combined with
the collection's existing autosave, changes persist automatically
without a manual save.
- InlineEditable: contentEditable wrapper backed by useField
- ProductPanelInfo: presentational product-panel JSX
- ProductPreviewEditor: default-exported custom view component
registered at admin.components.views.edit.livePreview
- Image is read-only; slug/price/SEO still edited via default form tab
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>