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:
@@ -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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user