Confirm before deleting creation works
This commit is contained in:
@@ -70,7 +70,7 @@ Use this guide when the user reports behavior. Start from the symptom row, inspe
|
||||
| Symptom | Check Files | What To Verify |
|
||||
| --- | --- | --- |
|
||||
| History missing after generation or login/account switch | `src/lib/creation-history-store.ts`, `src/app/api/creation-history/route.ts`, create panel component | History POST, `works` insert, URL not data URL except reverse prompt placeholder, and `miaojing_auth_updated` triggers a fresh server fetch. |
|
||||
| Detail delete removes only local history or record reappears after refresh | `src/components/creation-detail-dialog.tsx`, `src/lib/creation-history-store.ts`, `src/app/api/creation-history/route.ts`, `src/components/profile/creation-history-tab.tsx` | Logged-in deletion should call `DELETE /api/creation-history?id=...` first, then refresh local history from the server. Check bearer token availability and route ownership filter (`id` + `user_id`). |
|
||||
| Detail delete removes only local history, skips confirmation, or record reappears after refresh | `src/components/creation-detail-dialog.tsx`, `src/components/ui/alert-dialog.tsx`, `src/lib/creation-history-store.ts`, `src/app/api/creation-history/route.ts`, `src/components/profile/creation-history-tab.tsx` | The detail action is labeled `删除作品` and must open a confirmation dialog warning that deletion cannot be recovered. Logged-in deletion should call `DELETE /api/creation-history?id=...` first, then refresh local history from the server. Check bearer token availability and route ownership filter (`id` + `user_id`). |
|
||||
| Published work not in gallery | `src/lib/creation-history-store.ts`, `src/app/api/gallery/publish/route.ts`, `src/app/api/gallery/route.ts`, `src/app/gallery/page.tsx` | `is_public = true`, `status = completed`, media copied to gallery folder, filters. |
|
||||
| Imported gallery images do not render after production data import | `src/app/api/admin/data-export/route.ts`, `src/app/api/admin/data-import/route.ts`, `src/lib/local-storage.ts`, `src/app/api/local-storage/[...path]/route.ts`, DB `works.result_url` | New exports should include `_media`; import should persist media to local storage. If using an older export without `_media`, DB rows alone cannot recreate missing `/api/local-storage/*` files. |
|
||||
| Gallery delete does not remove public item | `src/app/api/gallery/route.ts`, admin UI route using it | DELETE unpublishes by setting `is_public = false`, not hard delete. |
|
||||
|
||||
@@ -59,7 +59,7 @@ Use this document to jump directly to code before broad searching.
|
||||
| Style presets | `src/components/create/style-preset-selector.tsx`, `src/lib/style-presets-client.ts`, `src/app/api/style-presets/route.ts`, `src/lib/style-preset-store.ts`, `src/lib/model-config.ts` | Style presets are stored in `image_style_presets`, seeded from defaults, sorted by `usage_count`, and incremented from image generation jobs. |
|
||||
| Loading/error panels | `src/components/create/generation-loading-panel.tsx`, `src/components/create/generation-task-list.tsx`, `src/components/create/generation-error-panel.tsx` | Shared generation status UI. `generation-task-list` keeps multiple active job cards constrained to the results column. |
|
||||
| Creation reuse drafts | `src/lib/creation-reuse.ts`, `src/app/create/page.tsx` | Shared localStorage/event bridge used by detail and reverse-prompt actions to prefill create panels. `/create?type=...` changes the active tab after navigation, so callers can route directly to text-to-image or image-to-image. |
|
||||
| Lightbox/fullscreen/detail actions | `src/components/lightbox.tsx`, `src/components/fullscreen-preview.tsx`, `src/components/creation-detail-dialog.tsx`, `src/components/image-metadata-badge.tsx` | Preview, copy, download, share, reuse config, edit output, and delete record. Image previews show actual natural resolution and computed aspect ratio in the upper-right metadata badge. `BareImagePreview` is the no-container overlay for uploaded reference image previews. |
|
||||
| Lightbox/fullscreen/detail actions | `src/components/lightbox.tsx`, `src/components/fullscreen-preview.tsx`, `src/components/creation-detail-dialog.tsx`, `src/components/image-metadata-badge.tsx` | Preview, copy, download, share, reuse config, edit output, and delete work. Delete work must use a confirmation dialog warning that deletion cannot be recovered before calling the server delete path. Image previews show actual natural resolution and computed aspect ratio in the upper-right metadata badge. `BareImagePreview` is the no-container overlay for uploaded reference image previews. |
|
||||
|
||||
## Generation System
|
||||
|
||||
|
||||
@@ -12,6 +12,16 @@ import {
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from '@/components/ui/dialog';
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogAction,
|
||||
AlertDialogCancel,
|
||||
AlertDialogContent,
|
||||
AlertDialogDescription,
|
||||
AlertDialogFooter,
|
||||
AlertDialogHeader,
|
||||
AlertDialogTitle,
|
||||
} from '@/components/ui/alert-dialog';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Download, Copy, FileSearch, ImageOff, Film, ImageIcon, Share2, CheckCircle2, Maximize2, RotateCcw, PencilLine, Trash2 } from 'lucide-react';
|
||||
@@ -128,6 +138,7 @@ export function CreationDetailDialog({ record, open, onClose, onPublishChange, o
|
||||
const [mediaAspectRatio, setMediaAspectRatio] = useState<number | null>(null);
|
||||
const [viewportSize, setViewportSize] = useState({ width: 1280, height: 900 });
|
||||
const [deleting, setDeleting] = useState(false);
|
||||
const [deleteConfirmOpen, setDeleteConfirmOpen] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (record) {
|
||||
@@ -257,7 +268,7 @@ export function CreationDetailDialog({ record, open, onClose, onPublishChange, o
|
||||
toast.success('已填入图生图');
|
||||
};
|
||||
|
||||
const handleDeleteRecord = async () => {
|
||||
const handleDeleteWork = async () => {
|
||||
if (deleting) return;
|
||||
setDeleting(true);
|
||||
try {
|
||||
@@ -266,8 +277,9 @@ export function CreationDetailDialog({ record, open, onClose, onPublishChange, o
|
||||
} else {
|
||||
await deleteCreationRecord(record.id);
|
||||
}
|
||||
setDeleteConfirmOpen(false);
|
||||
onClose();
|
||||
toast.success('记录已删除');
|
||||
toast.success('作品已删除');
|
||||
} catch (error) {
|
||||
toast.error(error instanceof Error ? error.message : '删除失败,请重试');
|
||||
} finally {
|
||||
@@ -275,6 +287,32 @@ export function CreationDetailDialog({ record, open, onClose, onPublishChange, o
|
||||
}
|
||||
};
|
||||
|
||||
const deleteConfirmDialog = (
|
||||
<AlertDialog open={deleteConfirmOpen} onOpenChange={setDeleteConfirmOpen}>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>确认删除作品?</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
删除该作品后将无法恢复,相关创作历史也会从服务器删除。是否确认删除?
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel disabled={deleting}>取消</AlertDialogCancel>
|
||||
<AlertDialogAction
|
||||
className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
|
||||
disabled={deleting}
|
||||
onClick={(event) => {
|
||||
event.preventDefault();
|
||||
void handleDeleteWork();
|
||||
}}
|
||||
>
|
||||
{deleting ? '删除中' : '确认删除'}
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
);
|
||||
|
||||
if (isReversePromptRecord) {
|
||||
const displayCreatedAt = new Date(record.createdAt).toLocaleString('zh-CN');
|
||||
const referenceImage = record.referenceImage && !isPlaceholder(record.referenceImage) ? record.referenceImage : null;
|
||||
@@ -360,11 +398,11 @@ export function CreationDetailDialog({ record, open, onClose, onPublishChange, o
|
||||
<Button
|
||||
variant="destructive"
|
||||
className="h-10 min-w-[102px] gap-1.5 px-3 text-sm font-semibold"
|
||||
onClick={handleDeleteRecord}
|
||||
onClick={() => setDeleteConfirmOpen(true)}
|
||||
disabled={deleting}
|
||||
>
|
||||
<Trash2 className="h-3.5 w-3.5" />
|
||||
{deleting ? '删除中' : '删除记录'}
|
||||
{deleting ? '删除中' : '删除作品'}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -378,6 +416,7 @@ export function CreationDetailDialog({ record, open, onClose, onPublishChange, o
|
||||
open={!!fullscreenSrc}
|
||||
onClose={() => setFullscreenSrc(null)}
|
||||
/>
|
||||
{deleteConfirmDialog}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -684,11 +723,11 @@ export function CreationDetailDialog({ record, open, onClose, onPublishChange, o
|
||||
<Button
|
||||
variant="destructive"
|
||||
className="h-10 min-w-[102px] gap-1.5 px-3 text-sm font-semibold"
|
||||
onClick={handleDeleteRecord}
|
||||
onClick={() => setDeleteConfirmOpen(true)}
|
||||
disabled={deleting}
|
||||
>
|
||||
<Trash2 className="h-3.5 w-3.5" />
|
||||
{deleting ? '删除中' : '删除记录'}
|
||||
{deleting ? '删除中' : '删除作品'}
|
||||
</Button>
|
||||
<Button className="h-10 min-w-[102px] gap-1.5 px-3 text-sm font-semibold" onClick={handleDownload} disabled={isPlaceholderUrl}>
|
||||
<Download className="h-3.5 w-3.5" />
|
||||
@@ -727,6 +766,7 @@ export function CreationDetailDialog({ record, open, onClose, onPublishChange, o
|
||||
open={!!fullscreenSrc}
|
||||
onClose={() => setFullscreenSrc(null)}
|
||||
/>
|
||||
{deleteConfirmDialog}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user