Update routing in next.config.ts to redirect "/tercih-robotu" to the hero form, add simple-icons dependency in package.json, and enhance database indexing for improved query performance. Remove unused SVG files and update metadata across various pages for better SEO and clarity.
All checks were successful
Deploy / deploy (push) Successful in 14m14s
All checks were successful
Deploy / deploy (push) Successful in 14m14s
This commit is contained in:
@@ -1,9 +1,17 @@
|
||||
import { NextResponse, type NextRequest } from "next/server";
|
||||
import { PUAN_TURLERI, rankWindowFacets, type PuanTuruKey } from "@/lib/db";
|
||||
import {
|
||||
PUAN_TURLERI,
|
||||
dilimAra,
|
||||
rankWindowFacets,
|
||||
type DilimKey,
|
||||
type PuanTuruKey,
|
||||
} from "@/lib/db";
|
||||
import { KATEGORILER } from "@/lib/kategoriler";
|
||||
|
||||
// Ana sayfadaki sihirbaz modalı için: sıralamaya uygun kategori/il/üniversite
|
||||
// tipi facet'leri. /sonuc bu veriyi server component'te doğrudan alır; burada
|
||||
// client'tan sıra girildikten sonra çekilir.
|
||||
// Sihirbaz modalı için: sıralamaya uygun kategori/il/üniversite tipi
|
||||
// facet'leri. Kademeli filtre: `kategori` (tekrarlı) il adımını,
|
||||
// `kategori` + `il` (tekrarlı) üniversite tipi adımını süzer — bir önceki
|
||||
// adımın seçimi bir sonrakinin seçeneklerini daraltır (bkz. rankWindowFacets).
|
||||
export function GET(request: NextRequest) {
|
||||
const { searchParams } = request.nextUrl;
|
||||
const sira = Number.parseInt(searchParams.get("sira") ?? "", 10);
|
||||
@@ -18,5 +26,31 @@ export function GET(request: NextRequest) {
|
||||
);
|
||||
}
|
||||
|
||||
return NextResponse.json({ facetler: rankWindowFacets(sira, tur) });
|
||||
const kategoriler = [
|
||||
...new Set(
|
||||
searchParams
|
||||
.getAll("kategori")
|
||||
.filter((k) => (KATEGORILER as readonly string[]).includes(k)),
|
||||
),
|
||||
];
|
||||
const iller = [
|
||||
...new Set(searchParams.getAll("il").filter((i) => i.trim().length > 0)),
|
||||
].slice(0, 5);
|
||||
|
||||
// ?dilimler=1: /meraklisina'daki canlı dilim şeridi için dilim başına
|
||||
// toplam sayılar da eklenir (limit 0 → yalnızca COUNT çalışır)
|
||||
const dilimToplam =
|
||||
searchParams.get("dilimler") === "1"
|
||||
? Object.fromEntries(
|
||||
(["hayal", "dengeli", "garanti"] as DilimKey[]).map((d) => [
|
||||
d,
|
||||
dilimAra(sira, tur, d, { limit: 0 }).toplam,
|
||||
]),
|
||||
)
|
||||
: undefined;
|
||||
|
||||
return NextResponse.json({
|
||||
facetler: rankWindowFacets(sira, tur, { kategoriler, iller }),
|
||||
...(dilimToplam ? { dilimToplam } : {}),
|
||||
});
|
||||
}
|
||||
|
||||
86
src/app/api/katalog-ara/route.ts
Normal file
86
src/app/api/katalog-ara/route.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
import { NextResponse, type NextRequest } from "next/server";
|
||||
import { getAllBolumler, getAllUniversiteler } from "@/lib/katalog";
|
||||
import { trBaslikDuzeni } from "@/lib/slug";
|
||||
|
||||
const SONUC_SINIRI = 6;
|
||||
|
||||
function normalize(metin: string): string {
|
||||
return metin
|
||||
.toLocaleLowerCase("tr-TR")
|
||||
.replace(/ı/g, "i")
|
||||
.replace(/ş/g, "s")
|
||||
.replace(/ğ/g, "g")
|
||||
.replace(/ü/g, "u")
|
||||
.replace(/ö/g, "o")
|
||||
.replace(/ç/g, "c")
|
||||
.replace(/â/g, "a")
|
||||
.replace(/î/g, "i")
|
||||
.replace(/û/g, "u")
|
||||
.normalize("NFKD")
|
||||
.replace(/[̀-ͯ]/g, "");
|
||||
}
|
||||
|
||||
function eslesmePuani(ad: string, sorgu: string): number {
|
||||
const normalAd = normalize(ad);
|
||||
if (normalAd === sorgu) return 0;
|
||||
if (normalAd.startsWith(sorgu)) return 1;
|
||||
if (normalAd.split(/\s+/).some((kelime) => kelime.startsWith(sorgu))) return 2;
|
||||
return 3;
|
||||
}
|
||||
|
||||
export function GET(request: NextRequest) {
|
||||
const hamSorgu = request.nextUrl.searchParams.get("q")?.trim() ?? "";
|
||||
const sorgu = normalize(hamSorgu).slice(0, 80);
|
||||
|
||||
if (sorgu.length < 2) {
|
||||
return NextResponse.json({ bolumler: [], universiteler: [] });
|
||||
}
|
||||
|
||||
const bolumler = getAllBolumler()
|
||||
.filter((bolum) => normalize(bolum.ad).includes(sorgu))
|
||||
.sort(
|
||||
(a, b) =>
|
||||
eslesmePuani(a.ad, sorgu) - eslesmePuani(b.ad, sorgu) ||
|
||||
b.programSayisi - a.programSayisi,
|
||||
)
|
||||
.slice(0, SONUC_SINIRI)
|
||||
.map((bolum) => ({
|
||||
href: `/bolum/${bolum.slug}`,
|
||||
ad: bolum.ad,
|
||||
programSayisi: bolum.programSayisi,
|
||||
enIyiSira: bolum.enIyiSira,
|
||||
seviye:
|
||||
bolum.lisans && bolum.onlisans
|
||||
? "Lisans ve önlisans"
|
||||
: bolum.lisans
|
||||
? "Lisans"
|
||||
: "Önlisans",
|
||||
}));
|
||||
|
||||
const universiteler = getAllUniversiteler()
|
||||
.filter((universite) => normalize(universite.ad).includes(sorgu))
|
||||
.sort(
|
||||
(a, b) =>
|
||||
eslesmePuani(a.ad, sorgu) - eslesmePuani(b.ad, sorgu) ||
|
||||
b.programSayisi - a.programSayisi,
|
||||
)
|
||||
.slice(0, SONUC_SINIRI)
|
||||
.map((universite) => ({
|
||||
href: `/universite/${universite.slug}`,
|
||||
ad: universite.ad,
|
||||
il: universite.il ? trBaslikDuzeni(universite.il) : null,
|
||||
tur:
|
||||
universite.unitur === "DEVLET"
|
||||
? "Devlet"
|
||||
: universite.unitur &&
|
||||
["VAKIF", "VAKIF MYO"].includes(universite.unitur)
|
||||
? "Vakıf"
|
||||
: universite.unitur
|
||||
? "KKTC / Yurt dışı"
|
||||
: null,
|
||||
programSayisi: universite.programSayisi,
|
||||
fakulteSayisi: universite.fakulteSayisi,
|
||||
}));
|
||||
|
||||
return NextResponse.json({ bolumler, universiteler });
|
||||
}
|
||||
43
src/app/api/programlar/route.ts
Normal file
43
src/app/api/programlar/route.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import { NextResponse, type NextRequest } from "next/server";
|
||||
import {
|
||||
PUAN_TURLERI,
|
||||
dilimAra,
|
||||
type DilimKey,
|
||||
type PuanTuruKey,
|
||||
} from "@/lib/db";
|
||||
|
||||
const SAYFA_BOYU = 20;
|
||||
|
||||
const DILIMLER: DilimKey[] = ["hayal", "dengeli", "garanti"];
|
||||
|
||||
// Sonuç sayfasındaki ham tablonun "daha fazla göster" sayfalaması: dilim
|
||||
// başına SAYFA_BOYU program, yakından uzağa sıralı. İlk sayfa server
|
||||
// component'te render edilir (bkz. sonuc/page.tsx); devamı buradan akar.
|
||||
export function GET(request: NextRequest) {
|
||||
const { searchParams } = request.nextUrl;
|
||||
const sira = Number.parseInt(searchParams.get("sira") ?? "", 10);
|
||||
const turParam = searchParams.get("tur") ?? "say";
|
||||
const tur: PuanTuruKey =
|
||||
turParam in PUAN_TURLERI ? (turParam as PuanTuruKey) : "say";
|
||||
const dilimParam = searchParams.get("dilim") ?? "";
|
||||
const offset = Number.parseInt(searchParams.get("offset") ?? "0", 10);
|
||||
|
||||
if (!Number.isFinite(sira) || sira < 1 || sira > 4_000_000) {
|
||||
return NextResponse.json(
|
||||
{ error: "Geçerli bir sıralama gerekli." },
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
if (!DILIMLER.includes(dilimParam as DilimKey)) {
|
||||
return NextResponse.json({ error: "Geçersiz dilim." }, { status: 400 });
|
||||
}
|
||||
if (!Number.isFinite(offset) || offset < 0 || offset > 100_000) {
|
||||
return NextResponse.json({ error: "Geçersiz offset." }, { status: 400 });
|
||||
}
|
||||
|
||||
const { programlar, toplam } = dilimAra(sira, tur, dilimParam as DilimKey, {
|
||||
limit: SAYFA_BOYU,
|
||||
offset,
|
||||
});
|
||||
return NextResponse.json({ programlar, toplam });
|
||||
}
|
||||
Reference in New Issue
Block a user