main
UI Deploy (Next-Auth Support) 🎨 / build-and-deploy (push) Has been cancelled

This commit is contained in:
Harun CAN
2026-04-09 12:05:20 +03:00
parent 5b03bec882
commit 804f5b395e
4 changed files with 221 additions and 111 deletions
@@ -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<string, any> | undefined;
@@ -350,38 +351,33 @@ export default function ProjectDetailPage() {
<span className="flex items-center gap-1">
<Clock size={12} /> {project.targetDuration}s
</span>
{isEditable ? (
<select
value={project.videoStyle}
onChange={(e) => handleStyleChange(e.target.value)}
className="bg-[var(--color-bg-base)] border border-[var(--color-border-faint)] rounded-md px-2 py-0.5 text-xs text-[var(--color-text-secondary)] focus:outline-none focus:border-violet-500/50 cursor-pointer"
>
{videoStyles.map((s) => (
<option key={s.id} value={s.id}>
{s.emoji} {s.label}
</option>
))}
</select>
) : (
<span>{currentStyle ? `${currentStyle.emoji} ${currentStyle.label}` : project.videoStyle}</span>
)}
{project.videoStyle === 'CINEMATIC' && isEditable ? (
<select
value={project.videoStyle}
onChange={(e) => handleStyleChange(e.target.value)}
disabled={isRendering}
className="bg-[var(--color-bg-base)] border border-[var(--color-border-faint)] rounded-md px-2 py-0.5 text-xs text-[var(--color-text-secondary)] focus:outline-none focus:border-violet-500/50 disabled:opacity-50 disabled:cursor-not-allowed cursor-pointer"
>
{videoStyles.map((s) => (
<option key={s.id} value={s.id}>
{s.emoji} {s.label}
</option>
))}
</select>
{project.videoStyle === 'CINEMATIC' && (
<select
value={project.cinematicReference || ''}
onChange={(e) => handleCinematicReferenceChange(e.target.value)}
className="bg-[var(--color-bg-base)] border border-[var(--color-border-faint)] rounded-md px-2 py-0.5 text-xs text-[var(--color-text-secondary)] focus:outline-none focus:border-violet-500/50 cursor-pointer w-44 truncate"
disabled={isRendering}
className="bg-[var(--color-bg-base)] border border-[var(--color-border-faint)] rounded-md px-2 py-0.5 text-xs text-[var(--color-text-secondary)] focus:outline-none focus:border-violet-500/50 disabled:opacity-50 disabled:cursor-not-allowed cursor-pointer w-44 truncate"
>
<option value="">🎬 Sinematik Yönetmen/Film...</option>
{CINEMATIC_REFERENCES.map(ref => (
<option key={ref.value} value={ref.value}>{ref.label}</option>
))}
</select>
) : project.videoStyle === 'CINEMATIC' && project.cinematicReference ? (
<span className="flex items-center gap-1">
🎬 <span className="truncate max-w-[150px]">{project.cinematicReference}</span>
</span>
) : null}
)}
<span className="uppercase text-[10px] tracking-wider">{project.language}</span>
<span className="text-[10px]">
@@ -415,11 +411,11 @@ export default function ProjectDetailPage() {
{/* Aksiyon butonları */}
<div className="flex flex-wrap items-center gap-2 mt-4 pt-4 border-t border-[var(--color-border-faint)]">
{/* Senaryo üret (draft, senaryo yok) */}
{isEditable && !hasScript && (
{!hasScript && (
<button
onClick={handleGenerateScript}
disabled={generateScriptMutation.isPending}
className="flex items-center gap-2 px-4 py-2.5 rounded-xl bg-gradient-to-r from-violet-500 to-violet-600 text-white text-sm font-medium shadow-lg shadow-violet-500/20 hover:shadow-violet-500/30 transition-shadow disabled:opacity-50"
disabled={isRendering || generateScriptMutation.isPending}
className="flex items-center gap-2 px-4 py-2.5 rounded-xl bg-gradient-to-r from-violet-500 to-violet-600 text-white text-sm font-medium shadow-lg shadow-violet-500/20 hover:shadow-violet-500/30 transition-shadow disabled:opacity-50 disabled:cursor-not-allowed"
>
{generateScriptMutation.isPending ? (
<Loader2 size={15} className="animate-spin" />
@@ -431,27 +427,27 @@ export default function ProjectDetailPage() {
)}
{/* Senaryo yeniden üret (draft/failed, senaryo var) */}
{isEditable && hasScript && (
{hasScript && (
<button
onClick={handleGenerateScript}
disabled={generateScriptMutation.isPending}
className="flex items-center gap-2 px-4 py-2.5 rounded-xl bg-[var(--color-bg-elevated)] text-[var(--color-text-secondary)] text-sm font-medium hover:bg-[var(--color-bg-surface)] transition-colors disabled:opacity-50"
disabled={isRendering || generateScriptMutation.isPending}
className="flex items-center gap-2 px-4 py-2.5 rounded-xl bg-[var(--color-bg-elevated)] text-[var(--color-text-secondary)] text-sm font-medium hover:bg-[var(--color-bg-surface)] transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
>
{generateScriptMutation.isPending ? (
<Loader2 size={15} className="animate-spin" />
) : (
<RefreshCw size={15} />
)}
Yeniden Üret
Senaryoyu Yeniden Üret
</button>
)}
{/* Onayla ve video üretimini başlat */}
{isEditable && hasScript && (
{hasScript && (
<button
onClick={handleApprove}
disabled={approveMutation.isPending}
className="flex items-center gap-2 px-4 py-2.5 rounded-xl bg-gradient-to-r from-emerald-500 to-emerald-600 text-white text-sm font-medium shadow-lg shadow-emerald-500/20 hover:shadow-emerald-500/30 transition-shadow disabled:opacity-50"
disabled={isRendering || approveMutation.isPending}
className="flex items-center gap-2 px-4 py-2.5 rounded-xl bg-gradient-to-r from-emerald-500 to-emerald-600 text-white text-sm font-medium shadow-lg shadow-emerald-500/20 hover:shadow-emerald-500/30 transition-shadow disabled:opacity-50 disabled:cursor-not-allowed"
>
{approveMutation.isPending ? (
<Loader2 size={15} className="animate-spin" />
@@ -463,14 +459,35 @@ export default function ProjectDetailPage() {
)}
</div>
{/* Hata mesajı */}
{/* Hata mesajı — kapatılabilir ve aksiyon butonlu */}
{project.errorMessage && (
<div className="mt-3 p-3 rounded-lg bg-red-500/10 border border-red-500/20">
<div className="flex items-center gap-2 mb-1">
<AlertCircle size={14} className="text-red-400" />
<span className="text-xs font-medium text-red-400">Hata</span>
<div className="flex items-center justify-between mb-1">
<div className="flex items-center gap-2">
<AlertCircle size={14} className="text-red-400" />
<span className="text-xs font-medium text-red-400">Hata</span>
</div>
<button
onClick={() => {
// Sayfayı yeniden yükleyerek güncel veriyi al
refetch();
}}
className="text-red-400/60 hover:text-red-400 transition-colors"
title="Kapat"
>
<X size={14} />
</button>
</div>
<p className="text-xs text-red-400/80">{project.errorMessage}</p>
<p className="text-xs text-red-400/80 mb-2">{project.errorMessage}</p>
{hasScript && (
<button
onClick={handleGenerateScript}
disabled={generateScriptMutation.isPending}
className="text-xs px-3 py-1.5 rounded-md bg-red-500/20 text-red-300 hover:bg-red-500/30 transition-colors disabled:opacity-50"
>
{generateScriptMutation.isPending ? 'Üretiliyor...' : '🔄 Senaryoyu Yeniden Üret'}
</button>
)}
</div>
)}
</motion.div>
@@ -512,6 +529,7 @@ export default function ProjectDetailPage() {
key={scene.id}
scene={scene}
isEditable={isEditable}
isRendering={isRendering}
onUpdate={handleSceneUpdate}
onRegenerate={handleSceneRegenerate}
onGenerateImage={handleGenerateImage}