12 Commits

Author SHA1 Message Date
Zan
b36adf8ebf gap fix 2025-12-02 17:03:25 +01:00
Zan
6d9a45941b remove dup teacher 2025-12-02 16:35:51 +01:00
Zan
581c8ba1d5 Day view refresh 2025-12-02 16:26:21 +01:00
Zan
56182279f3 chart recolor 2025-12-02 16:19:56 +01:00
Zan
33da0de509 import fix 2025-12-02 15:58:44 +01:00
Zan
486f33efcf uptd 2025-12-02 15:58:04 +01:00
Zan
b35154f906 Navbar 2025-12-02 15:38:23 +01:00
Zan
f984f9e7a2 fix 2025-12-02 14:22:40 +01:00
Zan
92e0397c61 Close msg button fix 2025-12-02 14:20:49 +01:00
Zan
a26163f41d Week select fix 2025-12-02 14:14:00 +01:00
Zan
bf1ad731fb Absences page desktop view 2025-12-01 22:50:50 +01:00
Zan
4ded1cce20 Fixes 2025-12-01 21:47:30 +01:00
21 changed files with 1693 additions and 1591 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -47,6 +47,7 @@ async function collectAbsencesData() {
absences.push({
date: formattedDate,
rawDate: date,
lesson: item.Oraszam?.toString() || "",
subject: item.Targy || "",
topic: item.Tema || "",
@@ -59,12 +60,19 @@ async function collectAbsencesData() {
});
}
const groupedAbsences = {};
absences.forEach((absence) => {
if (!groupedAbsences[absence.date]) {
groupedAbsences[absence.date] = [];
const groupedAbsences = absences.reduce((groups, absence) => {
const date = absence.date;
if (!groups[date]) {
groups[date] = [];
}
groupedAbsences[absence.date].push(absence);
groups[date].push(absence);
return groups;
}, {});
Object.keys(groupedAbsences).forEach(date => {
groupedAbsences[date].sort((a, b) => parseInt(a.lesson) - parseInt(b.lesson));
});
return { basicData, absences, groupedAbsences };
@@ -86,20 +94,87 @@ function createFilterCard(absences) {
const filterContent = document.createElement('div');
filterContent.className = 'filter-content';
const dateGroup = document.createElement('div');
dateGroup.className = 'filter-group';
const dateGroup = createFilterGroup(
'Calendar.svg',
'Dátum',
LanguageManager.t('absences.date'),
'input',
{ type: 'date', id: 'dateFilter', className: 'filter-input' }
);
const dateLabel = document.createElement('label');
const dateImg = document.createElement('img');
dateImg.src = chrome.runtime.getURL('icons/Calendar.svg');
dateImg.alt = 'Dátum';
dateLabel.appendChild(dateImg);
dateLabel.appendChild(document.createTextNode(' ' + LanguageManager.t('absences.date')));
const dateInput = document.createElement('input');
dateInput.type = 'date';
dateInput.id = 'dateFilter';
dateInput.className = 'filter-input';
dateGroup.appendChild(dateLabel);
dateGroup.appendChild(dateInput);
filterContent.appendChild(dateGroup);
const subjectGroup = createSubjectFilterGroup(absences);
const subjectGroup = document.createElement('div');
subjectGroup.className = 'filter-group';
const subjectLabel = document.createElement('label');
const subjectImg = document.createElement('img');
subjectImg.src = chrome.runtime.getURL('icons/Subject.svg');
subjectImg.alt = 'Tantárgy';
subjectLabel.appendChild(subjectImg);
subjectLabel.appendChild(document.createTextNode(' ' + LanguageManager.t('absences.subject')));
const subjectSelect = document.createElement('select');
subjectSelect.id = 'subjectFilter';
subjectSelect.className = 'filter-input';
const defaultSubjectOption = document.createElement('option');
defaultSubjectOption.value = '';
defaultSubjectOption.textContent = LanguageManager.t('absences.all_subjects');
subjectSelect.appendChild(defaultSubjectOption);
const subjects = [...new Set(absences.map(a => a.subject))].sort();
subjects.forEach(subject => {
const option = document.createElement('option');
option.value = subject;
option.textContent = subject;
subjectSelect.appendChild(option);
});
subjectGroup.appendChild(subjectLabel);
subjectGroup.appendChild(subjectSelect);
filterContent.appendChild(subjectGroup);
const justificationGroup = createJustificationFilterGroup();
const justificationGroup = document.createElement('div');
justificationGroup.className = 'filter-group';
const justificationLabel = document.createElement('label');
const justificationImg = document.createElement('img');
justificationImg.src = chrome.runtime.getURL('icons/BadgeCheck.svg');
justificationImg.alt = 'Igazolás';
justificationLabel.appendChild(justificationImg);
justificationLabel.appendChild(document.createTextNode(' ' + LanguageManager.t('absences.justification')));
const justificationSelect = document.createElement('select');
justificationSelect.id = 'justificationFilter';
justificationSelect.className = 'filter-input';
const justificationOptions = [
{ value: '', text: LanguageManager.t('absences.all_types') },
{ value: 'justified', text: LanguageManager.t('absences.justified') },
{ value: 'unjustified', text: LanguageManager.t('absences.unjustified') },
{ value: 'pending', text: LanguageManager.t('absences.pending') }
];
justificationOptions.forEach(optionData => {
const option = document.createElement('option');
option.value = optionData.value;
option.textContent = optionData.text;
justificationSelect.appendChild(option);
});
justificationGroup.appendChild(justificationLabel);
justificationGroup.appendChild(justificationSelect);
filterContent.appendChild(justificationGroup);
filterCard.appendChild(filterHeader);
@@ -108,118 +183,40 @@ function createFilterCard(absences) {
return filterCard;
}
function createFilterGroup(iconName, altText, labelText, elementType, attributes) {
const group = document.createElement('div');
group.className = 'filter-group';
function createStatsSection(absences) {
const statsSection = document.createElement('div');
statsSection.className = 'stats-section';
const label = document.createElement('label');
const img = document.createElement('img');
img.src = chrome.runtime.getURL(`icons/${iconName}`);
img.alt = altText;
img.style.width = '24px';
img.style.height = '24px';
label.appendChild(img);
label.appendChild(document.createTextNode(' ' + labelText));
const element = document.createElement(elementType);
Object.assign(element, attributes);
group.appendChild(label);
group.appendChild(element);
return group;
}
function createSubjectFilterGroup(absences) {
const group = document.createElement('div');
group.className = 'filter-group';
const label = document.createElement('label');
const img = document.createElement('img');
img.src = chrome.runtime.getURL('icons/Subject.svg');
img.alt = 'Tantárgy';
img.style.width = '24px';
img.style.height = '24px';
label.appendChild(img);
label.appendChild(document.createTextNode(' ' + LanguageManager.t('absences.subject')));
const select = document.createElement('select');
select.id = 'subjectFilter';
select.className = 'filter-input';
const defaultOption = document.createElement('option');
defaultOption.value = '';
defaultOption.textContent = LanguageManager.t('absences.all_subjects');
select.appendChild(defaultOption);
const subjects = [...new Set(absences.map(a => a.subject))].sort();
subjects.forEach(subject => {
const option = document.createElement('option');
option.value = subject;
option.textContent = subject;
select.appendChild(option);
});
group.appendChild(label);
group.appendChild(select);
return group;
}
function createJustificationFilterGroup() {
const group = document.createElement('div');
group.className = 'filter-group';
const label = document.createElement('label');
const img = document.createElement('img');
img.src = chrome.runtime.getURL('icons/BadgeCheck.svg');
img.alt = 'Igazolás';
img.style.width = '24px';
img.style.height = '24px';
label.appendChild(img);
label.appendChild(document.createTextNode(' ' + LanguageManager.t('absences.justification')));
const select = document.createElement('select');
select.id = 'justificationFilter';
select.className = 'filter-input';
const options = [
{ value: '', text: LanguageManager.t('absences.all_types') },
{ value: 'justified', text: LanguageManager.t('absences.justified') },
{ value: 'unjustified', text: LanguageManager.t('absences.unjustified') },
{ value: 'pending', text: LanguageManager.t('absences.pending') }
];
options.forEach(optionData => {
const option = document.createElement('option');
option.value = optionData.value;
option.textContent = optionData.text;
select.appendChild(option);
});
group.appendChild(label);
group.appendChild(select);
return group;
}
function createStatsOverview(absences) {
const statsOverview = document.createElement('div');
statsOverview.className = 'stats-overview';
const statsGrid = document.createElement('div');
statsGrid.className = 'stats-grid';
const stats = [
{ number: absences.length, label: LanguageManager.t('absences.total_absences') },
{ number: absences.filter(a => a.justificationStatus === 'justified').length, label: LanguageManager.t('absences.justified') },
{ number: absences.filter(a => a.justificationStatus === 'unjustified').length, label: LanguageManager.t('absences.unjustified') },
{ number: absences.filter(a => a.justificationStatus === 'pending').length, label: LanguageManager.t('absences.pending') }
{
type: 'total',
number: absences.length,
label: LanguageManager.t('absences.total_absences')
},
{
type: 'justified',
number: absences.filter(a => a.justificationStatus === 'justified').length,
label: LanguageManager.t('absences.justified')
},
{
type: 'unjustified',
number: absences.filter(a => a.justificationStatus === 'unjustified').length,
label: LanguageManager.t('absences.unjustified')
},
{
type: 'pending',
number: absences.filter(a => a.justificationStatus === 'pending').length,
label: LanguageManager.t('absences.pending')
}
];
stats.forEach(stat => {
const statCard = document.createElement('div');
statCard.className = 'stat-card';
statCard.className = `stat-card ${stat.type}`;
statCard.dataset.type = stat.type;
const statNumber = document.createElement('div');
statNumber.className = 'stat-number';
@@ -231,54 +228,179 @@ function createStatsOverview(absences) {
statCard.appendChild(statNumber);
statCard.appendChild(statLabel);
statsOverview.appendChild(statCard);
statsGrid.appendChild(statCard);
});
return statsOverview;
statsSection.appendChild(statsGrid);
return statsSection;
}
function createAbsencesContainer(absences) {
const container = document.createElement('div');
container.className = 'absences-container';
function createDayGroup(date, dayAbsences) {
const dayGroup = document.createElement('div');
dayGroup.className = 'day-group';
dayGroup.dataset.date = date;
const table = document.createElement('table');
table.className = 'absences-table';
const thead = document.createElement('thead');
thead.className = 'table-header';
const dayHeader = document.createElement('div');
dayHeader.className = 'day-header';
const headerRow = document.createElement('tr');
const headers = [
LanguageManager.t('absences.date'),
LanguageManager.t('absences.lesson'),
LanguageManager.t('absences.subject'),
LanguageManager.t('absences.topic'),
LanguageManager.t('absences.status')
];
const dayDate = document.createElement('div');
dayDate.className = 'day-date';
headers.forEach(headerText => {
const th = document.createElement('th');
th.textContent = headerText;
headerRow.appendChild(th);
const calendarIcon = document.createElement('img');
calendarIcon.src = chrome.runtime.getURL('icons/Calendar.svg');
calendarIcon.alt = 'Dátum';
const dateText = document.createElement('span');
dateText.textContent = formatDateWithDay(date);
dayDate.appendChild(calendarIcon);
dayDate.appendChild(dateText);
const dayCount = document.createElement('div');
dayCount.className = 'day-count';
dayCount.textContent = `${dayAbsences.length} ${LanguageManager.t('absences.hours')}`;
dayHeader.appendChild(dayDate);
dayHeader.appendChild(dayCount);
const dayAbsencesContainer = document.createElement('div');
dayAbsencesContainer.className = 'day-absences';
dayAbsences.forEach(absence => {
const absenceCard = createAbsenceCard(absence);
dayAbsencesContainer.appendChild(absenceCard);
});
thead.appendChild(headerRow);
dayGroup.appendChild(dayHeader);
dayGroup.appendChild(dayAbsencesContainer);
const tbody = document.createElement('tbody');
generateAbsencesRows(absences, tbody);
return dayGroup;
}
function formatDateWithDay(dateStr) {
const parts = dateStr.split('.');
const year = parseInt(parts[0]);
const month = parseInt(parts[1]) - 1;
const day = parseInt(parts[2]);
table.appendChild(thead);
table.appendChild(tbody);
container.appendChild(table);
const date = new Date(year, month, day);
const days = [
LanguageManager.t('common.sunday'),
LanguageManager.t('common.monday'),
LanguageManager.t('common.tuesday'),
LanguageManager.t('common.wednesday'),
LanguageManager.t('common.thursday'),
LanguageManager.t('common.friday'),
LanguageManager.t('common.saturday')
];
return container;
const dayName = days[date.getDay()];
return `${dateStr} - ${dayName.charAt(0).toUpperCase() + dayName.slice(1)}`;
}
function createAbsenceCard(absence) {
const card = document.createElement('div');
card.className = 'absence-card';
card.dataset.subject = absence.subject;
card.dataset.status = absence.justificationStatus;
card.dataset.date = absence.date;
const lessonDiv = document.createElement('div');
lessonDiv.className = 'absence-lesson';
lessonDiv.textContent = absence.lesson + '.';
const subjectDiv = document.createElement('div');
subjectDiv.className = 'absence-subject';
subjectDiv.textContent = absence.subject;
const topicDiv = document.createElement('div');
topicDiv.className = 'absence-topic';
topicDiv.textContent = absence.topic || '-';
topicDiv.title = absence.topic;
const statusDiv = document.createElement('div');
statusDiv.className = 'absence-status';
const statusBadge = document.createElement('span');
statusBadge.className = `status-badge ${absence.justificationStatus}`;
if (absence.justificationStatus === 'justified') {
const img = document.createElement('img');
img.src = chrome.runtime.getURL('icons/BadgeCheck.svg');
img.alt = 'Igazolt';
statusBadge.appendChild(img);
statusBadge.appendChild(document.createTextNode(' ' + LanguageManager.t('absences.justified')));
} else if (absence.justificationStatus === 'unjustified') {
const span = document.createElement('span');
span.className = 'material-icons-round';
span.textContent = 'cancel';
statusBadge.appendChild(span);
statusBadge.appendChild(document.createTextNode(' ' + LanguageManager.t('absences.unjustified')));
} else {
const img = document.createElement('img');
img.src = chrome.runtime.getURL('icons/pending.svg');
img.alt = 'Függőben';
statusBadge.appendChild(img);
statusBadge.appendChild(document.createTextNode(' ' + LanguageManager.t('absences.pending')));
}
statusDiv.appendChild(statusBadge);
card.appendChild(lessonDiv);
card.appendChild(subjectDiv);
card.appendChild(topicDiv);
card.appendChild(statusDiv);
return card;
}
function createAbsencesContent(groupedAbsences) {
const content = document.createElement('div');
content.className = 'absences-content';
const sortedDates = Object.keys(groupedAbsences).sort((a, b) => {
const dateA = new Date(a.replace(/\./g, '-').slice(0, -1));
const dateB = new Date(b.replace(/\./g, '-').slice(0, -1));
return dateB - dateA;
});
if (sortedDates.length === 0) {
const emptyState = document.createElement('div');
emptyState.className = 'empty-state';
const emptyIcon = document.createElement('img');
emptyIcon.src = chrome.runtime.getURL('icons/BadgeCheck.svg');
emptyIcon.alt = 'Nincs hiányzás';
const emptyTitle = document.createElement('h3');
emptyTitle.textContent = LanguageManager.t('absences.title');
const emptyText = document.createElement('p');
emptyText.textContent = LanguageManager.t('dashboard.not_supported');
emptyState.appendChild(emptyIcon);
emptyState.appendChild(emptyTitle);
emptyState.appendChild(emptyText);
content.appendChild(emptyState);
} else {
sortedDates.forEach(date => {
const dayGroup = createDayGroup(date, groupedAbsences[date]);
content.appendChild(dayGroup);
});
}
return content;
}
async function transformAbsencesPage() {
const { basicData, absences, groupedAbsences } = await collectAbsencesData();
document.body.textContent = '';
const container = document.createElement('div');
container.className = 'kreta-container';
const headerDiv = document.createElement('div');
const parser = new DOMParser();
const doc = parser.parseFromString(await createTemplate.header(), 'text/html');
@@ -287,244 +409,59 @@ async function transformAbsencesPage() {
headerDiv.appendChild(tempDiv.firstChild);
}
container.appendChild(headerDiv);
const main = document.createElement('main');
main.className = 'kreta-main';
const filterCard = createFilterCard(absences);
main.appendChild(filterCard);
const statsOverview = createStatsOverview(absences);
main.appendChild(statsOverview);
const absencesContainer = createAbsencesContainer(absences);
main.appendChild(absencesContainer);
const pageGrid = document.createElement('div');
pageGrid.className = 'absences-page';
const sidebar = document.createElement('div');
sidebar.className = 'absences-sidebar';
const filterCard = createFilterCard(absences);
sidebar.appendChild(filterCard);
const statsSection = createStatsSection(absences);
sidebar.appendChild(statsSection);
const absencesContent = createAbsencesContent(groupedAbsences);
pageGrid.appendChild(sidebar);
pageGrid.appendChild(absencesContent);
main.appendChild(pageGrid);
container.appendChild(main);
document.body.appendChild(container);
setupUserDropdown();
setupMobileNavigation();
setupEventListeners();
setupFilters();
setupFilters(groupedAbsences);
loadingScreen.hide();
}
function generateAbsencesRows(absences, tbody) {
const groupedByDate = absences.reduce((groups, absence) => {
const date = absence.date;
if (!groups[date]) {
groups[date] = [];
}
groups[date].push(absence);
return groups;
}, {});
const sortedDates = Object.keys(groupedByDate).sort(
(a, b) => new Date(b) - new Date(a),
);
sortedDates.forEach((date) => {
const dateAbsences = groupedByDate[date];
const divider = document.createElement('tr');
divider.className = 'date-group-divider';
divider.style.display = 'none';
tbody.appendChild(divider);
dateAbsences.forEach((absence) => {
const row = document.createElement('tr');
row.className = 'table-row';
row.dataset.subject = absence.subject;
row.dataset.justified = absence.justified;
row.dataset.date = absence.date;
row.dataset.dateGroup = date;
const dateCell = document.createElement('td');
dateCell.className = 'table-cell date-cell';
dateCell.dataset.label = LanguageManager.t('absences.date');
dateCell.textContent = absence.date;
row.appendChild(dateCell);
const lessonCell = document.createElement('td');
lessonCell.className = 'table-cell lesson-cell';
lessonCell.dataset.label = LanguageManager.t('absences.lesson');
lessonCell.textContent = absence.lesson + '.';
row.appendChild(lessonCell);
const subjectCell = document.createElement('td');
subjectCell.className = 'table-cell subject-cell';
subjectCell.dataset.label = LanguageManager.t('absences.subject');
subjectCell.textContent = absence.subject;
row.appendChild(subjectCell);
const topicCell = document.createElement('td');
topicCell.className = 'table-cell topic-cell';
topicCell.dataset.label = LanguageManager.t('absences.topic');
topicCell.title = absence.topic;
topicCell.textContent = absence.topic;
row.appendChild(topicCell);
const statusCell = document.createElement('td');
statusCell.className = 'table-cell status-cell';
statusCell.dataset.label = LanguageManager.t('absences.status');
const statusBadge = document.createElement('span');
statusBadge.className = `status-badge ${absence.justificationStatus}`;
if (absence.justificationStatus === 'justified') {
const img = document.createElement('img');
img.src = chrome.runtime.getURL('icons/BadgeCheck.svg');
img.alt = 'Igazolt';
img.style.width = '16px';
img.style.height = '16px';
statusBadge.appendChild(img);
statusBadge.appendChild(document.createTextNode(' ' + LanguageManager.t('absences.justified')));
} else if (absence.justificationStatus === 'unjustified') {
const span = document.createElement('span');
span.className = 'material-icons-round';
span.textContent = 'cancel';
statusBadge.appendChild(span);
statusBadge.appendChild(document.createTextNode(' ' + LanguageManager.t('absences.unjustified')));
} else {
const span = document.createElement('span');
span.className = 'material-icons-round';
span.textContent = 'pending';
statusBadge.appendChild(span);
statusBadge.appendChild(document.createTextNode(' ' + LanguageManager.t('absences.pending')));
}
statusCell.appendChild(statusBadge);
row.appendChild(statusCell);
tbody.appendChild(row);
});
});
}
function setupEventListeners() {
function setupMobileGrouping() {
if (window.innerWidth <= 480) {
createMobileGroups();
} else {
removeMobileGroups();
}
}
window.addEventListener("resize", setupMobileGrouping);
setupMobileGrouping();
}
function createMobileGroups() {
const tbody = document.querySelector(".absences-table tbody");
if (!tbody) return;
removeMobileGroups();
const rows = Array.from(tbody.querySelectorAll(".table-row"));
const groupedRows = {};
rows.forEach((row) => {
const date = row.dataset.date;
if (!groupedRows[date]) {
groupedRows[date] = [];
}
groupedRows[date].push(row);
});
const sortedDates = Object.keys(groupedRows).sort(
(a, b) => new Date(b) - new Date(a),
);
while (tbody.firstChild) {
tbody.removeChild(tbody.firstChild);
}
sortedDates.forEach((date) => {
const dateRows = groupedRows[date];
const dateGroup = document.createElement("div");
dateGroup.className = "date-group";
const dateHeader = document.createElement("div");
dateHeader.className = "date-group-header";
dateHeader.textContent = date;
const dateContent = document.createElement("div");
dateContent.className = "date-group-content";
dateRows.forEach((row) => {
dateContent.appendChild(row);
});
dateGroup.appendChild(dateHeader);
dateGroup.appendChild(dateContent);
tbody.appendChild(dateGroup);
});
}
function removeMobileGroups() {
const tbody = document.querySelector(".absences-table tbody");
if (!tbody) return;
const dateGroups = tbody.querySelectorAll(".date-group");
if (dateGroups.length === 0) return;
const allRows = [];
dateGroups.forEach((group) => {
const rows = group.querySelectorAll(".table-row");
rows.forEach((row) => allRows.push(row));
});
while (tbody.firstChild) {
tbody.removeChild(tbody.firstChild);
}
allRows.forEach((row) => tbody.appendChild(row));
}
function updateDateGroupsVisibility() {
if (window.innerWidth > 480) return;
const dateGroups = document.querySelectorAll(".date-group");
dateGroups.forEach((group) => {
const visibleRows = group.querySelectorAll(
'.table-row[style=""], .table-row:not([style])',
);
if (visibleRows.length > 0) {
group.style.display = "";
} else {
group.style.display = "none";
}
});
}
function setupFilters() {
function setupFilters(originalGroupedAbsences) {
try {
const filters = {
dateFilter: document.getElementById("dateFilter"),
subject: document.getElementById("subjectFilter"),
justified: document.getElementById("justificationFilter"),
};
const dateFilter = document.getElementById("dateFilter");
const subjectFilter = document.getElementById("subjectFilter");
const justificationFilter = document.getElementById("justificationFilter");
if (!filters.dateFilter || !filters.subject || !filters.justified) {
if (!dateFilter || !subjectFilter || !justificationFilter) {
console.warn("Some filter elements were not found in the DOM");
return;
}
const filterAbsences = () => {
try {
const dateFilterValue = filters.dateFilter.value;
const subject = filters.subject.value;
const justified = filters.justified.value;
const dateFilterValue = dateFilter.value;
const subject = subjectFilter.value;
const justified = justificationFilter.value;
const selectedDate = dateFilterValue ? new Date(dateFilterValue) : null;
document.querySelectorAll(".table-row").forEach((row) => {
const dateStr = row.dataset.date;
document.querySelectorAll(".absence-card").forEach((card) => {
const dateStr = card.dataset.date;
const dateParts = dateStr.split(".");
if (dateParts.length < 3) {
console.error(`Invalid date format: ${dateStr}`);
return;
}
@@ -532,100 +469,101 @@ function setupFilters() {
const parsedMonth = parseInt(dateParts[1].trim(), 10) - 1;
const parsedDay = parseInt(dateParts[2].trim(), 10);
if (isNaN(parsedDay) || isNaN(parsedMonth) || isNaN(parsedYear)) {
console.error(`Invalid date components: ${dateStr}`);
return;
}
const cardDate = new Date(parsedYear, parsedMonth, parsedDay);
const rowDate = new Date(parsedYear, parsedMonth, parsedDay);
let showRow = true;
let showCard = true;
if (selectedDate) {
if (
rowDate.getFullYear() !== selectedDate.getFullYear() ||
rowDate.getMonth() !== selectedDate.getMonth() ||
rowDate.getDate() !== selectedDate.getDate()
cardDate.getFullYear() !== selectedDate.getFullYear() ||
cardDate.getMonth() !== selectedDate.getMonth() ||
cardDate.getDate() !== selectedDate.getDate()
) {
showRow = false;
showCard = false;
}
}
if (subject && row.dataset.subject !== subject) {
showRow = false;
if (subject && card.dataset.subject !== subject) {
showCard = false;
}
if (justified) {
const statusElement = row.querySelector(".status-badge");
const hasStatus = statusElement.classList.contains(justified);
if (!hasStatus) showRow = false;
if (justified && card.dataset.status !== justified) {
showCard = false;
}
row.style.display = showRow ? "" : "none";
card.style.display = showCard ? "" : "none";
});
updateDateGroupsVisibility();
updateDayGroupsVisibility();
updateStatistics();
} catch (err) {
console.error("Error during filtering absences:", err);
}
};
Object.values(filters).forEach((filter) => {
try {
if (filter) {
filter.addEventListener("change", filterAbsences);
}
} catch (err) {
if (
err.message &&
err.message.includes("Extension context invalidated")
) {
console.warn(
"Extension context invalidated during event listener setup",
);
} else {
console.error("Error setting up filter event listener:", err);
}
[dateFilter, subjectFilter, justificationFilter].forEach((filter) => {
if (filter) {
filter.addEventListener("change", filterAbsences);
}
});
filterAbsences();
} catch (err) {
if (err.message && err.message.includes("Extension context invalidated")) {
console.warn("Extension context invalidated during filter setup");
} else {
console.error("Error setting up filters:", err);
}
console.error("Error setting up filters:", err);
}
}
function updateDayGroupsVisibility() {
document.querySelectorAll(".day-group").forEach((group) => {
const visibleCards = group.querySelectorAll('.absence-card:not([style*="display: none"])');
const dayCount = group.querySelector('.day-count');
if (visibleCards.length > 0) {
group.style.display = "";
if (dayCount) {
dayCount.textContent = `${visibleCards.length} ${LanguageManager.t('absences.hours')}`;
}
} else {
group.style.display = "none";
}
});
}
function updateStatistics() {
try {
const visibleRows = document.querySelectorAll(
'.table-row:not([style*="display: none"])',
);
const totalVisible = visibleRows.length;
const justifiedVisible = Array.from(visibleRows).filter((row) =>
row.querySelector(".status-badge.justified"),
const visibleCards = document.querySelectorAll('.absence-card:not([style*="display: none"])');
const totalVisible = visibleCards.length;
const justifiedVisible = Array.from(visibleCards).filter(
card => card.dataset.status === 'justified'
).length;
const unjustifiedVisible = Array.from(visibleRows).filter((row) =>
row.querySelector(".status-badge.unjustified"),
const unjustifiedVisible = Array.from(visibleCards).filter(
card => card.dataset.status === 'unjustified'
).length;
const pendingVisible = Array.from(visibleRows).filter((row) =>
row.querySelector(".status-badge.pending"),
const pendingVisible = Array.from(visibleCards).filter(
card => card.dataset.status === 'pending'
).length;
const statCards = document.querySelectorAll(".stat-card");
if (statCards[0])
statCards[0].querySelector(".stat-number").textContent = totalVisible;
if (statCards[1])
statCards[1].querySelector(".stat-number").textContent = justifiedVisible;
if (statCards[2])
statCards[2].querySelector(".stat-number").textContent =
unjustifiedVisible;
if (statCards[3])
statCards[3].querySelector(".stat-number").textContent = pendingVisible;
statCards.forEach(card => {
const type = card.dataset.type;
const numberEl = card.querySelector('.stat-number');
if (numberEl) {
switch(type) {
case 'total':
numberEl.textContent = totalVisible;
break;
case 'justified':
numberEl.textContent = justifiedVisible;
break;
case 'unjustified':
numberEl.textContent = unjustifiedVisible;
break;
case 'pending':
numberEl.textContent = pendingVisible;
break;
}
}
});
} catch (err) {
console.error("Error updating statistics:", err);
}

View File

@@ -354,13 +354,15 @@ h2 {
}
.user-dropdown-btn {
background: none;
background: var(--button-secondaryFill);
border: none;
cursor: pointer;
padding: 8px;
padding: 8px 12px;
display: flex;
align-items: center;
color: var(--text-primary);
border-radius: 12px;
box-shadow: 0px 1px var(--shadow-blur, 2px) 0px var(--accent-shadow);
}
.user-dropdown {

View File

@@ -505,7 +505,6 @@ class DashboardRenderer {
document.body.appendChild(kretaContainer);
setupUserDropdown();
setupMobileNavigation();
}
}

View File

@@ -1,335 +1,491 @@
.kreta-header {
padding:clamp(1rem,3vw,2rem);
display:grid;
grid-template-columns:minmax(300px,400px) 1fr minmax(200px,300px);
align-items:center;
gap:1rem;
background-color:var(--background);
padding: 1rem 2rem;
display: flex;
align-items: center;
justify-content: space-between;
background-color: var(--background);
gap: 1.5rem;
}
.school-info {
margin:0;
display:flex;
flex-direction:column;
display: flex;
flex-direction: column;
min-width: 200px;
flex-shrink: 0;
}
.logo-text {
color:var(--text-primary);
font-size:24px;
font-weight:600;
margin:0 0 0.5rem;
display:flex;
align-items:center;
color: var(--text-primary);
font-size: 22px;
font-weight: 600;
margin: 0;
display: flex;
align-items: center;
gap: 0.5rem;
}
.logo {
width:24px;
height:24px;
border-radius:8px;
margin-right:0.5rem;
width: 24px;
height: 24px;
border-radius: 8px;
}
.school-details {
color:var(--text-secondary);
font-size:14px;
white-space:nowrap;
overflow:hidden;
text-overflow:ellipsis;
color: var(--text-secondary);
font-size: 13px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 300px;
margin-top: 0.25rem;
}
.kreta-nav {
padding:0 clamp(0.5rem,3vw,1.5rem);
position:sticky;
top:0;
z-index:100;
display:flex;
justify-content:center;
display: flex;
justify-content: center;
flex: 1;
}
.nav-links {
display:flex;
gap:clamp(0.5rem,2vw,1rem);
padding:0.25rem;
align-items:center;
display: flex;
gap: 0.5rem;
align-items: center;
}
.nav-item {
display:flex;
align-items:center;
padding:8px 14px 8px 12px;
color:var(--text-secondary);
text-decoration:none;
font-weight:500;
white-space:nowrap;
border-radius:20px;
transition:all 0.2s ease;
gap:0.5rem;
text-decoration:none;
background:var(--button-secondaryFill);
box-shadow:0px 1px var(--shadow-blur,2px) 0px var(--accent-shadow);
}
.nav-item.active {
display:flex;
padding:8px 14px 8px 12px;
align-items:center;
gap:8px;
border-radius:20px;
background:var(--button-secondaryFill);
box-shadow:0px 1px var(--shadow-blur,2px) 0px var(--accent-shadow);
display: flex;
align-items: center;
padding: 10px 16px;
color: var(--text-secondary);
text-decoration: none;
font-weight: 500;
font-size: 14px;
white-space: nowrap;
border-radius: 12px;
transition: all 0.2s ease;
gap: 0.5rem;
background: var(--button-secondaryFill);
box-shadow: 0px 1px var(--shadow-blur, 2px) 0px var(--accent-shadow);
}
.nav-item:hover {
color:var(--text-primary);
background-color:var(--accent-15);
border-radius:8px;
text-decoration:none;
color: var(--text-primary);
background-color: var(--accent-15);
text-decoration: none;
}
.nav-item.active:hover {
color:var(--accent-accent);
background-color:var(--accent-15);
text-decoration:none;
.nav-item.active {
color: var(--accent-accent);
background: var(--accent-15);
}
.nav-item img,.nav-item svg {
width:24px;
height:24px;
.nav-item img,
.nav-item svg {
width: 20px;
height: 20px;
}
.nav-item.active img {
filter: var(--icon-filter) !important;
}
.user-profile {
position: relative;
flex-shrink: 0;
}
.user-dropdown-btn {
display: flex;
align-items: center;
gap: 0.75rem;
background: var(--button-secondaryFill);
border: none;
cursor: pointer;
padding: 8px 12px;
border-radius: 12px;
transition: all 0.2s ease;
box-shadow: 0px 1px var(--shadow-blur, 2px) 0px var(--accent-shadow);
}
.user-dropdown-btn:hover {
background: var(--accent-15);
}
.user-info {
text-align: right;
}
.user-name {
display: block;
color: var(--text-primary);
font-size: 14px;
font-weight: 500;
}
.nav-logout-timer {
display: block;
color: var(--text-secondary);
font-size: 12px;
}
.user-avatar-icon {
width: 32px;
height: 32px;
border-radius: 50%;
background: var(--accent-15);
display: flex;
align-items: center;
justify-content: center;
}
.user-avatar-icon svg {
width: 18px;
height: 18px;
fill: var(--accent-accent);
}
.dropdown-arrow {
width: 16px;
height: 16px;
fill: var(--text-secondary);
transition: transform 0.2s ease;
}
.user-dropdown-btn.open .dropdown-arrow {
transform: rotate(180deg);
}
.user-dropdown {
position: absolute;
top: calc(100% + 8px);
right: 0;
background: var(--card-card);
border-radius: 12px;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
min-width: 180px;
display: none;
z-index: 1000;
overflow: hidden;
}
.user-dropdown.show {
display: block;
animation: dropdownShow 0.2s ease;
}
.dropdown-item {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 12px 16px;
color: var(--text-primary);
text-decoration: none;
transition: background-color 0.2s;
font-size: 14px;
}
.dropdown-item:hover {
background: var(--hover);
color: var(--accent-accent);
text-decoration: none;
}
.dropdown-item img {
width: 18px;
height: 18px;
filter: var(--icon-filter);
}
.nav-item.active svg path {
fill:var(--accent-accent);
}
.user-profile {
position:relative;
justify-self:flex-end;
}
.user-dropdown-btn {
display:flex;
align-items:center;
gap:1rem;
background:none;
border:none;
cursor:pointer;
padding:0.5rem;
border-radius:8px;
transition:background-color 0.2s;
}
.user-dropdown-btn:hover {
background:var(--hover);
}
.user-info {
text-align:right;
}
.user-name {
display:block;
color:var(--text-primary);
font-size:16px;
}
.nav-logout-timer {
display:block;
color:var(--text-secondary);
font-size:14px;
}
.user-dropdown {
position:absolute;
top:100%;
right:0;
margin-top:0.5rem;
background:var(--card-card);
border-radius:12px;
box-shadow:0 4px 6px -1px rgba(0,0,0,0.1);
width:200px;
display:none;
z-index:1000;
}
.user-dropdown.show {
display:block;
animation:dropdownShow 0.2s ease;
}
.dropdown-item {
display:flex;
align-items:center;
gap:0.75rem;
padding:0.75rem 1rem;
color:var(--text-primary);
text-decoration:none;
transition:background-color 0.2s;
}
.dropdown-item:hover {
background:var(--hover);
color:var(--accent-accent);
border-radius:8px;
text-decoration:none;
.dropdown-item:hover img {
filter: none;
}
@keyframes dropdownShow {
from {
opacity:0;
transform:translateY(-10px);
}
to {
opacity:1;
transform:translateY(0);
}
}@media (max-width:1200px) {
.kreta-header {
grid-template-columns:minmax(250px,350px) 1fr minmax(180px,250px);
}
}/* Hamburger menu styles */
.nav-toggle {
display:none;
background:none;
border:none;
cursor:pointer;
padding:0.5rem;
border-radius:8px;
transition:background-color 0.2s;
}
.nav-toggle:hover {
background:var(--hover);
}
.nav-toggle svg {
width:24px;
height:24px;
fill:var(--text-primary);
}
@media (max-width:768px) {
.kreta-header {
grid-template-columns:1fr auto auto;
grid-template-areas:"school toggle user"
"nav nav nav";
padding:1rem;
gap:0.5rem;
}
.nav-toggle {
display:block;
grid-area:toggle;
}
.school-info {
grid-area:school;
max-width:none;
display:flex;
align-items:center;
gap:0.5rem;
}
.logo-text {
margin:0;
font-size:18px;
}
.school-details {
font-size:11px;
max-width:200px;
}
.kreta-nav {
grid-area:nav;
padding:0;
margin-top:0.5rem;
display:none;
}
.kreta-nav.show {
display:flex;
animation:slideDown 0.3s ease;
}
.kreta-nav::-webkit-scrollbar {
display:none;
}
.nav-links {
flex-direction:column;
width:100%;
gap:0.5rem;
background:var(--card-card);
border-radius:12px;
padding:1rem;
box-shadow:0px 1px var(--shadow-blur) 0px var(--accent-shadow);
}
.nav-item {
width:100%;
justify-content:flex-start;
padding:0.75rem;
font-size:14px;
}
.user-profile {
grid-area:user;
}
.user-info {
text-align:right;
max-width:120px;
}
.user-name {
font-size:13px;
white-space:nowrap;
overflow:hidden;
text-overflow:ellipsis;
}
.nav-logout-timer {
font-size:11px;
}
}@media (max-width:480px) {
.kreta-header {
grid-template-columns:1fr auto auto;
grid-template-areas:"school toggle user"
"nav nav nav";
padding:0.75rem;
gap:0.25rem;
}
.school-info {
min-width:0;
flex:1;
}
.logo-text {
font-size:16px;
}
.school-details {
font-size:10px;
max-width:150px;
}
.kreta-nav {
display:none;
}
.kreta-nav.show {
display:flex;
animation:slideDown 0.3s ease;
}
.nav-links {
flex-direction:column;
width:100%;
gap:0.5rem;
background:var(--card-card);
border-radius:12px;
padding:1rem;
box-shadow:0px 1px var(--shadow-blur) 0px var(--accent-shadow);
}
.nav-item {
width:100%;
justify-content:flex-start;
padding:0.75rem;
font-size:14px;
}
.user-info {
max-width:100px;
}
.user-name {
font-size:12px;
}
.nav-logout-timer {
font-size:10px;
}
}@media (max-width:360px) {
.kreta-header {
padding:0.5rem;
opacity: 0;
transform: translateY(-8px);
}
.logo-text {
font-size:14px;
to {
opacity: 1;
transform: translateY(0);
}
.school-details {
font-size:9px;
max-width:120px;
}
.user-info {
max-width:80px;
}
.user-name {
font-size:11px;
}
.nav-logout-timer {
font-size:9px;
}
}@keyframes slideDown {
@keyframes dropdownShowUp {
from {
opacity:0;
transform:translateY(-10px);
opacity: 0;
transform: translateY(8px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
to {
opacity:1;
transform:translateY(0);
.mobile-header,
.mobile-bottom-nav,
.mobile-user-dropdown {
display: none;
}
@media (max-width: 900px) {
.kreta-header {
display: none;
}
.mobile-header {
display: flex;
align-items: center;
justify-content: center;
padding: 1rem;
background-color: var(--background);
gap: 1rem;
}
.mobile-header .school-info {
align-items: center;
text-align: center;
min-width: auto;
}
.mobile-header .logo-text {
font-size: 20px;
justify-content: center;
}
.mobile-header .school-details {
max-width: 280px;
text-align: center;
}
.mobile-bottom-nav {
display: flex;
position: fixed;
bottom: 0;
left: 0;
right: 0;
background: var(--background);
padding: 8px 16px;
padding-bottom: calc(8px + env(safe-area-inset-bottom, 0px));
z-index: 1000;
box-shadow: 0 -2px 16px rgba(0, 0, 0, 0.1);
justify-content: space-between;
align-items: center;
gap: 8px;
border-radius: 20px 20px 0 0;
}
.mobile-nav-item {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
flex: 1;
padding: 8px 4px;
color: var(--text-secondary);
text-decoration: none;
font-size: 10px;
font-weight: 500;
border-radius: 12px;
transition: all 0.2s ease;
gap: 4px;
background: var(--card-card);
box-shadow: 0px 1px var(--shadow-blur, 2px) 0px var(--accent-shadow);
}
.mobile-nav-item:hover {
color: var(--text-primary);
background: var(--accent-15);
text-decoration: none;
}
.mobile-nav-item.active {
color: var(--accent-accent);
background: var(--accent-15);
}
.mobile-nav-item img {
width: 22px;
height: 22px;
transition: filter 0.2s ease;
}
.mobile-nav-item.active img {
filter: var(--icon-filter) !important;
}
.mobile-nav-item.active {
color: var(--accent-accent) !important;
background: var(--accent-15);
}
.mobile-user-btn {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
flex: 1;
padding: 8px 4px;
color: var(--text-secondary);
background: var(--card-card);
box-shadow: 0px 1px var(--shadow-blur, 2px) 0px var(--accent-shadow);
border: none;
font-size: 10px;
font-weight: 500;
border-radius: 12px;
transition: all 0.2s ease;
gap: 4px;
cursor: pointer;
}
.mobile-user-btn:hover {
color: var(--text-primary);
background: var(--accent-15);
}
.mobile-user-btn.active {
color: var(--accent-accent);
background: var(--accent-15);
}
.mobile-user-btn .user-avatar-icon {
width: 22px;
height: 22px;
}
.mobile-user-btn .user-avatar-icon svg {
width: 14px;
height: 14px;
}
.mobile-user-dropdown {
position: fixed;
bottom: calc(70px + env(safe-area-inset-bottom, 0px));
right: 12px;
background: var(--card-card);
border-radius: 12px;
box-shadow: 0 -4px 24px rgba(0, 0, 0, 0.15);
min-width: 200px;
display: none;
z-index: 1001;
overflow: hidden;
}
.mobile-user-dropdown.show {
display: block;
animation: dropdownShowUp 0.2s ease;
}
.mobile-dropdown-header {
padding: 12px 16px;
border-bottom: 1px solid var(--hover);
text-align: center;
}
.mobile-dropdown-header .user-name {
font-size: 14px;
font-weight: 600;
color: var(--text-primary);
}
.mobile-dropdown-header .nav-logout-timer {
font-size: 12px;
color: var(--text-secondary);
margin-top: 2px;
}
.mobile-dropdown-item {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 12px 16px;
color: var(--text-primary);
text-decoration: none;
transition: background-color 0.2s;
font-size: 14px;
}
.mobile-dropdown-item:hover {
background: var(--hover);
color: var(--accent-accent);
text-decoration: none;
}
.mobile-dropdown-item img {
width: 18px;
height: 18px;
filter: var(--icon-filter);
}
.mobile-dropdown-item:hover img {
filter: none;
}
body {
padding-bottom: calc(70px + env(safe-area-inset-bottom, 0px));
}
}
@media (max-width: 480px) {
.mobile-header {
padding: 0.75rem;
}
.mobile-header .logo-text {
font-size: 18px;
}
.mobile-header .school-details {
font-size: 11px;
max-width: 220px;
}
.mobile-nav-item {
padding: 6px 4px;
font-size: 9px;
}
.mobile-nav-item img {
width: 20px;
height: 20px;
}
.mobile-user-btn {
padding: 6px 4px;
font-size: 9px;
}
.mobile-user-btn .user-avatar-icon {
width: 20px;
height: 20px;
}
}
@media (max-width: 360px) {
.mobile-header .logo-text {
font-size: 16px;
}
.mobile-header .school-details {
font-size: 10px;
max-width: 180px;
}
.mobile-nav-item span {
display: none;
}
.mobile-user-btn span {
display: none;
}
.mobile-nav-item img {
width: 24px;
height: 24px;
}
.mobile-user-btn .user-avatar-icon {
width: 24px;
height: 24px;
}
}

View File

@@ -42,12 +42,17 @@ async function updateHeaderInfo() {
function startLogoutTimer(timeString) {
const startTime = parseInt(timeString?.match(/\d+/)?.[0] || "45");
let timeLeft = startTime * 60;
const timerElement = document.querySelector(".nav-logout-timer");
const updateTimer = () => {
const minutes = Math.floor(timeLeft / 60);
const seconds = timeLeft % 60;
timerElement.textContent = `${minutes}:${seconds.toString().padStart(2, "0")}`;
const timeText = `${minutes}:${seconds.toString().padStart(2, "0")}`;
const desktopTimer = document.querySelector("#logoutTimer");
const mobileTimer = document.querySelector("#mobileLogoutTimer");
if (desktopTimer) desktopTimer.textContent = timeText;
if (mobileTimer) mobileTimer.textContent = timeText;
if (timeLeft <= 0) {
window.location.href = "/Home/Logout";
@@ -59,10 +64,6 @@ function startLogoutTimer(timeString) {
setInterval(updateTimer, 1000);
}
document.addEventListener("DOMContentLoaded", async () => {
await updateHeaderInfo();
});
function setupUserDropdown() {
const userBtn = document.querySelector(".user-dropdown-btn");
const userDropdown = document.querySelector(".user-dropdown");
@@ -70,10 +71,36 @@ function setupUserDropdown() {
userBtn?.addEventListener("click", (e) => {
e.stopPropagation();
userDropdown?.classList.toggle("show");
userBtn?.classList.toggle("open");
});
document.addEventListener("click", () => {
userDropdown?.classList.remove("show");
const mobileUserBtn = document.querySelector("#mobileUserBtn");
const mobileUserDropdown = document.querySelector("#mobileUserDropdown");
mobileUserBtn?.addEventListener("click", (e) => {
e.stopPropagation();
mobileUserDropdown?.classList.toggle("show");
mobileUserBtn?.classList.toggle("active");
});
document.addEventListener("click", (e) => {
if (!userBtn?.contains(e.target) && !userDropdown?.contains(e.target)) {
userDropdown?.classList.remove("show");
userBtn?.classList.remove("open");
}
if (!mobileUserBtn?.contains(e.target) && !mobileUserDropdown?.contains(e.target)) {
mobileUserDropdown?.classList.remove("show");
mobileUserBtn?.classList.remove("active");
}
});
const mobileDropdownItems = document.querySelectorAll(".mobile-dropdown-item");
mobileDropdownItems.forEach(item => {
item.addEventListener("click", () => {
mobileUserDropdown?.classList.remove("show");
mobileUserBtn?.classList.remove("active");
});
});
}
@@ -84,94 +111,17 @@ function setupSettingsButton() {
const url = chrome.runtime.getURL("settings/index.html");
window.open(url, "_blank", "width=400,height=600");
});
}
function setupMobileNavigation() {
setTimeout(() => {
const navToggle = document.querySelector(".nav-toggle");
const nav = document.querySelector(".kreta-nav");
if (!navToggle || !nav) {
return;
}
const isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
if (isFirefox) {
let isNavOpen = false;
navToggle.addEventListener("click", (e) => {
e.preventDefault();
e.stopPropagation();
if (isNavOpen) {
nav.style.display = "none";
nav.classList.remove("show");
isNavOpen = false;
} else {
nav.style.display = "flex";
nav.classList.add("show");
isNavOpen = true;
}
});
navToggle.addEventListener("touchend", (e) => {
e.preventDefault();
e.stopPropagation();
if (isNavOpen) {
nav.style.display = "none";
nav.classList.remove("show");
isNavOpen = false;
} else {
nav.style.display = "flex";
nav.classList.add("show");
isNavOpen = true;
}
});
document.addEventListener("click", (e) => {
if (!nav.contains(e.target) && !navToggle.contains(e.target) && isNavOpen) {
nav.style.display = "none";
nav.classList.remove("show");
isNavOpen = false;
}
});
const navItems = document.querySelectorAll(".nav-item");
navItems.forEach((item) => {
item.addEventListener("click", () => {
nav.style.display = "none";
nav.classList.remove("show");
isNavOpen = false;
});
});
} else {
navToggle.addEventListener("click", (e) => {
e.preventDefault();
e.stopPropagation();
nav.classList.toggle("show");
});
document.addEventListener("click", (e) => {
if (!nav.contains(e.target) && !navToggle.contains(e.target)) {
nav.classList.remove("show");
}
});
const navItems = document.querySelectorAll(".nav-item");
navItems.forEach((item) => {
item.addEventListener("click", () => {
nav.classList.remove("show");
});
});
}
}, 100);
document.getElementById("mobileSettingsBtn")?.addEventListener("click", (e) => {
e.preventDefault();
e.stopPropagation();
const url = chrome.runtime.getURL("settings/index.html");
window.open(url, "_blank", "width=400,height=600");
});
}
document.addEventListener("DOMContentLoaded", async () => {
await updateHeaderInfo();
setupUserDropdown();
setupSettingsButton();
setupMobileNavigation();
});

File diff suppressed because one or more lines are too long

View File

@@ -276,16 +276,17 @@ body {
.user-dropdown-btn {
display:flex;
align-items:center;
gap:1rem;
background:none;
gap:0.75rem;
background:var(--button-secondaryFill);
border:none;
cursor:pointer;
padding:0.5rem;
border-radius:8px;
transition:background-color 0.2s;
padding:8px 12px;
border-radius:12px;
transition:all 0.2s ease;
box-shadow:0px 1px var(--shadow-blur, 2px) 0px var(--accent-shadow);
}
.user-dropdown-btn:hover {
background:var(--card-card);
background:var(--accent-15);
}
.user-info {
text-align:right;

View File

@@ -23,7 +23,6 @@
setupUserDropdown();
setupMobileNavigation();
const script = document.createElement("script");
script.src = chrome.runtime.getURL("grades/chart.js");
@@ -533,6 +532,9 @@
if (!ctx) return;
const gradePoints = calculateGradePoints(subjects);
const accentColor = getComputedStyle(document.documentElement)
.getPropertyValue("--accent-accent")
.trim();
new Chart(ctx, {
type: "line",
@@ -543,28 +545,8 @@
label: "Átlag",
data: gradePoints.map((p) => p.average),
borderWidth: 5,
borderColor: accentColor,
tension: 0.5,
segment: {
borderColor: (ctx) => {
const curr = ctx.p1.parsed.y;
if (!curr) return "transparent";
const color =
getComputedStyle(document.documentElement)
.getPropertyValue(
curr < 2
? "--grades-1"
: curr < 2.5
? "--grades-2"
: curr < 3.5
? "--grades-3"
: curr < 4.5
? "--grades-4"
: "--grades-5",
)
.trim() + "80";
return color;
},
},
fill: true,
backgroundColor: function (context) {
const chart = context.chart;
@@ -578,55 +560,12 @@
chartArea.top,
);
gradientBg.addColorStop(
0,
getComputedStyle(document.documentElement)
.getPropertyValue("--grades-1")
.trim() + "30",
);
gradientBg.addColorStop(
0.2,
getComputedStyle(document.documentElement)
.getPropertyValue("--grades-2")
.trim() + "30",
);
gradientBg.addColorStop(
0.4,
getComputedStyle(document.documentElement)
.getPropertyValue("--grades-3")
.trim() + "30",
);
gradientBg.addColorStop(
0.6,
getComputedStyle(document.documentElement)
.getPropertyValue("--grades-4")
.trim() + "30",
);
gradientBg.addColorStop(
0.8,
getComputedStyle(document.documentElement)
.getPropertyValue("--grades-5")
.trim() + "30",
);
gradientBg.addColorStop(0, accentColor + "30");
gradientBg.addColorStop(1, accentColor + "10");
return gradientBg;
},
pointBackgroundColor: (context) => {
const value = context.raw;
return getComputedStyle(document.documentElement)
.getPropertyValue(
value < 2
? "--grades-1"
: value < 2.5
? "--grades-2"
: value < 3.5
? "--grades-3"
: value < 4.5
? "--grades-4"
: "--grades-5",
)
.trim();
},
pointBackgroundColor: accentColor,
pointRadius: 0,
pointHoverRadius: 0,
},
@@ -641,15 +580,10 @@
max: 5,
ticks: {
stepSize: 1,
color: getComputedStyle(
document.documentElement,
).getPropertyValue("--text-secondary"),
color: accentColor,
},
grid: {
color:
getComputedStyle(document.documentElement).getPropertyValue(
"--text-teritary",
) + "20",
color: accentColor + "20",
lineWidth: 1,
borderDash: [5, 5],
},

1
icons/pending.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 24 24"><path fill="currentColor" fill-rule="evenodd" d="M2 12C2 6.477 6.477 2 12 2s10 4.477 10 10s-4.477 10-10 10S2 17.523 2 12m11-5a1 1 0 1 0-2 0v3.764a3 3 0 0 0 1.658 2.683l2.895 1.447a1 1 0 1 0 .894-1.788l-2.894-1.448a1 1 0 0 1-.553-.894z" clip-rule="evenodd"/></svg>

After

Width:  |  Height:  |  Size: 348 B

View File

@@ -1,7 +1,7 @@
{
"manifest_version": 3,
"name": "Firxa",
"version": "1.4.0",
"version": "1.4.2",
"description": "KRÉTA webes verziójának újraírása",
"icons": {
"128": "images/firka_logo_128.png"

View File

@@ -1,7 +1,7 @@
{
"manifest_version": 3,
"name": "Firxa",
"version": "1.4.0",
"version": "1.4.2",
"description": "KRÉTA webes verziójának újraírása",
"icons": {
"128": "images/firka_logo_128.png"

View File

@@ -128,16 +128,23 @@ body {
padding: 10px 12px;
max-width: 100%;
}
.bulk-actions-left,
.bulk-actions-left {
justify-content: space-between;
flex-wrap: nowrap;
gap: 6px;
order: 1;
}
.bulk-actions-right {
justify-content: center;
justify-content: space-between;
flex-wrap: wrap;
gap: 6px;
order: 2;
}
.bulk-btn,
.view-toggle button {
width: 32px;
height: 32px;
flex: 1;
}
.bulk-btn img,
.view-toggle button img {
@@ -151,12 +158,19 @@ body {
.view-toggle button {
width: 28px;
height: 28px;
flex: 1;
}
.bulk-btn img,
.view-toggle button img {
width: 16px;
height: 16px;
}
.bulk-actions-left {
order: 1;
}
.bulk-actions-right {
order: 2;
}
}
.messages-grid {

View File

@@ -162,7 +162,7 @@
modalContent.innerHTML = `
<div class="modal-header">
<h2>Üzenet részletei</h2>
<button class="modal-close" onclick="closeMessageModal()">×</button>
<button class="modal-close">×</button>
</div>
<div class="modal-body">
<div class="loading-content">
@@ -173,6 +173,9 @@
`;
modalOverlay.appendChild(modalContent);
document.body.appendChild(modalOverlay);
const closeBtn = modalContent.querySelector('.modal-close');
closeBtn.addEventListener('click', closeMessageModal);
if (!isRead) {
try {
@@ -594,9 +597,6 @@ async function switchView(view) {
if (typeof setupUserDropdown === 'function') {
setupUserDropdown();
}
if (typeof setupMobileNavigation === 'function') {
setupMobileNavigation();
}
await loadMessages(messagesContainer);

View File

@@ -219,6 +219,7 @@ to {
}
}@media (max-width:900px) {
.role-grid {
gap:0.5rem;
grid-template-columns:1fr;
}
.side-roles {

View File

@@ -248,8 +248,6 @@ h2 {
.custom-themes-section {
margin-top: 16px;
padding-top: 16px;
border-top: 1px solid var(--border-border, var(--text-teritary));
}
.custom-themes-header {
@@ -262,7 +260,13 @@ h2 {
color: var(--text-primary);
}
.add-theme-btn {
.custom-themes-buttons {
display: flex;
gap: 8px;
align-items: center;
}
.theme-btn {
display: flex;
align-items: center;
justify-content: center;
@@ -276,7 +280,7 @@ h2 {
transition: all 0.2s ease;
}
.add-theme-btn:hover {
.theme-btn:hover {
background: var(--accent-accent);
color: var(--button-secondaryFill);
}
@@ -523,7 +527,7 @@ h2 {
}
.theme-form input[type="text"] {
width: 100%;
width: 150px;
padding: 10px 12px;
border: 1px solid var(--border-border, var(--text-teritary));
border-radius: 8px;
@@ -587,12 +591,15 @@ h2 {
align-items: center;
justify-content: space-between;
margin-bottom: 12px;
gap: 12px;
}
.color-group label {
font-size: 13px;
font-size: 12px;
color: var(--text-secondary);
margin: 0;
white-space: nowrap;
flex-shrink: 0;
}
.color-input-wrapper {
@@ -602,13 +609,15 @@ h2 {
}
.color-input-wrapper input[type="color"] {
width: 32px;
height: 32px;
width: 28px;
height: 28px;
border: none;
border-radius: 6px;
cursor: pointer;
padding: 0;
background: transparent;
flex-shrink: 0;
border: 1px solid var(--border-border, var(--text-teritary));
}
.color-input-wrapper input[type="color"]::-webkit-color-swatch-wrapper {
@@ -621,15 +630,16 @@ h2 {
}
.color-hex {
width: 80px;
width: 72px;
padding: 6px 8px;
border: 1px solid var(--border-border, var(--text-teritary));
border-radius: 6px;
background: var(--button-secondaryFill);
color: var(--text-primary);
font-size: 12px;
font-size: 11px;
font-family: monospace;
text-transform: uppercase;
flex-shrink: 0;
}
.theme-preview-section {

View File

@@ -55,9 +55,14 @@
<div class="custom-themes-section">
<div class="custom-themes-header">
<span data-i18n="settings.custom_themes.title">Egyéni témák</span>
<button class="add-theme-btn" id="addCustomTheme">
<span class="material-icons-round">add</span>
</button>
<div class="custom-themes-buttons">
<button class="theme-btn" id="addCustomTheme" title="Új téma">
<span class="material-icons-round">add</span>
</button>
<button class="theme-btn" id="importCustomTheme" title="Téma importálása">
<span class="material-icons-round">download</span>
</button>
</div>
</div>
<div class="theme-grid custom-themes-grid" id="customThemesGrid">
</div>
@@ -225,7 +230,7 @@
</div>
<div class="modal-body">
<p data-i18n="settings.custom_themes.import_description">Illeszd be a téma kódot:</p>
<textarea id="importCode" placeholder="Illessze be a téma kódot ide..."></textarea>
<textarea id="importCode" placeholder="Illeszd be a téma kódt ide..."></textarea>
<p class="error-message" id="importError"></p>
</div>
<div class="modal-footer">

View File

@@ -503,6 +503,10 @@ document.addEventListener("DOMContentLoaded", async () => {
document.getElementById("cancelThemeEditor")?.addEventListener("click", closeThemeEditor);
document.getElementById("saveTheme")?.addEventListener("click", saveThemeFromEditor);
document.getElementById("importCustomTheme")?.addEventListener("click", () => {
document.getElementById("importThemeModal").classList.add("active");
});
document.querySelectorAll(".mode-option").forEach(btn => {
btn.addEventListener("click", () => {
document.querySelectorAll(".mode-option").forEach(b => b.classList.remove("active"));
@@ -550,16 +554,6 @@ document.addEventListener("DOMContentLoaded", async () => {
document.getElementById("closeShareModalBtn")?.addEventListener("click", closeShareModal);
document.getElementById("copyShareCode")?.addEventListener("click", copyShareCode);
let pressTimer;
const addBtn = document.getElementById("addCustomTheme");
addBtn?.addEventListener("mousedown", () => {
pressTimer = setTimeout(() => {
document.getElementById("importThemeModal").classList.add("active");
}, 500);
});
addBtn?.addEventListener("mouseup", () => clearTimeout(pressTimer));
addBtn?.addEventListener("mouseleave", () => clearTimeout(pressTimer));
document.getElementById("closeImportModal")?.addEventListener("click", closeImportModal);
document.getElementById("cancelImport")?.addEventListener("click", closeImportModal);
document.getElementById("confirmImport")?.addEventListener("click", importTheme);

View File

@@ -21,128 +21,6 @@ body {
display:flex;
flex-direction:column;
}
.kreta-header {
padding:clamp(1rem,3vw,2rem);
display:grid;
grid-template-columns:minmax(300px,400px) 1fr minmax(200px,300px);
align-items:center;
gap:1rem;
}
@media (max-width:1200px) {
.kreta-header {
grid-template-columns:minmax(250px,350px) 1fr minmax(180px,250px);
}
}@media (max-width:768px) {
.kreta-header {
grid-template-columns:1fr auto auto;
grid-template-areas:"school toggle user"
"nav nav nav";
padding:1rem;
gap:0.5rem;
}
}.school-info {
margin:0;
}
@media (max-width:768px) {
.school-info {
grid-area:school;
max-width:none;
display:flex;
align-items:center;
gap:0.5rem;
}
}.logo-text {
color:var(--text-primary);
font-size:24px;
font-weight:600;
margin:0 0 0.5rem;
display:flex;
align-items:center;
}
@media (max-width:768px) {
.logo-text {
margin:0;
font-size:20px;
}
}.logo {
width:24px;
border-radius:8px;
margin-right:0.5rem;
}
.school-details {
color:var(--text-secondary);
font-size:14px;
}
.school-details span {
display:block;
white-space:nowrap;
overflow:hidden;
text-overflow:ellipsis;
max-width:300px;
}
@media (max-width:768px) {
.school-details span {
max-width:200px;
}
.school-details {
font-size:12px;
}
}.user-profile {
position:relative;
justify-self:flex-end;
}
@media (max-width:768px) {
.user-profile {
grid-area:user;
}
}.user-dropdown-btn {
display:flex;
align-items:center;
gap:1rem;
background:none;
border:none;
cursor:pointer;
padding:0.5rem;
border-radius:8px;
transition:background-color 0.2s;
}
.user-dropdown-btn:hover {
background:var(--hover);
}
.user-info {
text-align:right;
}
.user-dropdown {
position:absolute;
top:100%;
right:0;
margin-top:0.5rem;
background:var(--card-card);
border-radius:12px;
box-shadow:0 4px 6px -1px rgba(0,0,0,0.1);
width:200px;
display:none;
z-index:1000;
}
.user-dropdown.show {
display:block;
animation:dropdownShow 0.2s ease;
}
.dropdown-item {
display:flex;
align-items:center;
gap:0.75rem;
padding:0.75rem 1rem;
color:var(--text-primary);
text-decoration:none;
transition:background-color 0.2s;
}
.dropdown-item:hover {
background:var(--hover);
color:var(--accent-accent);
border-radius:8px;
text-decoration:none;
}
.kreta-main {
flex:1;
padding:clamp(1rem,3vw,2rem);
@@ -431,8 +309,8 @@ body {
flex-direction:column;
align-items:center;
justify-content:center;
width:60px;
height:50px;
width:40px;
height:40px;
border:2px solid var(--button-secondaryFill);
border-radius:8px;
background:var(--button-secondaryFill);
@@ -513,7 +391,7 @@ body {
.week-modal-content {
background:var(--card-card);
border-radius:16px;
max-width:1000px;
max-width:700px;
max-height:70vh;
width:100%;
overflow:hidden;
@@ -531,12 +409,11 @@ body {
grid-template-columns:repeat(13,1fr);
gap:8px;
padding:20px;
max-height:calc(70vh - 80px);
overflow-y:auto;
}
.modal-week-cell {
width:40px;
height:40px;
width:60px;
height:60px;
font-size:12px;
}
@media (max-width:768px) {
@@ -583,29 +460,26 @@ body {
}
.week-modal-grid {
display:grid;
grid-template-columns:repeat(13,1fr);
gap:8px;
grid-template-columns:repeat(auto-fill, 60px);
gap:4px;
padding:24px;
max-height:70vh;
overflow-y:auto;
justify-content:center;
}
.week-modal-grid .week-cell {
width:60px;
height:50px;
height:60px;
font-size:14px;
}
@media (max-width:1200px) {
@media (max-width:768px) {
.week-modal-grid {
grid-template-columns:repeat(10,1fr);
}
}@media (max-width:768px) {
.week-modal-grid {
grid-template-columns:repeat(7,1fr);
gap:6px;
grid-template-columns:repeat(auto-fill, 40px);
gap:4px;
}
.week-modal-grid .week-cell {
width:50px;
height:45px;
width:40px;
height:40px;
font-size:12px;
}
.week-modal-content {
@@ -615,7 +489,7 @@ body {
padding:16px 20px;
}
.week-modal-grid {
padding:20px;
padding:16px;
}
}@media (max-width:768px) {
.week-grid {
@@ -637,8 +511,8 @@ body {
gap:4px;
}
.week-cell {
width:35px;
height:30px;
width:40px;
height:40px;
font-size:11px;
}
.week-controls {
@@ -654,7 +528,7 @@ body {
}
.day-nav-btn {
background:var(--card-card);
border:1px solid var(--accent-15);
box-shadow: 0px 1px var(--shadow-blur, 2px) 0px var(--accent-shadow);
border-radius:12px;
padding:12px 16px;
color:var(--text-secondary);
@@ -663,6 +537,7 @@ body {
display:flex;
align-items:center;
gap:8px;
border: #00000000 0px solid;
}
.day-nav-btn:hover {
background:var(--accent-15);

View File

@@ -711,19 +711,21 @@
const lessonDetails = document.createElement('div');
lessonDetails.className = 'lesson-details';
const teacherItem = document.createElement('div');
teacherItem.className = 'detail-item';
const teacherLabel = document.createElement('span');
teacherLabel.className = 'detail-label';
teacherLabel.textContent = LanguageManager.t('timetable.teacher_label');
const teacherValue = document.createElement('span');
teacherValue.className = `detail-value ${lesson.originalTeacher != '' ? 'line-through' : ''}`;
teacherValue.textContent = lesson.originalTeacher != '' ? lesson.originalTeacher : lesson.teacher;
teacherItem.appendChild(teacherLabel);
teacherItem.appendChild(teacherValue);
lessonDetails.appendChild(teacherItem);
if (!lesson.isSubstituted) {
const teacherItem = document.createElement('div');
teacherItem.className = 'detail-item';
const teacherLabel = document.createElement('span');
teacherLabel.className = 'detail-label';
teacherLabel.textContent = LanguageManager.t('timetable.teacher_label');
const teacherValue = document.createElement('span');
teacherValue.className = 'detail-value';
teacherValue.textContent = lesson.teacher;
teacherItem.appendChild(teacherLabel);
teacherItem.appendChild(teacherValue);
lessonDetails.appendChild(teacherItem);
}
if (lesson.originalTeacher != '') {
if (lesson.isSubstituted) {
const substituteItem = document.createElement('div');
substituteItem.className = 'detail-item';
const substituteLabel = document.createElement('span');
@@ -1700,7 +1702,7 @@
const timetableGrid = document.querySelector(".timetable-grid");
if (timetableGrid) {
const newContent = await generateTimeGrid(lessons, weekDates);
const newContent = await generateTimeGrid(allLessons, weekDates);
timetableGrid.innerHTML = '';
const parser1 = new DOMParser();
@@ -1982,8 +1984,10 @@
dayNavigationState.currentDayIndex = currentDayIndex;
} else {
const preservedDayIndex = dayNavigationState.currentDayIndex;
dayNavigationState.currentDayIndex = preservedDayIndex;
if (dayNavigationState.currentDayIndex >= 0 && dayNavigationState.currentDayIndex < 5) {
} else {
dayNavigationState.currentDayIndex = 0;
}
}
function updateDayDisplay() {
@@ -2501,9 +2505,6 @@
if (typeof setupUserDropdown === 'function') {
setupUserDropdown();
}
if (typeof setupMobileNavigation === 'function') {
setupMobileNavigation();
}
setupEventListeners(data);
initializeWeekSelector();
@@ -2646,6 +2647,7 @@
timetableContainer.appendChild(tempDiv.firstChild);
}
setupLessonCardListeners();
setupDayNavigation(weekDates);
setTimeout(async () => {
await updateHomeworkIconsFromCookie();

View File

@@ -17,72 +17,115 @@ const createTemplate = {
const baseUrl = schoolSubdomain ? `https://${schoolSubdomain}.e-kreta.hu` : "";
const schoolNameFull = `${data.schoolInfo.id} - ${data.schoolInfo.name}`;
const shortenedSchoolName = helper.shortenSchoolName(schoolNameFull);
const navItems = [
{ href: `${baseUrl}/Intezmeny/Faliujsag`, page: "dashboard", path: "/Intezmeny/Faliujsag", icon: "dashboard", label: LanguageManager.t("navigation.dashboard") },
{ href: `${baseUrl}/TanuloErtekeles/Osztalyzatok`, page: "grades", path: "/TanuloErtekeles/Osztalyzatok", icon: "grades", label: LanguageManager.t("navigation.grades") },
{ href: `${baseUrl}/Orarend/InformaciokOrarend`, page: "timetable", path: "/Orarend/InformaciokOrarend", icon: "timetable", label: LanguageManager.t("navigation.timetable") },
{ href: `${baseUrl}/Hianyzas/Hianyzasok`, page: "absences", path: "/Hianyzas/Hianyzasok", icon: "absences", label: LanguageManager.t("navigation.absences") },
{ href: "https://eugyintezes.e-kreta.hu/api/bff/login", page: "messages", path: "/" || "/uzenetek", icon: "messages", label: LanguageManager.t("navigation.messages") }
];
const desktopNavItems = navItems.map(item => {
const isActive = location.pathname === item.path;
return `<a href="${item.href}" data-page="${item.page}" class="nav-item ${isActive ? "active" : ""}">
<img src="${chrome.runtime.getURL("icons/" + item.icon + "-" + (isActive ? "active" : "inactive") + ".svg")}" alt="${item.label}">
${item.label}
</a>`;
}).join("");
const mobileNavItems = navItems.map(item => {
const isActive = location.pathname === item.path;
return `<a href="${item.href}" data-page="${item.page}" class="mobile-nav-item ${isActive ? "active" : ""}">
<img src="${chrome.runtime.getURL("icons/" + item.icon + "-" + (isActive ? "active" : "inactive") + ".svg")}" alt="${item.label}">
<span>${item.label}</span>
</a>`;
}).join("");
const userAvatarSvg = `<svg viewBox="0 0 24 24"><path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/></svg>`;
const element = `<header class="kreta-header">
<div class="school-info">
<p class="logo-text">
<img src="${chrome.runtime.getURL("images/firka_logo.png")}" alt="Firka" class="logo">
Firka
</p>
<div class="school-details" title="${schoolNameFull}">
${shortenedSchoolName}
</div>
</div>
<button class="nav-toggle" aria-label="${LanguageManager.t("navigation.nav_toggle")}">
<svg viewBox="0 0 24 24">
<path d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z"/>
</svg>
</button>
<nav class="kreta-nav">
<div class="nav-links">
<a href="${baseUrl}/Intezmeny/Faliujsag" data-page="dashboard" class="nav-item ${location.pathname == "/Intezmeny/Faliujsag" ? "active" : ""}">
<img src="${chrome.runtime.getURL("icons/dashboard-" + (location.pathname == "/Intezmeny/Faliujsag" ? "active" : "inactive") + ".svg")}" alt="${LanguageManager.t("navigation.dashboard")}">
${LanguageManager.t("navigation.dashboard")}
</a>
<a href="${baseUrl}/TanuloErtekeles/Osztalyzatok" data-page="grades" class="nav-item ${location.pathname == "/TanuloErtekeles/Osztalyzatok" ? "active" : ""}">
<img src="${chrome.runtime.getURL("icons/grades-" + (location.pathname == "/TanuloErtekeles/Osztalyzatok" ? "active" : "inactive") + ".svg")}" alt="${LanguageManager.t("navigation.grades")}">
${LanguageManager.t("navigation.grades")}
</a>
<a href="${baseUrl}/Orarend/InformaciokOrarend" data-page="timetable" class="nav-item ${location.pathname == "/Orarend/InformaciokOrarend" ? "active" : ""}">
<img src="${chrome.runtime.getURL("icons/timetable-" + (location.pathname == "/Orarend/InformaciokOrarend" ? "active" : "inactive") + ".svg")}" alt="${LanguageManager.t("navigation.timetable")}">
${LanguageManager.t("navigation.timetable")}
</a>
<a href="${baseUrl}/Hianyzas/Hianyzasok" data-page="absences" class="nav-item ${location.pathname == "/Hianyzas/Hianyzasok" ? "active" : ""}">
<img src="${chrome.runtime.getURL("icons/absences-" + (location.pathname == "/Hianyzas/Hianyzasok" ? "active" : "inactive") + ".svg")}" alt="${LanguageManager.t("navigation.absences")}">
${LanguageManager.t("navigation.absences")}
</a>
<a href="https://eugyintezes.e-kreta.hu/api/bff/login" data-page="messages" class="nav-item ${location.pathname == "/" ? "active" : ""}">
<img src="${chrome.runtime.getURL("icons/messages-" + (location.pathname == "/uzenetek" ? "active" : "inactive") + ".svg")}" alt="${LanguageManager.t("navigation.messages")}">
${LanguageManager.t("navigation.messages")}
</a>
</div>
</nav>
const element = `
<header class="kreta-header">
<div class="school-info">
<p class="logo-text">
<img src="${chrome.runtime.getURL("images/firka_logo.png")}" alt="Firka" class="logo">
Firka
</p>
<div class="school-details" title="${schoolNameFull}">
${shortenedSchoolName}
</div>
</div>
<nav class="kreta-nav">
<div class="nav-links">
${desktopNavItems}
</div>
</nav>
<div class="user-profile">
<button class="user-dropdown-btn">
<div class="user-info">
<span class="user-name">${data.userData.name}</span>
<span class="nav-logout-timer" id="logoutTimer">${data.userData.time}</span>
</div>
</button>
<div class="user-dropdown">
<a href="${baseUrl}/Adminisztracio/Profil" data-page="profile" class="dropdown-item">
<img src="${chrome.runtime.getURL("icons/profile.svg")}" alt="${LanguageManager.t("navigation.profile")}">
${LanguageManager.t("navigation.profile")}
</a>
<a href="#" class="dropdown-item" id="settingsBtn">
<img src="${chrome.runtime.getURL("icons/settings.svg")}" alt="${LanguageManager.t("navigation.settings")}">
${LanguageManager.t("navigation.settings")}
</a>
<a href="${baseUrl}/Home/Logout" data-page="logout" class="dropdown-item">
<img src="${chrome.runtime.getURL("icons/logout.svg")}" alt="${LanguageManager.t("navigation.logout")}">
${LanguageManager.t("navigation.logout")}
</a>
<div class="user-profile">
<button class="user-dropdown-btn">
<div class="user-info">
<span class="user-name">${data.userData.name}</span>
<span class="nav-logout-timer" id="logoutTimer">${data.userData.time}</span>
</div>
<div class="user-avatar-icon">
${userAvatarSvg}
</div>
</header>`;
</button>
<div class="user-dropdown">
<a href="${baseUrl}/Adminisztracio/Profil" data-page="profile" class="dropdown-item">
<img src="${chrome.runtime.getURL("icons/profile.svg")}" alt="${LanguageManager.t("navigation.profile")}">
${LanguageManager.t("navigation.profile")}
</a>
<a href="#" class="dropdown-item" id="settingsBtn">
<img src="${chrome.runtime.getURL("icons/settings.svg")}" alt="${LanguageManager.t("navigation.settings")}">
${LanguageManager.t("navigation.settings")}
</a>
<a href="${baseUrl}/Home/Logout" data-page="logout" class="dropdown-item">
<img src="${chrome.runtime.getURL("icons/logout.svg")}" alt="${LanguageManager.t("navigation.logout")}">
${LanguageManager.t("navigation.logout")}
</a>
</div>
</div>
</header>
<header class="mobile-header">
<div class="school-info">
<p class="logo-text">
<img src="${chrome.runtime.getURL("images/firka_logo.png")}" alt="Firka" class="logo">
Firka
</p>
<div class="school-details" title="${schoolNameFull}">
${shortenedSchoolName}
</div>
</div>
</header>
<nav class="mobile-bottom-nav">
${mobileNavItems}
<button class="mobile-user-btn" id="mobileUserBtn">
<div class="user-avatar-icon">
${userAvatarSvg}
</div>
<span>${LanguageManager.t("navigation.profile")}</span>
</button>
</nav>
<div class="mobile-user-dropdown" id="mobileUserDropdown">
<div class="mobile-dropdown-header">
<span class="user-name">${data.userData.name}</span>
<span class="nav-logout-timer" id="mobileLogoutTimer">${data.userData.time}</span>
</div>
<a href="${baseUrl}/Adminisztracio/Profil" data-page="profile" class="mobile-dropdown-item">
<img src="${chrome.runtime.getURL("icons/profile.svg")}" alt="${LanguageManager.t("navigation.profile")}">
${LanguageManager.t("navigation.profile")}
</a>
<a href="#" class="mobile-dropdown-item" id="mobileSettingsBtn">
<img src="${chrome.runtime.getURL("icons/settings.svg")}" alt="${LanguageManager.t("navigation.settings")}">
${LanguageManager.t("navigation.settings")}
</a>
<a href="${baseUrl}/Home/Logout" data-page="logout" class="mobile-dropdown-item">
<img src="${chrome.runtime.getURL("icons/logout.svg")}" alt="${LanguageManager.t("navigation.logout")}">
${LanguageManager.t("navigation.logout")}
</a>
</div>
`;
const startTime = parseInt(data.userData.time?.match(/\d+/)?.[0] || "45");
let timeLeft = startTime * 60;
@@ -90,11 +133,12 @@ const createTemplate = {
const updateTimer = () => {
const minutes = Math.floor(timeLeft / 60);
const seconds = timeLeft % 60;
const timerEl = document.getElementById("logoutTimer");
if (timerEl) {
timerEl.textContent = `${minutes}:${seconds.toString().padStart(2, "0")}`;
}
const timeText = `${minutes}:${seconds.toString().padStart(2, "0")}`;
const desktopTimer = document.getElementById("logoutTimer");
const mobileTimer = document.getElementById("mobileLogoutTimer");
if (desktopTimer) desktopTimer.textContent = timeText;
if (mobileTimer) mobileTimer.textContent = timeText;
if (timeLeft <= 0) {
window.location.href = "/Home/Logout";
} else {
@@ -110,7 +154,15 @@ const createTemplate = {
document.addEventListener("DOMContentLoaded", async () => {
await helper.waitForElement("#settingsBtn");
document.querySelector("#settingsBtn").addEventListener("click", (e) => {
document.querySelector("#settingsBtn")?.addEventListener("click", (e) => {
e.preventDefault();
e.stopPropagation();
const url = chrome.runtime.getURL("settings/index.html");
window.open(url, "_blank", "width=400,height=600");
});
document.querySelector("#mobileSettingsBtn")?.addEventListener("click", (e) => {
e.preventDefault();
e.stopPropagation();
const url = chrome.runtime.getURL("settings/index.html");