main
Some checks are pending
UI Deploy (Next-Auth Support) 🎨 / build-and-deploy (push) Waiting to run

This commit is contained in:
Harun CAN
2026-04-05 21:10:51 +03:00
parent dd8878d403
commit 5b03bec882
5 changed files with 124 additions and 13 deletions

View File

@@ -33,6 +33,7 @@ import { SceneCard } from '@/components/project/scene-card';
import { RenderProgress } from '@/components/project/render-progress';
import { VideoPlayer } from '@/components/project/video-player';
import { projectsApi } from '@/lib/api/api-service';
import { CINEMATIC_REFERENCES } from '@/constants/cinematic-references';
// X (Twitter) ikonunu burada da tanımlıyoruz
const XIcon = ({ size = 16 }: { size?: number }) => (
@@ -255,8 +256,18 @@ export default function ProjectDetailPage() {
try {
await projectsApi.update(id, { videoStyle: newStyleId } as any);
refetch();
} catch (err) {
console.error('Üslup (Stil) değiştirme hatası:', err);
} catch (error) {
console.error('Failed to update style:', error);
}
};
const handleCinematicReferenceChange = async (newRef: string) => {
if (newRef === project.cinematicReference) return;
try {
await projectsApi.update(id, { cinematicReference: newRef || undefined } as any);
refetch();
} catch (error) {
console.error('Failed to update cinematic reference:', error);
}
};
@@ -354,6 +365,24 @@ export default function ProjectDetailPage() {
) : (
<span>{currentStyle ? `${currentStyle.emoji} ${currentStyle.label}` : project.videoStyle}</span>
)}
{project.videoStyle === 'CINEMATIC' && isEditable ? (
<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"
>
<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]">
{new Date(project.createdAt).toLocaleDateString('tr-TR', {
@@ -485,7 +514,11 @@ export default function ProjectDetailPage() {
isEditable={isEditable}
onUpdate={handleSceneUpdate}
onRegenerate={handleSceneRegenerate}
onGenerateImage={handleGenerateImage}
onUpscaleImage={handleUpscaleImage}
isRegenerating={regeneratingSceneId === scene.id}
isGeneratingImage={generatingImageId === scene.id}
isUpscalingImage={upscalingImageId === scene.id}
/>
))}
</div>

View File

@@ -12,10 +12,11 @@ import {
AlertCircle,
ExternalLink,
Loader2,
Trash2,
} from "lucide-react";
import Link from "next/link";
import { cn } from "@/lib/utils";
import { useProjects } from "@/hooks/use-api";
import { useProjects, useDeleteProject } from "@/hooks/use-api";
const statusFilters = [
{ id: "all", label: "Tümü" },
@@ -84,6 +85,15 @@ export default function ProjectsPage() {
const [searchQuery, setSearchQuery] = useState("");
const { data, isLoading } = useProjects({ limit: 100 });
const deleteMutation = useDeleteProject();
const handleDelete = (e: React.MouseEvent, id: string) => {
e.preventDefault();
e.stopPropagation();
if (confirm("Bu projeyi silmek istediğinize emin misiniz?")) {
deleteMutation.mutate(id);
}
};
// useProjects returns PaginatedResponse<Project> which has .data as Project[]
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const raw = data as any;
@@ -243,10 +253,20 @@ export default function ProjectsPage() {
</div>
</div>
<span
className={`text-[10px] font-medium px-2.5 py-1 rounded-full border ${st.color} border-current/20 ${st.bgColor} shrink-0`}
className={`text-[10px] font-medium px-2.5 py-1 rounded-full border ${st.color} border-current/20 ${st.bgColor} shrink-0 mr-2`}
>
{st.label}
</span>
<button
onClick={(e) => handleDelete(e, project.id)}
className="p-2 rounded-lg text-[var(--color-text-ghost)] hover:text-red-400 hover:bg-red-500/10 transition-colors shrink-0 z-10 mr-1"
title="Projeyi Sil"
disabled={deleteMutation.isPending}
>
<Trash2 size={16} />
</button>
<ExternalLink
size={14}
className="text-[var(--color-text-ghost)] group-hover:text-violet-400 transition-colors shrink-0"

View File

@@ -2,7 +2,7 @@
import { useState } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import { Pencil, Check, X, RefreshCw, Clock, ArrowRight, Wand2, Image as ImageIcon, Mic, Maximize2 } from 'lucide-react';
import { Pencil, Check, X, RefreshCw, Clock, ArrowRight, Wand2, Image as ImageIcon, Mic, Maximize2, Sparkles } from 'lucide-react';
interface SceneCardProps {
scene: {
@@ -92,7 +92,7 @@ export function SceneCard({
{/* Aksiyon butonları */}
{isEditable && !isEditing && (
<div className="flex items-center gap-1 opacity-0 group-hover:opacity-100 transition-opacity">
<div className="flex items-center gap-1 opacity-100 md:opacity-0 md: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"
@@ -201,7 +201,7 @@ export function SceneCard({
{/* Görsel / Upscale Alanı */}
<div className="flex flex-col gap-2 pt-2">
{thumbnailAsset?.url ? (
{thumbnailAsset?.url && !isGeneratingImage ? (
<div className="relative group/thumb rounded-lg overflow-hidden border border-[var(--color-border-faint)] aspect-video max-w-sm">
<img
src={thumbnailAsset.url}
@@ -214,9 +214,24 @@ export function SceneCard({
</div>
</div>
) : (
<div className="rounded-lg border border-dashed border-[var(--color-border-faint)] bg-[var(--color-bg-deep)] aspect-video max-w-sm flex flex-col items-center justify-center p-4">
<div className="rounded-lg border border-dashed border-[var(--color-border-faint)] bg-[var(--color-bg-deep)] aspect-video max-w-sm flex flex-col items-center justify-center p-4 relative overflow-hidden">
{isGeneratingImage ? (
<div className="flex flex-col items-center justify-center animate-in fade-in zoom-in duration-300">
<div className="relative w-12 h-12 mb-3">
<div className="absolute inset-0 rounded-full border-2 border-emerald-500/20"></div>
<div className="absolute inset-0 rounded-full border-2 border-emerald-500 border-t-transparent animate-spin"></div>
<Sparkles size={16} className="absolute inset-0 m-auto text-emerald-400 animate-pulse" />
</div>
<p className="text-xs font-medium text-emerald-400 text-center animate-pulse">
AI Görsel Üretiyor...
</p>
</div>
) : (
<>
<ImageIcon size={24} className="text-[var(--color-text-ghost)] mb-2" />
<p className="text-xs text-[var(--color-text-muted)] text-center">Görsel Henüz Üretilmedi</p>
</>
)}
</div>
)}

View File

@@ -0,0 +1,30 @@
export const CINEMATIC_REFERENCES = [
// Yönetmen Stilleri
{ value: 'Denis Villeneuve', label: 'Denis Villeneuve (Blade Runner 2049, Dune)' },
{ value: 'Christopher Nolan', label: 'Christopher Nolan (Interstellar, Inception)' },
{ value: 'Wes Anderson', label: 'Wes Anderson (Simetri, Pastel Renkler)' },
{ value: 'Stanley Kubrick', label: 'Stanley Kubrick (Tek Nokta Perspektifi, Derinlik)' },
{ value: 'Quentin Tarantino', label: 'Quentin Tarantino (Dinamik Açılar, Canlı Renkler)' },
{ value: 'Zack Snyder', label: 'Zack Snyder (Karanlık, Ağır Çekim, Yüksek Kontrast)' },
{ value: 'Hayao Miyazaki', label: 'Hayao Miyazaki / Studio Ghibli (Anime, Sıcak, Büyülü)' },
{ value: 'Tim Burton', label: 'Tim Burton (Gotik, Ekspresyonist)' },
{ value: 'Andrei Tarkovsky', label: 'Andrei Tarkovsky (Şiirsel, Yavaş, Atmosferik)' },
{ value: 'David Fincher', label: 'David Fincher (Karanlık, Yeşil/Sarı Tonlar)' },
{ value: 'George Miller', label: 'George Miller (Mad Max, Kaotik, Çöl Tonları)' },
{ value: 'Ridley Scott', label: 'Ridley Scott (Epik, Yoğun Işık, Dumanlı)' },
// Film ve Oyun Stilleri
{ value: 'The Matrix', label: 'The Matrix (Yeşil Filtre, Cyberpunk)' },
{ value: 'Cyberpunk 2077', label: 'Cyberpunk 2077 (Neon Işıklar, Fütüristik)' },
{ value: 'Stranger Things', label: 'Stranger Things (80ler Retro, Synthwave)' },
{ value: 'Arcane', label: 'Arcane (Boyanmış Animasyon, Steampunk)' },
{ value: 'Spider-Verse', label: 'Spider-Verse (Çizgi Roman Stili, Dinamik Kareler)' },
{ value: 'Lord of the Rings', label: 'Lord of the Rings (Epik Fantastik, Geniş Açılar)' },
{ value: 'Sopranos', label: 'Cinéma Vérité (Gerçekçi Belgesel Stili)' },
{ value: 'Sin City', label: 'Sin City (Siyah Beyaz, Kırmızı Vurgu)' },
// Dönem ve Sanat Stilleri
{ value: 'Noir', label: 'Film Noir (1940\'lar Suç, Siyah Beyaz)' },
{ value: 'Vintage 1970s', label: '70ler Vintage (Grenli, Sıcak Tonlar)' },
{ value: 'Vaporwave', label: 'Vaporwave (Mor, Pembe, Retro-PC)' },
{ value: 'Anime 90s', label: '90lar Anime (VHS Efektli, Nostaljik)' },
{ value: 'Pixar 3D', label: 'Pixar 3D (Tatlı, Yuvarlak Hatlar, Yumuşak Işık)' },
];

View File

@@ -16,6 +16,7 @@ export interface Project {
language: string;
aspectRatio: string;
videoStyle: string;
cinematicReference?: string;
targetDuration: number;
creditsUsed: number;
thumbnailUrl?: string;
@@ -135,17 +136,28 @@ export interface PaginatedResponse<T> {
export interface CreateProjectPayload {
title: string;
description?: string;
topic?: string;
prompt?: string;
prompt: string;
language?: string;
aspectRatio?: string;
style?: string;
videoStyle?: string;
cinematicReference?: string;
targetDuration?: number;
seoKeywords?: string[];
referenceUrl?: string;
}
export interface UpdateProjectPayload {
title?: string;
description?: string;
prompt?: string;
language?: string;
aspectRatio?: string;
videoStyle?: string;
cinematicReference?: string;
targetDuration?: number;
seoKeywords?: string[];
}
export interface CreditBalance {
balance: number;
remaining: number;
@@ -232,6 +244,7 @@ export interface CreateFromTweetPayload {
language?: string;
aspectRatio?: string;
videoStyle?: string;
cinematicReference?: string;
targetDuration?: number;
}