366 lines
14 KiB
JavaScript
Executable File
366 lines
14 KiB
JavaScript
Executable File
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");
|
||
}
|