Files
frontend-app/src/pages/kaza.js
T

366 lines
14 KiB
JavaScript
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { calculateMissedPrayers } from "../utils/hijri.js";
const PRAYERS = [
{ id: "fajr", name: "SABAH", arabic: "Fecr" },
{ id: "dhuhr", name: "ÖĞLE", arabic: "Zuhur" },
{ id: "asr", name: "İKİNDİ", arabic: "Asr" },
{ id: "maghrib", name: "AKŞAM", arabic: "Mağrib" },
{ id: "isha", name: "YATSI", arabic: "İşa" },
{ id: "vitr", name: "VİTİR", arabic: "Vitr" },
];
export function renderKaza(pb) {
const user = pb.authStore.model;
const birthDate = user?.birthDate || user?.birthdate;
const gender = user?.gender || "male";
const html = `
<div class="relative bg-slate-900/80 border border-white/10 p-6 md:p-8 rounded-3xl shadow-2xl max-w-4xl w-full h-[85vh] flex flex-col overflow-hidden">
<button id="closeBtn" class="absolute top-4 right-4 bg-rose-500/20 hover:bg-rose-500/40 text-rose-300 border border-rose-500/30 w-8 h-8 rounded-full flex items-center justify-center font-bold text-xs transition-all">
</button>
<div class="flex items-center gap-3 mb-2">
<span class="text-3xl">📿</span>
<div>
<h2 class="text-2xl font-bold text-white">Kaza Namazı Sayaç & Takip</h2>
<p class="text-slate-400 text-xs">Hicri takvime göre kaza borcunuzu sabitleyin ve kıldıkça düşürün.</p>
</div>
</div>
${
!birthDate
? `
<div class="bg-slate-950/50 border border-white/10 p-6 rounded-2xl my-auto text-center max-w-md mx-auto w-full">
<h3 class="text-base font-bold text-white mb-2">Doğum Tarihi Gerekli</h3>
<p class="text-xs text-slate-400 mb-6">Kaza namazı borcunuzu hesaplayabilmemiz için profil bilgilerinizi girin.</p>
<form id="profileForm" class="space-y-4">
<div class="text-left">
<label class="block text-xs text-slate-300 mb-1">Doğum Tarihi ve Saati</label>
<input type="datetime-local" id="birthDateInput" required class="w-full bg-slate-900 border border-slate-800 rounded-xl px-4 py-2.5 text-white text-sm focus:border-indigo-500 focus:outline-none" />
</div>
<div class="text-left">
<label class="block text-xs text-slate-300 mb-1">Cinsiyet</label>
<select id="genderInput" required class="w-full bg-slate-900 border border-slate-800 rounded-xl px-4 py-2.5 text-white text-sm focus:border-indigo-500 focus:outline-none">
<option value="male">Erkek</option>
<option value="female">Kadın</option>
</select>
</div>
<button type="submit" class="w-full bg-indigo-600 hover:bg-indigo-500 text-white py-2.5 rounded-xl text-sm font-semibold transition-all">
Kaydet
</button>
</form>
</div>
`
: `
<!-- HESAPLAMA VE İLERLEME BARI -->
<div class="bg-slate-950/60 border border-white/10 p-5 rounded-2xl mb-6 mt-2 flex flex-col gap-4">
<div class="flex flex-wrap items-center justify-between gap-2 border-b border-white/10 pb-3">
<div>
<h3 class="text-xs font-bold text-indigo-300 uppercase tracking-wider">Kaza Borcu Tanımlama</h3>
<p class="text-[11px] text-slate-400">Hicri takvime göre kaza borcunuzu hesaplar ve PocketBase'e kaydeder.</p>
</div>
<button id="calcAndSaveBtn" class="bg-indigo-600 hover:bg-indigo-500 text-white px-4 py-2 rounded-xl text-xs font-semibold transition-all shadow-md active:scale-95">
⚡ Kaza Borçlarını Otomatik Hesapla & PB'ye Kaydet
</button>
</div>
<div>
<div class="flex items-center justify-between mb-2">
<span class="text-xs font-bold text-indigo-300 flex items-center gap-1.5">
<span>📊</span> KAZA BORCU TAMAMLAMA ORANI
</span>
<span id="overallPercentage" class="text-sm font-extrabold text-emerald-400">%0 Kılındı</span>
</div>
<div class="w-full h-4 bg-slate-900 rounded-full overflow-hidden border border-white/5 p-0.5">
<div id="overallProgressBar" class="h-full bg-linear-to-r from-indigo-500 via-purple-500 to-emerald-400 rounded-full transition-all duration-700 ease-out" style="width: 0%;"></div>
</div>
<div class="flex justify-between items-center text-[11px] text-slate-400 mt-2.5">
<span>Hesaplanan İlk Borç: <b id="initialTotalText" class="text-slate-300">0</b> vakit</span>
<span>Kalan Güncel Borç: <b id="totalRemainingText" class="text-amber-400">0</b> vakit</span>
</div>
</div>
</div>
<!-- VAKİT İLERLEME KARTLARI -->
<div class="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-6 gap-3 mb-6">
${PRAYERS.map(
(p) => `
<div class="bg-slate-950/40 border border-white/10 p-3.5 rounded-2xl text-center relative overflow-hidden group">
<p class="text-[11px] font-bold text-slate-300 mb-1">${p.name}</p>
<p class="text-xs text-slate-400">Kalan: <span id="remaining-${p.id}" class="font-extrabold text-amber-300">-</span></p>
<p class="text-[10px] text-slate-500 mt-0.5">İlk Borç: <span id="initial-${p.id}" class="text-slate-400">0</span></p>
<div class="w-full bg-slate-800 h-2 rounded-full mt-2.5 overflow-hidden">
<div id="progress-${p.id}" class="bg-emerald-400 h-full transition-all duration-500" style="width: 0%;"></div>
</div>
</div>
`,
).join("")}
</div>
<!-- HIZLI SAYAÇ GÜNCELLEME ALANI -->
<div class="bg-slate-950/40 border border-white/10 p-5 rounded-2xl mb-auto">
<h3 class="text-xs font-bold text-slate-300 uppercase tracking-wider mb-3">Kaza Kıldım (Borçtan Düş)</h3>
<form id="addKazaForm" class="flex flex-wrap items-center gap-3">
<select id="kazaPrayerType" class="bg-slate-900 border border-slate-800 rounded-xl px-4 py-2.5 text-white text-sm focus:outline-none">
${PRAYERS.map((p) => `<option value="${p.id}">${p.name}</option>`).join("")}
</select>
<input type="number" id="kazaCount" min="1" value="1" required class="w-24 bg-slate-900 border border-slate-800 rounded-xl px-4 py-2.5 text-white text-sm text-center focus:outline-none font-bold" />
<button type="button" id="subtractKazaBtn" class="bg-emerald-600 hover:bg-emerald-500 text-white px-5 py-2.5 rounded-xl text-sm font-semibold transition-all shadow-lg active:scale-95 flex items-center gap-1.5">
<span>✓</span> Kıldım (Borçtan Düş -)
</button>
<button type="button" id="addKazaBtn" class="bg-rose-600/30 hover:bg-rose-600/50 text-rose-300 border border-rose-500/30 px-4 py-2.5 rounded-xl text-sm font-medium transition-all active:scale-95">
Borç Ekle (+)
</button>
</form>
<p id="kazaMsg" class="text-xs mt-3 hidden"></p>
</div>
`
}
</div>
`;
return {
html,
initEvents: (container) => {
container.querySelector("#closeBtn")?.addEventListener("click", () => {
window.dispatchEvent(
new CustomEvent("navigate", { detail: "desktop" }),
);
});
if (!birthDate) {
container
.querySelector("#profileForm")
?.addEventListener("submit", async (e) => {
e.preventDefault();
const bInput = container.querySelector("#birthDateInput").value;
const gInput = container.querySelector("#genderInput").value;
try {
await pb.collection("users").update(pb.authStore.model.id, {
birthdate: bInput,
birthDate: bInput,
gender: gInput,
});
pb.authStore.model.birthdate = bInput;
pb.authStore.model.gender = gInput;
window.dispatchEvent(
new CustomEvent("navigate", { detail: "kaza" }),
);
} catch (err) {
alert("Profil kaydedilemedi.");
}
});
} else {
refreshKazaStats(container, pb, birthDate, gender);
container
.querySelector("#calcAndSaveBtn")
?.addEventListener("click", async () => {
if (
confirm(
"Kaza borcunuz yeniden hesaplanıp PocketBase'e kaydedilecek. Onaylıyor musunuz?",
)
) {
const res = await initAndSaveInitialKaza(pb, birthDate, gender);
if (res.success) {
await refreshKazaStats(container, pb, birthDate, gender);
showKazaMsg(
container.querySelector("#kazaMsg"),
`✓ Borç hesaplandı (${res.days} gün) ve PocketBase'e kaydedildi!`,
true,
);
} else {
showKazaMsg(
container.querySelector("#kazaMsg"),
`Hata: ${res.error}`,
false,
);
}
}
});
container
.querySelector("#subtractKazaBtn")
?.addEventListener("click", async () => {
await updateKazaDebt(container, pb, birthDate, gender, -1);
});
container
.querySelector("#addKazaBtn")
?.addEventListener("click", async () => {
await updateKazaDebt(container, pb, birthDate, gender, 1);
});
}
},
};
}
async function initAndSaveInitialKaza(pb, birthDate, gender) {
try {
const calc = calculateMissedPrayers(birthDate, gender, 12);
if (!calc || calc.days <= 0) {
return {
success: false,
error:
"Doğum tarihiniz henüz mükellefiyet yaşından (12 Hicri yıl) küçük.",
};
}
const userId = pb.authStore.model.id;
let userRecord = null;
try {
userRecord = await pb
.collection("kaza_logs")
.getFirstListItem(`user = "${userId}"`);
} catch (e) {}
// PB tarafındaki varsayılan sütunlara (fajr, dhuhr vb.) borcu yazıyoruz
const initialData = {
user: userId,
fajr: calc.fajr,
dhuhr: calc.dhuhr,
asr: calc.asr,
maghrib: calc.maghrib,
isha: calc.isha,
vitr: calc.vitr,
};
if (userRecord) {
await pb.collection("kaza_logs").update(userRecord.id, initialData);
} else {
await pb.collection("kaza_logs").create(initialData);
}
return { success: true, days: calc.days, fajr: calc.fajr };
} catch (err) {
console.error("PocketBase Kayıt Hatası:", err);
return {
success: false,
error: err?.message || "PocketBase'e kaydedilemedi.",
};
}
}
async function updateKazaDebt(container, pb, birthDate, gender, direction) {
const prayerId = container.querySelector("#kazaPrayerType").value;
const countInput = container.querySelector("#kazaCount");
const countValue = Math.max(1, parseInt(countInput?.value || "1", 10));
const changeAmount = countValue * direction;
const msg = container.querySelector("#kazaMsg");
try {
const userId = pb.authStore.model.id;
let userRecord = await pb
.collection("kaza_logs")
.getFirstListItem(`user = "${userId}"`);
if (!userRecord) {
showKazaMsg(
msg,
"Önce 'Kaza Borçlarını Otomatik Hesapla & PB'ye Kaydet' butonuna basmalısınız.",
false,
);
return;
}
const currentDebt = userRecord[prayerId] || 0;
const newDebt = Math.max(0, currentDebt + changeAmount);
await pb.collection("kaza_logs").update(userRecord.id, {
[prayerId]: newDebt,
});
showKazaMsg(
msg,
direction < 0
? `✓ ${countValue} vakit kılındı, borçtan düşüldü! Kalan: ${newDebt}`
: `${countValue} vakit borca eklendi. Kalan: ${newDebt}`,
true,
);
if (countInput) countInput.value = "1";
await refreshKazaStats(container, pb, birthDate, gender);
} catch (err) {
console.error("Güncelleme hatası:", err);
showKazaMsg(msg, "PocketBase güncellenirken hata oluştu.", false);
}
}
async function refreshKazaStats(container, pb, birthDate, gender) {
// İlk toplam borç verisini direkt doğum tarihinden dinamik alıyoruz (Hiç şaşmaz!)
const initialCalc = calculateMissedPrayers(birthDate, gender, 12);
const userId = pb.authStore.model.id;
let userRecord = null;
try {
userRecord = await pb
.collection("kaza_logs")
.getFirstListItem(`user = "${userId}"`);
} catch (err) {}
let grandInitialTotal = 0;
let grandCurrentDebtTotal = 0;
PRAYERS.forEach((p) => {
const initialDebt = initialCalc[p.id] || 0;
// PB kaydı henüz yoksa kalan borç = ilk borçtur
const currentDebt = userRecord
? (userRecord[p.id] ?? initialDebt)
: initialDebt;
grandInitialTotal += initialDebt;
grandCurrentDebtTotal += currentDebt;
const remainEl = container.querySelector(`#remaining-${p.id}`);
const initialEl = container.querySelector(`#initial-${p.id}`);
const progressEl = container.querySelector(`#progress-${p.id}`);
if (remainEl) remainEl.textContent = currentDebt;
if (initialEl) initialEl.textContent = initialDebt;
// Yüzde Hesabı: (Kılınan Vakit / İlk Borç) * 100
if (progressEl) {
const completed = Math.max(0, initialDebt - currentDebt);
const pct =
initialDebt > 0
? Math.min(
100,
Math.max(0, Math.round((completed / initialDebt) * 100)),
)
: 0;
progressEl.style.width = `${pct}%`;
}
});
const totalCompleted = Math.max(0, grandInitialTotal - grandCurrentDebtTotal);
const overallPct =
grandInitialTotal > 0
? Math.min(
100,
Math.max(0, Math.round((totalCompleted / grandInitialTotal) * 100)),
)
: 0;
const bar = container.querySelector("#overallProgressBar");
const pctText = container.querySelector("#overallPercentage");
const initText = container.querySelector("#initialTotalText");
const remText = container.querySelector("#totalRemainingText");
if (bar) bar.style.width = `${overallPct}%`;
if (pctText) pctText.textContent = `%${overallPct} Kılındı`;
if (initText) initText.textContent = grandInitialTotal;
if (remText) remText.textContent = grandCurrentDebtTotal;
}
function showKazaMsg(el, text, success) {
if (!el) return;
el.textContent = text;
el.className = `text-xs mt-3 ${success ? "text-emerald-400 font-medium" : "text-rose-400 font-medium"}`;
el.classList.remove("hidden");
}