refactor: remove TercihDegistiUyarisi component

Deleted the TercihDegistiUyarisi component from the project. This component was responsible for notifying users about changes in their preferences and suggesting a list update. The removal is part of a broader effort to streamline the codebase and improve maintainability.
This commit is contained in:
bilalgursen
2026-08-09 04:44:13 +03:00
parent 437e2bc96a
commit 8870845fa1

View File

@@ -1,302 +0,0 @@
"use client";
// /listem'deki "tercihlerin değişti" uyarısı: kayıtlı liste hangi tercihlerle
// kurulduysa (rapor params) kullanıcının güncel profili ondan ayrıldığında
// belirir ve listeyi yeni tercihlere göre yeniden kurmayı önerir.
//
// Yeniden kurulum normal üretimle AYNI kapıdan geçer (listeOlustur): 3 kredi,
// aynı requestId idempotency'si, aynı kredi-yetersiz/iade davranışı. Ayrı bir
// "güncelleme" ürünü ya da indirimli yol yok — kullanıcı yeni bir liste alıyor,
// bedeli de yeni liste bedeli.
//
// Uyarı kapatılabilir: kapatma kararı o anki profil imzasına yazılır, tercih
// yeniden değişirse uyarı geri gelir (bkz. profilImzasi).
import { useMemo, useRef, useState } from "react";
import Link from "next/link";
import { useRouter } from "next/navigation";
import { toast } from "sonner";
import { ArrowRight, Check, RefreshCcw, SlidersHorizontal, X } from "lucide-react";
import { Button } from "@/components/ui/button";
import { olay, siraKovasi } from "@/lib/analitik";
import { useTercihProfili } from "@/features/liste/hooks/use-tercih-profili";
import { listeOlustur } from "@/features/rapor/rapor-actions";
import type { RaporParams } from "@/features/rapor/types/rapor";
import {
farkCumlesi,
farkSatirlari,
profilImzasi,
tercihFarki,
} from "@/features/rapor/tercih-farki";
import { useUretimMesaji } from "./uretim-mesaji";
const GIZLE_ANAHTARI = "kolaytercih.listem-fark-gizle";
function gizlenenImzayiOku(): string | null {
if (typeof window === "undefined") return null;
try {
return localStorage.getItem(GIZLE_ANAHTARI);
} catch {
return null;
}
}
type Durum =
| { ad: "bekliyor" }
| { ad: "uretiliyor" }
| { ad: "tamamlandi" }
| { ad: "kredi-yetersiz" }
| { ad: "hata"; mesaj: string };
export function TercihDegistiUyarisi({
params,
kredi,
raporKredi,
hasPaket,
}: {
/** Kayıtlı listenin üretildiği tercihler */
params: RaporParams;
kredi: number;
raporKredi: number;
hasPaket: boolean;
}) {
const router = useRouter();
const profil = useTercihProfili();
const [durum, setDurum] = useState<Durum>({ ad: "bekliyor" });
// Profil client'ta çözüldüğü için bu bileşen SSR'da zaten hiçbir şey
// render etmiyor; gizleme kaydı ilk render'da güvenle okunabilir.
const [gizlenenImza, setGizlenenImza] = useState<string | null>(
gizlenenImzayiOku,
);
// Aynı deneme için sabit requestId: cevabı kaybolan istekte retry çifte
// harcamaz (bkz. liste-uretici.tsx ve spendCredits idempotency'si).
const requestIdRef = useRef<string | null>(null);
const farklar = useMemo(
() => (profil ? tercihFarki(params, profil) : []),
[params, profil],
);
const satirlar = useMemo(
() => (profil ? farkSatirlari(params, profil, farklar) : []),
[params, profil, farklar],
);
async function guncelle() {
if (!profil) return;
if (!requestIdRef.current) requestIdRef.current = crypto.randomUUID();
setDurum({ ad: "uretiliyor" });
try {
const sonuc = await listeOlustur({
sira: profil.sira,
tur: profil.tur,
secimler: profil.secimler,
requestId: requestIdRef.current,
});
requestIdRef.current = null;
if (sonuc.ok) {
olay("liste_guncellendi", {
tur: profil.tur,
sira_kovasi: siraKovasi(profil.sira),
degisen_alan: farklar.length,
});
toast.success("Listen yeni tercihlerine göre yeniden kuruldu.");
// Sunucu params'ı tazelenince fark kalmaz ve uyarı kendiliğinden
// kaybolur; refresh dönene kadar "güncellendi" hâli görünür.
setDurum({ ad: "tamamlandi" });
router.refresh();
return;
}
if (sonuc.code === "KREDI" || sonuc.code === "PAKET") {
olay("kredi_bitti");
setDurum({ ad: "kredi-yetersiz" });
} else if (sonuc.code === "AUTH") {
router.push(`/giris?callback=${encodeURIComponent("/listem")}`);
} else {
setDurum({ ad: "hata", mesaj: sonuc.error });
}
} catch {
setDurum({
ad: "hata",
mesaj: "Beklenmeyen bir hata oluştu, tekrar dener misin?",
});
}
}
function gizle() {
if (!profil) return;
const imza = profilImzasi(profil);
try {
localStorage.setItem(GIZLE_ANAHTARI, imza);
} catch {}
setGizlenenImza(imza);
}
if (!profil || farklar.length === 0) return null;
if (durum.ad === "tamamlandi") {
return (
<div
role="status"
className="mb-6 flex items-center gap-3 rounded-2xl border border-emerald-200 bg-emerald-50 px-4 py-3 text-sm font-medium text-emerald-800"
>
<Check className="size-4 shrink-0" aria-hidden />
Listen yeni tercihlerine göre güncellendi.
</div>
);
}
// Kapatılmış değişiklik: kullanıcı aynı tercihlerle bir daha rahatsız
// edilmez. Üretim/hata hâlleri kapatmadan bağımsız görünür kalır.
if (durum.ad === "bekliyor" && gizlenenImza === profilImzasi(profil)) {
return null;
}
const krediYeter = kredi >= raporKredi;
return (
<section
aria-live="polite"
className="mb-6 rounded-2xl border border-amber-200 bg-amber-50 p-4 sm:p-5"
>
<div className="flex items-start gap-3">
<span className="flex size-9 shrink-0 items-center justify-center rounded-full bg-amber-100 text-amber-700">
<SlidersHorizontal className="size-4" aria-hidden />
</span>
<div className="min-w-0 flex-1">
<h2 className="font-heading font-bold text-amber-900">
Tercihlerin değişti
</h2>
<p className="mt-1 text-sm text-amber-900/80">
Aşağıdaki liste eski tercihlerinle kuruldu; {farkCumlesi(farklar)}{" "}
o zamandan beri değişti. Yeni tercihlerine göre yeniden kurabilirsin
yeni liste bunun yerine geçer ve {raporKredi} kredi düşer.
</p>
<dl className="mt-3 flex flex-col gap-1.5 text-sm">
{satirlar.map((satir) => (
<div
key={satir.alan}
className="flex flex-col gap-0.5 sm:flex-row sm:items-baseline sm:gap-2"
>
<dt className="shrink-0 text-xs font-medium uppercase tracking-wide text-amber-700">
{satir.etiket}
</dt>
<dd className="min-w-0 text-amber-900">
<span className="text-amber-900/60">{satir.eski}</span>
<span className="px-1.5 text-amber-700" aria-label="yerine">
</span>
<span className="font-semibold">{satir.yeni}</span>
</dd>
</div>
))}
</dl>
{durum.ad === "uretiliyor" ? (
<UretimSatiri />
) : durum.ad === "kredi-yetersiz" ? (
<KrediYetersiz hasPaket={hasPaket} raporKredi={raporKredi} />
) : (
<div className="mt-4 flex flex-wrap items-center gap-3">
{durum.ad === "hata" ? (
<>
<p className="w-full text-sm text-red-700">{durum.mesaj}</p>
<Button
onClick={() => void guncelle()}
className="cursor-pointer bg-orange-500 text-white transition-colors duration-200 hover:bg-orange-600"
>
<RefreshCcw className="size-4" aria-hidden />
Tekrar dene
</Button>
</>
) : krediYeter ? (
<Button
onClick={() => void guncelle()}
className="cursor-pointer bg-orange-500 text-white transition-[background-color,transform] duration-200 active:scale-[0.97] hover:bg-orange-600"
>
Listemi güncelle {raporKredi} kredi
<ArrowRight className="size-4" aria-hidden />
</Button>
) : (
<Button
asChild
className="cursor-pointer bg-orange-500 text-white transition-[background-color,transform] duration-200 active:scale-[0.97] hover:bg-orange-600"
>
<Link href="/paket">
{hasPaket ? "Kredi yükle ve güncelle" : "Paketi aktive et"}
<ArrowRight className="size-4" aria-hidden />
</Link>
</Button>
)}
<button
type="button"
onClick={gizle}
className="cursor-pointer text-sm font-medium text-amber-800 underline underline-offset-2 transition-colors hover:text-amber-900"
>
Şimdilik kalsın
</button>
{!krediYeter && durum.ad !== "hata" ? (
<p className="w-full text-xs text-amber-900/70">
Güncelleme {raporKredi} kredi; bakiyen {kredi} kredi.
</p>
) : null}
</div>
)}
</div>
{durum.ad === "bekliyor" ? (
<button
type="button"
onClick={gizle}
className="flex size-9 shrink-0 cursor-pointer items-center justify-center rounded-full border border-amber-200 text-amber-800 transition-colors duration-200 active:scale-[0.96] hover:bg-amber-100"
>
<X className="size-4" aria-hidden />
<span className="sr-only">Uyarıyı kapat</span>
</button>
) : null}
</div>
</section>
);
}
function UretimSatiri() {
const mesaj = useUretimMesaji();
return (
<div className="mt-4 flex flex-col gap-1">
<p className="flex items-center gap-2 text-sm font-medium text-amber-900">
<RefreshCcw className="size-4 animate-spin" aria-hidden />
Listen yeni tercihlerine göre kuruluyor
</p>
<p className="text-sm text-amber-900/70">{mesaj}</p>
<p className="text-xs text-amber-900/60">
Bu işlem yarım dakika kadar sürebilir, sayfayı kapatma.
</p>
</div>
);
}
function KrediYetersiz({
hasPaket,
raporKredi,
}: {
hasPaket: boolean;
raporKredi: number;
}) {
return (
<div className="mt-4 flex flex-wrap items-center gap-3">
<p className="w-full text-sm text-amber-900">
{hasPaket
? `Güncelleme ${raporKredi} kredi ve bakiyen yetmedi. Kredi yüklediğinde listen ve sohbet geçmişin duruyor, kaldığın yerden devam edersin.`
: `Güncelleme ${raporKredi} kredi ve bakiyen yetmedi. Tercih Dönemi Paketi'yle kredilerin, tam liste, PDF ve revizyon hakları birlikte gelir.`}
</p>
<Button
asChild
className="cursor-pointer bg-orange-500 text-white transition-colors duration-200 hover:bg-orange-600"
>
<Link href="/paket">
{hasPaket ? "Kredi yükle" : "Paketi aktive et"}
<ArrowRight className="size-4" aria-hidden />
</Link>
</Button>
</div>
);
}