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).
Kovak Yazılım — Kurumsal Site + Admin Panel
Next.js 16 + TypeScript + Tailwind v4 + Appwrite ile geliştirilmiş kurumsal site ve içerik yönetim paneli.
Teknoloji
- Framework: Next.js 16 (App Router, Turbopack, React 19)
- Stil: Tailwind CSS v4
- Backend: Appwrite (TablesDB + Storage + Auth) —
https://db.kovaksoft.com - İçerik: Markdown (marked.js)
- İkonlar: lucide-react + inline SVG
- Form: React Server Actions +
useActionState
Kurulum
npm install
cp .env.example .env.local
# .env.local içine APPWRITE_API_KEY'i Appwrite Console'dan oluşturup ekle
npm run dev
Site: http://localhost:3000 Admin: http://localhost:3000/admin/login
İlk Admin Kullanıcısı
- Appwrite Console → Auth → Users → Create User
- Email + şifre belirle
/admin/loginüzerinden giriş yap
Appwrite Yapılandırması
Project ID: 69f27b51000a5bee46ce
Database ID: kovak-yazilim-db
Bucket ID: kovak-yazilim-media
Tablolar
| Tablo | İçerik |
|---|---|
services |
Hizmet kartları (slug, title, description, icon, order, featured) |
projects |
Referans projeler (slug, title, description, image_url, live_url, category, technologies[], year, featured) |
blog_posts |
Blog yazıları (slug, title, excerpt, content, cover_image, author, status, published_at, tags[], seo_*) |
testimonials |
Müşteri yorumları (name, role, company, message, rating, image_url, order, featured) |
seo_pages |
Sayfa bazlı SEO override (path, title, description, og_image, canonical, noindex) |
seo_settings |
Global SEO ayarları (singleton — rowId: global) |
contact_messages |
İletişim formu kayıtları (anonim create, users read/update/delete) |
Storage
kovak-yazilim-media — 10 MB max, image-only (jpg/png/webp/gif/svg/avif). Public read.
API Key
Appwrite Console → Settings → API Keys → Create
Scopes: databases.read, tables.read, rows.read, rows.write, files.read, files.write, users.read
Admin Paneli
/admin altında:
- Pano (
/admin) — Sayım kartları + hızlı aksiyonlar - Blog (
/admin/blog) — Yazı CRUD, draft/published durumu, markdown editor - Hizmetler (
/admin/hizmetler) — Hizmet CRUD, lucide ikon seçici - Projeler (
/admin/projeler) — Portfolyo CRUD - Referanslar (
/admin/referanslar) — Müşteri yorumları - SEO (
/admin/seo) — Global meta + sayfa bazlı override - Mesajlar (
/admin/iletisim) — Form inbox, status (new/read/replied/archived) - Medya (
/admin/medya) — Appwrite Storage browser, upload/delete
Auth Akışı
lib/auth.ts → getCurrentUser() & requireUser()
Login → account.createEmailPasswordSession → session secret HTTP-only cookie (kovak_session)
Admin layout (app/admin/(protected)/layout.tsx) requireUser() çağrısı yapar — yetkisiz giriş /admin/login'e redirect.
SEO Sistemi
lib/seo.ts → buildMetadata(path, fallback)
Sıralama (override öncelikli):
seo_pagestablosunda o path için kayıt varsa → onun title/description/og_image kullanılır- Yoksa sayfanın kendi fallback
Metadataobjesi - O da yoksa
seo_settings(global) - O da yoksa
lib/site-config.ts
Yapı
app/
(site)/ # Public site (Header + Footer ortak)
page.tsx # Anasayfa
hizmetler/ # /hizmetler
projeler/ # /projeler
blog/ # /blog, /blog/[slug]
hakkimizda/
iletisim/
admin/
login/ # /admin/login (auth dışı)
(protected)/ # requireUser() ile korunan grup
page.tsx # /admin
blog/
hizmetler/
projeler/
referanslar/
seo/
iletisim/
medya/
actions.ts # Public Server Action: submitContact
layout.tsx # Root layout (html/body)
components/
admin/ # Sidebar, topbar, form helpers, delete button
header.tsx, footer.tsx
hero.tsx, services-grid.tsx, projects-grid.tsx, testimonials.tsx
contact-form.tsx
lib/
appwrite.ts # Browser client
appwrite-server.ts # adminClient (API key) + sessionClient (cookie)
auth.ts # Session helpers
admin-actions.ts # Tüm CRUD server actions (gate() ile auth check)
data.ts # listX / getX sorguları
seo.ts # buildMetadata
site-config.ts # Marka + fallback değerler
types.ts # Row tipleri
public/logo.png # Logo
Build
npm run build # 23 route, public sayfalar static, admin dynamic
npm start
Gitea Remote
git remote add origin ssh://git@git.kovaksoft.com:2222/kovakmedya/kovakyazilim.git
git push -u origin main