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

379 lines
15 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 hesaplayın ve PocketBase'de takip edin.</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 Hicri takvime göre hesaplayabilmemiz için doğum tarihini ve saatinizi 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">Hicri Kaza Borcu Tanımlama</h3>
<p class="text-[11px] text-slate-400">12 yaş mükellefiyeti ve doğum saatiniz (hilal düzeltmesi) baz alınarak borcunuz sabitlenir.</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> GENEL KAZA BİTİRME ORANI
</span>
<span id="overallPercentage" class="text-sm font-extrabold text-emerald-400">%0</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 w-[0%]"></div>
</div>
<div class="flex justify-between items-center text-[11px] text-slate-400 mt-2.5">
<span>Kılınan Toplam: <b id="totalCompletedText" class="text-emerald-400">0</b> vakit</span>
<span>Kalan Toplam 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">Kılınan: <span id="completed-${p.id}" class="text-emerald-400">0</span></p>
<div class="w-full bg-slate-800 h-1.5 rounded-full mt-2.5 overflow-hidden">
<div id="progress-${p.id}" class="bg-emerald-400 h-full w-[0%] transition-all duration-500"></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 / Düzenle</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="addKazaBtn" 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 (+1)
</button>
<button type="button" id="removeKazaBtn" 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">
Geri Al (-)
</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. PocketBase kullanıcı alanlarını kontrol edin.",
);
}
});
} else {
refreshKazaStats(container, pb);
container
.querySelector("#calcAndSaveBtn")
?.addEventListener("click", async () => {
if (
confirm(
"Doğum tarihiniz ve saatiniz Hicri takvime dönüştürülüp 12 yaş borcunuz PocketBase'e kaydedilecek. Onaylıyor musunuz?",
)
) {
const res = await initAndSaveInitialKaza(pb, birthDate, gender);
if (res.success) {
await refreshKazaStats(container, pb);
showKazaMsg(
container.querySelector("#kazaMsg"),
`✓ Hicri borç hesaplandı (${res.days} gün) ve PocketBase'e kaydedildi!`,
true,
);
} else {
showKazaMsg(
container.querySelector("#kazaMsg"),
`Hata: ${res.error}`,
false,
);
}
}
});
container
.querySelector("#addKazaBtn")
?.addEventListener("click", async () => {
await updateKazaCount(container, pb, 1);
});
container
.querySelector("#removeKazaBtn")
?.addEventListener("click", async () => {
await updateKazaCount(container, pb, -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 görünüyor.",
};
}
const userId = pb.authStore.model.id;
let userRecord = null;
try {
userRecord = await pb
.collection("kaza_logs")
.getFirstListItem(`user = "${userId}"`);
} catch (e) {
// Kayıt yoksa yeni oluşturulacak
}
const initialData = {
user: userId,
target_fajr: calc.fajr,
target_dhuhr: calc.dhuhr,
target_asr: calc.asr,
target_maghrib: calc.maghrib,
target_isha: calc.isha,
target_vitr: calc.vitr,
fajr: userRecord?.fajr || 0,
dhuhr: userRecord?.dhuhr || 0,
asr: userRecord?.asr || 0,
maghrib: userRecord?.maghrib || 0,
isha: userRecord?.isha || 0,
vitr: userRecord?.vitr || 0,
};
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 kayıt yaparken hata oluştu. kaza_logs izinlerini kontrol edin.",
};
}
}
async function updateKazaCount(container, pb, direction) {
const prayerId = container.querySelector("#kazaPrayerType").value;
const countInput = container.querySelector("#kazaCount");
const amount =
Math.max(1, parseInt(countInput?.value || "1", 10)) * 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 yukarıdaki 'Kaza Borçlarını Otomatik Hesapla & PB'ye Kaydet' butonuna tıklamalısınız.",
false,
);
return;
}
const currentCompleted = userRecord[prayerId] || 0;
const newCompleted = Math.max(0, currentCompleted + amount);
await pb.collection("kaza_logs").update(userRecord.id, {
[prayerId]: newCompleted,
});
showKazaMsg(
msg,
amount > 0
? `✓ ${amount} vakit kılınanlara eklendi!`
: `${Math.abs(amount)} vakit düşüldü.`,
true,
);
if (countInput) countInput.value = "1";
await refreshKazaStats(container, pb);
} catch (err) {
console.error("Güncelleme hatası:", err);
showKazaMsg(msg, "Güncelleme başarısız.", false);
}
}
async function refreshKazaStats(container, pb) {
const userId = pb.authStore.model.id;
let userRecord = null;
try {
userRecord = await pb
.collection("kaza_logs")
.getFirstListItem(`user = "${userId}"`);
} catch (err) {}
if (!userRecord) {
PRAYERS.forEach((p) => {
const remainEl = container.querySelector(`#remaining-${p.id}`);
const completedEl = container.querySelector(`#completed-${p.id}`);
if (remainEl) remainEl.textContent = "Hesaplanmadı";
if (completedEl) completedEl.textContent = "0";
});
return;
}
let grandTotalTarget = 0;
let grandTotalCompleted = 0;
PRAYERS.forEach((p) => {
const target = userRecord[`target_${p.id}`] || 0;
const completed = userRecord[p.id] || 0;
const remaining = Math.max(0, target - completed);
grandTotalTarget += target;
grandTotalCompleted += completed;
const remainEl = container.querySelector(`#remaining-${p.id}`);
const completedEl = container.querySelector(`#completed-${p.id}`);
const progressEl = container.querySelector(`#progress-${p.id}`);
if (remainEl) remainEl.textContent = remaining;
if (completedEl) completedEl.textContent = completed;
if (progressEl) {
const pct =
target > 0
? Math.min(100, Math.round((completed / target) * 100))
: 100;
progressEl.style.width = `${pct}%`;
}
});
const grandTotalRemaining = Math.max(
0,
grandTotalTarget - grandTotalCompleted,
);
const overallPct =
grandTotalTarget > 0
? Math.min(
100,
Math.round((grandTotalCompleted / grandTotalTarget) * 100),
)
: 100;
const bar = container.querySelector("#overallProgressBar");
const pctText = container.querySelector("#overallPercentage");
const compText = container.querySelector("#totalCompletedText");
const remText = container.querySelector("#totalRemainingText");
if (bar) bar.style.width = `${overallPct}%`;
if (pctText) pctText.textContent = `%${overallPct}`;
if (compText) compText.textContent = grandTotalCompleted;
if (remText) remText.textContent = grandTotalRemaining;
}
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");
}