- app/sonuc/actions.ts → features/rapor/rapor-actions.ts birebir taşındı (kredi harcama/iade idempotency yolları bayt bayt aynı, yalnızca import yolları değişti); liste-uretici/revizyon-kutusu action'ı doğrudan import ediyor - lib/rapor-kaydi.ts rapor-queries.ts'e emildi: kullanicininRaporu aynı tek okuma kapısı; listem sayfasındaki inline drizzle sorgusu sohbetGecmisiGetir oldu; tadimlikSec re-export'u eklendi - listem-govde, sohbet-client, revizyon-kutusu, liste-uretici, listem-bos-cta, rapor-listesi, tadimlik-satiri, yazdir-butonu features/rapor/components/'a - Yeni async ListemIcerik (verifySession + maskeleme + bosDurum dalı içeride; çift await searchParams kalktı) ve RaporYazdir (3 redirect + yazdırılabilir belge); skeleton'lar eski loading.tsx geometrileriyle aynı dosyada - /listem ve /rapor/yazdir senkron: searchParams.then() + sayfada Suspense; maxDuration=300 ve metadata sayfada kaldı; iki loading.tsx silindi - Davranış değişikliği yok; /api/soru el değmedi Co-Authored-By: Claude Fable 5 <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, garanti dilimini 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>
|
||
);
|
||
}
|