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

This commit is contained in:
Harun CAN
2026-03-30 00:22:06 +03:00
parent 45a540c530
commit 8bd995ea18
44 changed files with 3721 additions and 11852 deletions
+196
View File
@@ -0,0 +1,196 @@
'use client';
import { useState } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import { Pencil, Check, X, RefreshCw, Clock, ArrowRight, Wand2, Image, Mic } from 'lucide-react';
interface SceneCardProps {
scene: {
id: string;
order: number;
title?: string;
narrationText: string;
visualPrompt: string;
subtitleText?: string;
duration: number;
transitionType: string;
mediaAssets?: Array<{ id: string; type: string; url?: string }>;
};
isEditable: boolean;
onUpdate?: (sceneId: string, data: { narrationText?: string; visualPrompt?: string; subtitleText?: string }) => void;
onRegenerate?: (sceneId: string) => void;
isRegenerating?: boolean;
}
export function SceneCard({ scene, isEditable, onUpdate, onRegenerate, isRegenerating }: SceneCardProps) {
const [isEditing, setIsEditing] = useState(false);
const [editNarration, setEditNarration] = useState(scene.narrationText);
const [editVisual, setEditVisual] = useState(scene.visualPrompt);
const handleSave = () => {
onUpdate?.(scene.id, {
narrationText: editNarration,
visualPrompt: editVisual,
subtitleText: editNarration,
});
setIsEditing(false);
};
const handleCancel = () => {
setEditNarration(scene.narrationText);
setEditVisual(scene.visualPrompt);
setIsEditing(false);
};
return (
<motion.div
layout
initial={{ opacity: 0, y: 12 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: scene.order * 0.05, duration: 0.4 }}
className="relative group"
>
<div className="card-surface p-4 md:p-5 hover:border-violet-500/20 transition-all duration-300">
{/* Header */}
<div className="flex items-center justify-between mb-3">
<div className="flex items-center gap-2.5">
<div className="w-8 h-8 rounded-lg bg-gradient-to-br from-violet-500/20 to-violet-600/10 flex items-center justify-center">
<span className="text-xs font-bold text-violet-400">{scene.order}</span>
</div>
<div>
<h4 className="text-sm font-semibold text-[var(--color-text-primary)]">
{scene.title || `Sahne ${scene.order}`}
</h4>
<div className="flex items-center gap-2 mt-0.5">
<span className="flex items-center gap-1 text-[10px] text-[var(--color-text-ghost)]">
<Clock size={10} /> {scene.duration}s
</span>
<span className="flex items-center gap-1 text-[10px] text-[var(--color-text-ghost)]">
<ArrowRight size={10} /> {scene.transitionType.toLowerCase()}
</span>
</div>
</div>
</div>
{/* Aksiyon butonları */}
{isEditable && !isEditing && (
<div className="flex items-center gap-1 opacity-0 group-hover:opacity-100 transition-opacity">
<button
onClick={() => setIsEditing(true)}
className="w-7 h-7 rounded-lg flex items-center justify-center text-[var(--color-text-muted)] hover:text-violet-400 hover:bg-violet-500/10 transition-colors"
title="Düzenle"
>
<Pencil size={13} />
</button>
<button
onClick={() => onRegenerate?.(scene.id)}
disabled={isRegenerating}
className="w-7 h-7 rounded-lg flex items-center justify-center text-[var(--color-text-muted)] hover:text-cyan-400 hover:bg-cyan-500/10 transition-colors disabled:opacity-40"
title="AI ile yeniden üret"
>
<RefreshCw size={13} className={isRegenerating ? 'animate-spin' : ''} />
</button>
</div>
)}
</div>
<AnimatePresence mode="wait">
{isEditing ? (
<motion.div
key="editing"
initial={{ opacity: 0, height: 0 }}
animate={{ opacity: 1, height: 'auto' }}
exit={{ opacity: 0, height: 0 }}
className="space-y-3"
>
{/* Narrasyon düzenleme */}
<div>
<label className="flex items-center gap-1.5 text-xs font-medium text-[var(--color-text-muted)] mb-1.5">
<Mic size={12} /> Narrasyon
</label>
<textarea
value={editNarration}
onChange={(e) => setEditNarration(e.target.value)}
rows={3}
className="w-full px-3 py-2 rounded-lg bg-[var(--color-bg-deep)] border border-[var(--color-border-faint)] text-sm text-[var(--color-text-primary)] resize-none focus:outline-none focus:ring-1 focus:ring-violet-500/40 transition-all"
/>
</div>
{/* Görsel prompt düzenleme */}
<div>
<label className="flex items-center gap-1.5 text-xs font-medium text-[var(--color-text-muted)] mb-1.5">
<Image size={12} /> Görsel Prompt
</label>
<textarea
value={editVisual}
onChange={(e) => setEditVisual(e.target.value)}
rows={2}
className="w-full px-3 py-2 rounded-lg bg-[var(--color-bg-deep)] border border-[var(--color-border-faint)] text-sm text-[var(--color-text-secondary)] resize-none focus:outline-none focus:ring-1 focus:ring-cyan-500/40 transition-all"
/>
</div>
{/* Kaydet/İptal */}
<div className="flex items-center gap-2 pt-1">
<button
onClick={handleSave}
className="flex items-center gap-1.5 px-3 py-1.5 rounded-lg bg-violet-500/15 text-violet-400 text-xs font-medium hover:bg-violet-500/25 transition-colors"
>
<Check size={13} /> Kaydet
</button>
<button
onClick={handleCancel}
className="flex items-center gap-1.5 px-3 py-1.5 rounded-lg bg-[var(--color-bg-elevated)] text-[var(--color-text-muted)] text-xs font-medium hover:text-[var(--color-text-secondary)] transition-colors"
>
<X size={13} /> İptal
</button>
</div>
</motion.div>
) : (
<motion.div key="viewing" className="space-y-2.5">
{/* Narrasyon */}
<div className="flex gap-2">
<div className="w-5 h-5 rounded-md bg-violet-500/10 flex items-center justify-center shrink-0 mt-0.5">
<Mic size={11} className="text-violet-400" />
</div>
<p className="text-sm text-[var(--color-text-secondary)] leading-relaxed">
{scene.narrationText}
</p>
</div>
{/* Görsel Prompt */}
<div className="flex gap-2">
<div className="w-5 h-5 rounded-md bg-cyan-500/10 flex items-center justify-center shrink-0 mt-0.5">
<Image size={11} className="text-cyan-400" />
</div>
<p className="text-xs text-[var(--color-text-ghost)] leading-relaxed italic">
{scene.visualPrompt}
</p>
</div>
{/* Medya önizleme (varsa) */}
{scene.mediaAssets && scene.mediaAssets.length > 0 && (
<div className="flex gap-2 pt-1">
{scene.mediaAssets.slice(0, 3).map((asset) => (
<div
key={asset.id}
className="w-16 h-16 rounded-lg bg-[var(--color-bg-deep)] border border-[var(--color-border-faint)] flex items-center justify-center overflow-hidden"
>
{asset.url ? (
<img src={asset.url} alt="" className="w-full h-full object-cover" />
) : (
<Wand2 size={14} className="text-[var(--color-text-ghost)]" />
)}
</div>
))}
</div>
)}
</motion.div>
)}
</AnimatePresence>
</div>
{/* Sahne bağlantı çizgisi */}
<div className="absolute left-7 -bottom-3 w-px h-3 bg-gradient-to-b from-[var(--color-border-faint)] to-transparent" />
</motion.div>
);
}