"use client"; import { useState, useEffect, useRef, useTransition, useCallback } from "react"; import { Image as ImageIcon, Upload, Trash2, Loader2, Plus, AlertCircle } from "lucide-react"; interface Screenshot { id: string; attributes: { fileName: string; fileSize: number; uploadOperations: UploadOperation[] | null; assetDeliveryState: { state: "COMPLETE" | "FAILED" | "UPLOAD_COMPLETE" | "AWAITING_UPLOAD"; errors?: { code: string; description: string }[]; } | null; templateUrl: string | null; }; } interface UploadOperation { method: string; url: string; length: number; offset: number; requestHeaders: { name: string; value: string }[]; } interface ScreenshotManagerProps { versionId: string; versionString: string; } export default function ScreenshotManager({ versionId, versionString }: ScreenshotManagerProps) { const [localizationId, setLocalizationId] = useState(null); const [screenshots, setScreenshots] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [uploading, setUploading] = useState(false); const [deletingId, setDeletingId] = useState(null); const [dragging, setDragging] = useState(false); const fileInputRef = useRef(null); const [, startTransition] = useTransition(); const fetchScreenshots = useCallback(async (locId: string) => { setError(null); const res = await fetch(`/api/asc/screenshots?localizationId=${locId}`); const d = await res.json(); if (!res.ok) { setError(d.error ?? "Ekran görüntüleri yüklenemedi"); return; } setScreenshots(d.screenshots ?? []); }, []); useEffect(() => { setLoading(true); // Step 1: resolve the first localization id for this version fetch(`/api/asc/localizations?versionId=${versionId}`) .then((r) => r.json()) .then(async (d) => { const locs = d.data ?? []; if (locs.length === 0) { setLoading(false); return; } const firstId: string = locs[0].id; setLocalizationId(firstId); await fetchScreenshots(firstId); }) .catch((e) => setError(e.message)) .finally(() => setLoading(false)); }, [versionId, fetchScreenshots]); async function uploadFile(file: File) { if (!localizationId) return; setUploading(true); setError(null); try { // Step 1: Reserve const reserveRes = await fetch(`/api/asc/screenshots`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ localizationId, fileName: file.name, fileSize: file.size }), }); const reserveData = await reserveRes.json(); if (!reserveRes.ok) { setError(reserveData.error ?? "Yükleme başlatılamadı"); return; } const screenshotId: string = reserveData.data?.id; const uploadOps: UploadOperation[] = reserveData.data?.attributes?.uploadOperations ?? []; // Step 2: Upload each part for (const op of uploadOps) { const slice = file.slice(op.offset, op.offset + op.length); const headers: Record = {}; for (const h of op.requestHeaders) headers[h.name] = h.value; await fetch(op.url, { method: op.method, headers, body: slice }); } // Step 3: Commit const commitRes = await fetch(`/api/asc/screenshots/commit`, { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ screenshotId }), }); if (!commitRes.ok) { const d = await commitRes.json(); setError(d.error ?? "Yükleme tamamlanamadı"); return; } await fetchScreenshots(localizationId); } catch (e) { setError(e instanceof Error ? e.message : "Bilinmeyen hata"); } finally { setUploading(false); } } function handleFiles(files: FileList | null) { if (!files || files.length === 0) return; startTransition(() => { Array.from(files).forEach((f) => uploadFile(f)); }); } async function handleDelete(id: string) { setDeletingId(id); setError(null); try { const res = await fetch(`/api/asc/screenshots?screenshotId=${id}`, { method: "DELETE" }); if (!res.ok) { const d = await res.json(); setError(d.error ?? "Silinemedi"); } else { setScreenshots((prev) => prev.filter((s) => s.id !== id)); } } finally { setDeletingId(null); } } function onDragOver(e: React.DragEvent) { e.preventDefault(); setDragging(true); } function onDragLeave() { setDragging(false); } function onDrop(e: React.DragEvent) { e.preventDefault(); setDragging(false); handleFiles(e.dataTransfer.files); } return (
{/* Header */}

Ekran Görüntüleri iPhone 6.5″ · v{versionString} {screenshots.length}/10

handleFiles(e.target.files)} />
{/* Body */}
{error && (
{error}
)} {loading ? (
) : !localizationId ? (

Bu sürüm için lokalizasyon bulunamadı.

) : ( <> {screenshots.length > 0 && (
{screenshots.map((s) => { const src = s.attributes.templateUrl; const state = s.attributes.assetDeliveryState?.state; return (
{src ? ( // eslint-disable-next-line @next/next/no-img-element {s.attributes.fileName} ) : (
{state === "AWAITING_UPLOAD" ? "Yükleniyor…" : state ?? "İşleniyor"}
)}
{state && state !== "COMPLETE" && (
{state === "UPLOAD_COMPLETE" ? "İşleniyor…" : state}
)}
); })}
)} {screenshots.length < 10 && (
!uploading && fileInputRef.current?.click()} className={`border-2 border-dashed rounded-xl p-8 flex flex-col items-center gap-3 cursor-pointer transition-all ${ dragging ? "border-sky-500 bg-sky-500/10" : "border-white/10 hover:border-white/20 hover:bg-white/[0.02]" } ${uploading ? "pointer-events-none opacity-60" : ""}`} > {uploading ? : }

{uploading ? "Yükleniyor…" : "PNG / JPEG sürükle veya tıkla"}

iPhone 6.5″ · Önerilen: 1242 × 2688 px

)} )}
); }