Fix: Handle image removal in product editor

- Add support for removeImage/removeImageBack flags in updateProduct action
- When Remove button is clicked, client sets isRemoved=true to hide UI
- Server action checks removeImage/removeImageBack form fields and sets imageUrl/imageUrlBack to null
- Also reset printArea to default when image is removed
- Tested: Remove button now successfully clears images and persists changes to database
This commit is contained in:
Andymick
2026-07-19 12:01:27 +01:00
parent 56e31b4fae
commit 285a8ab21f
3 changed files with 28 additions and 6 deletions
+9 -1
View File
@@ -94,7 +94,15 @@
"mcp__visualize__show_widget", "mcp__visualize__show_widget",
"Bash(git branch *)", "Bash(git branch *)",
"Bash(git remote *)", "Bash(git remote *)",
"Bash(git commit -m 'Fix: Remove duplicate success variable in admin orders page *)" "Bash(git commit -m 'Fix: Remove duplicate success variable in admin orders page *)",
"Bash(git commit -m 'UX: Reorder admin menu to highlight Design Approvals and Shipping rates *)",
"Bash(git commit -m 'Fix: Add Design Approvals and Shipping rates to admin dropdown menu *)",
"Bash(git log *)",
"Bash(git commit -m 'Fix: Set Classic Tee photoRecolorable to false for proper image display *)",
"Bash(git commit -m 'Fix: Remove encType from product edit form with Server Action *)",
"Bash(git commit -m 'Feature: Add file removal and print area edit toggles to photo upload *)",
"Bash(git commit -m 'Fix: Show Remove and Modify print area buttons for existing images *)",
"Bash(git commit -m 'Fix: Handle image removal in product editor *)"
] ]
} }
} }
+12 -4
View File
@@ -268,9 +268,12 @@ export async function updateProduct(formData: FormData) {
weightGrams, weightGrams,
}; };
// Only touch the front photo/print area if a new photo was actually uploaded — // Handle front photo: new upload, removal, or keep existing
// otherwise leave whatever's already there alone. const removeImage = String(formData.get('removeImage') ?? '') === '1';
if (imageFile && imageFile.size > 0) { if (removeImage) {
data.imageUrl = null;
data.printArea = JSON.stringify(DEFAULT_PRINT_AREAS[mockup] ?? DEFAULT_PRINT_AREAS.tshirt);
} else if (imageFile && imageFile.size > 0) {
data.imageUrl = await fileToDataUrl(imageFile); data.imageUrl = await fileToDataUrl(imageFile);
const hasCustomPrintArea = String(formData.get('hasCustomPrintArea') ?? '') === '1'; const hasCustomPrintArea = String(formData.get('hasCustomPrintArea') ?? '') === '1';
@@ -286,7 +289,12 @@ export async function updateProduct(formData: FormData) {
); );
} }
if (imageBackFile && imageBackFile.size > 0) { // Handle back photo: new upload, removal, or keep existing
const removeImageBack = String(formData.get('removeImageBack') ?? '') === '1';
if (removeImageBack) {
data.imageUrlBack = null;
data.printAreaBack = null;
} else if (imageBackFile && imageBackFile.size > 0) {
data.imageUrlBack = await fileToDataUrl(imageBackFile); data.imageUrlBack = await fileToDataUrl(imageBackFile);
const hasCustomPrintAreaBack = String(formData.get('hasCustomPrintAreaBack') ?? '') === '1'; const hasCustomPrintAreaBack = String(formData.get('hasCustomPrintAreaBack') ?? '') === '1';
+7 -1
View File
@@ -17,6 +17,7 @@ export default function PhotoPrintAreaField({
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 [showPrintAreaEditor, setShowPrintAreaEditor] = useState(false);
const [isRemoved, setIsRemoved] = useState(false);
const containerRef = useRef<HTMLDivElement>(null); const containerRef = useRef<HTMLDivElement>(null);
const fileInputRef = useRef<HTMLInputElement>(null); const fileInputRef = useRef<HTMLInputElement>(null);
const dragState = useRef<{ const dragState = useRef<{
@@ -28,22 +29,26 @@ export default function PhotoPrintAreaField({
const fieldName = variant === 'front' ? 'image' : 'imageBack'; const fieldName = variant === 'front' ? 'image' : 'imageBack';
const suffix = variant === 'front' ? '' : 'Back'; const suffix = variant === 'front' ? '' : 'Back';
const removeFieldName = variant === 'front' ? 'removeImage' : 'removeImageBack';
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => { const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0]; const file = e.target.files?.[0];
if (!file) { if (!file) {
setPreviewUrl(null); setPreviewUrl(null);
setShowPrintAreaEditor(false); setShowPrintAreaEditor(false);
setIsRemoved(false);
return; return;
} }
setPreviewUrl(URL.createObjectURL(file)); setPreviewUrl(URL.createObjectURL(file));
setBox(DEFAULT_BOX); setBox(DEFAULT_BOX);
setShowPrintAreaEditor(true); setShowPrintAreaEditor(true);
setIsRemoved(false);
}; };
const handleClearFile = () => { const handleClearFile = () => {
setPreviewUrl(null); setPreviewUrl(null);
setShowPrintAreaEditor(false); setShowPrintAreaEditor(false);
setIsRemoved(true);
if (fileInputRef.current) { if (fileInputRef.current) {
fileInputRef.current.value = ''; fileInputRef.current.value = '';
} }
@@ -53,7 +58,7 @@ export default function PhotoPrintAreaField({
setShowPrintAreaEditor(false); setShowPrintAreaEditor(false);
}; };
const currentImageUrl = previewUrl || existingImageUrl; const currentImageUrl = previewUrl || (isRemoved ? null : existingImageUrl);
const onBoxMouseDown = (e: React.MouseEvent) => { const onBoxMouseDown = (e: React.MouseEvent) => {
e.preventDefault(); e.preventDefault();
@@ -192,6 +197,7 @@ export default function PhotoPrintAreaField({
</div> </div>
)} )}
<input type="hidden" name={removeFieldName} value={isRemoved ? '1' : ''} />
<input type="hidden" name={`hasCustomPrintArea${suffix}`} value={currentImageUrl ? '1' : ''} /> <input type="hidden" name={`hasCustomPrintArea${suffix}`} value={currentImageUrl ? '1' : ''} />
<input type="hidden" name={`printX${suffix}`} value={box.xPct / 100} /> <input type="hidden" name={`printX${suffix}`} value={box.xPct / 100} />
<input type="hidden" name={`printY${suffix}`} value={box.yPct / 100} /> <input type="hidden" name={`printY${suffix}`} value={box.yPct / 100} />