"use server"; import { revalidatePath } from "next/cache"; import { DATABASE_ID, ID, MEDIA_BUCKET_ID, storage, TABLES, tablesDB, } from "@/lib/appwrite-rest"; import { requireSessionSecret } from "@/lib/auth"; function slugify(s: string) { return s .toLowerCase() .replace(/[ğ]/g, "g") .replace(/[ü]/g, "u") .replace(/[ş]/g, "s") .replace(/[ı]/g, "i") .replace(/[ö]/g, "o") .replace(/[ç]/g, "c") .replace(/[^a-z0-9]+/g, "-") .replace(/^-+|-+$/g, "") .slice(0, 80); } function num(v: FormDataEntryValue | null) { if (v === null || v === "") return null; const n = Number(v); return Number.isFinite(n) ? n : null; } function bool(v: FormDataEntryValue | null) { return v === "on" || v === "true" || v === "1"; } function str(v: FormDataEntryValue | null) { if (v === null) return null; const s = String(v).trim(); return s === "" ? null : s; } function strArr(v: FormDataEntryValue | null) { if (v === null) return null; return String(v) .split(",") .map((x) => x.trim()) .filter(Boolean); } // ─── Media ─────────────────────────────────────────────────────── export async function uploadMedia(formData: FormData): Promise { const secret = await requireSessionSecret(); const file = formData.get("file"); if (!(file instanceof File) || file.size === 0) { throw new Error("Dosya seçilmedi"); } await storage.createFile(MEDIA_BUCKET_ID, ID.unique(), file, secret); revalidatePath("/admin/medya"); } export async function deleteMediaFile(fileId: string) { const secret = await requireSessionSecret(); await storage.deleteFile(MEDIA_BUCKET_ID, fileId, secret); revalidatePath("/admin/medya"); } // ─── Blog ──────────────────────────────────────────────────────── export async function saveBlogPost(formData: FormData) { const secret = await requireSessionSecret(); const id = str(formData.get("id")); const title = str(formData.get("title")); if (!title) throw new Error("Başlık zorunlu"); const slug = str(formData.get("slug")) || slugify(title); const status = (str(formData.get("status")) ?? "draft") as "draft" | "published"; const data = { slug, title, excerpt: str(formData.get("excerpt")), content: str(formData.get("content")), cover_image: str(formData.get("cover_image")), cover_file_id: str(formData.get("cover_file_id")), author: str(formData.get("author")), status, published_at: status === "published" ? str(formData.get("published_at")) || new Date().toISOString() : null, tags: strArr(formData.get("tags")), seo_title: str(formData.get("seo_title")), seo_description: str(formData.get("seo_description")), seo_image: str(formData.get("seo_image")), }; if (id) { await tablesDB.updateRow(DATABASE_ID, TABLES.blogPosts, id, data, secret); } else { await tablesDB.createRow( DATABASE_ID, TABLES.blogPosts, ID.unique(), data, secret, ); } revalidatePath("/admin/blog"); revalidatePath("/blog"); if (slug) revalidatePath(`/blog/${slug}`); } export async function deleteBlogPost(formData: FormData) { const secret = await requireSessionSecret(); const id = String(formData.get("id")); await tablesDB.deleteRow(DATABASE_ID, TABLES.blogPosts, id, secret); revalidatePath("/admin/blog"); revalidatePath("/blog"); } // ─── Services ──────────────────────────────────────────────────── export async function saveService(formData: FormData) { const secret = await requireSessionSecret(); const id = str(formData.get("id")); const title = str(formData.get("title")); if (!title) throw new Error("Başlık zorunlu"); const description = str(formData.get("description")); 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, description, 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); } else { await tablesDB.createRow( DATABASE_ID, TABLES.services, slug, data, secret, ); } revalidatePath("/admin/hizmetler"); revalidatePath("/hizmetler"); revalidatePath("/"); } export async function deleteService(formData: FormData) { const secret = await requireSessionSecret(); const id = String(formData.get("id")); await tablesDB.deleteRow(DATABASE_ID, TABLES.services, id, secret); revalidatePath("/admin/hizmetler"); revalidatePath("/hizmetler"); } // ─── Projects ──────────────────────────────────────────────────── export async function saveProject(formData: FormData) { const secret = await requireSessionSecret(); const id = str(formData.get("id")); const title = str(formData.get("title")); if (!title) throw new Error("Başlık zorunlu"); const slug = str(formData.get("slug")) || slugify(title); 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, description, image_url: str(formData.get("image_url")), live_url: str(formData.get("live_url")), category: str(formData.get("category")), 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) { await tablesDB.updateRow(DATABASE_ID, TABLES.projects, id, data, secret); } else { await tablesDB.createRow( DATABASE_ID, TABLES.projects, ID.unique(), data, secret, ); } revalidatePath("/admin/projeler"); revalidatePath("/projeler"); revalidatePath("/"); } export async function deleteProject(formData: FormData) { const secret = await requireSessionSecret(); const id = String(formData.get("id")); await tablesDB.deleteRow(DATABASE_ID, TABLES.projects, id, secret); revalidatePath("/admin/projeler"); revalidatePath("/projeler"); } // ─── Testimonials ──────────────────────────────────────────────── export async function saveTestimonial(formData: FormData) { const secret = await requireSessionSecret(); const id = str(formData.get("id")); const name = str(formData.get("name")); if (!name) throw new Error("Ad zorunlu"); const message = str(formData.get("message")); if (!message) throw new Error("Mesaj zorunlu"); const data = { name, role: str(formData.get("role")), company: str(formData.get("company")), message, rating: num(formData.get("rating")) ?? 5, image_url: str(formData.get("image_url")), order: num(formData.get("order")) ?? 0, featured: bool(formData.get("featured")), }; if (id) { await tablesDB.updateRow(DATABASE_ID, TABLES.testimonials, id, data, secret); } else { await tablesDB.createRow( DATABASE_ID, TABLES.testimonials, ID.unique(), data, secret, ); } revalidatePath("/admin/referanslar"); revalidatePath("/"); } export async function deleteTestimonial(formData: FormData) { const secret = await requireSessionSecret(); const id = String(formData.get("id")); await tablesDB.deleteRow(DATABASE_ID, TABLES.testimonials, id, secret); revalidatePath("/admin/referanslar"); } // ─── Site Settings (homepage content) ──────────────────────────── export async function saveSiteSettings(formData: FormData) { const secret = await requireSessionSecret(); // Hero stats: 3 satır halinde "value|label" formatında — JSON array'e çevir const statsRaw = String(formData.get("hero_stats") ?? ""); const stats = statsRaw .split("\n") .map((line) => { const [value, label] = line.split("|").map((s) => s.trim()); if (!value || !label) return null; return JSON.stringify({ value, label }); }) .filter((x): x is string => x !== null); // Trust items: "icon|value|label" satırlar const trustRaw = String(formData.get("trust_items") ?? ""); const trust = trustRaw .split("\n") .map((line) => { const [icon, value, label] = line.split("|").map((s) => s.trim()); if (!value || !label) return null; return JSON.stringify({ icon: icon || "Sparkles", value, label }); }) .filter((x): x is string => x !== null); // Why us / Process: blok '---' ile ayrılır; her blok için: // why_us: ilk satır = icon|title, kalanı description // process: ilk satır = title, kalanı description function parseBlocks(raw: string, withIcon: boolean): string[] { return raw .split("\n---\n") .map((block) => { const lines = block.trim().split("\n"); if (lines.length < 2) return null; if (withIcon) { const [icon, title] = lines[0].split("|").map((s) => s.trim()); const description = lines.slice(1).join("\n").trim(); if (!title || !description) return null; return JSON.stringify({ icon: icon || "Sparkles", title, description, }); } const title = lines[0].trim(); const description = lines.slice(1).join("\n").trim(); if (!title || !description) return null; return JSON.stringify({ title, description }); }) .filter((x): x is string => x !== null); } const whyUs = parseBlocks(String(formData.get("why_us") ?? ""), true); const processSteps = parseBlocks( String(formData.get("process_steps") ?? ""), false, ); // Client logos: her satıra bir URL const logosRaw = String(formData.get("client_logos") ?? ""); const logos = logosRaw .split("\n") .map((s) => s.trim()) .filter(Boolean); const data = { hero_badge: str(formData.get("hero_badge")), hero_title: str(formData.get("hero_title")), hero_subtitle: str(formData.get("hero_subtitle")), hero_cta_primary_label: str(formData.get("hero_cta_primary_label")), hero_cta_primary_href: str(formData.get("hero_cta_primary_href")), hero_cta_secondary_label: str(formData.get("hero_cta_secondary_label")), hero_cta_secondary_href: str(formData.get("hero_cta_secondary_href")), hero_stats: stats.length > 0 ? stats : null, services_eyebrow: str(formData.get("services_eyebrow")), services_title: str(formData.get("services_title")), services_description: str(formData.get("services_description")), projects_eyebrow: str(formData.get("projects_eyebrow")), projects_title: str(formData.get("projects_title")), projects_description: str(formData.get("projects_description")), testimonials_eyebrow: str(formData.get("testimonials_eyebrow")), testimonials_title: str(formData.get("testimonials_title")), testimonials_description: str(formData.get("testimonials_description")), cta_title: str(formData.get("cta_title")), cta_description: str(formData.get("cta_description")), cta_button_label: str(formData.get("cta_button_label")), cta_button_href: str(formData.get("cta_button_href")), contact_phone: str(formData.get("contact_phone")), contact_phone_raw: str(formData.get("contact_phone_raw")), contact_email: str(formData.get("contact_email")), contact_address: str(formData.get("contact_address")), contact_hours_weekday: str(formData.get("contact_hours_weekday")), contact_hours_weekend: str(formData.get("contact_hours_weekend")), social_linkedin: str(formData.get("social_linkedin")), social_instagram: str(formData.get("social_instagram")), social_twitter: str(formData.get("social_twitter")), social_facebook: str(formData.get("social_facebook")), footer_tagline: str(formData.get("footer_tagline")), whatsapp_message: str(formData.get("whatsapp_message")), client_logos: logos.length > 0 ? logos : null, trust_items: trust.length > 0 ? trust : null, why_us: whyUs.length > 0 ? whyUs : null, process_steps: processSteps.length > 0 ? processSteps : null, lead_form_title: str(formData.get("lead_form_title")), lead_form_description: str(formData.get("lead_form_description")), google_review_url: str(formData.get("google_review_url")), google_rating: num(formData.get("google_rating")), google_review_count: num(formData.get("google_review_count")), }; try { await tablesDB.updateRow( DATABASE_ID, TABLES.siteSettings, "homepage", data, secret, ); } catch { await tablesDB.createRow( DATABASE_ID, TABLES.siteSettings, "homepage", data, secret, ); } revalidatePath("/", "layout"); revalidatePath("/admin/site"); } // ─── SEO Settings ──────────────────────────────────────────────── export async function saveSeoSettings(formData: FormData) { const secret = await requireSessionSecret(); const data = { site_name: str(formData.get("site_name")), site_description: str(formData.get("site_description")), default_og_image: str(formData.get("default_og_image")), twitter_handle: str(formData.get("twitter_handle")), facebook_url: str(formData.get("facebook_url")), linkedin_url: str(formData.get("linkedin_url")), instagram_url: str(formData.get("instagram_url")), google_site_verification: str(formData.get("google_site_verification")), gtm_id: str(formData.get("gtm_id")), }; try { await tablesDB.updateRow( DATABASE_ID, TABLES.seoSettings, "global", data, secret, ); } catch { await tablesDB.createRow( DATABASE_ID, TABLES.seoSettings, "global", data, secret, ); } revalidatePath("/", "layout"); revalidatePath("/admin/seo"); } // ─── SEO Page ──────────────────────────────────────────────────── export async function saveSeoPage(formData: FormData) { const secret = await requireSessionSecret(); const id = str(formData.get("id")); const path = str(formData.get("path")); if (!path) throw new Error("Path zorunlu"); const data = { path, title: str(formData.get("title")), description: str(formData.get("description")), og_image: str(formData.get("og_image")), canonical: str(formData.get("canonical")), noindex: bool(formData.get("noindex")), }; if (id) { await tablesDB.updateRow(DATABASE_ID, TABLES.seoPages, id, data, secret); } else { await tablesDB.createRow( DATABASE_ID, TABLES.seoPages, ID.unique(), data, secret, ); } revalidatePath(path); revalidatePath("/admin/seo"); } export async function deleteSeoPage(formData: FormData) { const secret = await requireSessionSecret(); const id = String(formData.get("id")); await tablesDB.deleteRow(DATABASE_ID, TABLES.seoPages, id, secret); revalidatePath("/admin/seo"); } // ─── Contact ───────────────────────────────────────────────────── export async function updateMessageStatus(formData: FormData) { const secret = await requireSessionSecret(); const id = String(formData.get("id")); const status = String(formData.get("status")) as | "new" | "read" | "replied" | "archived"; await tablesDB.updateRow( DATABASE_ID, TABLES.contactMessages, id, { status }, secret, ); revalidatePath("/admin/iletisim"); } export async function deleteMessage(formData: FormData) { const secret = await requireSessionSecret(); const id = String(formData.get("id")); await tablesDB.deleteRow( DATABASE_ID, TABLES.contactMessages, id, secret, ); revalidatePath("/admin/iletisim"); }