Files
kovakyazilim/app/admin/(protected)/ekip/form.tsx
T
Ege Can Komur dbc55e7527 feat: MediaPicker — sürükle-bırak + progress bar + kütüphane modal
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).
2026-05-20 04:11:41 +03:00

76 lines
2.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { Save } from "lucide-react";
import {
Field,
FormActions,
FormShell,
GhostLink,
PageHeader,
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";
export function TeamMemberForm({ row }: { row?: TeamMemberRow }) {
return (
<div>
<PageHeader
title={row ? "Üyeyi düzenle" : "Yeni ekip üyesi"}
backHref="/admin/ekip"
/>
<form action={saveTeamMember}>
{row && <input type="hidden" name="id" value={row.$id} />}
<FormShell>
<div className="grid gap-5 md:grid-cols-2">
<Field label="Ad Soyad" name="name" required defaultValue={row?.name} />
<Field
label="Pozisyon / Rol"
name="role"
defaultValue={row?.role}
placeholder="Kurucu / Geliştirici / Tasarımcı"
/>
<Field
label="LinkedIn URL"
name="linkedin_url"
type="url"
defaultValue={row?.linkedin_url}
/>
<Field
label="Sıra"
name="order"
type="number"
defaultValue={row?.order ?? 0}
/>
</div>
<div className="mt-5">
<MediaPicker
label="Fotoğraf"
name="photo_url"
defaultValue={row?.photo_url}
maxSizeMB={5}
help="Kare format ideal. Hakkımızda sayfasında gösterilir."
/>
</div>
<div className="mt-5">
<Textarea
label="Biyografi"
name="bio"
rows={4}
defaultValue={row?.bio}
placeholder="2-3 cümle özet — uzmanlık, deneyim, ilgi alanı."
/>
</div>
<FormActions>
<GhostLink href="/admin/ekip">İptal</GhostLink>
<PrimaryButton>
<Save className="size-4" /> Kaydet
</PrimaryButton>
</FormActions>
</FormShell>
</form>
</div>
);
}