generated from fahricansecer/boilerplate-fe
This commit is contained in:
@@ -18,7 +18,7 @@ import {
|
||||
X,
|
||||
} from 'lucide-react';
|
||||
import Link from 'next/link';
|
||||
import { useState } from 'react';
|
||||
import { useState, useEffect } from 'react';
|
||||
import {
|
||||
useProject,
|
||||
useGenerateScript,
|
||||
@@ -27,7 +27,8 @@ import {
|
||||
useDeleteProject,
|
||||
useGenerateSceneImage,
|
||||
useUpscaleSceneImage,
|
||||
useRegenerateScene
|
||||
useRegenerateScene,
|
||||
useCancelRender
|
||||
} from '@/hooks/use-api';
|
||||
import { useRenderProgress } from '@/hooks/use-render-progress';
|
||||
import { SceneCard } from '@/components/project/scene-card';
|
||||
@@ -124,12 +125,25 @@ export default function ProjectDetailPage() {
|
||||
const router = useRouter();
|
||||
const [showMenu, setShowMenu] = useState(false);
|
||||
const [regeneratingSceneId, setRegeneratingSceneId] = useState<string | null>(null);
|
||||
const [mounted, setMounted] = useState(false);
|
||||
|
||||
// Veri hook'ları
|
||||
const { data: project, isLoading, error, refetch } = useProject(id);
|
||||
useEffect(() => {
|
||||
setMounted(true);
|
||||
}, []);
|
||||
|
||||
// Veri hook'ları (Aktif işlem varsa 3 saniyede bir polling yap)
|
||||
const { data: project, isLoading, error, refetch } = useProject(id, {
|
||||
refetchInterval: (data: any) => {
|
||||
if (!data) return false;
|
||||
const isGenerating = data.status === 'GENERATING_MEDIA' ||
|
||||
data.renderJobs?.some((j: any) => j.status === 'QUEUED' || j.status === 'PROCESSING');
|
||||
return isGenerating ? 3000 : false;
|
||||
}
|
||||
});
|
||||
const generateScriptMutation = useGenerateScript();
|
||||
const approveMutation = useApproveAndQueue();
|
||||
const deleteMutation = useDeleteProject();
|
||||
const cancelRenderMutation = useCancelRender();
|
||||
|
||||
const generateImageMutation = useGenerateSceneImage();
|
||||
const upscaleImageMutation = useUpscaleSceneImage();
|
||||
@@ -203,6 +217,15 @@ export default function ProjectDetailPage() {
|
||||
});
|
||||
};
|
||||
|
||||
// İptal et
|
||||
const handleCancelRender = () => {
|
||||
if (confirm('Aktif video üretimini iptal etmek istediğinize emin misiniz?')) {
|
||||
cancelRenderMutation.mutate(id, {
|
||||
onSuccess: () => refetch(),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Sil
|
||||
const handleDelete = () => {
|
||||
if (confirm('Bu projeyi silmek istediğinize emin misiniz?')) {
|
||||
@@ -457,6 +480,22 @@ export default function ProjectDetailPage() {
|
||||
Onayla & Video Üret
|
||||
</button>
|
||||
)}
|
||||
|
||||
{/* Üretimi İptal Et (Sadece aktif işlem varsa) */}
|
||||
{isRendering && (
|
||||
<button
|
||||
onClick={handleCancelRender}
|
||||
disabled={cancelRenderMutation.isPending}
|
||||
className="flex items-center gap-2 px-4 py-2.5 rounded-xl bg-red-500/10 text-red-400 text-sm font-medium hover:bg-red-500/20 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
{cancelRenderMutation.isPending ? (
|
||||
<Loader2 size={15} className="animate-spin" />
|
||||
) : (
|
||||
<X size={15} />
|
||||
)}
|
||||
Üretimi İptal Et
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Hata mesajı — kapatılabilir ve aksiyon butonlu */}
|
||||
@@ -571,34 +610,63 @@ export default function ProjectDetailPage() {
|
||||
{/* ── Render Geçmişi ── */}
|
||||
{project.renderJobs && project.renderJobs.length > 0 && (
|
||||
<motion.div variants={fadeUp}>
|
||||
<h2 className="text-sm font-semibold text-[var(--color-text-primary)] mb-3 flex items-center gap-2">
|
||||
<Clock size={15} className="text-cyan-400" />
|
||||
Render Geçmişi
|
||||
</h2>
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<h2 className="text-sm font-semibold text-[var(--color-text-primary)] flex items-center gap-2">
|
||||
<Clock size={15} className="text-cyan-400" />
|
||||
Render Geçmişi
|
||||
</h2>
|
||||
{project.status === 'GENERATING_MEDIA' || project.renderJobs.some((j: any) => j.status === 'QUEUED' || j.status === 'PROCESSING') ? (
|
||||
<button
|
||||
onClick={handleCancelRender}
|
||||
disabled={cancelRenderMutation.isPending}
|
||||
className="flex items-center gap-1.5 px-3 py-1.5 rounded-lg bg-red-500/10 text-red-400 hover:bg-red-500/20 text-xs font-medium transition-colors"
|
||||
>
|
||||
{cancelRenderMutation.isPending ? <Loader2 size={13} className="animate-spin" /> : <X size={13} />}
|
||||
İptal Et
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
{project.renderJobs.map((job) => (
|
||||
<div key={job.id} className="card-surface p-3 flex items-center justify-between">
|
||||
<div className="flex items-center gap-2.5">
|
||||
<div className={`w-2 h-2 rounded-full ${
|
||||
job.status === 'COMPLETED' ? 'bg-emerald-400' :
|
||||
job.status === 'FAILED' ? 'bg-red-400' :
|
||||
'bg-amber-400 animate-pulse'
|
||||
}`} />
|
||||
<span className="text-xs text-[var(--color-text-secondary)]">
|
||||
Deneme #{job.attemptNumber}
|
||||
</span>
|
||||
<span className="text-[10px] text-[var(--color-text-ghost)]">
|
||||
{job.status}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-3 text-[10px] text-[var(--color-text-ghost)]">
|
||||
{job.processingTimeMs && (
|
||||
<span>{(job.processingTimeMs / 1000).toFixed(1)}s</span>
|
||||
)}
|
||||
<span>
|
||||
{new Date(job.createdAt).toLocaleTimeString('tr-TR', { hour: '2-digit', minute: '2-digit' })}
|
||||
</span>
|
||||
<div key={job.id} className="card-surface p-3 flex flex-col gap-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2.5">
|
||||
<div className={`w-2 h-2 rounded-full ${
|
||||
job.status === 'COMPLETED' ? 'bg-emerald-400' :
|
||||
job.status === 'FAILED' ? 'bg-red-400' :
|
||||
job.status === 'CANCELLED' ? 'bg-slate-400' :
|
||||
'bg-amber-400 animate-pulse'
|
||||
}`} />
|
||||
<span className="text-xs text-[var(--color-text-secondary)]">
|
||||
Deneme #{job.attemptNumber}
|
||||
</span>
|
||||
<span className="text-[10px] text-[var(--color-text-ghost)]">
|
||||
{job.status}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-3 text-[10px] text-[var(--color-text-ghost)]">
|
||||
{job.processingTimeMs && (
|
||||
<span>{(job.processingTimeMs / 1000).toFixed(1)}s</span>
|
||||
)}
|
||||
{mounted ? (
|
||||
<span>
|
||||
{new Date(job.createdAt).toLocaleTimeString('tr-TR', { hour: '2-digit', minute: '2-digit' })}
|
||||
</span>
|
||||
) : (
|
||||
<span>--:--</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{(job.status === 'QUEUED' || job.status === 'PROCESSING') && (
|
||||
<div className="w-full bg-slate-800 rounded-full h-1.5 mt-1 overflow-hidden">
|
||||
<div
|
||||
className="bg-amber-400 h-1.5 rounded-full transition-all duration-1000 ease-out relative"
|
||||
style={{ width: `${job.progress || (job.status === 'QUEUED' ? 5 : 50)}%` }}
|
||||
>
|
||||
<div className="absolute inset-0 bg-white/20 animate-pulse" />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user