feat(ui): Arc bileşenleri, landing bölümleri ve bildirim yığını
Sonner/vaul yerine kendi bildirim yığını ve alt sayfa; çip grubu, kart destesi, kaydırma eylemi, yazı dönüşümü, bölme tutamacı; landing'e program duvarı, süzülen haplar ve veri akışı; yaylar tek dosyada (lib/hareket.ts). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -8,12 +8,13 @@ import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { SecimHaplari } from "@/components/ui/secim-haplari";
|
||||
import {
|
||||
Drawer,
|
||||
DrawerClose,
|
||||
DrawerContent,
|
||||
DrawerDescription,
|
||||
DrawerTitle,
|
||||
} from "@/components/ui/drawer";
|
||||
AltSayfa,
|
||||
AltSayfaKapat,
|
||||
AltSayfaGovde,
|
||||
AltSayfaIcerik,
|
||||
AltSayfaAciklama,
|
||||
AltSayfaBaslik,
|
||||
} from "@/components/ui/alt-sayfa";
|
||||
import { PikselZemin } from "@/components/piksel-zemin";
|
||||
import { useGorunumOlayi } from "@/components/use-gorunum-olayi";
|
||||
import {
|
||||
@@ -305,19 +306,15 @@ export function CtaSiraForm({
|
||||
// başlığı yalnız ekran okuyucuya.
|
||||
const siraAdimi = !kompakt && adim === "sira";
|
||||
const cekmece = (
|
||||
<Drawer open={acik} onOpenChange={acikDegisti} handleOnly>
|
||||
<DrawerContent
|
||||
className="max-h-[92dvh] bg-card sm:mx-auto sm:max-w-2xl sm:border-x sm:border-slate-200 sm:shadow-2xl"
|
||||
>
|
||||
<DrawerClose className="absolute top-3 right-3 z-10 flex size-9 cursor-pointer items-center justify-center rounded-full border border-slate-200 bg-popover text-slate-500 transition-[background-color,transform] duration-150 hover:bg-slate-100 hover:text-slate-800 active:scale-[0.96]">
|
||||
<AltSayfa open={acik} onOpenChange={acikDegisti}>
|
||||
<AltSayfaIcerik>
|
||||
<AltSayfaKapat className="absolute top-3 right-3 z-10 flex size-9 cursor-pointer items-center justify-center rounded-full border border-slate-200 bg-popover text-slate-500 transition-[background-color,transform] duration-150 hover:bg-slate-100 hover:text-slate-800 active:scale-[0.96]">
|
||||
<X className="size-4" aria-hidden />
|
||||
<span className="sr-only">Kapat</span>
|
||||
</DrawerClose>
|
||||
</AltSayfaKapat>
|
||||
|
||||
<div
|
||||
className="min-h-0 flex-1 overflow-y-auto px-4 pt-5 pb-[max(1.25rem,env(safe-area-inset-bottom))] sm:px-6"
|
||||
>
|
||||
<DrawerTitle
|
||||
<AltSayfaGovde className="px-4 pt-3 pb-[max(1.25rem,env(safe-area-inset-bottom))] sm:px-6">
|
||||
<AltSayfaBaslik
|
||||
className={
|
||||
siraAdimi
|
||||
? "px-10 text-center font-heading text-2xl font-bold text-slate-900"
|
||||
@@ -325,8 +322,8 @@ export function CtaSiraForm({
|
||||
}
|
||||
>
|
||||
Sıralamana yakın programlar hangileri?
|
||||
</DrawerTitle>
|
||||
<DrawerDescription
|
||||
</AltSayfaBaslik>
|
||||
<AltSayfaAciklama
|
||||
className={
|
||||
siraAdimi
|
||||
? "mx-auto mt-1 max-w-md px-4 text-center text-sm text-slate-500"
|
||||
@@ -336,7 +333,7 @@ export function CtaSiraForm({
|
||||
Puan türünü seç, sıralamanı yaz; hayal, dengeli ve güvenli
|
||||
dilimlerindeki programları gerçek YÖK Atlas verisiyle bu sayfadan
|
||||
ayrılmadan gör. Ücretsiz, üyelik gerekmez.
|
||||
</DrawerDescription>
|
||||
</AltSayfaAciklama>
|
||||
{siraAdimi ? (
|
||||
<div className="mt-5">{siraFormu}</div>
|
||||
) : facetler && sira != null ? (
|
||||
@@ -351,9 +348,9 @@ export function CtaSiraForm({
|
||||
onTamamla={secimleriTamamla}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
</AltSayfaGovde>
|
||||
</AltSayfaIcerik>
|
||||
</AltSayfa>
|
||||
);
|
||||
|
||||
if (kompakt) {
|
||||
|
||||
@@ -20,12 +20,9 @@ import {
|
||||
import { authClient } from "@/lib/auth-client";
|
||||
import { olay, siraKovasi } from "@/lib/analitik";
|
||||
import { onbellekliJsonGetir } from "@/lib/istek-cache";
|
||||
import {
|
||||
profilDuzenlemeyiAc,
|
||||
useTercihProfili,
|
||||
} from "@/features/liste/hooks/use-tercih-profili";
|
||||
import { useTercihProfili } from "@/features/liste/hooks/use-tercih-profili";
|
||||
import type { SihirbazFacetleri } from "@/types/yokatlas";
|
||||
import { PUAN_TURU_ETIKET, sonucHref, type SihirbazSecimleri } from "@/features/sihirbaz/sihirbaz-sabitler";
|
||||
import { sonucHref, type SihirbazSecimleri } from "@/features/sihirbaz/sihirbaz-sabitler";
|
||||
import { tercihProfiliYaz } from "@/features/sihirbaz/sihirbaz-profil";
|
||||
|
||||
// lib/db sunucu tarafı; etiketler client bundle'a sızmasın diye burada.
|
||||
@@ -107,11 +104,10 @@ export function HeroForm() {
|
||||
}
|
||||
|
||||
// Kayıtlı profil varsa sihirbazı atla, doğrudan sonuç sayfasına götür.
|
||||
// "Farklı sıralama gir" navbar'daki Değiştir ile aynı düzenleme modalını
|
||||
// (TercihProfiliKapisi, layout'ta monte) açar.
|
||||
// Sıralamayı değiştirmek navbar'daki sıralama hapından yapılır.
|
||||
if (kayitliProfil) {
|
||||
return (
|
||||
<div className="flex w-full max-w-md flex-col items-center gap-3">
|
||||
<div className="flex w-full max-w-md flex-col items-center">
|
||||
<Button
|
||||
asChild
|
||||
size="lg"
|
||||
@@ -122,18 +118,6 @@ export function HeroForm() {
|
||||
<ArrowRight className="size-4" aria-hidden />
|
||||
</Link>
|
||||
</Button>
|
||||
<p className="text-sm text-slate-600">
|
||||
{kayitliProfil.sira.toLocaleString("tr-TR")} ·{" "}
|
||||
{PUAN_TURU_ETIKET[kayitliProfil.tur] ?? kayitliProfil.tur} sıralamanla
|
||||
devam edersin.{" "}
|
||||
<button
|
||||
type="button"
|
||||
onClick={profilDuzenlemeyiAc}
|
||||
className="cursor-pointer font-medium text-slate-900 underline underline-offset-2 hover:text-slate-700"
|
||||
>
|
||||
Farklı sıralama gir
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import { useEffect, useMemo, useState } from "react";
|
||||
import { ArrowLeft, ArrowRight, Search, X } from "lucide-react";
|
||||
import { useReducedMotion } from "motion/react";
|
||||
import useMeasure from "react-use-measure";
|
||||
import { toast } from "sonner";
|
||||
import { toast } from "@/components/ui/bildirim";
|
||||
import { cities } from "turkey-map-react/lib/data";
|
||||
import { TransitionPanel } from "@/components/core/transition-panel";
|
||||
import { Button } from "@/components/ui/button";
|
||||
@@ -23,47 +23,8 @@ import { trBaslikDuzeni } from "@/lib/slug";
|
||||
import type { SihirbazFacetleri } from "@/types/yokatlas";
|
||||
import { ONCELIKLER, UNIVERSITE_TIPI_ETIKET, type SihirbazSecimleri, type UniversiteTipi } from "@/features/sihirbaz/sihirbaz-sabitler";
|
||||
import { OZEL_DURUMLAR, OZEL_DURUM_ETIKET, type OzelDurum } from "@/lib/ozel-durum";
|
||||
|
||||
export function Cip({
|
||||
secili,
|
||||
onClick,
|
||||
children,
|
||||
kucuk = false,
|
||||
ton = "notr",
|
||||
className = "",
|
||||
}: {
|
||||
secili: boolean;
|
||||
onClick: () => void;
|
||||
children: React.ReactNode;
|
||||
kucuk?: boolean;
|
||||
/**
|
||||
* bilgi: özel kontenjan durumu çipi (DESIGN.md §4.21) — pasif hâli bilgi
|
||||
* mavisi tonunda (§2 `primary` rolü), seçili hâli nötr çiple aynı.
|
||||
*/
|
||||
ton?: "notr" | "bilgi";
|
||||
className?: string;
|
||||
}) {
|
||||
// Şişko ve sade (DESIGN.md §1.1): çip 44 px dokunma hedefi; il
|
||||
// çipleri (kucuk) çok sayıda olduğu için 40 px. Hover §2.1 tek turuncu.
|
||||
const boyut = kucuk ? "h-10 px-3.5 text-sm" : "h-11 px-4 text-sm";
|
||||
const temel = `inline-flex cursor-pointer items-center rounded-full border font-semibold transition-[background-color,border-color,color,transform] duration-150 active:scale-[0.97] ${boyut} ${className}`;
|
||||
const pasif =
|
||||
ton === "bilgi"
|
||||
? "border-primary/30 bg-primary/10 text-primary hover:border-orange-500 hover:bg-orange-50/60 hover:text-orange-500"
|
||||
: "border-slate-200 bg-card text-slate-700 hover:border-orange-500 hover:bg-orange-50/60 hover:text-orange-500";
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClick}
|
||||
aria-pressed={secili}
|
||||
className={
|
||||
secili ? `${temel} border-primary bg-primary text-white` : `${temel} ${pasif}`
|
||||
}
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
// Seçim çipi tek kaynaktan (DESIGN.md §4.24): seçilince tik büyür, yazı kayar.
|
||||
import { CipGrubu, SecimCipi as Cip } from "@/components/ui/cip-grubu";
|
||||
|
||||
const ADIM_ADLARI = ["Alan", "Şehir", "Öncelik"] as const;
|
||||
|
||||
@@ -337,7 +298,7 @@ export function SihirbazAdimlar({
|
||||
<p className="mt-1 text-sm text-slate-500">
|
||||
Sıralamanla ulaşabileceğin alanlar. En az bir tane seç.
|
||||
</p>
|
||||
<div className="mt-4 flex flex-wrap gap-2">
|
||||
<CipGrubu etiket="İlgi alanları" className="mt-4">
|
||||
{facetler.kategoriler.map((k) => (
|
||||
<Cip
|
||||
key={k.ad}
|
||||
@@ -348,7 +309,7 @@ export function SihirbazAdimlar({
|
||||
<span className="ml-1.5 text-xs font-medium opacity-70">{k.adet}</span>
|
||||
</Cip>
|
||||
))}
|
||||
</div>
|
||||
</CipGrubu>
|
||||
</div>
|
||||
<div ref={panelRef}>
|
||||
<h3 className="font-heading text-xl font-bold text-slate-900">
|
||||
@@ -387,8 +348,9 @@ export function SihirbazAdimlar({
|
||||
görünüm ilk 10 il; kalanı "+N il daha" rozetiyle açılır. Arama
|
||||
yalnızca görünümü daraltır, seçimleri etkilemez ve kırpmayı
|
||||
devre dışı bırakır. */}
|
||||
<div
|
||||
className={`mt-3 flex max-h-64 flex-wrap gap-2 overflow-y-auto sm:max-h-none sm:overflow-visible ${
|
||||
<CipGrubu
|
||||
etiket="Şehirler"
|
||||
className={`mt-3 max-h-64 overflow-y-auto sm:max-h-none sm:overflow-visible ${
|
||||
facetYukleniyor ? "animate-pulse opacity-60" : ""
|
||||
}`}
|
||||
>
|
||||
@@ -424,7 +386,7 @@ export function SihirbazAdimlar({
|
||||
Daha az göster
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
</CipGrubu>
|
||||
|
||||
{ilAramaNorm &&
|
||||
gorunenTurkiyeIlleri.length === 0 &&
|
||||
@@ -439,8 +401,9 @@ export function SihirbazAdimlar({
|
||||
<p className="mb-2 text-sm font-semibold text-slate-700">
|
||||
KKTC & Yurt Dışı
|
||||
</p>
|
||||
<div
|
||||
className={`flex flex-wrap gap-2 ${
|
||||
<CipGrubu
|
||||
etiket="KKTC ve yurt dışı"
|
||||
className={`${
|
||||
facetYukleniyor ? "animate-pulse opacity-60" : ""
|
||||
}`}
|
||||
>
|
||||
@@ -455,7 +418,7 @@ export function SihirbazAdimlar({
|
||||
<span className="ml-1.5 text-xs font-medium opacity-70">{i.adet}</span>
|
||||
</Cip>
|
||||
))}
|
||||
</div>
|
||||
</CipGrubu>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
@@ -500,8 +463,9 @@ export function SihirbazAdimlar({
|
||||
<p className="mb-2 text-sm font-semibold text-slate-700">
|
||||
Üniversite tipi
|
||||
</p>
|
||||
<div
|
||||
className={`flex flex-wrap gap-2 ${
|
||||
<CipGrubu
|
||||
etiket="Üniversite tipi"
|
||||
className={`${
|
||||
facetYukleniyor ? "animate-pulse opacity-60" : ""
|
||||
}`}
|
||||
>
|
||||
@@ -525,7 +489,7 @@ export function SihirbazAdimlar({
|
||||
>
|
||||
Fark etmez
|
||||
</Cip>
|
||||
</div>
|
||||
</CipGrubu>
|
||||
</div>
|
||||
<div>
|
||||
<p className="mb-2 text-sm font-semibold text-slate-700">
|
||||
@@ -533,7 +497,7 @@ export function SihirbazAdimlar({
|
||||
</p>
|
||||
{/* Telefonda iki sütun, uzun ad iki satıra sarar: tek sütunda
|
||||
yedi çip 44 px'le pencereyi 812 px'in dışına itiyordu. */}
|
||||
<div className="grid grid-cols-2 gap-2 sm:flex sm:flex-wrap">
|
||||
<CipGrubu etiket="Senin için önemli olan" className="grid grid-cols-2 sm:flex sm:flex-wrap">
|
||||
{ONCELIKLER.map((o) => (
|
||||
<Cip
|
||||
key={o}
|
||||
@@ -544,7 +508,7 @@ export function SihirbazAdimlar({
|
||||
{o}
|
||||
</Cip>
|
||||
))}
|
||||
</div>
|
||||
</CipGrubu>
|
||||
<p className="mt-2 text-sm text-slate-500">
|
||||
Listeyi daraltmaz; programların sırasını ve gerekçelerini
|
||||
yönlendirir (istihdam ve kazanç TÜİK 2025, araştırma sırası URAP,
|
||||
@@ -559,7 +523,7 @@ export function SihirbazAdimlar({
|
||||
Özel kontenjan durumun{" "}
|
||||
<span className="font-normal text-slate-500">(isteğe bağlı)</span>
|
||||
</p>
|
||||
<div className="grid grid-cols-2 gap-2 sm:flex sm:flex-wrap">
|
||||
<CipGrubu etiket="Özel kontenjan durumun" className="grid grid-cols-2 sm:flex sm:flex-wrap">
|
||||
{OZEL_DURUMLAR.map((d) => (
|
||||
<Cip
|
||||
key={d}
|
||||
@@ -577,7 +541,7 @@ export function SihirbazAdimlar({
|
||||
{OZEL_DURUM_ETIKET[d]}
|
||||
</Cip>
|
||||
))}
|
||||
</div>
|
||||
</CipGrubu>
|
||||
<p className="mt-2 text-sm text-slate-500">
|
||||
ÖSYM bu durumlar için ayrı kontenjan ayırır. İşaretlersen o
|
||||
kontenjanı olan programlar listende öne çıkar ve danışman bunu
|
||||
|
||||
Reference in New Issue
Block a user