feat(odeme): checkout ekranında site kromunu daralt
Ödeme adımında ekranda ~20 kaçış yolu vardı (SEO footer kolonları, katalog arama, user nav) ve geri sayım banner'ı kart formunun üstünde duruyordu. Ayrıca layout'un alt progressive blur katmanı iyzico iframe'inin son satırlarını bulanıklaştırıyordu. - kromGizliMi: /odeme kroma kapatıldı, /odeme/sonuc istisna tutuldu (orada kullanıcıyı ürüne döndürmek istiyoruz) - OdemeUstSerit: SiteHeader'ın tasarım dilinde daraltılmış şerit — logo bilerek link değil, tek çıkış "Paket sayfasına dön" - Sipariş özeti kartı kaldırıldı: ürün ve tutar /paket'te seçiliyor, iyzico da ödeme butonunda tutarı gösteriyor - Yasal linkler + iyzico logo bandı ayrı footer yerine doğrudan formun altında; bant sayfada artık tek kez görünüyor Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
import { Suspense } from "react";
|
import { Suspense } from "react";
|
||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
import { SiteFooter } from "@/components/site-footer";
|
import { OdemeUstSerit } from "@/features/odeme/components/odeme-kromu";
|
||||||
import {
|
import {
|
||||||
OdemeSayfasi,
|
OdemeSayfasi,
|
||||||
OdemeSayfasiSkeleton,
|
OdemeSayfasiSkeleton,
|
||||||
@@ -17,7 +17,10 @@ export default function OdemePage({
|
|||||||
searchParams: Promise<{ siparis?: string }>;
|
searchParams: Promise<{ siparis?: string }>;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<div className="flex min-h-screen flex-col bg-slate-50 text-slate-900">
|
// -mb-28: layout'taki ScrollFlow sarmalayıcısının pb-28'ini nötrler — o
|
||||||
|
// padding gizlenen alt blur katmanı içindi, checkout'ta krom kapalı.
|
||||||
|
<div className="-mb-28 flex min-h-dvh flex-col bg-slate-50 text-slate-900">
|
||||||
|
<OdemeUstSerit />
|
||||||
<main className="mx-auto flex w-full max-w-xl flex-1 flex-col px-4 py-10">
|
<main className="mx-auto flex w-full max-w-xl flex-1 flex-col px-4 py-10">
|
||||||
<Suspense fallback={<OdemeSayfasiSkeleton />}>
|
<Suspense fallback={<OdemeSayfasiSkeleton />}>
|
||||||
{searchParams.then(({ siparis }) => (
|
{searchParams.then(({ siparis }) => (
|
||||||
@@ -25,7 +28,6 @@ export default function OdemePage({
|
|||||||
))}
|
))}
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</main>
|
</main>
|
||||||
<SiteFooter />
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,14 +4,25 @@ import { usePathname } from "next/navigation";
|
|||||||
|
|
||||||
// Site kromunun (banner, header, üst/alt blur) gizleneceği rotalar
|
// Site kromunun (banner, header, üst/alt blur) gizleneceği rotalar
|
||||||
// (tam eşleşme veya alt yol). KaydetBanner da aynı listeye uyar.
|
// (tam eşleşme veya alt yol). KaydetBanner da aynı listeye uyar.
|
||||||
const HIDDEN_PREFIXES = ["/giris"];
|
// /odeme: checkout ekranında nav/arama/geri sayım birer kaçış yolu; üstelik
|
||||||
|
// alt blur katmanı iyzico iframe'inin son satırlarını bulanıklaştırıyor.
|
||||||
|
const HIDDEN_PREFIXES = ["/giris", "/odeme"];
|
||||||
|
|
||||||
export function kromGizliMi(pathname: string) {
|
// Kromun geri geldiği istisnalar: ödeme sonucu sayfasında kullanıcıyı ürüne
|
||||||
return HIDDEN_PREFIXES.some(
|
// döndürmek istiyoruz, o yüzden orada tam krom kalır.
|
||||||
|
const ISTISNA_PREFIXES = ["/odeme/sonuc"];
|
||||||
|
|
||||||
|
function yolEslesir(pathname: string, prefixler: string[]) {
|
||||||
|
return prefixler.some(
|
||||||
(p) => pathname === p || pathname.startsWith(`${p}/`),
|
(p) => pathname === p || pathname.startsWith(`${p}/`),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function kromGizliMi(pathname: string) {
|
||||||
|
if (yolEslesir(pathname, ISTISNA_PREFIXES)) return false;
|
||||||
|
return yolEslesir(pathname, HIDDEN_PREFIXES);
|
||||||
|
}
|
||||||
|
|
||||||
export function HeaderGate({ children }: { children: React.ReactNode }) {
|
export function HeaderGate({ children }: { children: React.ReactNode }) {
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
if (kromGizliMi(pathname)) return null;
|
if (kromGizliMi(pathname)) return null;
|
||||||
|
|||||||
54
src/features/odeme/components/odeme-kromu.tsx
Normal file
54
src/features/odeme/components/odeme-kromu.tsx
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
import Image from "next/image";
|
||||||
|
import Link from "next/link";
|
||||||
|
import { ArrowLeft } from "lucide-react";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checkout üst şeridi: /odeme'de site header'ı ve SEO footer'ı kapalı
|
||||||
|
* (bkz. `kromGizliMi`), yerine bu daraltılmış şerit gelir. Ayrı bir footer
|
||||||
|
* yok — yasal linkler ve iyzico bandı doğrudan formun altında duruyor
|
||||||
|
* (bkz. `OdemeSayfasi`).
|
||||||
|
*
|
||||||
|
* Tasarım dili birebir `SiteHeader`dan alınır — aynı kap (`max-w-6xl`), aynı
|
||||||
|
* logo ölçüleri, aynı tipografi, çerçevesiz/zeminsiz yerleşim. Fark yalnızca
|
||||||
|
* içerikte: ödeme adımında kaçış yolu bırakmıyoruz, bu yüzden arama + user
|
||||||
|
* nav yerine tek çıkış olarak "Paket sayfasına dön" duruyor ve logo bilerek
|
||||||
|
* link değil.
|
||||||
|
*/
|
||||||
|
export function OdemeUstSerit() {
|
||||||
|
return (
|
||||||
|
// SiteHeader sticky; burada arkasında progressive blur katmanı olmadığı
|
||||||
|
// için (krom kapalı) statik bırakıldı — durağan haldeki görünüm aynı.
|
||||||
|
<header className="mt-4 sm:mt-6 print:hidden">
|
||||||
|
<div className="mx-auto flex max-w-6xl flex-wrap items-center justify-between gap-x-2 gap-y-2 px-3 sm:gap-x-3 sm:px-4 lg:h-16 lg:flex-nowrap">
|
||||||
|
<div
|
||||||
|
aria-label="KolayTercih"
|
||||||
|
className="flex min-w-0 shrink items-center font-bricolage"
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
src="/kolay-tercih-mark.svg"
|
||||||
|
alt=""
|
||||||
|
width={44}
|
||||||
|
height={44}
|
||||||
|
aria-hidden="true"
|
||||||
|
className="size-8 shrink-0 sm:size-11"
|
||||||
|
/>
|
||||||
|
<span className="truncate">
|
||||||
|
<span className="text-lg font-light tracking-tighter sm:text-3xl">
|
||||||
|
Kolay
|
||||||
|
</span>
|
||||||
|
<span className="text-lg font-semibold tracking-tighter sm:text-3xl">
|
||||||
|
Tercih
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<Link
|
||||||
|
href="/paket"
|
||||||
|
className="inline-flex shrink-0 items-center gap-1.5 text-sm text-slate-500 transition-colors hover:text-slate-700"
|
||||||
|
>
|
||||||
|
<ArrowLeft className="size-4" aria-hidden />
|
||||||
|
Paket sayfasına dön
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,20 +1,15 @@
|
|||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { notFound, redirect } from "next/navigation";
|
import { notFound, redirect } from "next/navigation";
|
||||||
import { ArrowLeft, Lock } from "lucide-react";
|
import { Lock } from "lucide-react";
|
||||||
import { verifySession } from "@/lib/session";
|
import { verifySession } from "@/lib/session";
|
||||||
import { URUNLER } from "@/lib/credits";
|
|
||||||
import { SectionEyebrow } from "@/components/pixel-decor";
|
import { SectionEyebrow } from "@/components/pixel-decor";
|
||||||
import { Skeleton } from "@/components/ui/skeleton";
|
import { Skeleton } from "@/components/ui/skeleton";
|
||||||
import { siparisGetir } from "../odeme-queries";
|
import { siparisGetir } from "../odeme-queries";
|
||||||
import { IyzicoFormu } from "./iyzico-formu";
|
import { IyzicoFormu } from "./iyzico-formu";
|
||||||
|
|
||||||
function tl(kurus: number) {
|
// Kendi tasarımımızdaki ödeme sayfası: kart formu iyzico'nun gömülü
|
||||||
return (kurus / 100).toLocaleString("tr-TR", { maximumFractionDigits: 0 });
|
// iframe'inden gelir (kart verisi bize hiç uğramaz).
|
||||||
}
|
|
||||||
|
|
||||||
// Kendi tasarımımızdaki ödeme sayfası: sipariş özeti bizden, kart formu
|
|
||||||
// iyzico'nun gömülü iframe'inden gelir (kart verisi bize hiç uğramaz).
|
|
||||||
export async function OdemeSayfasi({ siparisId }: { siparisId?: string }) {
|
export async function OdemeSayfasi({ siparisId }: { siparisId?: string }) {
|
||||||
if (!siparisId) redirect("/paket");
|
if (!siparisId) redirect("/paket");
|
||||||
// Oturum düşmüşse geri dönüşte ?siparis= kaybolmasın
|
// Oturum düşmüşse geri dönüşte ?siparis= kaybolmasın
|
||||||
@@ -34,49 +29,24 @@ export async function OdemeSayfasi({ siparisId }: { siparisId?: string }) {
|
|||||||
redirect("/paket");
|
redirect("/paket");
|
||||||
}
|
}
|
||||||
|
|
||||||
const urun = URUNLER[order.product];
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<Link
|
{/* "Paket sayfasına dön" linki checkout üst şeridinde (OdemeUstSerit) */}
|
||||||
href="/paket"
|
<div className="text-center">
|
||||||
className="inline-flex items-center gap-1.5 text-sm text-slate-500 transition-colors hover:text-slate-700"
|
|
||||||
>
|
|
||||||
<ArrowLeft className="size-4" aria-hidden />
|
|
||||||
Paket sayfasına dön
|
|
||||||
</Link>
|
|
||||||
|
|
||||||
<div className="mt-4 text-center">
|
|
||||||
<SectionEyebrow>Güvenli ödeme</SectionEyebrow>
|
<SectionEyebrow>Güvenli ödeme</SectionEyebrow>
|
||||||
<h1 className="mt-3 font-heading text-2xl font-bold">
|
<h1 className="mt-3 font-heading text-2xl font-bold">
|
||||||
Ödemeyi tamamla
|
Ödemeyi tamamla
|
||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Sipariş özeti */}
|
{/* Sipariş özeti bilerek yok: paket/ürün ve tutar bir önceki adımda
|
||||||
<section
|
(/paket) seçildi, iyzico formu da ödeme butonunda tutarı gösteriyor —
|
||||||
aria-label="Sipariş özeti"
|
burada üçüncü kez tekrar etmiyoruz. */}
|
||||||
className="mt-6 rounded-2xl border border-slate-200 bg-white p-5 shadow-sm"
|
|
||||||
>
|
|
||||||
<div className="flex items-baseline justify-between gap-4">
|
|
||||||
<div>
|
|
||||||
<p className="font-heading font-bold">{urun.label}</p>
|
|
||||||
<p className="mt-1 text-sm text-slate-600">
|
|
||||||
{order.credits} Yapay Zeka danışman kredisi
|
|
||||||
{order.product === "paket" &&
|
|
||||||
" · 24 tercihlik liste, risk analizi ve PDF rapor"}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<p className="shrink-0 font-heading text-2xl font-bold">
|
|
||||||
{tl(order.amountKurus)} TL
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
{/* iyzico gömülü kart formu */}
|
{/* iyzico gömülü kart formu */}
|
||||||
<section
|
<section
|
||||||
aria-label="Kart bilgileri"
|
aria-label="Kart bilgileri"
|
||||||
className="mt-4 rounded-2xl border border-slate-200 bg-white p-5 shadow-sm"
|
className="mt-6 rounded-2xl border border-slate-200 bg-white p-5 shadow-sm"
|
||||||
>
|
>
|
||||||
<IyzicoFormu icerik={order.iyzicoFormContent} />
|
<IyzicoFormu icerik={order.iyzicoFormContent} />
|
||||||
</section>
|
</section>
|
||||||
@@ -85,19 +55,39 @@ export async function OdemeSayfasi({ siparisId }: { siparisId?: string }) {
|
|||||||
<Lock className="size-3.5" aria-hidden />
|
<Lock className="size-3.5" aria-hidden />
|
||||||
256-bit SSL ile şifreli bağlantı
|
256-bit SSL ile şifreli bağlantı
|
||||||
</div>
|
</div>
|
||||||
|
<p className="mt-3 text-center text-xs text-slate-500">
|
||||||
|
Kart bilgilerin iyzico'da işlenir, bize hiç ulaşmaz
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{/* Ayrı bir footer yerine yasal linkler ve iyzico bandı doğrudan formun
|
||||||
|
altında: mesafeli satış mevzuatı ve iyzico işyeri sözleşmesi bunların
|
||||||
|
ödeme adımında görünür olmasını istiyor. */}
|
||||||
|
<nav
|
||||||
|
aria-label="Yasal sayfalar"
|
||||||
|
className="mt-3 flex flex-wrap items-center justify-center gap-x-4 gap-y-2 text-xs text-slate-500"
|
||||||
|
>
|
||||||
|
<Link href="/gizlilik" className="hover:text-slate-700 hover:underline">
|
||||||
|
Gizlilik & KVKK
|
||||||
|
</Link>
|
||||||
|
<Link
|
||||||
|
href="/kosullar#iade"
|
||||||
|
className="hover:text-slate-700 hover:underline"
|
||||||
|
>
|
||||||
|
Kullanım & iade koşulları
|
||||||
|
</Link>
|
||||||
|
<Link href="/iletisim" className="hover:text-slate-700 hover:underline">
|
||||||
|
İletişim
|
||||||
|
</Link>
|
||||||
|
</nav>
|
||||||
|
{/* iyzico işyeri sözleşmesi gereği: "iyzico ile Öde" + kart logoları
|
||||||
|
görünür olmalı. Resmî logo bandı (iyzico logo paketi). */}
|
||||||
<Image
|
<Image
|
||||||
src="/odeme/iyzico-logo-band.svg"
|
src="/odeme/iyzico-logo-band.svg"
|
||||||
alt="iyzico ile Öde — Mastercard, Visa, American Express ve Troy ile güvenli ödeme"
|
alt="iyzico ile Öde — Mastercard, Visa, American Express ve Troy ile güvenli ödeme"
|
||||||
width={429}
|
width={429}
|
||||||
height={32}
|
height={32}
|
||||||
className="mx-auto mt-3 h-5 w-auto"
|
className="mx-auto mt-5 h-6 w-auto"
|
||||||
/>
|
/>
|
||||||
<p className="mt-3 text-center text-xs text-slate-500">
|
|
||||||
Kart bilgilerin iyzico'da işlenir, bize hiç ulaşmaz ·{" "}
|
|
||||||
<Link href="/kosullar#iade" className="underline hover:text-slate-700">
|
|
||||||
İade koşulları
|
|
||||||
</Link>
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -105,11 +95,9 @@ export async function OdemeSayfasi({ siparisId }: { siparisId?: string }) {
|
|||||||
export function OdemeSayfasiSkeleton() {
|
export function OdemeSayfasiSkeleton() {
|
||||||
return (
|
return (
|
||||||
<div aria-live="polite" className="w-full">
|
<div aria-live="polite" className="w-full">
|
||||||
<Skeleton className="h-5 w-40" />
|
<Skeleton className="mx-auto h-7 w-32 rounded-full" />
|
||||||
<Skeleton className="mx-auto mt-4 h-7 w-32 rounded-full" />
|
|
||||||
<Skeleton className="mx-auto mt-3 h-8 w-56" />
|
<Skeleton className="mx-auto mt-3 h-8 w-56" />
|
||||||
<Skeleton className="mt-6 h-24 w-full rounded-2xl" />
|
<Skeleton className="mt-6 h-96 w-full rounded-2xl" />
|
||||||
<Skeleton className="mt-4 h-96 w-full rounded-2xl" />
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user