Feature: Add file removal and print area edit toggles to photo upload

- Add 'Modify print area →' button to navigate from file selection to print area editor
- Add 'Remove' button to clear selected file and start over
- Add '← Back to file' button in print area editor to return and change files
- Persist print area settings while toggling between views

Users can now more easily manage their image uploads and refine the print area
without losing their settings or being locked into a workflow.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
Andymick
2026-07-19 11:50:57 +01:00
co-authored by Claude Haiku 4.5
parent 7fa324bc07
commit 684a837998
+72 -23
View File
@@ -10,7 +10,9 @@ const MIN_SIZE = 6; // percent
export default function PhotoPrintAreaField({ variant = 'front' }: { variant?: 'front' | 'back' }) { export default function PhotoPrintAreaField({ variant = 'front' }: { variant?: 'front' | 'back' }) {
const [previewUrl, setPreviewUrl] = useState<string | null>(null); const [previewUrl, setPreviewUrl] = useState<string | null>(null);
const [box, setBox] = useState<Box>(DEFAULT_BOX); const [box, setBox] = useState<Box>(DEFAULT_BOX);
const [showPrintAreaEditor, setShowPrintAreaEditor] = useState(false);
const containerRef = useRef<HTMLDivElement>(null); const containerRef = useRef<HTMLDivElement>(null);
const fileInputRef = useRef<HTMLInputElement>(null);
const dragState = useRef<{ const dragState = useRef<{
mode: 'move' | 'resize'; mode: 'move' | 'resize';
startX: number; startX: number;
@@ -25,10 +27,24 @@ export default function PhotoPrintAreaField({ variant = 'front' }: { variant?: '
const file = e.target.files?.[0]; const file = e.target.files?.[0];
if (!file) { if (!file) {
setPreviewUrl(null); setPreviewUrl(null);
setShowPrintAreaEditor(false);
return; return;
} }
setPreviewUrl(URL.createObjectURL(file)); setPreviewUrl(URL.createObjectURL(file));
setBox(DEFAULT_BOX); setBox(DEFAULT_BOX);
setShowPrintAreaEditor(true);
};
const handleClearFile = () => {
setPreviewUrl(null);
setShowPrintAreaEditor(false);
if (fileInputRef.current) {
fileInputRef.current.value = '';
}
};
const handleBackToFile = () => {
setShowPrintAreaEditor(false);
}; };
const onBoxMouseDown = (e: React.MouseEvent) => { const onBoxMouseDown = (e: React.MouseEvent) => {
@@ -73,31 +89,64 @@ export default function PhotoPrintAreaField({ variant = 'front' }: { variant?: '
return ( return (
<div> <div>
<input {!previewUrl || !showPrintAreaEditor ? (
id={fieldName} <>
name={fieldName} <input
type="file" ref={fileInputRef}
accept="image/*" id={fieldName}
onChange={handleFileChange} name={fieldName}
className="w-full border border-line bg-paper px-3 py-2 text-sm" type="file"
/> accept="image/*"
{variant === 'front' ? ( onChange={handleFileChange}
<p className="mt-1 text-xs text-muted"> className="w-full border border-line bg-paper px-3 py-2 text-sm"
Uploading a real photo replaces the illustration everywhere this product is shown. One />
tradeoff: the color swatches below can no longer recolor a photo the way they recolor the {variant === 'front' ? (
illustration they&apos;ll still be saved with each order, but the picture itself stays <p className="mt-1 text-xs text-muted">
fixed. Leave this blank to keep using the illustration (which does recolor live). Uploading a real photo replaces the illustration everywhere this product is shown. One
</p> tradeoff: the color swatches below can no longer recolor a photo the way they recolor the
) : ( illustration they&apos;ll still be saved with each order, but the picture itself stays
<p className="mt-1 text-xs text-muted"> fixed. Leave this blank to keep using the illustration (which does recolor live).
If set, customers get a Front/Back toggle and can add a separate design to the back </p>
drag a print-area box below just like the front. ) : (
</p> <p className="mt-1 text-xs text-muted">
)} If set, customers get a Front/Back toggle and can add a separate design to the back
drag a print-area box below just like the front.
</p>
)}
{previewUrl && ( {previewUrl && !showPrintAreaEditor && (
<div className="mt-3 flex gap-2">
<button
type="button"
onClick={() => setShowPrintAreaEditor(true)}
className="text-sm text-clay hover:text-clay-dark font-medium"
>
Modify print area
</button>
<button
type="button"
onClick={handleClearFile}
className="text-sm text-splash-pink hover:text-splash-pink-dark font-medium"
>
Remove
</button>
</div>
)}
</>
) : null}
{previewUrl && showPrintAreaEditor && (
<div className="mt-4"> <div className="mt-4">
<p className="tag-label mb-2">Drag the box to mark the print area</p> <div className="flex items-center justify-between mb-2">
<p className="tag-label">Drag the box to mark the print area</p>
<button
type="button"
onClick={handleBackToFile}
className="text-sm text-clay hover:text-clay-dark font-medium"
>
Back to file
</button>
</div>
<p className="mb-2 text-xs text-muted"> <p className="mb-2 text-xs text-muted">
Cropped to a square here that&apos;s the same crop the design tool uses, so the box Cropped to a square here that&apos;s the same crop the design tool uses, so the box
lines up correctly. lines up correctly.