feat: SEO altyapısı + admin editör/favicon/menü düzeltmeleri

Admin & site:
- @tailwindcss/typography ekle → editör ve yayın içeriği prose stilleriyle düzgün render
- Favicon: logo.png'den kare app/icon.png + apple-icon.png, varsayılan favicon.ico kaldırıldı
- SEO keyword: seo_settings.default_keywords + seo_pages.keywords + buildMetadata birleştirme
- Menü düzeni admin'den yönetilebilir (site_settings.nav_items, /admin/menu, header & mobile-menu refactor)

SEO:
- app/sitemap.ts (statik + blog/hizmet/çözüm/proje/sektör dinamik)
- app/robots.ts (sitemap ref + /admin,/api disallow)
- app/llms.txt/route.ts (AI/LLM rehberi)
- BlogPosting/Service/FAQ/Article JSON-LD wire (json-ld bileşenleri bağlandı)
- buildMetadata: blog/proje OG görseli + type article + keywords birleştirme düzeltmesi
- blog tags → keyword
This commit is contained in:
egecankomur
2026-06-04 07:15:18 +03:00
parent a321ac5c9b
commit d49c9aa225
26 changed files with 780 additions and 151 deletions
+30
View File
@@ -555,6 +555,34 @@ export async function saveSiteSettings(formData: FormData) {
revalidatePath("/admin/site");
}
// ─── Navigation Menu ─────────────────────────────────────────────
export async function saveNavMenu(formData: FormData) {
const secret = await requireSessionSecret();
// Client form, sıralı menüyü JSON string olarak nav_items'a koyar.
const navItems = str(formData.get("nav_items"));
const data = { nav_items: navItems };
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/menu");
}
// ─── SEO Settings ────────────────────────────────────────────────
export async function saveSeoSettings(formData: FormData) {
@@ -562,6 +590,7 @@ export async function saveSeoSettings(formData: FormData) {
const data = {
site_name: str(formData.get("site_name")),
site_description: str(formData.get("site_description")),
default_keywords: str(formData.get("default_keywords")),
default_og_image: str(formData.get("default_og_image")),
twitter_handle: str(formData.get("twitter_handle")),
facebook_url: str(formData.get("facebook_url")),
@@ -603,6 +632,7 @@ export async function saveSeoPage(formData: FormData) {
path,
title: str(formData.get("title")),
description: str(formData.get("description")),
keywords: str(formData.get("keywords")),
og_image: str(formData.get("og_image")),
canonical: str(formData.get("canonical")),
noindex: bool(formData.get("noindex")),