mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-01-31 06:42:03 +00:00
fix(kanban): persist image previews when switching tabs in Add Feature modal
Lifts image preview state up to parent components to prevent loss of preview thumbnails when switching between tabs. Added previewMap/onPreviewMapChange props to DescriptionImageDropZone for parent-controlled state management. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4 <noreply@anthropic.com>
This commit is contained in:
@@ -205,5 +205,37 @@
|
||||
"skipTests": true,
|
||||
"model": "sonnet",
|
||||
"thinkingLevel": "none"
|
||||
},
|
||||
{
|
||||
"id": "feature-1765360665626-u2vhr80aa",
|
||||
"category": "Uncategorized",
|
||||
"description": "I dont want you to change any code describe what u see in attached image",
|
||||
"steps": [],
|
||||
"status": "backlog",
|
||||
"startedAt": "2025-12-10T09:57:47.494Z",
|
||||
"imagePaths": [
|
||||
{
|
||||
"id": "img-1765360662146-d5qi79j88",
|
||||
"path": "/Users/shirone/Library/Application Support/automaker/images/1765360662144-2pt9bt1u4_image-test.png",
|
||||
"filename": "image-test.png",
|
||||
"mimeType": "image/png"
|
||||
}
|
||||
],
|
||||
"skipTests": true,
|
||||
"model": "gpt-5.1-codex",
|
||||
"thinkingLevel": "none"
|
||||
},
|
||||
{
|
||||
"id": "feature-1765360739103-3h218d1nn",
|
||||
"category": "Kanban",
|
||||
"description": "When u write new feature for ai agent and attacht context images and change tab to choose diff model and go back to prompt tab the image preview break and im not sure if it even saved properly in state to be later attached check it out for me",
|
||||
"steps": [],
|
||||
"status": "waiting_approval",
|
||||
"startedAt": "2025-12-10T09:59:02.988Z",
|
||||
"imagePaths": [],
|
||||
"skipTests": true,
|
||||
"summary": "Fixed image preview breaking when switching tabs in Add Feature modal. Added previewMap/onPreviewMapChange props to DescriptionImageDropZone component to lift preview state up to parent. Modified: description-image-dropzone.tsx (added parent-controlled state support), board-view.tsx (added newFeaturePreviewMap and followUpPreviewMap state, wired up to DescriptionImageDropZone). Image paths were already stored correctly in state - only the preview thumbnails (base64) were lost on tab switch due to component unmounting.",
|
||||
"model": "opus",
|
||||
"thinkingLevel": "high"
|
||||
}
|
||||
]
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import React, { useState, useRef, useCallback } from "react";
|
||||
import React, { useState, useRef, useCallback, useEffect } from "react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { ImageIcon, X, Loader2 } from "lucide-react";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
@@ -14,6 +14,9 @@ export interface FeatureImagePath {
|
||||
mimeType: string;
|
||||
}
|
||||
|
||||
// Map to store preview data by image ID (persisted across component re-mounts)
|
||||
export type ImagePreviewMap = Map<string, string>;
|
||||
|
||||
interface DescriptionImageDropZoneProps {
|
||||
value: string;
|
||||
onChange: (value: string) => void;
|
||||
@@ -24,6 +27,9 @@ interface DescriptionImageDropZoneProps {
|
||||
disabled?: boolean;
|
||||
maxFiles?: number;
|
||||
maxFileSize?: number; // in bytes, default 10MB
|
||||
// Optional: pass preview map from parent to persist across tab switches
|
||||
previewMap?: ImagePreviewMap;
|
||||
onPreviewMapChange?: (map: ImagePreviewMap) => void;
|
||||
}
|
||||
|
||||
const ACCEPTED_IMAGE_TYPES = [
|
||||
@@ -45,12 +51,31 @@ export function DescriptionImageDropZone({
|
||||
disabled = false,
|
||||
maxFiles = 5,
|
||||
maxFileSize = DEFAULT_MAX_FILE_SIZE,
|
||||
previewMap,
|
||||
onPreviewMapChange,
|
||||
}: DescriptionImageDropZoneProps) {
|
||||
const [isDragOver, setIsDragOver] = useState(false);
|
||||
const [isProcessing, setIsProcessing] = useState(false);
|
||||
const [previewImages, setPreviewImages] = useState<Map<string, string>>(
|
||||
new Map()
|
||||
// Use parent-provided preview map if available, otherwise use local state
|
||||
const [localPreviewImages, setLocalPreviewImages] = useState<Map<string, string>>(
|
||||
() => new Map()
|
||||
);
|
||||
|
||||
// Determine which preview map to use - prefer parent-controlled state
|
||||
const previewImages = previewMap !== undefined ? previewMap : localPreviewImages;
|
||||
const setPreviewImages = useCallback((updater: Map<string, string> | ((prev: Map<string, string>) => Map<string, string>)) => {
|
||||
if (onPreviewMapChange) {
|
||||
const currentMap = previewMap !== undefined ? previewMap : localPreviewImages;
|
||||
const newMap = typeof updater === 'function' ? updater(currentMap) : updater;
|
||||
onPreviewMapChange(newMap);
|
||||
} else {
|
||||
setLocalPreviewImages((prev) => {
|
||||
const newMap = typeof updater === 'function' ? updater(prev) : updater;
|
||||
return newMap;
|
||||
});
|
||||
}
|
||||
}, [onPreviewMapChange, previewMap, localPreviewImages]);
|
||||
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
const currentProject = useAppStore((state) => state.currentProject);
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ import { FeatureImageUpload } from "@/components/ui/feature-image-upload";
|
||||
import {
|
||||
DescriptionImageDropZone,
|
||||
FeatureImagePath as DescriptionImagePath,
|
||||
ImagePreviewMap,
|
||||
} from "@/components/ui/description-image-dropzone";
|
||||
import {
|
||||
Dialog,
|
||||
@@ -197,6 +198,13 @@ export function BoardView() {
|
||||
const [followUpImagePaths, setFollowUpImagePaths] = useState<
|
||||
DescriptionImagePath[]
|
||||
>([]);
|
||||
// Preview maps to persist image previews across tab switches
|
||||
const [newFeaturePreviewMap, setNewFeaturePreviewMap] = useState<ImagePreviewMap>(
|
||||
() => new Map()
|
||||
);
|
||||
const [followUpPreviewMap, setFollowUpPreviewMap] = useState<ImagePreviewMap>(
|
||||
() => new Map()
|
||||
);
|
||||
|
||||
// Make current project available globally for modal
|
||||
useEffect(() => {
|
||||
@@ -716,6 +724,8 @@ export function BoardView() {
|
||||
model: "opus",
|
||||
thinkingLevel: "none",
|
||||
});
|
||||
// Clear the preview map when the feature is added
|
||||
setNewFeaturePreviewMap(new Map());
|
||||
setShowAddDialog(false);
|
||||
};
|
||||
|
||||
@@ -991,6 +1001,7 @@ export function BoardView() {
|
||||
setFollowUpFeature(null);
|
||||
setFollowUpPrompt("");
|
||||
setFollowUpImagePaths([]);
|
||||
setFollowUpPreviewMap(new Map());
|
||||
|
||||
// Show success toast immediately
|
||||
toast.success("Follow-up started", {
|
||||
@@ -1485,7 +1496,13 @@ export function BoardView() {
|
||||
</div>
|
||||
|
||||
{/* Add Feature Dialog */}
|
||||
<Dialog open={showAddDialog} onOpenChange={setShowAddDialog}>
|
||||
<Dialog open={showAddDialog} onOpenChange={(open) => {
|
||||
setShowAddDialog(open);
|
||||
// Clear preview map when dialog closes
|
||||
if (!open) {
|
||||
setNewFeaturePreviewMap(new Map());
|
||||
}
|
||||
}}>
|
||||
<DialogContent
|
||||
compact={!isMaximized}
|
||||
data-testid="add-feature-dialog"
|
||||
@@ -1536,6 +1553,8 @@ export function BoardView() {
|
||||
setNewFeature({ ...newFeature, imagePaths: images })
|
||||
}
|
||||
placeholder="Describe the feature..."
|
||||
previewMap={newFeaturePreviewMap}
|
||||
onPreviewMapChange={setNewFeaturePreviewMap}
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
@@ -2077,6 +2096,7 @@ export function BoardView() {
|
||||
setFollowUpFeature(null);
|
||||
setFollowUpPrompt("");
|
||||
setFollowUpImagePaths([]);
|
||||
setFollowUpPreviewMap(new Map());
|
||||
}
|
||||
}}
|
||||
>
|
||||
@@ -2115,6 +2135,8 @@ export function BoardView() {
|
||||
images={followUpImagePaths}
|
||||
onImagesChange={setFollowUpImagePaths}
|
||||
placeholder="Describe what needs to be fixed or changed..."
|
||||
previewMap={followUpPreviewMap}
|
||||
onPreviewMapChange={setFollowUpPreviewMap}
|
||||
/>
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
@@ -2130,6 +2152,7 @@ export function BoardView() {
|
||||
setFollowUpFeature(null);
|
||||
setFollowUpPrompt("");
|
||||
setFollowUpImagePaths([]);
|
||||
setFollowUpPreviewMap(new Map());
|
||||
}}
|
||||
>
|
||||
Cancel
|
||||
|
||||
Reference in New Issue
Block a user