15 Commits

Author SHA1 Message Date
ordinarthur
1b5664e826 fix(admin): register HomePreviewEditor in importMap
All checks were successful
Build & Deploy to K3s / build-and-deploy (push) Successful in 3m0s
The importMap is static and committed — it doesn't auto-detect new
Payload admin components at build time, which is why the homepage global
rendered blank (getFromImportMap: PayloadComponent not found).

Regenerated with `payload generate:importmap` and added the same command
to the Dockerfile so any future admin component wires up automatically.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-21 19:18:09 +02:00
ordinarthur
b6fd89978e feat(admin): visual inline editor for the homepage
All checks were successful
Build & Deploy to K3s / build-and-deploy (push) Successful in 2m55s
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>
2026-04-21 18:53:45 +02:00
ordinarthur
1dbcef4660 style(frontend): match carousel nav to admin (square dots, overlay arrows)
All checks were successful
Build & Deploy to K3s / build-and-deploy (push) Successful in 2m59s
Frontend product panel now mirrors the admin preview: 14px black square
dots with a 1px yellow border and a 6px yellow inner square when active,
and prev/next arrows as dark overlay buttons on the image sides that
fade in on hover.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-21 18:34:43 +02:00
ordinarthur
55ab6175a1 fix(admin): pass required schemaPath to addFieldRow
All checks were successful
Build & Deploy to K3s / build-and-deploy (push) Successful in 3m2s
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-21 18:20:38 +02:00
ordinarthur
aa87b6da8a fix(admin): mark form modified when adding/removing product images
Some checks failed
Build & Deploy to K3s / build-and-deploy (push) Failing after 52s
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>
2026-04-21 18:17:30 +02:00
ordinarthur
d44bad4c68 feat(admin): multi-image carousel with add/remove in product editor
All checks were successful
Build & Deploy to K3s / build-and-deploy (push) Successful in 2m52s
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>
2026-04-21 18:08:10 +02:00
ordinarthur
a41dfba9e6 fix(admin): resolve and display current product image
All checks were successful
Build & Deploy to K3s / build-and-deploy (push) Successful in 2m45s
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>
2026-04-21 14:29:15 +02:00
ordinarthur
94fdb37dc3 fix(admin): render visual editor as ui field inside default form
All checks were successful
Build & Deploy to K3s / build-and-deploy (push) Successful in 2m48s
The views.edit.default override replaced Payload's <Form> wrapper, so
useField had no form-state context — every field returned undefined
and setValue threw "N is not a function" during autosave.

Flatten the collection into a single ui field that renders
ProductPreviewEditor, with every real field marked admin.hidden:true.
The panel now lives inside Payload's default form, so useField gets
real values and autosave works.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-21 14:17:26 +02:00
ordinarthur
8f3a26e883 fix(admin): sync inline-editable DOM text imperatively
All checks were successful
Build & Deploy to K3s / build-and-deploy (push) Successful in 2m45s
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>
2026-04-21 14:04:12 +02:00
ordinarthur
f5671008a7 feat(admin): replace default edit view with visual preview editor
All checks were successful
Build & Deploy to K3s / build-and-deploy (push) Successful in 2m39s
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>
2026-04-21 13:54:50 +02:00
ordinarthur
7962975dbd fix(admin): unblock product create page
All checks were successful
Build & Deploy to K3s / build-and-deploy (push) Successful in 2m39s
Two bugs compounded to render /admin/collections/products/create blank:

1. Dockerfile's STRIPE_SECRET_KEY build-time placeholder was too short
   (11 chars) to pass the regex that gates Stripe plugin sync. Build
   produced an importMap missing plugin-stripe components like LinkToDoc,
   warning at runtime and failing to render.

2. With autosave enabled, Payload creates a blank draft on page load.
   Plugin-stripe's beforeValidate hook fired against empty data, calling
   Stripe products.create with no name, throwing a 500 that aborted the
   page render.

Fix: use a 20+ char placeholder in the Dockerfile so the build-time
importMap mirrors runtime, and add a beforeValidate hook that sets
skipSync=true whenever productDisplayName is empty so Stripe sync
waits until the product has content.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-21 13:00:29 +02:00
ordinarthur
8fc3b2365a feat(admin): click-to-edit visual editor for products
Some checks failed
Build & Deploy to K3s / build-and-deploy (push) Failing after 14m11s
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>
2026-04-21 12:09:45 +02:00
ordinarthur
aeabd79ac6 fix: regenerate migration with stripe plugin fields
All checks were successful
Build & Deploy to K3s / build-and-deploy (push) Successful in 3m6s
Previous run generated the initial migration without STRIPE_SECRET_KEY
set, so the stripe plugin's sync was disabled and the stripeID/skipSync
columns were omitted. Prod then crashed on queries referencing
products.stripe_i_d. Regenerated with a placeholder key so the plugin
contributes its fields to the schema.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-21 11:02:19 +02:00
ordinarthur
955dbeb63b feat: add initial migration and run it on container start
All checks were successful
Build & Deploy to K3s / build-and-deploy (push) Successful in 3m6s
Payload's postgresAdapter `push: true` only runs in dev. Prod needs
committed migrations; the container now runs `payload migrate` before
starting Next.js so the schema is created on first deploy.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-21 10:45:03 +02:00
ordinarthur
bf5bf977e9 feat: replace Astro + Sanity + Fastify with Next.js + Payload CMS
All checks were successful
Build & Deploy to K3s / build-and-deploy (push) Successful in 4m13s
Single Next.js 15 app now serves frontend SSR, admin CMS, and Stripe API.
Replaces the Sanity quota-limited headless CMS with self-hosted Payload 3.0
on Postgres, removing the split-service topology (ssr/api/proxy → web).

- nextjs/: Next.js 15 app with Payload 3.0, Postgres adapter, Stripe plugin
- k8s/: new single-pod deployment + Postgres StatefulSet + PVCs (media, db)
- .gitea/workflows/deploy.yml: single-image build, tears down legacy pods

New Gitea secrets required: PAYLOAD_SECRET, POSTGRES_PASSWORD.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-21 10:28:29 +02:00