From cf46e30a7e9c65a8ee3d13699e36d87e86a99183 Mon Sep 17 00:00:00 2001 From: Ege Can Komur Date: Wed, 20 May 2026 04:03:21 +0300 Subject: [PATCH] =?UTF-8?q?feat:=20TR=20rakip=20analizi=20=E2=86=92=20sat?= =?UTF-8?q?=C4=B1=C5=9F=20blokerleri=20d=C3=BCzeltildi?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rakip analizi (kocaelidijital.com, promedyanet.com, lf.com.tr) sonrası satış blokerleri tespit edildi ve aşağıdaki bölümler eklendi: 1. ANASAYFADA SSS (8 hazır soru, admin'den düzenlenebilir) - Fiyat, süre, ödeme, garanti, hosting, SEO, mevcut site yenileme, sadece tasarım hizmeti gibi en sık sorulan sorular - HomepageFaq component (sticky sol + accordion sağ) - site_settings.homepage_faq[] (JSON {q,a}) 2. RISK REVERSAL bölümü (Guarantee component) - 'İlk taslak ücretsiz, memnun değilseniz devam etmiyoruz' - 4 garanti maddesi checklist - site_settings.guarantee_title/description/items 3. PROJE METRİKLERİ (vaka çalışması güçlendirme) - projects.metrics[] (JSON {value,label}) - Detay sayfada büyük metric kartları - Admin formda 'değer | etiket' satır formatı 4. HERO COPY GÜNCELLEMESİ (admin'den düzenlenebilir) - 'Kocaeli'de 2-3 hafta içinde yayında olan, satan kurumsal web siteleri' - 'İlk tasarım taslakı ücretsiz' vurgusu - Trust band: 30 dk yanıt + ücretsiz taslak + 4.9 memnuniyet 5. /SITE-ANALIZI LEAD MAGNET SAYFASI - URL + ad + email + telefon formu - 6 analiz başlığı (CWV, mobil, SEO, güvenlik, içerik, rakip) - contact_messages'a source=quick-site-audit ile yazılır - 'subject' alanı ile inbox'ta ayırt edilebilir 6. EKİP BÖLÜMÜ (Hakkımızda sayfasında) - Yeni team_members tablosu (name, role, bio, photo, linkedin) - /admin/ekip CRUD sayfası - TeamGrid component 7. SEKTÖR LANDING SAYFALARI (/sektor/[slug]) - Yeni industries tablosu (slug, title, content, features, faq, SEO) - /admin/sektorler CRUD sayfası - SEO + ad-targeted landing template - Hero + trust + features + content + garanti + projeler + hizmetler + FAQ + JSON-LD Admin /admin/site formuna yeni bölümler: - 'Risk reversal / Garanti' (title + description + items) - 'Anasayfa SSS' (---' bloklarla) App sidebar'a 'Sektörler' ve 'Ekip' linkleri eklendi. Footer'a 'Ücretsiz Site Analizi' linki eklendi. 36 route üretiliyor (önceki 31'den +5: /site-analizi, /sektor/[slug], /admin/ekip + alt, /admin/sektorler + alt). --- app/(site)/hakkimizda/page.tsx | 21 +- app/(site)/page.tsx | 6 + app/(site)/projeler/[slug]/page.tsx | 43 ++++ app/(site)/sektor/[slug]/page.tsx | 210 ++++++++++++++++++ app/(site)/site-analizi/page.tsx | 137 ++++++++++++ app/admin/(protected)/ekip/[id]/edit/page.tsx | 16 ++ app/admin/(protected)/ekip/form.tsx | 71 ++++++ app/admin/(protected)/ekip/new/page.tsx | 5 + app/admin/(protected)/ekip/page.tsx | 79 +++++++ app/admin/(protected)/projeler/form.tsx | 27 +++ .../(protected)/sektorler/[id]/edit/page.tsx | 16 ++ app/admin/(protected)/sektorler/form.tsx | 140 ++++++++++++ app/admin/(protected)/sektorler/new/page.tsx | 5 + app/admin/(protected)/sektorler/page.tsx | 83 +++++++ app/admin/(protected)/site/page.tsx | 57 +++++ components/admin/sidebar.tsx | 4 + components/audit-form.tsx | 115 ++++++++++ components/footer.tsx | 4 + components/guarantee.tsx | 79 +++++++ components/homepage-faq.tsx | 51 +++++ components/team-grid.tsx | 57 +++++ lib/admin-actions.ts | 106 +++++++++ lib/appwrite-rest.ts | 2 + lib/data.ts | 23 ++ lib/types.ts | 34 +++ 25 files changed, 1390 insertions(+), 1 deletion(-) create mode 100644 app/(site)/sektor/[slug]/page.tsx create mode 100644 app/(site)/site-analizi/page.tsx create mode 100644 app/admin/(protected)/ekip/[id]/edit/page.tsx create mode 100644 app/admin/(protected)/ekip/form.tsx create mode 100644 app/admin/(protected)/ekip/new/page.tsx create mode 100644 app/admin/(protected)/ekip/page.tsx create mode 100644 app/admin/(protected)/sektorler/[id]/edit/page.tsx create mode 100644 app/admin/(protected)/sektorler/form.tsx create mode 100644 app/admin/(protected)/sektorler/new/page.tsx create mode 100644 app/admin/(protected)/sektorler/page.tsx create mode 100644 components/audit-form.tsx create mode 100644 components/guarantee.tsx create mode 100644 components/homepage-faq.tsx create mode 100644 components/team-grid.tsx diff --git a/app/(site)/hakkimizda/page.tsx b/app/(site)/hakkimizda/page.tsx index b107ce0..5097b52 100644 --- a/app/(site)/hakkimizda/page.tsx +++ b/app/(site)/hakkimizda/page.tsx @@ -2,6 +2,8 @@ import type { Metadata } from "next"; import Image from "next/image"; import { SectionTitle } from "@/components/section-title"; import { CheckCircle2 } from "lucide-react"; +import { TeamGrid } from "@/components/team-grid"; +import { listTeamMembers } from "@/lib/data"; import { buildMetadata } from "@/lib/seo"; export async function generateMetadata(): Promise { @@ -35,7 +37,9 @@ const values = [ }, ]; -export default function AboutPage() { +export default async function AboutPage() { + const team = await listTeamMembers(); + return ( <>
@@ -76,6 +80,21 @@ export default function AboutPage() {
+ {team.length > 0 && ( +
+
+ +
+ +
+
+
+ )} +
{[ diff --git a/app/(site)/page.tsx b/app/(site)/page.tsx index 1a462c7..545c148 100644 --- a/app/(site)/page.tsx +++ b/app/(site)/page.tsx @@ -11,6 +11,8 @@ import { LogoCloud } from "@/components/logo-cloud"; import { QuickLeadForm } from "@/components/quick-lead-form"; import { WhyUs } from "@/components/why-us"; import { ProcessSteps } from "@/components/process-steps"; +import { Guarantee } from "@/components/guarantee"; +import { HomepageFaq } from "@/components/homepage-faq"; import { OrganizationLd } from "@/components/json-ld"; import { getSiteSettings, @@ -118,6 +120,8 @@ export default async function Home() { + +
@@ -165,6 +169,8 @@ export default async function Home() {
)} + +
diff --git a/app/(site)/projeler/[slug]/page.tsx b/app/(site)/projeler/[slug]/page.tsx index 7a7a951..9622ec2 100644 --- a/app/(site)/projeler/[slug]/page.tsx +++ b/app/(site)/projeler/[slug]/page.tsx @@ -7,6 +7,23 @@ import { marked } from "marked"; import { getProjectBySlug, listProjects } from "@/lib/data"; import { buildMetadata } from "@/lib/seo"; import { Gallery } from "@/components/gallery"; +import { TrendingUp } from "lucide-react"; +import type { ProjectMetric } from "@/lib/types"; + +function parseMetrics(items?: string[] | null): ProjectMetric[] { + if (!items) return []; + const out: ProjectMetric[] = []; + for (const raw of items) { + try { + const obj = JSON.parse(raw) as Partial; + if (obj.value && obj.label) out.push({ value: obj.value, label: obj.label }); + } catch { + const [value, label] = raw.split("|").map((s) => s.trim()); + if (value && label) out.push({ value, label }); + } + } + return out; +} export async function generateMetadata({ params, @@ -41,6 +58,8 @@ export default async function ProjectDetailPage({ ? (marked.parse(project.content, { async: false }) as string) : ""; + const metrics = parseMetrics(project.metrics); + const meta: { icon: React.ReactNode; label: string; value: string }[] = []; if (project.client_name) meta.push({ icon: , label: "Müşteri", value: project.client_name }); @@ -140,6 +159,30 @@ export default async function ProjectDetailPage({ />
)} + + {metrics.length > 0 && ( +
+

+ + Sonuçlar +

+
+ {metrics.map((m, i) => ( +
+

+ {m.value} +

+

+ {m.label} +

+
+ ))} +
+
+ )}
diff --git a/app/(site)/sektor/[slug]/page.tsx b/app/(site)/sektor/[slug]/page.tsx new file mode 100644 index 0000000..e21028e --- /dev/null +++ b/app/(site)/sektor/[slug]/page.tsx @@ -0,0 +1,210 @@ +import Image from "next/image"; +import Link from "next/link"; +import type { Metadata } from "next"; +import { notFound } from "next/navigation"; +import { ArrowRight, ArrowLeft, CheckCircle2 } from "lucide-react"; +import { marked } from "marked"; +import { + getIndustryBySlug, + listProjects, + listServices, + getSiteSettings, +} from "@/lib/data"; +import { buildMetadata } from "@/lib/seo"; +import { ProjectsGrid } from "@/components/projects-grid"; +import { ServicesGrid } from "@/components/services-grid"; +import { TrustBand } from "@/components/trust-band"; +import { Guarantee } from "@/components/guarantee"; +import { QuickLeadForm } from "@/components/quick-lead-form"; +import { FaqList } from "@/components/faq-list"; +import { FaqLd } from "@/components/json-ld"; +import { SectionTitle } from "@/components/section-title"; +import type { FaqItem } from "@/lib/types"; + +export async function generateMetadata({ + params, +}: { + params: Promise<{ slug: string }>; +}): Promise { + const { slug } = await params; + const industry = await getIndustryBySlug(slug); + if (!industry) return { title: "Sektör bulunamadı" }; + return buildMetadata(`/sektor/${slug}`, { + title: industry.seo_title || industry.title, + description: industry.seo_description || industry.subtitle || undefined, + }); +} + +function parseFaq(items?: string[] | null): FaqItem[] { + if (!items) return []; + const out: FaqItem[] = []; + for (const raw of items) { + try { + const obj = JSON.parse(raw) as Partial; + if (obj.q && obj.a) out.push({ q: obj.q, a: obj.a }); + } catch { + /* ignore */ + } + } + return out; +} + +export default async function IndustryPage({ + params, +}: { + params: Promise<{ slug: string }>; +}) { + const { slug } = await params; + const industry = await getIndustryBySlug(slug); + if (!industry) notFound(); + + const [services, projects, settings] = await Promise.all([ + listServices(), + listProjects({ limit: 6 }), + getSiteSettings(), + ]); + + const faqItems = parseFaq(industry.faq); + const html = industry.content + ? (marked.parse(industry.content, { async: false }) as string) + : ""; + + return ( + <> + + +
+
+
+ + Anasayfa + + +
+
+ + Sektöre özel çözüm + +

+ {industry.title} +

+ {industry.subtitle && ( +

+ {industry.subtitle} +

+ )} + +
+ + Ücretsiz keşif görüşmesi + + + + Ücretsiz site analizi + +
+
+ +
+ +
+
+
+
+ + + + {industry.features && industry.features.length > 0 && ( +
+
+ +
    + {industry.features.map((f) => ( +
  • + + {f} +
  • + ))} +
+
+
+ )} + + {html && ( +
+
+
+
+
+ )} + + + + {projects.length > 0 && ( +
+
+ +
+ +
+
+
+ )} + + {services.length > 0 && ( +
+
+ +
+ +
+
+
+ )} + + {faqItems.length > 0 && ( +
+
+ + +
+
+ )} + + ); +} diff --git a/app/(site)/site-analizi/page.tsx b/app/(site)/site-analizi/page.tsx new file mode 100644 index 0000000..53624a7 --- /dev/null +++ b/app/(site)/site-analizi/page.tsx @@ -0,0 +1,137 @@ +import type { Metadata } from "next"; +import { Search, Gauge, Smartphone, TrendingUp, Lock, FileText } from "lucide-react"; +import { AuditForm } from "@/components/audit-form"; +import { buildMetadata } from "@/lib/seo"; + +export async function generateMetadata(): Promise { + return buildMetadata("/site-analizi", { + title: "Ücretsiz Site Analizi", + description: + "Web sitenizin SEO, hız, mobil uyumluluk ve dönüşüm performansını ölçen ücretsiz analiz raporu. 24 saat içinde e-postanıza.", + }); +} + +const CHECKS = [ + { + icon: Gauge, + title: "Sayfa hızı (Core Web Vitals)", + description: "LCP, INP, CLS skorları + iyileştirme önerileri.", + }, + { + icon: Smartphone, + title: "Mobil uyumluluk", + description: "Mobile-friendly testi + responsive sorunları.", + }, + { + icon: TrendingUp, + title: "SEO temelleri", + description: "Meta etiketler, schema, sitemap, internal linking analizi.", + }, + { + icon: Lock, + title: "Güvenlik & SSL", + description: "HTTPS, güvenlik başlıkları, açık güvenlik açıkları taraması.", + }, + { + icon: FileText, + title: "İçerik kalitesi", + description: "Heading yapısı, alt text, içerik uzunluğu, okunabilirlik.", + }, + { + icon: Search, + title: "Rakip karşılaştırması", + description: "Sektörünüzdeki 2-3 rakiple anahtar metriklerin kıyaslanması.", + }, +]; + +export default function AuditPage() { + return ( + <> +
+
+
+ +
+
+ + ✦ Ücretsiz — taahhüt yok + +

+ Web sitenizin satış kapasitesini 24 saatte ölçelim +

+

+ Mevcut sitenizin SEO, hız, mobil uyumluluk ve dönüşüm + performansını analiz edip, neyin daha çok müşteri getireceğini + net bir raporla gösteriyoruz. +

+ +
    +
  • + + ✓ + + Sitenizin Google'da neden yeterince görünmediği +
  • +
  • + + ✓ + + Ziyaretçi neden müşteriye dönüşmüyor — somut nedenler +
  • +
  • + + ✓ + + Rakiplerinize göre hangi alanlarda gerideniz +
  • +
  • + + ✓ + + Öncelikli iyileştirme listesi (etki/efor matrisli) +
  • +
+
+ +
+ +
+
+
+ +
+
+

+ Raporda neler var? +

+

+ Otomatik araç çıktısı değil — uzmanlarımızın değerlendirdiği, + önceliklendirilmiş ve eyleme dökülebilir bir doküman. +

+ +
+ {CHECKS.map((c) => { + const Icon = c.icon; + return ( +
+
+ +
+

+ {c.title} +

+

+ {c.description} +

+
+ ); + })} +
+
+
+ + ); +} diff --git a/app/admin/(protected)/ekip/[id]/edit/page.tsx b/app/admin/(protected)/ekip/[id]/edit/page.tsx new file mode 100644 index 0000000..60820ed --- /dev/null +++ b/app/admin/(protected)/ekip/[id]/edit/page.tsx @@ -0,0 +1,16 @@ +import { notFound } from "next/navigation"; +import { getRow } from "@/lib/data"; +import { TABLES } from "@/lib/appwrite-rest"; +import type { TeamMemberRow } from "@/lib/types"; +import { TeamMemberForm } from "../../form"; + +export default async function EditTeamPage({ + params, +}: { + params: Promise<{ id: string }>; +}) { + const { id } = await params; + const row = await getRow(TABLES.teamMembers, id); + if (!row) notFound(); + return ; +} diff --git a/app/admin/(protected)/ekip/form.tsx b/app/admin/(protected)/ekip/form.tsx new file mode 100644 index 0000000..d3be29a --- /dev/null +++ b/app/admin/(protected)/ekip/form.tsx @@ -0,0 +1,71 @@ +import { Save } from "lucide-react"; +import { + Field, + FormActions, + FormShell, + GhostLink, + PageHeader, + PrimaryButton, + Textarea, +} from "@/components/admin/form"; +import { saveTeamMember } from "@/lib/admin-actions"; +import type { TeamMemberRow } from "@/lib/types"; + +export function TeamMemberForm({ row }: { row?: TeamMemberRow }) { + return ( +
+ +
+ {row && } + +
+ + + + + +
+
+