Files
egecankomur d49c9aa225 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
2026-06-04 07:15:18 +03:00

120 lines
3.3 KiB
TypeScript
Raw Permalink 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.
// Üst menü (header + mobil) düzeni. Öğeler sabit bir kayıttan gelir; admin
// panelinden yalnızca SIRA, GÖRÜNÜRLÜK ve (opsiyonel) ETİKET düzenlenir.
// Bu modül hem sunucu (header) hem istemci (admin formu) tarafında import
// edilebilir — bu yüzden "server-only" YOK.
export type NavKey =
| "home"
| "services"
| "solutions"
| "projects"
| "blog"
| "about"
| "contact";
export interface NavRegistryEntry {
key: NavKey;
label: string; // varsayılan etiket
href: string;
mega?: boolean; // Hizmetler — mega menü olarak render edilir
}
export const NAV_REGISTRY: Record<NavKey, NavRegistryEntry> = {
home: { key: "home", label: "Anasayfa", href: "/" },
services: { key: "services", label: "Hizmetler", href: "/hizmetler", mega: true },
solutions: { key: "solutions", label: "Çözümler", href: "/cozumler" },
projects: { key: "projects", label: "Projeler", href: "/projeler" },
blog: { key: "blog", label: "Blog", href: "/blog" },
about: { key: "about", label: "Hakkımızda", href: "/hakkimizda" },
contact: { key: "contact", label: "İletişim", href: "/iletisim" },
};
export const DEFAULT_NAV_ORDER: NavKey[] = [
"home",
"services",
"solutions",
"projects",
"blog",
"about",
"contact",
];
export interface NavItem {
key: NavKey;
label: string; // çözülmüş etiket (override veya varsayılan)
href: string;
mega: boolean;
visible: boolean;
}
interface StoredNavItem {
key: string;
visible?: boolean;
label?: string | null;
}
/**
* site_settings.nav_items içindeki JSON'ı kayıt ile birleştirir.
* - Kayıtlı sıra önceliklidir, geçersiz/silinmiş key'ler atlanır.
* - Kayıtta olmayan (yeni eklenen) öğeler varsayılan sırayla sona eklenir.
* - JSON yoksa/bozuksa tam varsayılan menü döner.
*/
export function resolveNavItems(navItemsJson?: string | null): NavItem[] {
let stored: StoredNavItem[] = [];
if (navItemsJson) {
try {
const parsed = JSON.parse(navItemsJson);
if (Array.isArray(parsed)) stored = parsed as StoredNavItem[];
} catch {
/* bozuk JSON — varsayılanlara düş */
}
}
const seen = new Set<NavKey>();
const ordered: NavItem[] = [];
for (const item of stored) {
const reg = NAV_REGISTRY[item.key as NavKey];
if (!reg || seen.has(reg.key)) continue;
seen.add(reg.key);
ordered.push({
key: reg.key,
label: item.label?.trim() || reg.label,
href: reg.href,
mega: !!reg.mega,
visible: item.visible !== false,
});
}
for (const key of DEFAULT_NAV_ORDER) {
if (seen.has(key)) continue;
const reg = NAV_REGISTRY[key];
ordered.push({
key: reg.key,
label: reg.label,
href: reg.href,
mega: !!reg.mega,
visible: true,
});
}
return ordered;
}
/** Admin formundan gelen düzeni depolanacak kompakt JSON'a çevirir. */
export function serializeNavItems(
items: { key: NavKey; visible: boolean; label?: string }[],
): string {
return JSON.stringify(
items.map((i) => {
const reg = NAV_REGISTRY[i.key];
const out: StoredNavItem = { key: i.key, visible: i.visible };
// Sadece varsayılandan farklıysa etiketi sakla
if (i.label && i.label.trim() && i.label.trim() !== reg.label) {
out.label = i.label.trim();
}
return out;
}),
);
}