Yalnız kullanıcıya görünen etiket değişir; iç anahtar, tip, DB değeri, API parametresi, mermaid classDef ve analitik değeri `garanti` olarak kalır (veri göçü yok). - risk.ts: tek kaynak DILIM_ETIKET + dilimdenFarkliRiskEtiketi; rapor listesi, yazdır tablosu, /sonuc sekmeleri, seçimlerim paneli ve meraklısına şeridi buradan okur - "Güvenli · Güvenli" / "Güvenli (Güvenli)": satır detayında risk etiketi yalnız dilimin varsayılanından farklıysa, yazdır ve seçimlerimde yalnız kelime farklıysa yazılır - LLM prompt'ları: `garanti` teknik anahtar, adaya "güvenli" denir sözlüğü (rapor, soru, tadımlık); liste özeti modele görünen dilim adıyla gider - meraklısına şeması (SEMA düğümü, aria-label), 2. bölüm anlatımı ve AGENTS.md şema kuralı görünen adla eşitlendi - "yerleşme garantisi vermez" türü vaat reddi cümleleri aynen kaldı Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
101 lines
3.5 KiB
TypeScript
101 lines
3.5 KiB
TypeScript
"use client";
|
||
|
||
// Paketlinin geri bildirime dayalı liste revizyonu (/listem üzerinde).
|
||
// listeRevize sunucu aksiyonunu çağırır; başarıda sayfayı yeniler ki
|
||
// sunucuda güncellenen rapor ve kredi bakiyesi ekrana yansısın.
|
||
|
||
import { useRef, useState } from "react";
|
||
import { useRouter } from "next/navigation";
|
||
import { RefreshCcw } from "lucide-react";
|
||
import { toast } from "sonner";
|
||
import { Button } from "@/components/ui/button";
|
||
import { listeRevize } from "@/features/rapor/rapor-actions";
|
||
import { olay } from "@/lib/analitik";
|
||
|
||
export function RevizyonKutusu({ kalanHak }: { kalanHak: number }) {
|
||
const [feedback, setFeedback] = useState("");
|
||
const [pending, setPending] = useState(false);
|
||
const router = useRouter();
|
||
// Aynı deneme için sabit requestId: cevap kaybolan network hatasında retry
|
||
// çifte harcamaz. Sunucudan cevap gelince sıfırlanır (harcama ya tamamlandı
|
||
// ya iade edildi; sonraki deneme taze id ile başlamalı — bkz. sonuc/actions.ts).
|
||
const requestIdRef = useRef<string | null>(null);
|
||
|
||
if (kalanHak <= 0) {
|
||
return (
|
||
<section
|
||
id="revizyon"
|
||
className="scroll-mt-24 rounded-2xl border border-slate-200 bg-white p-5"
|
||
>
|
||
<p className="text-sm text-slate-500">
|
||
Revizyon hakların doldu. Danışman sohbetinden listen hakkında soru
|
||
sormaya devam edebilirsin.
|
||
</p>
|
||
</section>
|
||
);
|
||
}
|
||
|
||
async function revizeEt() {
|
||
setPending(true);
|
||
try {
|
||
if (!requestIdRef.current) requestIdRef.current = crypto.randomUUID();
|
||
const sonuc = await listeRevize({
|
||
feedback: feedback.trim(),
|
||
requestId: requestIdRef.current,
|
||
});
|
||
requestIdRef.current = null;
|
||
if (sonuc.ok) {
|
||
olay("liste_revize", { sayfa: "listem" });
|
||
toast.success("Listen yeniden kuruldu.");
|
||
setFeedback("");
|
||
router.refresh();
|
||
} else {
|
||
toast.error(sonuc.error);
|
||
}
|
||
} catch {
|
||
toast.error("Beklenmeyen bir hata oluştu, tekrar dener misin?");
|
||
} finally {
|
||
setPending(false);
|
||
}
|
||
}
|
||
|
||
return (
|
||
<section
|
||
id="revizyon"
|
||
className="scroll-mt-24 rounded-2xl border border-slate-200 bg-white p-5"
|
||
>
|
||
<h2 className="font-heading text-base font-bold">
|
||
Listede değişiklik mi istiyorsun?
|
||
</h2>
|
||
<p className="mb-3 mt-1 text-sm text-slate-600">
|
||
Ne değişsin istediğini yaz; liste aynı gerçek veriyle yeniden kurulsun
|
||
(3 kredi).
|
||
</p>
|
||
<textarea
|
||
value={feedback}
|
||
onChange={(e) => setFeedback(e.target.value)}
|
||
rows={2}
|
||
maxLength={1000}
|
||
placeholder="ör. İlk 5 tercihte daha fazla İstanbul olsun, güvenli dilimi genişlet…"
|
||
disabled={pending}
|
||
className="w-full rounded-lg border border-slate-200 bg-white px-3 py-2 text-sm shadow-xs outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50"
|
||
/>
|
||
<div className="mt-3 flex items-center gap-3">
|
||
<Button
|
||
type="button"
|
||
variant="outline"
|
||
disabled={pending || feedback.trim().length < 5}
|
||
onClick={revizeEt}
|
||
className="cursor-pointer"
|
||
>
|
||
<RefreshCcw className="size-4" aria-hidden />
|
||
{pending ? "Yeniden kuruluyor…" : "Revize et (3 kredi)"}
|
||
</Button>
|
||
<span className="text-xs text-slate-500">
|
||
Kalan hak: <span className="font-semibold">{kalanHak}</span>
|
||
</span>
|
||
</div>
|
||
</section>
|
||
);
|
||
}
|