Files
Ege Can Komur f49df9cbeb feat: Hakkımızda sayfası yönetilebilir (site_settings + /admin/site)
Önce hard-coded olan tüm metinler artık /admin/site > 'Hakkımızda sayfası'
bölümünden düzenlenebilir.

site_settings'e 9 yeni alan eklendi:
- about_eyebrow, about_title, about_description (üst hero)
- about_values (string array JSON {title, description}) — 4 değer kartı
- about_hero_image (opsiyonel, boşsa logo gösterilir)
- about_team_eyebrow, about_team_title, about_team_description
- about_stats (string array JSON {value, label}) — alt navy bant

Mevcut WP değerleri default olarak seed edildi.

Hakkımızda sayfası (app/(site)/hakkimizda/page.tsx) artık:
- Tüm metinler settings'ten okunuyor (fallback default'lar var)
- Hero image varsa logo yerine onu gösteriyor
- Stats sıfırdan farklı sayıda olabilir (3 yerine 2/4)

Admin form (/admin/site):
- Yeni 'Hakkımızda sayfası' section
- 4 alt-bölüm: Üst hero / Değerler / Ekip / Stats
- MediaPicker ile hero image
- Markdown benzeri textarea'lar (--- ayırıcı, | seperator)
2026-05-20 20:50:30 +03:00

162 lines
5.9 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.
import type { Metadata } from "next";
import Image from "next/image";
import { CheckCircle2 } from "lucide-react";
import { SectionTitle } from "@/components/section-title";
import { TeamGrid } from "@/components/team-grid";
import { getSiteSettings, listTeamMembers } from "@/lib/data";
import { buildMetadata } from "@/lib/seo";
import type { AboutValue, StatItem } from "@/lib/types";
export async function generateMetadata(): Promise<Metadata> {
return buildMetadata("/hakkimizda", {
title: "Hakkımızda",
description:
"Kovak Yazılım, Kocaeli merkezli bir teknoloji ajansıdır. Web, mobil ve CRM çözümleri üretir.",
});
}
const DEFAULT_VALUES: AboutValue[] = [
{ title: "Uçtan uca üretim", description: "Fikir aşamasından lansmana, lansman sonrası bakıma kadar tek bir ekip." },
{ title: "Ölçülebilir sonuç", description: "Her projeyi performans, dönüşüm ve kullanıcı deneyimi metrikleriyle değerlendiriyoruz." },
{ title: "Şeffaf süreç", description: "Her sprint demo ile başlar, her engel açıkça konuşulur. Sürprize yer yok." },
{ title: "Uzun vadeli ortaklık", description: "Proje biter, iş büyür. Bakım ve geliştirme süreçlerinde yanınızdayız." },
];
const DEFAULT_STATS: StatItem[] = [
{ value: "50+", label: "Tamamlanan proje" },
{ value: "30+", label: "Mutlu müşteri" },
{ value: "10+", label: "Yıllık deneyim" },
];
function parseValues(items?: string[] | null): AboutValue[] {
if (!items || items.length === 0) return DEFAULT_VALUES;
const out: AboutValue[] = [];
for (const raw of items) {
try {
const obj = JSON.parse(raw) as Partial<AboutValue>;
if (obj.title && obj.description) out.push({ title: obj.title, description: obj.description });
} catch {
/* ignore */
}
}
return out.length > 0 ? out : DEFAULT_VALUES;
}
function parseStats(items?: string[] | null): StatItem[] {
if (!items || items.length === 0) return DEFAULT_STATS;
const out: StatItem[] = [];
for (const raw of items) {
try {
const obj = JSON.parse(raw) as Partial<StatItem>;
if (obj.value && obj.label) out.push({ value: obj.value, label: obj.label });
} catch {
/* ignore */
}
}
return out.length > 0 ? out : DEFAULT_STATS;
}
export default async function AboutPage() {
const [team, settings] = await Promise.all([
listTeamMembers(),
getSiteSettings(),
]);
const eyebrow = settings?.about_eyebrow ?? "Hakkımızda";
const title = settings?.about_title ?? "Kocaeli'den dünyaya dijital ürünler";
const description =
settings?.about_description ??
"Kovak Yazılım, kurumsal markalardan girişimlere kadar geniş bir yelpazedeki müşterileri için web, mobil ve CRM çözümleri üretir. Hızlı, ölçeklenebilir ve estetik.";
const values = parseValues(settings?.about_values);
const heroImage = settings?.about_hero_image ?? null;
const teamEyebrow = settings?.about_team_eyebrow ?? "Ekibimiz";
const teamTitle = settings?.about_team_title ?? "Projenizde Kimlerle Çalışırsınız?";
const teamDescription =
settings?.about_team_description ??
"Sizin projenizde birebir çalışacak kurucular — teknik altyapı ve ürün geliştirmenin arkasındaki isimler.";
const stats = parseStats(settings?.about_stats);
return (
<>
<section className="mx-auto max-w-7xl px-6 py-20">
<div className="grid items-center gap-12 md:grid-cols-2">
<div>
<SectionTitle
align="left"
eyebrow={eyebrow}
title={title}
description={description}
/>
<ul className="mt-10 space-y-4">
{values.map((v) => (
<li key={v.title} className="flex gap-3">
<CheckCircle2 className="mt-1 size-5 shrink-0 text-[var(--sky-600)]" />
<div>
<p className="font-semibold text-[var(--navy)]">{v.title}</p>
<p className="text-sm text-[var(--muted)]">{v.description}</p>
</div>
</li>
))}
</ul>
</div>
<div className="relative">
<div className="absolute inset-0 -z-10 rounded-3xl bg-gradient-to-br from-[var(--sky-50)] to-[var(--navy-50)]" />
<div className="relative flex aspect-square items-center justify-center overflow-hidden rounded-3xl p-12">
{heroImage ? (
<Image
src={heroImage}
alt={title}
fill
sizes="(min-width: 768px) 50vw, 100vw"
className="object-cover"
priority
/>
) : (
<Image
src="/logo.png"
alt="Kovak Yazılım"
width={400}
height={400}
className="size-full object-contain drop-shadow-xl"
/>
)}
</div>
</div>
</div>
</section>
{team.length > 0 && (
<section className="border-y border-[var(--border)] bg-gray-50 py-20">
<div className="mx-auto max-w-7xl px-6">
<SectionTitle
eyebrow={teamEyebrow}
title={teamTitle}
description={teamDescription}
/>
<div className="mt-14">
<TeamGrid members={team} />
</div>
</div>
</section>
)}
{stats.length > 0 && (
<section className="bg-[var(--navy)] py-20 text-white">
<div className="mx-auto grid max-w-7xl gap-12 px-6 md:grid-cols-3">
{stats.map((s) => (
<div key={s.label} className="text-center">
<p className="text-5xl font-bold">{s.value}</p>
<p className="mt-2 text-sm text-white/70">{s.label}</p>
</div>
))}
</div>
</section>
)}
</>
);
}