Files
firka-extension/absences/absences.js
2026-01-12 19:03:17 +01:00

495 lines
17 KiB
JavaScript

async function collectAbsencesData() {
const basicData = {
schoolInfo: {
name: await storageManager.get("schoolName", "OM azonosító - Iskola neve"),
id: await storageManager.get("schoolCode", ""),
},
userData: {
name: await storageManager.get("userName", "Felhasználónév"),
time:
document.querySelector(".usermenu_timer")?.textContent?.trim() ||
"45:00",
},
};
try {
const currentDomain = window.location.hostname;
const response = await fetch(
`https://${currentDomain}/api/HianyzasokApi/GetHianyzasGrid?sort=MulasztasDatum-desc&page=1&pageSize=500&group=&filter=&data=%7B%7D&_=${Date.now()}`,
{
method: "GET",
credentials: "include",
headers: {
Accept: "application/json, text/javascript, */*; q=0.01",
"X-Requested-With": "XMLHttpRequest",
},
},
);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const apiData = await response.json();
const absences = [];
if (apiData.Data && Array.isArray(apiData.Data)) {
apiData.Data.forEach((item) => {
const date = new Date(item.MulasztasDatum);
const formattedDate = `${date.getFullYear()}.${(date.getMonth() + 1).toString().padStart(2, "0")}.${date.getDate().toString().padStart(2, "0")}.`;
let justificationStatus = "pending";
if (item.Igazolt_BOOL === true) {
justificationStatus = "justified";
} else if (item.Igazolt_BOOL === false && item.IgazolasTipus !== null) {
justificationStatus = "unjustified";
}
const absenceType = item.MulasztasTipus === 1500 ? 'absence' : item.MulasztasTipus === 1499 ? 'late' : 'other';
absences.push({
date: formattedDate,
rawDate: date,
dateKey: `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, "0")}-${date.getDate().toString().padStart(2, "0")}`,
lesson: item.Oraszam?.toString() || "",
subject: item.Targy || "",
topic: item.Tema || "",
type: item.MulasztasTipus_DNAME || "",
absenceType: absenceType,
absenceTypeId: item.MulasztasTipus,
justified: item.Igazolt_BOOL === true,
justificationStatus: justificationStatus,
purposeful: item.TanoraiCeluMulasztas_BNAME || "",
justificationType: item.IgazolasTipus_DNAME || "",
minutes: item.KesesPercben || 0,
});
});
}
const groupedByDate = absences.reduce((groups, absence) => {
const key = absence.dateKey;
if (!groups[key]) {
groups[key] = [];
}
groups[key].push(absence);
return groups;
}, {});
Object.keys(groupedByDate).forEach(date => {
groupedByDate[date].sort((a, b) => parseInt(a.lesson) - parseInt(b.lesson));
});
return { basicData, absences, groupedByDate };
} catch (error) {
console.error("Hiba az API hívás során:", error);
return { basicData, absences: [], groupedByDate: {} };
}
}
function createStatsSection(absences) {
const statsSection = document.createElement('div');
statsSection.className = 'stats-section';
const statsTitle = document.createElement('h2');
statsTitle.className = 'stats-title';
statsTitle.textContent = LanguageManager.t('absences.title');
statsSection.appendChild(statsTitle);
const statsGrid = document.createElement('div');
statsGrid.className = 'stats-grid';
const totalAbsences = absences.filter(a => a.absenceType === 'absence').length;
const totalLates = absences.filter(a => a.absenceType === 'late').length;
const justified = absences.filter(a => a.justificationStatus === 'justified').length;
const unjustified = absences.filter(a => a.justificationStatus === 'unjustified').length;
const pending = absences.filter(a => a.justificationStatus === 'pending').length;
const stats = [
{ type: 'total', number: absences.length, label: LanguageManager.t('absences.total_absences') },
{ type: 'absence', number: totalAbsences, label: LanguageManager.t('absences.absence_type') },
{ type: 'late', number: totalLates, label: LanguageManager.t('absences.late_type') },
{ type: 'justified', number: justified, label: LanguageManager.t('absences.justified') },
{ type: 'unjustified', number: unjustified, label: LanguageManager.t('absences.unjustified') },
{ type: 'pending', number: pending, label: LanguageManager.t('absences.pending') },
];
stats.forEach(stat => {
const statCard = document.createElement('div');
statCard.className = `stat-card ${stat.type}`;
const statNumber = document.createElement('div');
statNumber.className = 'stat-number';
statNumber.textContent = stat.number;
const statLabel = document.createElement('div');
statLabel.className = 'stat-label';
statLabel.textContent = stat.label;
statCard.appendChild(statNumber);
statCard.appendChild(statLabel);
statsGrid.appendChild(statCard);
});
statsSection.appendChild(statsGrid);
return statsSection;
}
function getSchoolYearStart() {
const now = new Date();
const year = now.getMonth() >= 8 ? now.getFullYear() : now.getFullYear() - 1;
return new Date(year, 8, 1);
}
function createCalendarSection(groupedByDate, absences) {
const calendarSection = document.createElement('div');
calendarSection.className = 'calendar-section';
const schoolYearStart = getSchoolYearStart();
const now = new Date();
const months = [];
let currentDate = new Date(schoolYearStart);
while (currentDate <= now) {
months.push({
year: currentDate.getFullYear(),
month: currentDate.getMonth()
});
currentDate.setMonth(currentDate.getMonth() + 1);
}
months.forEach(({ year, month }) => {
const monthCalendar = createMonthCalendar(year, month, groupedByDate, absences);
calendarSection.appendChild(monthCalendar);
});
return calendarSection;
}
function createMonthCalendar(year, month, groupedByDate, absences) {
const monthContainer = document.createElement('div');
monthContainer.className = 'month-container';
const monthHeader = document.createElement('div');
monthHeader.className = 'month-header';
const monthNames = [
LanguageManager.t('common.january') || 'Január',
LanguageManager.t('common.february') || 'Február',
LanguageManager.t('common.march') || 'Március',
LanguageManager.t('common.april') || 'Április',
LanguageManager.t('common.may') || 'Május',
LanguageManager.t('common.june') || 'Június',
LanguageManager.t('common.july') || 'Július',
LanguageManager.t('common.august') || 'Augusztus',
LanguageManager.t('common.september') || 'Szeptember',
LanguageManager.t('common.october') || 'Október',
LanguageManager.t('common.november') || 'November',
LanguageManager.t('common.december') || 'December'
];
monthHeader.textContent = `${monthNames[month]} ${year}`;
monthContainer.appendChild(monthHeader);
const calendarGrid = document.createElement('div');
calendarGrid.className = 'calendar-grid';
const dayNames = [
LanguageManager.t('common.mon') || 'H',
LanguageManager.t('common.tue') || 'K',
LanguageManager.t('common.wed') || 'Sze',
LanguageManager.t('common.thu') || 'Cs',
LanguageManager.t('common.fri') || 'P',
LanguageManager.t('common.sat') || 'Szo',
LanguageManager.t('common.sun') || 'V'
];
dayNames.forEach(day => {
const dayHeader = document.createElement('div');
dayHeader.className = 'calendar-day-header';
dayHeader.textContent = day;
calendarGrid.appendChild(dayHeader);
});
const firstDay = new Date(year, month, 1);
const lastDay = new Date(year, month + 1, 0);
const daysInMonth = lastDay.getDate();
let startDay = firstDay.getDay() - 1;
if (startDay < 0) startDay = 6;
for (let i = 0; i < startDay; i++) {
const emptyCell = document.createElement('div');
emptyCell.className = 'calendar-day empty';
calendarGrid.appendChild(emptyCell);
}
const today = new Date();
today.setHours(0, 0, 0, 0);
for (let day = 1; day <= daysInMonth; day++) {
const dateKey = `${year}-${(month + 1).toString().padStart(2, "0")}-${day.toString().padStart(2, "0")}`;
const dayAbsences = groupedByDate[dateKey] || [];
const currentDayDate = new Date(year, month, day);
currentDayDate.setHours(0, 0, 0, 0);
const dayCell = document.createElement('div');
dayCell.className = 'calendar-day';
if (currentDayDate > today) {
dayCell.classList.add('future');
}
if (currentDayDate.getTime() === today.getTime()) {
dayCell.classList.add('today');
}
if (dayAbsences.length > 0) {
dayCell.classList.add('has-absence');
const hasUnjustified = dayAbsences.some(a => a.justificationStatus === 'unjustified');
const hasPending = dayAbsences.some(a => a.justificationStatus === 'pending');
const hasJustified = dayAbsences.some(a => a.justificationStatus === 'justified');
const hasAbsence = dayAbsences.some(a => a.absenceType === 'absence');
const hasLate = dayAbsences.some(a => a.absenceType === 'late');
if (hasUnjustified) {
dayCell.classList.add('status-unjustified');
} else if (hasPending) {
dayCell.classList.add('status-pending');
} else if (hasJustified) {
dayCell.classList.add('status-justified');
}
if (hasAbsence && hasLate) {
dayCell.classList.add('type-mixed');
} else if (hasLate) {
dayCell.classList.add('type-late');
}
dayCell.addEventListener('click', () => {
openAbsenceModal(dateKey, dayAbsences);
});
const countBadge = document.createElement('span');
countBadge.className = 'absence-count';
countBadge.textContent = dayAbsences.length;
dayCell.appendChild(countBadge);
}
const dayNumber = document.createElement('span');
dayNumber.className = 'day-number';
dayNumber.textContent = day;
dayCell.appendChild(dayNumber);
calendarGrid.appendChild(dayCell);
}
monthContainer.appendChild(calendarGrid);
return monthContainer;
}
function openAbsenceModal(dateKey, dayAbsences) {
const existingModal = document.querySelector('.absence-modal-overlay');
if (existingModal) {
existingModal.remove();
}
const overlay = document.createElement('div');
overlay.className = 'absence-modal-overlay';
const modal = document.createElement('div');
modal.className = 'absence-modal';
const modalHeader = document.createElement('div');
modalHeader.className = 'modal-header';
const dateParts = dateKey.split('-');
const displayDate = new Date(parseInt(dateParts[0]), parseInt(dateParts[1]) - 1, parseInt(dateParts[2]));
const dayNames = [
LanguageManager.t('common.sunday') || 'Vasárnap',
LanguageManager.t('common.monday') || 'Hétfő',
LanguageManager.t('common.tuesday') || 'Kedd',
LanguageManager.t('common.wednesday') || 'Szerda',
LanguageManager.t('common.thursday') || 'Csütörtök',
LanguageManager.t('common.friday') || 'Péntek',
LanguageManager.t('common.saturday') || 'Szombat'
];
const modalTitle = document.createElement('h2');
modalTitle.textContent = `${dateParts[0]}.${dateParts[1]}.${dateParts[2]}. - ${dayNames[displayDate.getDay()]}`;
modalHeader.appendChild(modalTitle);
const modalContent = document.createElement('div');
modalContent.className = 'modal-content';
dayAbsences.forEach(absence => {
const absenceCard = document.createElement('div');
absenceCard.className = `modal-absence-card ${absence.justificationStatus}`;
const headerRow = document.createElement('div');
headerRow.className = 'modal-card-header';
const lessonSpan = document.createElement('span');
lessonSpan.className = 'modal-lesson';
lessonSpan.textContent = `${absence.lesson}.`;
const typeIndicator = document.createElement('span');
typeIndicator.className = `type-indicator ${absence.absenceType}`;
typeIndicator.textContent = absence.absenceType === 'late'
? (LanguageManager.t('absences.late_type') || 'Késés')
: (LanguageManager.t('absences.absence_type') || 'Hiányzás');
const statusBadge = document.createElement('span');
statusBadge.className = `modal-status-badge ${absence.justificationStatus}`;
let statusText = '';
if (absence.justificationStatus === 'justified') {
statusText = LanguageManager.t('absences.justified') || 'Igazolt';
} else if (absence.justificationStatus === 'unjustified') {
statusText = LanguageManager.t('absences.unjustified') || 'Igazolatlan';
} else {
statusText = LanguageManager.t('absences.pending') || 'Igazolásra vár';
}
statusBadge.textContent = statusText;
headerRow.appendChild(lessonSpan);
headerRow.appendChild(typeIndicator);
headerRow.appendChild(statusBadge);
const subjectDiv = document.createElement('div');
subjectDiv.className = 'modal-subject';
subjectDiv.textContent = absence.subject;
const infoRow = document.createElement('div');
infoRow.className = 'modal-info-row';
if (absence.topic) {
const topicSpan = document.createElement('span');
topicSpan.className = 'modal-info-item';
topicSpan.textContent = absence.topic;
infoRow.appendChild(topicSpan);
}
if (absence.justificationType) {
const justificationSpan = document.createElement('span');
justificationSpan.className = 'modal-info-item';
justificationSpan.textContent = absence.justificationType;
infoRow.appendChild(justificationSpan);
}
if (absence.absenceType === 'late' && absence.minutes > 0) {
const minutesSpan = document.createElement('span');
minutesSpan.className = 'modal-info-item modal-minutes';
minutesSpan.textContent = `${absence.minutes} ${LanguageManager.t('absences.minutes') || 'perc'}`;
infoRow.appendChild(minutesSpan);
}
absenceCard.appendChild(headerRow);
absenceCard.appendChild(subjectDiv);
if (infoRow.children.length > 0) {
absenceCard.appendChild(infoRow);
}
modalContent.appendChild(absenceCard);
});
modal.appendChild(modalHeader);
modal.appendChild(modalContent);
overlay.appendChild(modal);
overlay.addEventListener('click', (e) => {
if (e.target === overlay) {
overlay.remove();
}
});
const handleEscape = (e) => {
if (e.key === 'Escape') {
overlay.remove();
document.removeEventListener('keydown', handleEscape);
}
};
document.addEventListener('keydown', handleEscape);
document.body.appendChild(overlay);
requestAnimationFrame(() => {
overlay.classList.add('visible');
});
}
function createLegend() {
const legend = document.createElement('div');
legend.className = 'calendar-legend';
const legendItems = [
{ class: 'status-justified', label: LanguageManager.t('absences.justified') || 'Igazolt' },
{ class: 'status-pending', label: LanguageManager.t('absences.pending') || 'Igazolásra vár' },
{ class: 'status-unjustified', label: LanguageManager.t('absences.unjustified') || 'Igazolatlan' },
{ class: 'type-late-legend', label: LanguageManager.t('absences.late_type') || 'Késés', isType: true },
];
legendItems.forEach(item => {
const legendItem = document.createElement('div');
legendItem.className = 'legend-item';
const legendColor = document.createElement('div');
legendColor.className = `legend-color ${item.class}`;
const legendLabel = document.createElement('span');
legendLabel.textContent = item.label;
legendItem.appendChild(legendColor);
legendItem.appendChild(legendLabel);
legend.appendChild(legendItem);
});
return legend;
}
async function transformAbsencesPage() {
const { basicData, absences, groupedByDate } = await collectAbsencesData();
document.body.textContent = '';
const container = document.createElement('div');
container.className = 'kreta-container';
const headerDiv = document.createElement('div');
const template = document.createElement('template');
template.innerHTML = await createTemplate.header();
const tempDiv = template.content;
while (tempDiv.firstChild) {
headerDiv.appendChild(tempDiv.firstChild);
}
container.appendChild(headerDiv);
const main = document.createElement('main');
main.className = 'kreta-main';
const pageContent = document.createElement('div');
pageContent.className = 'absences-page';
const statsSection = createStatsSection(absences);
pageContent.appendChild(statsSection);
const legend = createLegend();
pageContent.appendChild(legend);
const calendarSection = createCalendarSection(groupedByDate, absences);
pageContent.appendChild(calendarSection);
main.appendChild(pageContent);
container.appendChild(main);
document.body.appendChild(container);
setupUserDropdown();
loadingScreen.hide();
}
if (window.location.href.includes("/Hianyzas/Hianyzasok")) {
transformAbsencesPage().catch((error) => {
console.error(LanguageManager.t("absences.page_transform_error"), error);
});
}