fix(fe): upload slab photos to the server before publish

ensureServerSlab sent calibration_photo_id at create, but slab_photos rows are
only created by POST /slabs/{id}/photos, which the frontend never called - so
the INSERT hit FOREIGN KEY (calibration_photo_id REFERENCES slab_photos(id))
and every create failed with a generic 400, blocking the whole create->publish
walk.

- stop sending calibration_photo_id at create
- upload every draft photo (original + processed PNG) right after the slab row
  exists, via new review/photoUpload.ts (multipart FormData, same error
  envelope so failures surface through fail(e))
- link calibration_photo_id in the existing review PUT, once the row exists
- extract the role rule into lineup.photoRole() so the review strip and the
  uploader cannot disagree
This commit is contained in:
2026-09-25 12:14:35 -07:00
parent 5c7e5c663e
commit e5afc7524b
5 changed files with 87 additions and 4 deletions
+2 -2
View File
@@ -3,7 +3,7 @@
import { apiFetch, ApiError } from '../lib/api';
import type { SlabDraft, Taxonomy, TaxonomyCategory, TaxonomyTerm } from '../lib/types';
import { deriveThicknessBandId } from './thicknessBand';
import { keptInOrder, missingMinimums, moveItem, roleLabel, resolvePhotoUrl, type LineupItem, type PhotoRole } from './lineup';
import { keptInOrder, missingMinimums, moveItem, photoRole, roleLabel, resolvePhotoUrl, type LineupItem, type PhotoRole } from './lineup';
import { nextPrimary, selectedSummary } from './woodPrimary';
import { buildShortDescription, buildTitle } from './copy';
@@ -110,7 +110,7 @@
// ── Photo strip (REVIEW-5): kept order, leftmost = primary (PUBLISH-45) ──
$: lineup = slab.photos.map((photo, index): LineupItem => ({
id: photo.id,
role: photo.id === slab.calibrationPhotoId ? 'calibration' : photo.processedBlob ? 'overlay' : 'batch',
role: photoRole(photo.id, slab.calibrationPhotoId, Boolean(photo.processedBlob)),
isCalibration: photo.id === slab.calibrationPhotoId,
kept: index < slab.photos.length,
}));