refactor(sezon): tercih takvimi tek kaynakta (lib/sezon.ts)

Üst banttaki elle gömülü 2026 başlangıç/bitiş damgaları `src/lib/sezon.ts`
içindeki `SEZON_TAKVIMI`ye taşındı. Aşama hesabı (`sezonAsamasi`) ve geri
sayım hedefi (`sezonHedefi`) artık oradan geliyor; `devam-ediyor` dalının
davranışı birebir aynı. 2027 takvimi ÖSYM tarafından açıklanmadığı için
`null` bırakıldı — tarih uydurulmadı.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
bilalgursen
2026-09-22 01:18:24 +03:00
parent e21cda4d6f
commit 54fb72d18c
2 changed files with 91 additions and 14 deletions

View File

@@ -3,12 +3,10 @@
import { startTransition, useEffect, useState } from "react"; import { startTransition, useEffect, useState } from "react";
import NumberFlow, { NumberFlowGroup } from "@number-flow/react"; import NumberFlow, { NumberFlowGroup } from "@number-flow/react";
import { CalendarDays, Clock3 } from "lucide-react"; import { CalendarDays, Clock3 } from "lucide-react";
import { sezonAsamasi, sezonHedefi, type SezonAsamasi } from "@/lib/sezon";
const TERCIH_BASLANGICI = new Date("2026-07-29T00:00:00+03:00").getTime();
const TERCIH_BITISI = new Date("2026-08-10T23:59:59+03:00").getTime();
type GeriSayim = { type GeriSayim = {
asama: "baslamadi" | "devam-ediyor" | "sona-erdi"; asama: SezonAsamasi;
gun: number; gun: number;
saat: number; saat: number;
dakika: number; dakika: number;
@@ -16,16 +14,9 @@ type GeriSayim = {
}; };
function geriSayimiHesapla(simdi: number): GeriSayim { function geriSayimiHesapla(simdi: number): GeriSayim {
const asama = const asama = sezonAsamasi(simdi);
simdi < TERCIH_BASLANGICI const hedef = sezonHedefi(asama);
? "baslamadi" const kalan = hedef === null ? 0 : Math.max(0, hedef - simdi);
: simdi <= TERCIH_BITISI
? "devam-ediyor"
: "sona-erdi";
const hedef =
asama === "baslamadi" ? TERCIH_BASLANGICI : TERCIH_BITISI;
const kalan = Math.max(0, hedef - simdi);
return { return {
asama, asama,

86
src/lib/sezon.ts Normal file
View File

@@ -0,0 +1,86 @@
/**
* Tercih sezonu takvimi — TEK KAYNAK. 2027 takvimi açıklanınca buraya yazılır;
* başka hiçbir yer değişmez. Saf: yalnız `veri-yillari` sabitlerini okur, yan
* etkisi yok — client, server ve betikler aynı dosyayı kullanabilir.
*/
import { BASLIK_YILI, TERCIH_YILI } from "@/lib/veri-yillari";
/** Bir sezonun ÖSYM takvimi. ISO 8601, Türkiye saati (+03:00) ofsetiyle. */
export type SezonAraligi = {
readonly baslangic: string;
readonly bitis: string;
};
/**
* Yıl → tercih dönemi aralığı. `null` = takvim henüz açıklanmadı; tarih
* UYDURULMAZ, sezon "takvim-belirsiz" davranışına düşer.
*/
export const SEZON_TAKVIMI: Readonly<Record<number, SezonAraligi | null>> = {
2026: {
baslangic: "2026-07-29T00:00:00+03:00",
bitis: "2026-08-10T23:59:59+03:00",
},
// ÖSYM 2027 tercih takvimini açıklamadı (22 Eyl 2026 itibarıyla).
2027: null,
};
export type SezonAsamasi =
| "takvim-belirsiz"
| "baslamadi"
| "devam-ediyor"
| "sona-erdi";
/** Verilen anda sezonun hangi aşamada olduğu. */
export function sezonAsamasi(
simdi: number,
yil: number = TERCIH_YILI,
): SezonAsamasi {
const takvim = SEZON_TAKVIMI[yil] ?? null;
if (!takvim) {
return "takvim-belirsiz";
}
const baslangic = new Date(takvim.baslangic).getTime();
const bitis = new Date(takvim.bitis).getTime();
return simdi < baslangic
? "baslamadi"
: simdi <= bitis
? "devam-ediyor"
: "sona-erdi";
}
/**
* Geri sayımın hedef zaman damgası: dönem başlamadıysa başlangıç, sürüyorsa
* bitiş. `null` dönen aşamalarda (takvim belirsiz ya da dönem kapandı) sayılacak
* bir şey yoktur — çağıran geri sayımı hiç render etmemelidir.
*/
export function sezonHedefi(
asama: SezonAsamasi,
yil: number = TERCIH_YILI,
): number | null {
const takvim = SEZON_TAKVIMI[yil] ?? null;
if (!takvim) {
return null;
}
if (asama === "baslamadi") {
return new Date(takvim.baslangic).getTime();
}
return asama === "devam-ediyor" ? new Date(takvim.bitis).getTime() : null;
}
/** Üst bandın saat ikonunun yanındaki metin — aşama başına tek kaynak. */
export const BANT_METNI: Record<SezonAsamasi, string> = {
"takvim-belirsiz": `${TERCIH_YILI} tercihine hazırlık · ${BASLIK_YILI} yerleştirme verisi yüklü`,
baslamadi: "Başlamasına",
"devam-ediyor": "Sona ermesine",
"sona-erdi": `${TERCIH_YILI} tercihleri tamamlandı`,
};
/** Üst bandın `aria-label`'ı. */
export const BANT_ARIA = `${TERCIH_YILI} YKS tercih dönemi`;