Add "detay" script to package.json and enhance ListemSekmeleri component functionality
All checks were successful
Deploy / deploy (push) Successful in 16m29s

- Introduced a new "detay" script in package.json for additional processing.
- Refactored ListemSekmeleri component to improve tab functionality and state management.
- Updated UI elements for better user interaction and clarity, including renaming "Seçtiklerim" to "Kendi Listem" for consistency across the application.
- Made adjustments to related components for improved integration and user experience.
This commit is contained in:
bilalgursen
2026-08-01 21:58:28 +03:00
parent f27f3ffaac
commit 716f7bb3fb
17 changed files with 506 additions and 105 deletions

View File

@@ -1,36 +1,85 @@
"use client";
import { useState } from "react";
import { ListChecks, Sparkles } from "lucide-react";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { Tabs as TabsPrimitive } from "radix-ui";
import { SecimlerimPaneli } from "@/components/manuel-liste/secimlerim-paneli";
import { useManuelListe } from "@/components/manuel-liste/store";
export type ListemSekme = "secimlerim" | "ai";
export function ListemSekmeleri({
varsayilan,
aiIcerik,
aiHazir,
}: {
varsayilan: ListemSekme;
aiIcerik: React.ReactNode;
aiHazir?: boolean;
}) {
const [sekme, setSekme] = useState<ListemSekme>(varsayilan);
const liste = useManuelListe();
function degistir(deger: string) {
const yeni = deger as ListemSekme;
setSekme(yeni);
const url = new URL(window.location.href);
url.searchParams.set("sekme", yeni);
window.history.replaceState(null, "", url);
}
return (
<Tabs defaultValue={varsayilan} className="mt-2 gap-6">
<TabsList variant="line" className="h-auto w-full justify-start gap-1">
<TabsTrigger value="secimlerim" className="cursor-pointer px-3 py-2.5">
<TabsPrimitive.Root value={sekme} onValueChange={degistir} className="mt-6">
<TabsPrimitive.List
aria-label="Liste görünümü"
className="relative mx-auto grid w-full max-w-md grid-cols-2 rounded-full border border-slate-200 bg-slate-100 p-1"
>
<span
aria-hidden
className="absolute inset-y-1 left-1 w-[calc(50%-0.25rem)] rounded-full bg-white shadow-sm transition-transform duration-200 [transition-timing-function:cubic-bezier(0.23,1,0.32,1)]"
style={{
transform: sekme === "ai" ? "translateX(100%)" : "translateX(0)",
}}
/>
<TabsPrimitive.Trigger
value="secimlerim"
className="relative z-10 flex h-10 cursor-pointer items-center justify-center gap-2 rounded-full text-sm font-semibold text-slate-500 transition-colors duration-200 hover:text-slate-700 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring data-[state=active]:text-slate-900"
>
<ListChecks className="size-4" aria-hidden />
Seçtiklerim
</TabsTrigger>
<TabsTrigger value="ai" className="cursor-pointer px-3 py-2.5">
Kendi Listem
{liste.length > 0 ? (
<span className="min-w-5 rounded-full bg-primary/10 px-1.5 py-0.5 text-center text-[11px] font-bold tabular-nums text-primary">
{liste.length}
</span>
) : null}
</TabsPrimitive.Trigger>
<TabsPrimitive.Trigger
value="ai"
className="relative z-10 flex h-10 cursor-pointer items-center justify-center gap-2 rounded-full text-sm font-semibold text-slate-500 transition-colors duration-200 hover:text-slate-700 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring data-[state=active]:text-slate-900"
>
<Sparkles className="size-4" aria-hidden />
AI Listem
</TabsTrigger>
</TabsList>
<TabsContent value="secimlerim" className="mt-0">
{aiHazir ? (
<span
className="size-1.5 rounded-full bg-emerald-500"
aria-hidden
title="AI listen hazır"
/>
) : null}
</TabsPrimitive.Trigger>
</TabsPrimitive.List>
<TabsPrimitive.Content
value="secimlerim"
className="mt-6 outline-none data-[state=active]:animate-in data-[state=active]:fade-in-0 data-[state=active]:slide-in-from-bottom-1 data-[state=active]:duration-200"
>
<SecimlerimPaneli />
</TabsContent>
<TabsContent value="ai" className="mt-0">
</TabsPrimitive.Content>
<TabsPrimitive.Content
value="ai"
className="mt-6 outline-none data-[state=active]:animate-in data-[state=active]:fade-in-0 data-[state=active]:slide-in-from-bottom-1 data-[state=active]:duration-200"
>
{aiIcerik}
</TabsContent>
</Tabs>
</TabsPrimitive.Content>
</TabsPrimitive.Root>
);
}

