From 804f5b395e449eb95cd3ae949ed89974f9562637 Mon Sep 17 00:00:00 2001 From: Harun CAN Date: Thu, 9 Apr 2026 12:05:20 +0300 Subject: [PATCH] main --- next-env.d.ts | 2 +- .../dashboard/projects/[id]/page.tsx | 94 +++++---- .../(dashboard)/dashboard/projects/page.tsx | 188 +++++++++++++----- src/components/project/scene-card.tsx | 48 ++--- 4 files changed, 221 insertions(+), 111 deletions(-) diff --git a/next-env.d.ts b/next-env.d.ts index 9edff1c..c4b7818 100644 --- a/next-env.d.ts +++ b/next-env.d.ts @@ -1,6 +1,6 @@ /// /// -import "./.next/types/routes.d.ts"; +import "./.next/dev/types/routes.d.ts"; // NOTE: This file should not be edited // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/src/app/[locale]/(dashboard)/dashboard/projects/[id]/page.tsx b/src/app/[locale]/(dashboard)/dashboard/projects/[id]/page.tsx index 0de4ecd..6d47528 100644 --- a/src/app/[locale]/(dashboard)/dashboard/projects/[id]/page.tsx +++ b/src/app/[locale]/(dashboard)/dashboard/projects/[id]/page.tsx @@ -15,6 +15,7 @@ import { Film, Trash2, MoreVertical, + X, } from 'lucide-react'; import Link from 'next/link'; import { useState } from 'react'; @@ -244,7 +245,7 @@ export default function ProjectDetailPage() { const statusInfo = STATUS_MAP[project.status] || STATUS_MAP.DRAFT; const StatusIcon = statusInfo.icon; const isRendering = ['PENDING', 'GENERATING_MEDIA', 'RENDERING', 'GENERATING_SCRIPT'].includes(project.status); - const isEditable = !isRendering; + const isEditable = !isRendering; // COMPLETED, DRAFT, FAILED → editable const hasScript = project.scenes && project.scenes.length > 0; const isCompleted = project.status === 'COMPLETED'; const tweetData = project.sourceTweetData as Record | undefined; @@ -350,38 +351,33 @@ export default function ProjectDetailPage() { {project.targetDuration}s - {isEditable ? ( - - ) : ( - {currentStyle ? `${currentStyle.emoji} ${currentStyle.label}` : project.videoStyle} - )} - {project.videoStyle === 'CINEMATIC' && isEditable ? ( + + + {project.videoStyle === 'CINEMATIC' && ( - ) : project.videoStyle === 'CINEMATIC' && project.cinematicReference ? ( - - 🎬 {project.cinematicReference} - - ) : null} + )} {project.language} @@ -415,11 +411,11 @@ export default function ProjectDetailPage() { {/* Aksiyon butonları */}
{/* Senaryo üret (draft, senaryo yok) */} - {isEditable && !hasScript && ( + {!hasScript && ( )} {/* Onayla ve video üretimini başlat */} - {isEditable && hasScript && ( + {hasScript && (
- {/* Hata mesajı */} + {/* Hata mesajı — kapatılabilir ve aksiyon butonlu */} {project.errorMessage && (
-
- - Hata +
+
+ + Hata +
+
-

{project.errorMessage}

+

{project.errorMessage}

+ {hasScript && ( + + )}
)} @@ -512,6 +529,7 @@ export default function ProjectDetailPage() { key={scene.id} scene={scene} isEditable={isEditable} + isRendering={isRendering} onUpdate={handleSceneUpdate} onRegenerate={handleSceneRegenerate} onGenerateImage={handleGenerateImage} diff --git a/src/app/[locale]/(dashboard)/dashboard/projects/page.tsx b/src/app/[locale]/(dashboard)/dashboard/projects/page.tsx index 6cd8512..89369b4 100644 --- a/src/app/[locale]/(dashboard)/dashboard/projects/page.tsx +++ b/src/app/[locale]/(dashboard)/dashboard/projects/page.tsx @@ -1,7 +1,7 @@ "use client"; -import { useState, useMemo } from "react"; -import { motion } from "framer-motion"; +import { useState, useMemo, useCallback } from "react"; +import { motion, AnimatePresence } from "framer-motion"; import { Plus, Search, @@ -13,6 +13,7 @@ import { ExternalLink, Loader2, Trash2, + X, } from "lucide-react"; import Link from "next/link"; import { cn } from "@/lib/utils"; @@ -83,17 +84,28 @@ interface ProjectItem { export default function ProjectsPage() { const [activeFilter, setActiveFilter] = useState("all"); const [searchQuery, setSearchQuery] = useState(""); + const [deleteTarget, setDeleteTarget] = useState<{ id: string; title: string } | null>(null); const { data, isLoading } = useProjects({ limit: 100 }); const deleteMutation = useDeleteProject(); - const handleDelete = (e: React.MouseEvent, id: string) => { + // Silme onay modal'ını aç (native confirm yerine) + const openDeleteConfirm = useCallback((e: React.MouseEvent, project: ProjectItem) => { e.preventDefault(); e.stopPropagation(); - if (confirm("Bu projeyi silmek istediğinize emin misiniz?")) { - deleteMutation.mutate(id); - } - }; + setDeleteTarget({ id: project.id, title: project.title }); + }, []); + + // Silme işlemini gerçekleştir + const confirmDelete = useCallback(() => { + if (!deleteTarget) return; + deleteMutation.mutate(deleteTarget.id, { + onSettled: () => { + setDeleteTarget(null); + }, + }); + }, [deleteTarget, deleteMutation]); + // useProjects returns PaginatedResponse which has .data as Project[] // eslint-disable-next-line @typescript-eslint/no-explicit-any const raw = data as any; @@ -220,63 +232,141 @@ export default function ProjectsPage() { const st = statusMap[project.status] ?? statusMap.draft; const StIcon = st.icon; return ( - -
- -
-
-

- {project.title} -

-
- - {new Date(project.createdAt).toLocaleDateString( - "tr-TR", - { - day: "numeric", - month: "short", - year: "numeric", - }, - )} - - {project.language && • {project.language}} - {typeof project.creditsUsed === "number" && - project.creditsUsed > 0 && ( - • {project.creditsUsed} kredi - )} +
+
-
- - {st.label} - +
+

+ {project.title} +

+
+ + {new Date(project.createdAt).toLocaleDateString( + "tr-TR", + { + day: "numeric", + month: "short", + year: "numeric", + }, + )} + + {project.language && • {project.language}} + {typeof project.creditsUsed === "number" && + project.creditsUsed > 0 && ( + • {project.creditsUsed} kredi + )} +
+
+ + {st.label} + + + - - - +
); }) )} )} + + {/* ─── Silme Onay Modal ─── */} + + {deleteTarget && ( + !deleteMutation.isPending && setDeleteTarget(null)} + > + e.stopPropagation()} + > + {/* Kapatma butonu */} + + + {/* Icon */} +
+ +
+ + {/* İçerik */} +

+ Projeyi Sil +

+

+ Bu projeyi silmek istediğinize emin misiniz? +

+

+ “{deleteTarget.title}” +

+ + {/* Butonlar */} +
+ + +
+
+
+ )} +
); } diff --git a/src/components/project/scene-card.tsx b/src/components/project/scene-card.tsx index a5990dc..05bf20c 100644 --- a/src/components/project/scene-card.tsx +++ b/src/components/project/scene-card.tsx @@ -17,6 +17,7 @@ interface SceneCardProps { mediaAssets?: Array<{ id: string; type: string; url?: string }>; }; isEditable: boolean; + isRendering?: boolean; onUpdate?: (sceneId: string, data: { narrationText?: string; visualPrompt?: string; subtitleText?: string }) => void; onRegenerate?: (sceneId: string) => void; onGenerateImage?: (sceneId: string, customPrompt?: string) => void; @@ -29,6 +30,7 @@ interface SceneCardProps { export function SceneCard({ scene, isEditable, + isRendering = false, onUpdate, onRegenerate, onGenerateImage, @@ -91,19 +93,20 @@ export function SceneCard({ {/* Aksiyon butonları */} - {isEditable && !isEditing && ( + {!isEditing && (
)} - {isEditable && ( -
+ {/* Görsel üretim butonları — tüm projelerde her zaman göster, render sürecinde disable et */} +
+ + {thumbnailAsset?.url && ( - {thumbnailAsset?.url && ( - - )} -
- )} + )} +
)}