From dbc55e7527a2aa173878b1bb2e6904cb741dbd6e Mon Sep 17 00:00:00 2001 From: Ege Can Komur Date: Wed, 20 May 2026 04:11:41 +0300 Subject: [PATCH] =?UTF-8?q?feat:=20MediaPicker=20=E2=80=94=20s=C3=BCr?= =?UTF-8?q?=C3=BCkle-b=C4=B1rak=20+=20progress=20bar=20+=20k=C3=BCt=C3=BCp?= =?UTF-8?q?hane=20modal?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mevcut sorun: - Her görsel için medya sayfasına git, yükle, URL kopyala, forma yapıştır → 4 adım - Sürükle-bırak yok, progress yok, hangi dosyanın yüklendiği belirsiz Çözüm: MediaPicker component (tek/çoklu mode) API route'ları: - POST /api/admin/media/upload — session auth + Appwrite Storage upload - GET /api/admin/media/list — kütüphane modal için dosya listesi Component özellikleri: - Sürükle-bırak drop zone (hover state ile) - Multiple file upload (çoklu mode) - XHR ile gerçek progress bar (%) — Server Action ile alınamazdı - Görsel preview (single: aspect-video, multiple: aspect-square grid) - Hover'da × ile kaldırma - Multiple mode'da sırasını değiştirme - 'Kütüphaneden seç' modal — daha önce yüklenmiş görselleri grid'de göster, tıklayınca seç - Error handling (dosya boyutu, ağ hatası vb.) - Başarılı yüklemeyi 2 saniye gösterip kaybetme Form alanları → MediaPicker (URL field'ları kaldırıldı): - Blog: cover_image, seo_image - Hizmet: hero_image - Proje: image_url (kapak), gallery (çoklu) - Referans: image_url - Sektör: hero_image - Ekip: photo_url - SEO sayfa: og_image - SEO global: default_og_image - Site Settings: client_logos (çoklu) Backward compat: form data formatı aynı kalıyor — hidden input ile URL satır satır. admin-actions değişmedi. URL elle yapıştırmak hala mümkün (kütüphaneden URL kopyala). --- app/admin/(protected)/blog/form.tsx | 20 +- app/admin/(protected)/ekip/form.tsx | 18 +- app/admin/(protected)/hizmetler/form.tsx | 8 +- app/admin/(protected)/projeler/form.tsx | 25 +- app/admin/(protected)/referanslar/form.tsx | 9 +- app/admin/(protected)/sektorler/form.tsx | 7 +- app/admin/(protected)/seo/page-form.tsx | 5 +- app/admin/(protected)/seo/page.tsx | 15 +- app/admin/(protected)/site/page.tsx | 11 +- app/api/admin/media/list/route.ts | 33 ++ app/api/admin/media/upload/route.ts | 36 ++ components/admin/media-picker.tsx | 492 +++++++++++++++++++++ 12 files changed, 627 insertions(+), 52 deletions(-) create mode 100644 app/api/admin/media/list/route.ts create mode 100644 app/api/admin/media/upload/route.ts create mode 100644 components/admin/media-picker.tsx diff --git a/app/admin/(protected)/blog/form.tsx b/app/admin/(protected)/blog/form.tsx index ec6196f..797ce75 100644 --- a/app/admin/(protected)/blog/form.tsx +++ b/app/admin/(protected)/blog/form.tsx @@ -8,6 +8,7 @@ import { Select, Textarea, } from "@/components/admin/form"; +import { MediaPicker } from "@/components/admin/media-picker"; import { saveBlogPost } from "@/lib/admin-actions"; import type { BlogPostRow } from "@/lib/types"; import { Save } from "lucide-react"; @@ -81,19 +82,16 @@ export function BlogForm({ post }: { post?: BlogPostRow }) { rows={14} placeholder={"# Başlık\n\nMarkdown desteklenir…"} /> - - @@ -107,11 +105,11 @@ export function BlogForm({ post }: { post?: BlogPostRow }) { defaultValue={post?.seo_title} help="Boş bırakırsanız yazı başlığı kullanılır." /> -
diff --git a/app/admin/(protected)/ekip/form.tsx b/app/admin/(protected)/ekip/form.tsx index d3be29a..63c8899 100644 --- a/app/admin/(protected)/ekip/form.tsx +++ b/app/admin/(protected)/ekip/form.tsx @@ -8,6 +8,7 @@ import { PrimaryButton, Textarea, } from "@/components/admin/form"; +import { MediaPicker } from "@/components/admin/media-picker"; import { saveTeamMember } from "@/lib/admin-actions"; import type { TeamMemberRow } from "@/lib/types"; @@ -29,13 +30,6 @@ export function TeamMemberForm({ row }: { row?: TeamMemberRow }) { defaultValue={row?.role} placeholder="Kurucu / Geliştirici / Tasarımcı" /> -
+ +
+ +