View File

@@ -85,7 +85,7 @@ export default async function ListemPage({
<p className="mt-3 text-slate-600">
Sıralamanı girip birkaç adımda 24 tercihlik risk analizli listeni kur;
sonra burada görüntüleyip AI danışmanla üzerinde konuşabilirsin.
Seçtiklerim sekmesinde ise tablolardan eklediğin programlar durur.
Kendi Listem sekmesinde ise tablolardan eklediğin programlar durur.
</p>
<PagePixelDivider seed={67} className="mx-auto mt-6" />
<Button
@@ -101,17 +101,17 @@ export default async function ListemPage({
if (!raporVar) {
return (
<main className="mx-auto w-full max-w-7xl px-4 py-8 sm:py-10">
<h1 className="font-heading text-2xl font-bold tracking-tight sm:text-3xl">
Tercih karar merkezin
</h1>
<p className="mt-2 max-w-2xl text-sm leading-6 text-slate-600">
Seçtiklerin ve AI listen burada yan yana ikisi birbirinden bağımsız.
</p>
<PagePixelDivider seed={71} className="mt-6" />
<ListemSekmeleri
varsayilan={varsayilanSekme}
aiIcerik={aiBos}
/>
<div className="mx-auto max-w-2xl text-center">
<h1 className="font-heading text-2xl font-bold tracking-tight sm:text-3xl">
Tercih karar merkezin
</h1>
<p className="mt-2 text-sm leading-6 text-slate-600">
Kendi listen ve AI listen burada yan yana ikisi birbirinden
bağımsız.
</p>
<PagePixelDivider seed={71} className="mx-auto mt-6" />
</div>
<ListemSekmeleri varsayilan={varsayilanSekme} aiIcerik={aiBos} />
</main>
);
}
@@ -375,14 +375,21 @@ export default async function ListemPage({
return (
<main className="mx-auto w-full max-w-7xl px-4 py-8 sm:py-10">
<h1 className="font-heading text-2xl font-bold tracking-tight sm:text-3xl">
Tercih karar merkezin
</h1>
<p className="mt-2 max-w-2xl text-sm leading-6 text-slate-600">
Seçtiklerin ve AI listen burada yan yana ikisi birbirinden bağımsız.
</p>
<PagePixelDivider seed={71} className="mt-6" />
<ListemSekmeleri varsayilan={varsayilanSekme} aiIcerik={aiIcerik} />
<div className="mx-auto max-w-2xl text-center">
<h1 className="font-heading text-2xl font-bold tracking-tight sm:text-3xl">
Tercih karar merkezin
</h1>
<p className="mt-2 text-sm leading-6 text-slate-600">
Kendi listen ve AI listen burada yan yana ikisi birbirinden
bağımsız.
</p>
<PagePixelDivider seed={71} className="mx-auto mt-6" />
</div>
<ListemSekmeleri
varsayilan={varsayilanSekme}
aiIcerik={aiIcerik}
aiHazir
/>
</main>
);
}

View File

@@ -13,6 +13,8 @@ import type { SihirbazFacetleri } from "@/lib/db";
import {
SIHIRBAZ_STORAGE_KEY,
sihirbazDogrula,
tercihProfiliSil,
tercihProfiliYaz,
type SihirbazSecimleri,
} from "@/lib/sihirbaz";
import { SihirbazModal } from "./sihirbaz-modal";
@@ -210,9 +212,7 @@ export function SihirbazBolumu({
});
setKredi(sonuc.kredi);
setPaketli(sonuc.hasPaket);
try {
localStorage.removeItem(SIHIRBAZ_STORAGE_KEY);
} catch {}
tercihProfiliSil();
// AI çıktısı bu sayfada gösterilmez: üretim animasyonu açık kalır,
// liste (paketsizse paywall'lı hâliyle) /listem'de karşılar.
router.push("/listem");
@@ -240,12 +240,7 @@ export function SihirbazBolumu({
}
function secimleriKaydetVeGirise(secimler: SihirbazSecimleri) {
try {
localStorage.setItem(
SIHIRBAZ_STORAGE_KEY,
JSON.stringify({ sira, tur, secimler }),
);
} catch {}
tercihProfiliYaz({ sira, tur, secimler });
const geri = `/sonuc?sira=${sira}&tur=${tur}&sihirbaz=1`;
router.push(`/giris?callback=${encodeURIComponent(geri)}`);
}