const state = {
cycleLength: 28,
periodLength: 5,
periodStart: null,
viewDate: luxon.DateTime.local(),
chart: null
};
document.addEventListener('DOMContentLoaded', () => {
document.getElementById('period-start').valueAsDate = new Date();
document.getElementById('calculate-btn').addEventListener('click', calculateCycle);
document.getElementById('prev-month').addEventListener('click', showPreviousMonth);
document.getElementById('next-month').addEventListener('click', showNextMonth);
});
function calculateCycle() {
state.cycleLength = parseInt(document.getElementById('cycle-length').value) || 28;
state.periodLength = parseInt(document.getElementById('period-length').value) || 5;
const periodStart = document.getElementById('period-start').value;
if (!periodStart) {
alert('Επιλέξτε την ημερομηνία έναρξης της περιόδου');
return;
}
state.periodStart = luxon.DateTime.fromISO(periodStart);
state.viewDate = state.periodStart;
document.getElementById('result-container').classList.remove('hidden');
updateCycleDetails();
updateChart();
renderCalendar();
}
function updateCycleDetails() {
if (!state.periodStart) return;
const nextPeriodStart = state.periodStart.plus({ days: state.cycleLength });
const ovulationDay = nextPeriodStart.minus({ days: 14 });
const fertileStart = ovulationDay.minus({ days: 5 });
const fertileEnd = ovulationDay.plus({ days: 1 });
const safePeriod1Start = state.periodStart.plus({ days: state.periodLength });
const safePeriod1End = fertileStart.minus({ days: 1 });
const safePeriod2Start = fertileEnd.plus({ days: 1 });
const safePeriod2End = nextPeriodStart.minus({ days: 1 });
document.getElementById('cycle-details').innerHTML = `
Εμμηνορροϊκή περίοδος
${state.periodStart.toFormat('yyyy-MM-dd')} - ${state.periodStart.plus({ days: state.periodLength-1 }).toFormat('MM-dd')}
Προσοχή στην ξεκούραση και την υγιεινή
Περίοδος κινδύνου
${fertileStart.toFormat('MM-dd')} - ${fertileEnd.toFormat('MM-dd')}
Υψηλή πιθανότητα σύλληψης (ημέρα ωορρηξίας περίπου 30%)
Ημέρα ωορρηξίας
${ovulationDay.toFormat('MM-dd')}
Η υψηλότερη πιθανότητα σύλληψης
Περίοδος χαμηλής πιθανότητας σύλληψης
${safePeriod1Start.toFormat('MM-dd')} - ${safePeriod1End.toFormat('MM-dd')}
${safePeriod2Start.toFormat('MM-dd')} - ${safePeriod2End.toFormat('MM-dd')}
Χαμηλή πιθανότητα σύλληψης (<5%)
Επόμενη προβλεπόμενη περίοδος
${nextPeriodStart.toFormat('yyyy-MM-dd')}
`;
}
function updateChart() {
const ctx = document.getElementById('cycle-chart').getContext('2d');
if (state.chart) {
state.chart.destroy();
}
const totalDays = state.cycleLength;
const fertileDays = 7;
const safeDays = totalDays - state.periodLength - fertileDays;
state.chart = new Chart(ctx, {
type: 'doughnut',
data: {
labels: ['Εμμηνορροϊκή περίοδος', 'Περίοδος κινδύνου', 'Ημέρα ωορρηξίας', 'Ασφαλής περίοδος'],
datasets: [{
data: [state.periodLength, 6, 1, safeDays],
backgroundColor: ['#fecaca', '#fef08a', '#f59e0b', '#bbf7d0'],
borderWidth: 0
}]
},
options: {
responsive: true,
plugins: {
legend: { position: 'bottom' },
tooltip: {
callbacks: {
label: function(context) {
const label = context.label || '';
const value = context.raw;
const percentage = Math.round((value / totalDays) * 100);
return `${label}: ${value}days (${percentage}%)`;
}
}
}
},
cutout: '65%',
animation: {
animateScale: true,
animateRotate: true
}
}
});
}
function renderCalendar() {
if (!state.periodStart) return;
const monthStart = state.viewDate.startOf('month');
const monthEnd = state.viewDate.endOf('month');
const startDay = monthStart.weekday;
const daysInMonth = monthEnd.day;
document.getElementById('current-month').textContent = state.viewDate.toFormat('yyyy-MM');
const calendarDays = document.getElementById('calendar-days');
calendarDays.innerHTML = '';
const prevMonthEnd = monthStart.minus({ days: 1 });
for (let i = startDay - 1; i >= 0; i--) {
const day = prevMonthEnd.day - i;
const date = prevMonthEnd.set({ day: day });
calendarDays.appendChild(createDayElement(date, day, true));
}
const today = luxon.DateTime.local();
for (let day = 1; day<= daysInMonth; day++) {
const date = monthStart.set({ day: day });
calendarDays.appendChild(createDayElement(date, day, false, date.hasSame(today, 'day')));
}
const daysToAdd = 42 - (startDay + daysInMonth);
for (let day = 1; day<= daysToAdd; day++) {
const date = monthEnd.plus({ days: day });
calendarDays.appendChild(createDayElement(date, day, true));
}
}
function createDayElement(date, day, isOtherMonth, isToday = false) {
const dayElement = document.createElement('div');
dayElement.className = 'calendar-day';
dayElement.textContent = day;
if (isOtherMonth) {
dayElement.classList.add('text-gray-400');
return dayElement;
}
if (isToday) dayElement.classList.add('today');
let currentPeriod = state.periodStart;
while (date > currentPeriod.plus({ days: state.cycleLength })) {
currentPeriod = currentPeriod.plus({ days: state.cycleLength });
}
const nextPeriod = currentPeriod.plus({ days: state.cycleLength });
const ovulationDay = nextPeriod.minus({ days: 14 });
const fertileStart = ovulationDay.minus({ days: 5 });
const fertileEnd = ovulationDay.plus({ days: 1 });
const tooltip = document.createElement('div');
tooltip.className = 'tooltip';
if (date >= currentPeriod && date< currentPeriod.plus({ days: state.periodLength })) {
dayElement.classList.add('menstrual');
tooltip.innerHTML = `Εμμηνορροϊκή περίοδος
Προσοχή στην ξεκούραση και την υγιεινή
`;
}
else if (date >= fertileStart && date<= fertileEnd) {
if (date.hasSame(ovulationDay, 'day')) {
dayElement.classList.add('ovulation');
tooltip.innerHTML = `Ημέρα ωορρηξίας
Υψηλότερη πιθανότητα σύλληψης (περίπου 30%)
`;
} else {
dayElement.classList.add('fertile');
const daysDiff = Math.abs(date.diff(ovulationDay, 'days').days);
const probability = 30 - daysDiff * 5;
tooltip.innerHTML = `Περίοδος κινδύνου
Πιθανότητα σύλληψης περίπου ${probability}%
`;
}
}
else {
dayElement.classList.add('safe');
tooltip.innerHTML = `Ασφαλής περίοδος
Χαμηλή πιθανότητα σύλληψης (<5%)
`;
}
dayElement.appendChild(tooltip);
return dayElement;
}
function showPreviousMonth() {
state.viewDate = state.viewDate.minus({ months: 1 });
renderCalendar();
}
function showNextMonth() {
state.viewDate = state.viewDate.plus({ months: 1 });
renderCalendar();
}