d49c9aa225
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
120 lines
3.3 KiB
TypeScript
120 lines
3.3 KiB
TypeScript
// Ü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;
|
||
}),
|
||
);
|
||
}
|