feat: hizmet ve proje detay sayfaları + galeri sistemi
Yeni Appwrite kolonları: - services: content (markdown), features[], faq[] (JSON-encoded), hero_image - projects: gallery[], content (markdown), client_name, industry, duration, service_slug Public sayfalar: - /hizmetler/[slug]: hero + features checklist + markdown content + FAQ accordion + ilgili projeler (service_slug eşleşmesi) - /projeler/[slug]: hero + meta tablosu (müşteri/sektör/süre/yıl) + kapak görseli + markdown vaka çalışması + lightbox galeri + diğer projeler Yeni componentler: - components/gallery.tsx: lightbox galeri (keyboard nav, prev/next, ESC kapat) - components/faq-list.tsx: accordion FAQ (tek seferde tek açık) Admin formları: - Hizmet formu: hero_image, content (markdown), features (virgülle), FAQ (her blok '---' ile ayrılır, ilk satır soru, kalanı cevap) - Proje formu: gallery (her satıra bir URL), content (markdown), client_name, industry, duration, service_slug (dropdown — hizmetlerden seçim) Linkler: - ServicesGrid kartları → /hizmetler/[slug] - ProjectsGrid kartları → /projeler/[slug] (live_url butonu ayrı, target=_blank) 29 route üretiliyor.
This commit is contained in:
@@ -129,6 +129,19 @@ export async function saveService(formData: FormData) {
|
||||
if (!description) throw new Error("Açıklama zorunlu");
|
||||
const slug = str(formData.get("slug")) || slugify(title);
|
||||
|
||||
// FAQ as JSON-encoded array. Each item: {"q":"...","a":"..."}
|
||||
const faqRaw = String(formData.get("faq") ?? "");
|
||||
const faq = faqRaw
|
||||
.split("\n---\n")
|
||||
.map((block) => {
|
||||
const lines = block.trim().split("\n");
|
||||
const q = lines[0]?.trim();
|
||||
const a = lines.slice(1).join("\n").trim();
|
||||
if (!q || !a) return null;
|
||||
return JSON.stringify({ q, a });
|
||||
})
|
||||
.filter((x): x is string => x !== null);
|
||||
|
||||
const data = {
|
||||
slug,
|
||||
title,
|
||||
@@ -136,6 +149,10 @@ export async function saveService(formData: FormData) {
|
||||
icon: str(formData.get("icon")),
|
||||
order: num(formData.get("order")) ?? 0,
|
||||
featured: bool(formData.get("featured")),
|
||||
content: str(formData.get("content")),
|
||||
features: strArr(formData.get("features"))?.filter(Boolean) ?? null,
|
||||
faq: faq.length > 0 ? faq : null,
|
||||
hero_image: str(formData.get("hero_image")),
|
||||
};
|
||||
if (id) {
|
||||
await tablesDB.updateRow(DATABASE_ID, TABLES.services, id, data, secret);
|
||||
@@ -172,6 +189,13 @@ export async function saveProject(formData: FormData) {
|
||||
const description = str(formData.get("description"));
|
||||
if (!description) throw new Error("Açıklama zorunlu");
|
||||
|
||||
// Gallery: one URL per line
|
||||
const galleryRaw = String(formData.get("gallery") ?? "");
|
||||
const gallery = galleryRaw
|
||||
.split("\n")
|
||||
.map((s) => s.trim())
|
||||
.filter(Boolean);
|
||||
|
||||
const data = {
|
||||
slug,
|
||||
title,
|
||||
@@ -182,6 +206,12 @@ export async function saveProject(formData: FormData) {
|
||||
technologies: strArr(formData.get("technologies")),
|
||||
year: num(formData.get("year")),
|
||||
featured: bool(formData.get("featured")),
|
||||
gallery: gallery.length > 0 ? gallery : null,
|
||||
content: str(formData.get("content")),
|
||||
client_name: str(formData.get("client_name")),
|
||||
industry: str(formData.get("industry")),
|
||||
duration: str(formData.get("duration")),
|
||||
service_slug: str(formData.get("service_slug")),
|
||||
};
|
||||
|
||||
if (id) {
|
||||
|
||||
Reference in New Issue
Block a user