From 107e20f5c165d2bb83058820caf014a03b3ad5ee Mon Sep 17 00:00:00 2001 From: Zan1456 <62830223+Zan1456@users.noreply.github.com> Date: Fri, 13 Jun 2025 13:26:21 +0200 Subject: [PATCH] . --- absences/absences.js | 30 +- dashboard/dashboard.css | 239 +++++-- dashboard/dashboard.js | 403 +++++++---- forgotpassword/forgotpassword.js | 20 +- global/language.js | 131 ++++ global/maintenance.css | 40 +- global/maintenance.js | 50 +- global/navigation.js | 4 +- grades/grades.js | 35 +- homework/homework.js | 48 +- i18n/en.json | 319 +++++++++ i18n/hu.json | 319 +++++++++ images/cactus.png | Bin 0 -> 43397 bytes login/login.js | 24 +- login/twofactor.js | 20 +- manifest.json | 9 +- profile/profile.js | 30 +- roleselect/roleselect.js | 16 +- search/search.js | 2 +- settings/index.css | 37 +- settings/index.html | 43 +- settings/index.js | 63 +- timetable/timetable.css | 335 ++++++++- timetable/timetable.js | 1085 ++++++++++++++++++------------ tools/createTemplate.js | 34 +- tools/loadingScreen.js | 16 +- 26 files changed, 2561 insertions(+), 791 deletions(-) create mode 100644 global/language.js create mode 100644 i18n/en.json create mode 100644 i18n/hu.json create mode 100644 images/cactus.png diff --git a/absences/absences.js b/absences/absences.js index 56e02ce..0dade5f 100644 --- a/absences/absences.js +++ b/absences/absences.js @@ -54,23 +54,23 @@ async function transformAbsencesPage() {
-

Szűrés

+

${LanguageManager.t('absences.filter_title')}

- - - - + + + +
@@ -97,7 +97,7 @@ async function transformAbsencesPage() {
event ${date} - ${dayAbsences.length} óra + ${dayAbsences.length} ${LanguageManager.t('absences.hours')}
${dayAbsences.map(absence => ` @@ -106,7 +106,7 @@ async function transformAbsencesPage() { data-justified="${absence.justified}">
schedule - ${absence.lesson}. óra + ${absence.lesson}. ${LanguageManager.t('absences.lesson').toLowerCase()}
${absence.subject}
@@ -114,10 +114,10 @@ async function transformAbsencesPage() {
${absence.justificationStatus === 'justified' ? - `Igazolt check_circle` : + `${LanguageManager.t('absences.justified')} check_circle` : absence.justificationStatus === 'unjustified' ? - `Igazolatlan cancel` : - `Igazolásra vár pending`} + `${LanguageManager.t('absences.unjustified')} cancel` : + `${LanguageManager.t('absences.pending')} pending`}
`).join('')} @@ -252,6 +252,6 @@ function setupFilters() { if (window.location.href.includes('/Hianyzas/Hianyzasok')) { transformAbsencesPage().catch(error => { - console.error('Hiba történt az oldal átalakítása során:', error); + console.error(LanguageManager.t('absences.page_transform_error'), error); }); } \ No newline at end of file diff --git a/dashboard/dashboard.css b/dashboard/dashboard.css index 6a46c4b..33cadd6 100644 --- a/dashboard/dashboard.css +++ b/dashboard/dashboard.css @@ -14,6 +14,10 @@ body { font-size: 16px; } +h2 { + background-color: #00000000 !important; +} + .kreta-container { min-height: 100vh; display: flex; @@ -35,42 +39,191 @@ body { gap: 20px; } - -.card { +.widget-card { background: var(--card-card); - padding: 20px; - padding-top: 5px !important; border-radius: 24px; overflow: hidden; animation: fadeIn 0.5s ease forwards; - box-shadow: 0px 1px var(--shadow-blur) 0px var(--accent-shadow); + box-shadow: 0px 1px var(--shadow-blur, 2px) 0px var(--accent-shadow); display: flex; flex-direction: column; height: 100%; + border: none; + min-height: 400px; +} + +.widget-header { + padding: 20px 20px 0 20px; + background: var(--card-card) !important; +} + +.widget-card-title { + font-size: 18px; + font-weight: 600; + color: var(--text-primary); + margin: 0; + padding-bottom: 16px; +} + +.widget-content { + flex: 1; + padding: 0 20px; + background: var(--card-card); + display: flex; + flex-direction: column; + justify-content: flex-start; +} + +.widget-footer { + padding: 0px 20px 20px 20px; + background: var(--card-card); +} + +.widget-link { + margin: 0; + padding: 0; } .card h2 { - font-size: 16px; + font-size: 18px; font-weight: 600; color: var(--text-primary); - background-color: var(--card-card); + margin: 0; + padding: 20px 0 16px 0; } .card:last-child { grid-column: 1 / -1; } - -.grade-item, .absence-item, .note-item, .exam-item, .news-item { - border-radius: 6px; +.widget-item { + border-radius: 12px; transition: transform 0.2s ease, box-shadow 0.2s ease; - border: 1px solid var(--card-card); + border: none; + background: var(--card-card); + box-shadow: 0px 1px var(--shadow-blur, 2px) 0px var(--accent-shadow); display: flex; flex-direction: column; - margin-bottom: 12px; - padding: 12px; + padding: 8px; + position: relative; + min-height: 80px; } +.widget-item:hover { + transform: translateY(-2px); + box-shadow: 0px 2px calc(var(--shadow-blur, 2px) * 2) 0px var(--accent-shadow); +} + +.widget-row { + display: flex; + align-items: flex-start; + justify-content: space-between; + width: 100%; + gap: 12px; + min-height: 40px; +} + +.widget-row.grade-row { + align-items: center; + justify-content: flex-start; +} + +.widget-details { + flex: 1; + display: flex; + flex-direction: column; + gap: 6px; + min-width: 0; +} + +.widget-details.grade-details { + flex: 1; + margin-right: auto; +} + +.widget-title { + color: var(--text-primary); + font-weight: 600; + font-size: 16px; + line-height: 1.3; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.widget-subtitle { + color: var(--text-secondary); + font-weight: 500; + font-size: 14px; + line-height: 1.2; +} + +.widget-content { + color: var(--text-secondary); + font-size: 14px; + line-height: 1.4; + margin-top: 4px; +} + +.widget-date { + color: var(--text-secondary); + font-size: 14px; + font-weight: 500; + white-space: nowrap; + text-align: right; + align-self: center; +} + +.grade-type-with-date { + display: flex; + justify-content: space-between; + align-items: center; + width: 100%; + gap: 8px; +} + +.grade-type-with-date .grade-type { + flex: 1; + min-width: 0; +} + +.note-author { + color: var(--text-tertiary); + font-size: 12px; + font-weight: 400; + margin-top: 4px; +} + +.grade-date { + color: var(--text-secondary); + font-size: 13px; + font-weight: 400; + white-space: nowrap; + text-align: right; +} + + + +.widget-author { + color: var(--text-tertiary); + font-size: 12px; + font-weight: 400; +} + +.widget-meta { + display: flex; + flex-direction: column; + align-items: flex-end; + gap: 4px; + text-align: right; +} + +.widget-empty { + color: var(--text-secondary); + font-style: italic; + text-align: center; + padding: 20px; +} .grade { width: 32px; @@ -84,15 +237,25 @@ body { color: var(--text-primary); font-size: 22px; } -.subject-name, .absence-type, .note-title, .exam-subject { - color: var(--text-primary); - font-weight: 600; + +.news-title { font-size: 16px; + font-weight: 600; + margin-bottom: 8px; } -.grade-type, .absence-date, .note-date, .exam-date { - color: var(--text-secondary); - font-weight: 500; + +.news-content { font-size: 14px; + line-height: 1.4; + color: var(--text-secondary); +} + +.exam-type { + color: var(--accent-accent); + font-size: 12px; + font-weight: 500; + text-transform: uppercase; + letter-spacing: 0.5px; } .more-link { margin-top: auto; @@ -180,15 +343,25 @@ body { width: 20px; height: 20px; } -.subject-name, .absence-type, .note-title, .exam-subject { - color: var(--text-primary); - font-weight: 600; + +.news-title { font-size: 16px; + font-weight: 600; + margin-bottom: 8px; } -.grade-type, .absence-date, .note-date, .exam-date { - color: var(--text-secondary); - font-weight: 500; + +.news-content { font-size: 14px; + line-height: 1.4; + color: var(--text-secondary); +} + +.exam-type { + color: var(--accent-accent); + font-size: 12px; + font-weight: 500; + text-transform: uppercase; + letter-spacing: 0.5px; } .more-link { margin-top: auto; @@ -212,22 +385,12 @@ body { font-size: 0.875rem; } -.grade-row { +.exam-info { display: flex; - align-items: center; - justify-content: space-between; - width: 100%; -} - -.grade-details { - flex: 1; - margin-right: 10px; -} - -.grade-date, .exam-date { + gap: 8px; + margin-top: 4px; color: var(--text-secondary); font-size: 14px; - white-space: nowrap; } .exam-info { diff --git a/dashboard/dashboard.js b/dashboard/dashboard.js index 015708b..4ec40fb 100644 --- a/dashboard/dashboard.js +++ b/dashboard/dashboard.js @@ -1,4 +1,4 @@ -const utils = { +const DashboardUtils = { formatGradeValue(value) { const trimmedValue = value?.trim() || ''; if (trimmedValue.toLowerCase() === 'szöveges') { @@ -10,7 +10,7 @@ const utils = { parseDate(dateStr) { return dateStr?.trim() || ''; }, - + formatHungarianDate(dateStr) { if (!dateStr) return ''; @@ -21,20 +21,29 @@ const utils = { const day = parseInt(dateParts[2], 10); if (isNaN(month) || month < 1 || month > 12) return dateStr; - - const hungarianMonths = [ - 'január', 'február', 'március', 'április', 'május', 'június', - 'július', 'augusztus', 'szeptember', 'október', 'november', 'december' + + if (typeof window.LanguageManager !== 'undefined') { + const monthKeys = [ + 'months.january', 'months.february', 'months.march', 'months.april', + 'months.may', 'months.june', 'months.july', 'months.august', + 'months.september', 'months.october', 'months.november', 'months.december' + ]; + const monthName = window.LanguageManager.t(monthKeys[month - 1]); + return `${monthName} ${day}.`; + } + + const monthKeys = [ + 'months.january', 'months.february', 'months.march', 'months.april', 'months.may', 'months.june', + 'months.july', 'months.august', 'months.september', 'months.october', 'months.november', 'months.december' ]; - return `${hungarianMonths[month - 1]} ${day}.`; + return `${LanguageManager.t(monthKeys[month - 1])} ${day}.`; } }; - -class DashboardDataExtractor { +class DashboardDataManager { constructor() { - this.data = { + this.dashboardData = { grades: [], absences: [], notes: [], @@ -43,46 +52,69 @@ class DashboardDataExtractor { }; } - extractGrades() { + extractGradeData() { const gradeRows = document.querySelectorAll('#legutobbiErtekelesek tr:not(:first-child)'); - this.data.grades = Array.from(gradeRows).map(row => { + this.dashboardData.grades = Array.from(gradeRows).map(row => { const gradeValue = row.querySelector('span[style*="font-size: 200%"]')?.textContent; const gradeInfo = row.querySelector('span[style*="float: right"]')?.textContent; if (!gradeValue || !gradeInfo) return null; const [fullSubject, date] = gradeInfo.split('\n').map(str => str.trim()); - const { subject, type } = this.parseSubjectInfo(fullSubject); + const { subject, type, dateInSubject } = this.parseSubjectInformation(fullSubject); return { - value: utils.formatGradeValue(gradeValue), + value: DashboardUtils.formatGradeValue(gradeValue), subject, - date: utils.parseDate(date), - type: type || 'Értékelés' + date: DashboardUtils.parseDate(date), + type: type || LanguageManager.t('dashboard.evaluation'), + dateInSubject: dateInSubject || null }; }).filter(Boolean); } - parseSubjectInfo(fullSubject) { - const months = ['január', 'február', 'március', 'április', 'május', 'június', - 'július', 'augusztus', 'szeptember', 'október', 'november', 'december']; - const monthPattern = new RegExp(months.join('|'), 'i'); - const monthMatch = fullSubject.match(monthPattern); + parseSubjectInformation(fullSubject) { + const hungarianMonths = ['január', 'február', 'március', 'április', 'május', 'június', + 'július', 'augusztus', 'szeptember', 'október', 'november', 'december']; + const monthPattern = hungarianMonths.join('|'); + const datePattern = new RegExp(`(${monthPattern})\\s+(\\d{1,2})\\.?$`, 'i'); + const dateMatch = fullSubject.match(datePattern); - if (!monthMatch) return { subject: fullSubject, type: '' }; + if (dateMatch) { + const subjectPart = fullSubject.substring(0, dateMatch.index).trim(); + const datePart = dateMatch[0].trim(); + return { + subject: subjectPart, + type: '', + dateInSubject: datePart + }; + } + + const months = [ + LanguageManager.t('months.january'), LanguageManager.t('months.february'), LanguageManager.t('months.march'), + LanguageManager.t('months.april'), LanguageManager.t('months.may'), LanguageManager.t('months.june'), + LanguageManager.t('months.july'), LanguageManager.t('months.august'), LanguageManager.t('months.september'), + LanguageManager.t('months.october'), LanguageManager.t('months.november'), LanguageManager.t('months.december') + ]; + const fallbackMonthPattern = new RegExp(months.join('|'), 'i'); + const monthMatch = fullSubject.match(fallbackMonthPattern); - const monthIndex = fullSubject.lastIndexOf(monthMatch[0]); - return { - subject: fullSubject.substring(0, monthIndex).trim(), - type: fullSubject.substring(monthIndex).trim() - }; + if (monthMatch) { + const monthIndex = fullSubject.lastIndexOf(monthMatch[0]); + return { + subject: fullSubject.substring(0, monthIndex).trim(), + type: fullSubject.substring(monthIndex).trim() + }; + } + + return { subject: fullSubject, type: '' }; } - extractAbsences() { + extractAbsenceData() { const absenceRows = document.querySelectorAll('#legutobbiMulasztasok tr:not(:first-child)'); - this.data.absences = Array.from(absenceRows).map(row => { + this.dashboardData.absences = Array.from(absenceRows).map(row => { const spans = row.querySelectorAll('span'); if (spans.length < 4) return null; @@ -95,10 +127,10 @@ class DashboardDataExtractor { }).filter(Boolean); } - extractNotes() { + extractNoteData() { const noteRows = document.querySelectorAll('#legutobbiFeljegyzesek tr:not(:first-child)'); - this.data.notes = Array.from(noteRows).map(row => { + this.dashboardData.notes = Array.from(noteRows).map(row => { const spans = row.querySelectorAll('span'); if (spans.length < 3) return null; @@ -110,10 +142,10 @@ class DashboardDataExtractor { }).filter(Boolean); } - extractExams() { + extractExamData() { const examRows = document.querySelectorAll('#legutobbiBejelentettSzamonkeres tr:not(:first-child)'); - this.data.upcomingExams = Array.from(examRows).map(row => { + this.dashboardData.upcomingExams = Array.from(examRows).map(row => { const spans = row.querySelectorAll('span'); if (spans.length < 4) return null; @@ -126,56 +158,79 @@ class DashboardDataExtractor { }).filter(Boolean); } - extractNews() { - const newsContainer = document.querySelector('.faliujsag-lista, #faliujsagLista'); - if (!newsContainer) return; + async extractNewsData() { - const newsItems = newsContainer.querySelectorAll('.nb-item, .news-item'); - - this.data.news = Array.from(newsItems).map(item => { - const titleElement = item.querySelector('.subject h4, .news-title'); - const contentElement = item.querySelector('.content, .news-content'); + try { + const timestamp = Date.now(); + const apiUrl = `https://${window.location.hostname}/Intezmeny/Faliujsag/GetMoreEntries?startindex=0&range=10&_=${timestamp}`; - - const dateElement = item.querySelector('.nb-date, .news-date'); - let dateStr = ''; - - if (dateElement) { - const yearElement = dateElement.querySelector('.year'); - const monthElement = dateElement.querySelector('.month'); - const dayElement = dateElement.querySelector('.day'); - - if (yearElement && monthElement && dayElement) { - dateStr = `${yearElement.textContent} ${monthElement.textContent} ${dayElement.textContent}`; - } else { - dateStr = dateElement.textContent; + const response = await fetch(apiUrl, { + method: 'GET', + credentials: 'include', + headers: { + 'Accept': 'application/json, text/javascript, */*; q=0.01', + 'X-Requested-With': 'XMLHttpRequest' } + }); + + if (!response.ok) { + throw new Error(`API request failed with status: ${response.status}`); } + const data = await response.json(); - const authorElement = item.querySelector('.auth-name span, .news-author'); + if (!data.FaliujsagElemek || !Array.isArray(data.FaliujsagElemek)) { + return; + } - return { - title: titleElement?.textContent?.trim() || '', - date: dateStr.trim(), - content: contentElement?.textContent?.trim() || '', - author: authorElement?.textContent?.trim() || '' - }; - }).filter(news => news.title || news.content); + data.FaliujsagElemek.forEach((item, index) => { + let formattedDate = ''; + if (item.DatumNap && item.DatumHonap && item.DatumEv) { + formattedDate = `${item.DatumEv}. ${item.DatumHonap} ${item.DatumNap}.`; + } else if (item.Idopont) { + const match = item.Idopont.match(/\/Date\((\d+)\)\//); + if (match) { + const timestamp = parseInt(match[1]); + const date = new Date(timestamp); + formattedDate = date.toLocaleDateString('hu-HU'); + } + } + + if (!formattedDate) { + formattedDate = new Date().toLocaleDateString('hu-HU'); + } + + let cleanContent = item.EsemenySzovege || ''; + cleanContent = cleanContent.replace(/<[^>]*>/g, ''); + cleanContent = cleanContent.replace(/\r\n/g, ' '); + cleanContent = cleanContent.replace(/\s+/g, ' ').trim(); + + const newsItem = { + title: item.EsemenyCime || `Hír ${index + 1}`, + content: cleanContent || 'Nincs elérhető tartalom', + date: formattedDate, + author: `${item.Nev || 'Ismeretlen'} (${item.Munkakor || 'Ismeretlen'})` + }; + + this.dashboardData.news.push(newsItem); + }); + + } catch (error) { + console.error('❌ Error fetching news from API:', error); + } } - extractAll() { - this.extractGrades(); - this.extractAbsences(); - this.extractNotes(); - this.extractExams(); - this.extractNews(); - return this.data; + async extractAllData() { + this.extractGradeData(); + this.extractAbsenceData(); + this.extractNoteData(); + this.extractExamData(); + await this.extractNewsData(); + return this.dashboardData; } } - -class DashboardUI { +class DashboardRenderer { constructor(data) { this.data = { ...data, @@ -192,134 +247,204 @@ class DashboardUI { this.shortenedSchoolName = helper.shortenSchoolName(this.schoolNameFull); } - generateMainContentHTML() { + generateMainContent() { return `
- ${this.generateGradeCard()} - ${this.generateAbsenceCard()} - ${this.generateNoteCard()} - ${this.generateExamCard()} - ${this.generateNewsCard()} + ${this.createGradeCard()} + ${this.createAbsenceCard()} + ${this.createNoteCard()} + ${this.createExamCard()} + ${this.createNewsCard()}
`; } - generateNewsCard() { + createNewsCard() { const newsItems = this.data.news.map(news => ` -
-
-
${news.date}
- ${news.author ? `
${news.author}
` : ''} -
-
-

${news.title}

-
${news.content}
-
-
- `).join(''); - - return this.generateCard('Hírek', newsItems || 'Jelenleg ez egy nem támogatott funkció', '/Intezmeny/Faliujsag', 'Összes hír'); - } - generateGradeCard() { - const gradeItems = this.data.grades.map(grade => ` -
-
-
${grade.value}
-
-
${grade.subject}
-
${grade.type}
+
+
+
+
${news.title}
+
${news.content}
+
+
+ ${news.date ? `
${news.date}
` : ''} + ${news.author ? `
${news.author}
` : ''}
- ${grade.date ? `
${grade.date}
` : ''}
`).join(''); - return this.generateCard('Értékeléseid', gradeItems, '/TanuloErtekeles/Osztalyzatok', 'Összes jegyed'); + return this.createCard(LanguageManager.t('dashboard.news'), newsItems || LanguageManager.t('dashboard.not_supported'), '/Intezmeny/Faliujsag', LanguageManager.t('dashboard.all_news')); } - generateAbsenceCard() { + + createGradeCard() { + const gradeItems = this.data.grades.map(grade => ` +
+
+
${grade.value}
+
+
${grade.subject}
+
+
${grade.type}
+ ${grade.dateInSubject || grade.date ? `
${grade.dateInSubject || grade.date}
` : ''} +
+
+
+
+ `).join(''); + + return this.createCard(LanguageManager.t('dashboard.grades'), gradeItems, '/TanuloErtekeles/Osztalyzatok', LanguageManager.t('dashboard.all_grades')); + } + + createAbsenceCard() { const absenceItems = this.data.absences.map(absence => ` -
-
-
${absence.type}
-
${absence.date}
+
+
+
+
${absence.type}
+
${absence.date}
+
+ ${absence.day ? `
${absence.day}
` : ''}
`).join(''); - return this.generateCard('Mulasztások', absenceItems, '/Hianyzas/Hianyzasok', 'Összes mulasztás'); + return this.createCard(LanguageManager.t('dashboard.absences'), absenceItems, '/Hianyzas/Hianyzasok', LanguageManager.t('dashboard.all_absences')); } - generateNoteCard() { + + createNoteCard() { const noteItems = this.data.notes.map(note => ` -
-
-
${note.title}
-
${note.date}
+
+
+
+
${note.title}
+
${note.date}
+
+ ${note.author ? `
${note.author}
` : ''}
`).join(''); - return this.generateCard('Feljegyzések', noteItems, '/TanuloErtekeles/InformaciokFeljegyzesek', 'Összes üzeneted'); + return this.createCard(LanguageManager.t('dashboard.notes'), noteItems, '/TanuloErtekeles/InformaciokFeljegyzesek', LanguageManager.t('dashboard.all_messages')); } - generateExamCard() { + + createExamCard() { const examItems = this.data.upcomingExams.map(exam => ` -
-
-
${exam.subject}
-
${utils.formatHungarianDate(exam.date)}
+
+
+
+
${exam.subject}
+
${exam.type || ''}
+
+
${DashboardUtils.formatHungarianDate(exam.date)}
`).join(''); - return this.generateCard('Bejelentett dolgozatok', examItems, '/Tanulo/TanuloBejelentettSzamonkeresek', 'Összes dolgozat'); + return this.createCard(LanguageManager.t('dashboard.exams'), examItems, '/Tanulo/TanuloBejelentettSzamonkeresek', LanguageManager.t('dashboard.all_exams')); } - generateCard(title, content, linkHref, linkText) { + + createCard(title, content, linkHref, linkText) { return ` -
-

${title}

-
- ${content || `Jelenleg ez egy nem támogatott funkció`} - + `; } - + render() { document.body.innerHTML = `
${createTemplate.header()} - ${this.generateMainContentHTML()} + ${this.generateMainContent()}
`; setupUserDropdown(); } } -class DashboardApp { +class DashboardApplication { constructor() { - this.initialize(); + this.init(); } - async initialize() { - if (!window.location.href.includes('/Intezmeny/Faliujsag')) return; + async init() { + if (!window.location.href.includes('/Intezmeny/Faliujsag')) { + return; + } + + while (typeof window.LanguageManager === 'undefined' || + !window.LanguageManager.t('dashboard.grades') || + window.LanguageManager.t('dashboard.grades') === 'dashboard.grades') { + await new Promise(resolve => setTimeout(resolve, 50)); + } + + while (!document.querySelector('.faliujsag-lista, #faliujsagLista')) { + await new Promise(resolve => setTimeout(resolve, 100)); + } + let newsItemsFound = false; + let attempts = 0; + const maxAttempts = 50; + + while (!newsItemsFound && attempts < maxAttempts) { + const newsContainer = document.querySelector('.faliujsag-lista, #faliujsagLista'); + if (newsContainer) { + const possibleSelectors = [ + '.nb-item', + '.news-item', + '.faliujsag-item', + '.list-group-item', + 'li', + 'div[class*="item"]', + 'div[class*="news"]' + ]; + + for (const selector of possibleSelectors) { + const items = newsContainer.querySelectorAll(selector); + if (items.length > 0) { + newsItemsFound = true; + break; + } + } + + if (!newsItemsFound) { + if (newsContainer.children.length > 0 || newsContainer.textContent.trim().length > 0) { + newsItemsFound = true; + } + } + } + + if (!newsItemsFound) { + attempts++; + await new Promise(resolve => setTimeout(resolve, 100)); + } + } try { - const dataExtractor = new DashboardDataExtractor(); - const dashboardData = dataExtractor.extractAll(); + const dataManager = new DashboardDataManager(); + const dashboardData = await dataManager.extractAllData(); createTemplate.importFonts(); - const ui = new DashboardUI(dashboardData); - ui.render(); + const renderer = new DashboardRenderer(dashboardData); + renderer.render(); } catch (error) { console.error('Error initializing dashboard:', error); } } } - -new DashboardApp(); \ No newline at end of file +new DashboardApplication(); \ No newline at end of file diff --git a/forgotpassword/forgotpassword.js b/forgotpassword/forgotpassword.js index d87dbfe..1c17093 100644 --- a/forgotpassword/forgotpassword.js +++ b/forgotpassword/forgotpassword.js @@ -25,31 +25,31 @@
-

Elfelejtett jelszó

+

${LanguageManager.t('forgotpassword.title')}

- + -
Kérjük, add meg az OM azonosítód.
+ placeholder="${LanguageManager.t('forgotpassword.om_id_placeholder')}" required> +
${LanguageManager.t('forgotpassword.om_id_required')}
- + -
Kérjük, add meg az e-mail címed.
+ placeholder="${LanguageManager.t('forgotpassword.email_placeholder')}" required> +
${LanguageManager.t('forgotpassword.email_required')}
- Vissza a bejelentkezéshez + ${LanguageManager.t('forgotpassword.back_to_login')}
@@ -130,7 +130,7 @@ window.location.href = '/Adminisztracio/Login'; } else { - alert(result.Message || 'Hiba történt a jelszó visszaállítása során. (Kérlek használd az eredeti kréta oldalt erre)'); + alert(result.Message || LanguageManager.t('forgotpassword.error_message')); grecaptcha.reset(); } } catch (error) { diff --git a/global/language.js b/global/language.js new file mode 100644 index 0000000..e9c7857 --- /dev/null +++ b/global/language.js @@ -0,0 +1,131 @@ +(function() { + let currentLanguage = 'hu'; + let translations = {}; + + async function setLanguage(language) { + try { + currentLanguage = language; + + cookieManager.set('languagePreference', language); + localStorage.setItem('languagePreference', language); + + await loadTranslations(language); + applyTranslations(); + + + window.dispatchEvent(new CustomEvent('languageChanged', { + detail: { language: language } + })); + + chrome.runtime.sendMessage({ + action: 'languageChanged', + language: language + }).catch(() => {}); + } catch (error) {} + } + + async function loadTranslations(language) { + try { + const url = chrome.runtime.getURL(`i18n/${language}.json`); + const response = await fetch(url); + if (!response.ok) { + throw new Error(`Failed to load ${language}.json - Status: ${response.status}`); + } + translations = await response.json(); + } catch (error) { + if (language !== 'hu') { + try { + const fallbackUrl = chrome.runtime.getURL('i18n/hu.json'); + const response = await fetch(fallbackUrl); + translations = await response.json(); + } catch (fallbackError) {} + } + } + } + + function applyTranslations() { + const elements = document.querySelectorAll('[data-i18n]'); + + elements.forEach(element => { + const key = element.getAttribute('data-i18n'); + const translation = getTranslation(key); + + if (translation && translation !== key) { + + const attr = element.getAttribute('data-i18n-attr'); + if (attr) { + element.setAttribute(attr, translation); + } else { + element.textContent = translation; + } + } + }); + } + + function getTranslation(keyPath, fallback = '') { + const keys = keyPath.split('.'); + let value = translations; + + for (const key of keys) { + if (value && typeof value === 'object' && key in value) { + value = value[key]; + } else { + + return fallback || keyPath; + } + } + + return typeof value === 'string' ? value : fallback || keyPath; + } + + async function initializeLanguage() { + const cookieLanguage = cookieManager.get('languagePreference'); + const localStorageLanguage = localStorage.getItem('languagePreference'); + + const language = cookieLanguage || localStorageLanguage || 'hu'; + + await setLanguage(language); + + + if (cookieLanguage !== localStorageLanguage) { + if (cookieLanguage) { + localStorage.setItem('languagePreference', cookieLanguage); + } else if (localStorageLanguage) { + cookieManager.set('languagePreference', localStorageLanguage); + } + } + } + + + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', initializeLanguage); + } else { + initializeLanguage(); + } + + + chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { + if (message.action === 'changeLanguage') { + setLanguage(message.language); + sendResponse({ success: true }); + } + + if (message.action === 'getLanguage') { + sendResponse({ language: currentLanguage }); + } + + return true; + }); + + + window.LanguageManager = { + getCurrentLanguage: () => currentLanguage, + changeLanguage: setLanguage, + t: getTranslation, + getAvailableLanguages: () => [ + { code: 'hu', name: 'Magyar' }, + { code: 'en', name: 'English' } + ] + }; + +})(); \ No newline at end of file diff --git a/global/maintenance.css b/global/maintenance.css index 4cc9452..a09347c 100644 --- a/global/maintenance.css +++ b/global/maintenance.css @@ -1,3 +1,28 @@ +@font-face { + font-family: 'Montserrat'; + src: url('chrome-extension://__MSG_@@extension_id__/fonts/Montserrat-Regular.woff2') format('woff2'); + font-weight: 400; + font-style: normal; +} +@font-face { + font-family: 'Montserrat'; + src: url('chrome-extension://__MSG_@@extension_id__/fonts/Montserrat-Medium.woff2') format('woff2'); + font-weight: 500; + font-style: normal; + } + @font-face { + font-family: 'Montserrat'; + src: url('chrome-extension://__MSG_@@extension_id__/fonts/Montserrat-SemiBold.woff2') format('woff2'); + font-weight: 600; + font-style: normal; + } + @font-face { + font-family: 'Figtree'; + src: url('chrome-extension://__MSG_@@extension_id__/fonts/Figtree-Regular.woff2') format('woff2'); + font-weight: 400; + font-style: normal; + } + body.maintenance-mode { margin: 0; padding: 0; @@ -7,7 +32,7 @@ body.maintenance-mode { justify-content: center; background-color: var(--background); color: var(--text-primary); - font-family: 'Inter', sans-serif; + font-family: 'Figtree', sans-serif; } body { @@ -35,6 +60,7 @@ body { font-weight: 600; margin-bottom: 1rem; color: var(--accent-accent); + font-family: 'Montserrat', sans-serif; } .maintenance-message { @@ -42,10 +68,22 @@ body { line-height: 1.5; margin-bottom: 1.5rem; color: var(--text-primary); + font-family: 'Figtree', sans-serif; } .maintenance-footer { font-size: 0.875rem; color: var(--text-secondary); margin-top: 2rem; + font-family: 'Figtree', sans-serif; +} + +.maintenance-cactus { + position: fixed; + bottom: 0px; + right: 20px; + width: 120px; + height: 120px; + opacity: 1; + z-index: 1000; } \ No newline at end of file diff --git a/global/maintenance.js b/global/maintenance.js index d9b172b..5e01f30 100644 --- a/global/maintenance.js +++ b/global/maintenance.js @@ -7,18 +7,46 @@ function loadMaintenanceCSS() { function checkMaintenancePage() { const maintenanceContent = document.querySelector('.login_content'); - if (maintenanceContent && maintenanceContent.textContent.includes('frissítés alatt')) { + const bodyText = document.body ? document.body.textContent : ''; + + const specificMaintenanceMessage = 'Kedves Felhasználók! A KRÉTA rendszer jelenleg frissítés alatt van, hamarosan újra elérhetővé válik. Köszönjük türelmüket és megértésüket! KRÉTA Csapat'; + const hasSpecificMessage = bodyText.includes('Kedves Felhasználók!') && + bodyText.includes('A KRÉTA rendszer jelenleg frissítés alatt van') && + bodyText.includes('KRÉTA Csapat'); + + const hasGeneralMaintenance = maintenanceContent && + (maintenanceContent.textContent.includes('frissítés alatt') || + maintenanceContent.textContent.includes('under maintenance')); + + if (hasSpecificMessage || hasGeneralMaintenance) { const body = document.body; const mainLogo = chrome.runtime.getURL('images/firka_logo_128.png'); + const cactusImage = chrome.runtime.getURL('images/cactus.png'); + + const removeLoadingElements = () => { + const loadingScreen = document.querySelector('.loading-screen'); + if (loadingScreen) loadingScreen.remove(); + + const kretaProgressBar = document.querySelector('#KretaProgressBar'); + if (kretaProgressBar) kretaProgressBar.remove(); + + const modalBackground = document.querySelector('.modalBckgroundMain'); + if (modalBackground) modalBackground.remove(); + + const overlays = document.querySelectorAll('.modalBckgroundMain, .loading-screen, #KretaProgressBar'); + overlays.forEach(overlay => overlay.remove()); + }; - + removeLoadingElements(); + setTimeout(removeLoadingElements, 100); + const existingStyles = document.querySelectorAll('link[rel="stylesheet"], style'); existingStyles.forEach(style => style.remove()); - - + body.innerHTML = ''; body.classList.add('maintenance-mode'); body.classList.add('theme-enabled'); + body.classList.add('loaded'); loadMaintenanceCSS(); @@ -34,20 +62,25 @@ function checkMaintenancePage() { const title = document.createElement('h1'); title.className = 'maintenance-title'; - title.textContent = 'Karbantartás'; + title.textContent = window.LanguageManager ? window.LanguageManager.t('maintenance.title') : 'Karbantartás'; const messageDiv = document.createElement('div'); messageDiv.className = 'maintenance-message'; const paragraph1 = document.createElement('p'); - paragraph1.textContent = 'A KRÉTA rendszer jelenleg frissítés alatt van, hamarosan újra elérhetővé válik.'; + paragraph1.textContent = window.LanguageManager ? window.LanguageManager.t('maintenance.message1') : 'A KRÉTA rendszer jelenleg frissítés alatt van, hamarosan újra elérhetővé válik.'; const paragraph2 = document.createElement('p'); - paragraph2.textContent = 'Köszönjük türelmüket és megértésüket!'; + paragraph2.textContent = window.LanguageManager ? window.LanguageManager.t('maintenance.message2') : 'Köszönjük türelmüket és megértésüket!'; const footer = document.createElement('div'); footer.className = 'maintenance-footer'; - footer.textContent = 'KRÉTA Csapat'; + footer.textContent = window.LanguageManager ? window.LanguageManager.t('maintenance.team') : 'KRÉTA Csapat'; + + const cactus = document.createElement('img'); + cactus.src = cactusImage; + cactus.alt = 'Cactus'; + cactus.className = 'maintenance-cactus'; messageDiv.appendChild(paragraph1); @@ -59,6 +92,7 @@ function checkMaintenancePage() { container.appendChild(footer); body.appendChild(container); + body.appendChild(cactus); } } diff --git a/global/navigation.js b/global/navigation.js index 932195c..1f709c0 100644 --- a/global/navigation.js +++ b/global/navigation.js @@ -6,8 +6,8 @@ const COOKIE_KEYS = { }; const DEFAULT_VALUES = { - SCHOOL: 'Iskola', - USER: 'Felhasználó', + SCHOOL: LanguageManager.t('navigation.school_default'), + USER: LanguageManager.t('navigation.user_default'), TIMER: '45:00' }; diff --git a/grades/grades.js b/grades/grades.js index 64afbb7..0951e2b 100644 --- a/grades/grades.js +++ b/grades/grades.js @@ -27,7 +27,6 @@ loadingScreen.hide(); } catch (error) { - console.error('Error transforming grades page:', error); loadingScreen.hide(); } } @@ -42,7 +41,20 @@ const subjectName = cells[2].textContent.trim(); if (subjectName && subjectName !== 'Magatartás/Szorgalom') { const grades = []; - const months = ['Szeptember', 'Oktober', 'November', 'December', 'JanuarI', 'JanuarII', 'Februar', 'Marcius', 'Aprilis', 'Majus', 'JuniusI', 'JuniusII']; + const months = [ + LanguageManager.t('grades.september'), + LanguageManager.t('grades.october'), + LanguageManager.t('grades.november'), + LanguageManager.t('grades.december'), + LanguageManager.t('grades.january_1'), + LanguageManager.t('grades.january_2'), + LanguageManager.t('grades.february'), + LanguageManager.t('grades.march'), + LanguageManager.t('grades.april'), + LanguageManager.t('grades.may'), + LanguageManager.t('grades.june_1'), + LanguageManager.t('grades.june_2') + ]; months.forEach((month, index) => { const gradeElements = cells[index + 3].querySelectorAll('span[data-tanuloertekelesid]'); @@ -136,7 +148,20 @@ function generateGradeItem(grade) { const semesterClass = grade.isSemesterGrade ? 'semester-grade' : ''; const dateObj = new Date(grade.date); - const monthNames = ['Január', 'Február', 'Március', 'Április', 'Május', 'Június', 'Július', 'Augusztus', 'Szeptember', 'Október', 'November', 'December']; + const monthNames = [ + LanguageManager.t('months.january'), + LanguageManager.t('months.february'), + LanguageManager.t('months.march'), + LanguageManager.t('months.april'), + LanguageManager.t('months.may'), + LanguageManager.t('months.june'), + LanguageManager.t('months.july'), + LanguageManager.t('months.august'), + LanguageManager.t('months.september'), + LanguageManager.t('months.october'), + LanguageManager.t('months.november'), + LanguageManager.t('months.december') + ]; const formattedDate = `${monthNames[dateObj.getMonth()]} ${dateObj.getDate()}`; const shortenedTheme = shortenEvaluationName(grade.theme); return ` @@ -183,7 +208,7 @@
-
Jegyek (${totalGrades}db)
+
${LanguageManager.t('grades.chart_title')} (${totalGrades}db)
${studentAverage > 0 ? studentAverage.toFixed(2) : '-'} @@ -210,7 +235,7 @@
${semesterGrades.length > 0 ? `
-

Félévi értékelések

+

${LanguageManager.t('grades.semester_evaluations')}

${semesterGrades.map(grade => `
diff --git a/homework/homework.js b/homework/homework.js index 2a14bdc..355949d 100644 --- a/homework/homework.js +++ b/homework/homework.js @@ -75,7 +75,15 @@ function formatApiDate(dateString) { const month = String(date.getMonth() + 1).padStart(2, '0'); const day = String(date.getDate()).padStart(2, '0'); - const dayNames = ['vasárnap', 'hétfő', 'kedd', 'szerda', 'csütörtök', 'péntek', 'szombat']; + const dayNames = [ + 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') + ]; const dayName = dayNames[date.getDay()]; return `${month}.${day}. (${dayName})`; @@ -120,16 +128,16 @@ async function transformHomeworkPage() {
-

Szűrés

+

${LanguageManager.t('homework.filter_title')}

- + ${[...new Set(homeworkItems.map(item => item.teacher))] .sort() .map(teacher => ``) @@ -152,13 +160,13 @@ async function transformHomeworkPage() {
@@ -202,7 +210,7 @@ function renderHomeworkList(groupedHomework) { if (sortedDates.length === 0) { return `
-

Nincs megjeleníthető házi feladat.

+

${LanguageManager.t('homework.no_homework')}

`; } @@ -255,12 +263,20 @@ function formatDateHeader(dateStr) { const date = new Date(currentYear, month, day); if (date.toDateString() === today.toDateString()) { - return 'Ma - ' + dateStr; + return LanguageManager.t('common.today') + ' - ' + dateStr; } else if (date.toDateString() === tomorrow.toDateString()) { - return 'Holnap - ' + dateStr; + return LanguageManager.t('common.tomorrow') + ' - ' + dateStr; } - const weekdays = ['Vasárnap', 'Hétfő', 'Kedd', 'Szerda', 'Csütörtök', 'Péntek', 'Szombat']; + const weekdays = [ + 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 `${weekdays[date.getDay()]} - ${dateStr}`; } @@ -376,7 +392,7 @@ function setupFilters(homeworkItems, groupedHomework) { if (!emptyState) { emptyState = document.createElement('div'); emptyState.className = 'empty-state'; - emptyState.innerHTML = '

Nincs a szűrési feltételeknek megfelelő házi feladat.

'; + emptyState.innerHTML = `

${LanguageManager.t('homework.no_matching_homework')}

`; homeworkList.appendChild(emptyState); } diff --git a/i18n/en.json b/i18n/en.json new file mode 100644 index 0000000..fed49fd --- /dev/null +++ b/i18n/en.json @@ -0,0 +1,319 @@ +{ + "loading": { + "text": "Loading...", + "subtext": "Please wait!" + }, + "settings": { + "title": "Settings", + "theme": "Theme", + "language": "Language", + "themes": { + "light_blue": "Light Blue", + "light_green": "Light Green", + "dark_blue": "Dark Blue", + "dark_green": "Dark Green", + "dark_red": "Dark Red", + "dark_purple": "Dark Purple", + "dark_orange": "Dark Orange", + "dark_pink": "Dark Pink", + "dark_yellow": "Dark Yellow", + "dark_cyan": "Dark Cyan", + "dark_lime": "Dark Lime", + "dark_indigo": "Dark Indigo" + }, + "languages": { + "hu": "Magyar", + "en": "English" + }, + "about": { + "title": "About", + "description": "Firka is an open-source project that creates a custom user interface for the KRÉTA system.", + "github": "GitHub" + }, + "support": { + "title": "Support", + "description": "If you like our work and would like to support the development, you can do so in the following way:", + "kofi": "Ko-Fi" + } + }, + "navigation": { + "dashboard": "Home", + "timetable": "Timetable", + "grades": "Grades", + "homework": "Homework", + "absences": "Absences", + "other": "Other", + "profile": "Profile", + "settings": "Settings", + "logout": "Logout", + "nav_toggle": "Open navigation" + }, + "dashboard": { + "welcome": "Welcome", + "recent_grades": "Recent grades", + "upcoming_lessons": "Upcoming lessons", + "homework_due": "Homework due", + "news": "News", + "grades": "Your grades", + "absences": "Absences", + "notes": "Notes", + "exams": "Announced tests", + "all_news": "All news", + "all_grades": "All your grades", + "all_absences": "All absences", + "all_messages": "All your messages", + "all_exams": "All tests", + "not_supported": "There is currently no data to show", + "evaluation": "Evaluation" + }, + "grades": { + "title": "Grades", + "subject": "Subject", + "grade": "Grade", + "date": "Date", + "teacher": "Teacher", + "average": "Average", + "chart_title": "Grades", + "semester_evaluations": "Semester evaluations", + "semester_average": "Semester average", + "no_grades": "No grades", + "september": "September", + "october": "October", + "november": "November", + "december": "December", + "january_1": "January I", + "january_2": "January II", + "february": "February", + "march": "March", + "april": "April", + "may": "May", + "june_1": "June I", + "june_2": "June II" + }, + "timetable": { + "title": "Timetable", + "lesson": "Lesson", + "time": "Time", + "subject": "Subject", + "teacher": "Teacher", + "classroom": "Classroom", + "homework_indicator": "Homework", + "test_indicator": "Test", + "teacher_label": "Teacher:", + "substitute_teacher_label": "Substitute teacher:", + "classroom_label": "Classroom:", + "time_label": "Time:", + "status_label": "Status:", + "substitution": "Substitution", + "cancelled": "Cancelled", + "has_homework": "Has homework", + "no_lessons_this_week": "No lessons this week or timeout occurred", + "monday": "Monday", + "tuesday": "Tuesday", + "wednesday": "Wednesday", + "thursday": "Thursday", + "friday": "Friday", + "found_current_week": "Found current week" + }, + "homework": { + "title": "Homework", + "due_date": "Due date", + "subject": "Subject", + "description": "Description", + "filter_title": "Filter", + "all_subjects": "All subjects", + "all_teachers": "All teachers", + "all_deadlines": "All deadlines", + "tomorrow_deadline": "Tomorrow's deadline", + "this_week": "This week", + "next_week": "Next week", + "no_homework": "No homework to display.", + "no_filtered_homework": "No homework matching the filter criteria.", + "teacher": "Teacher", + "no_matching_homework": "No homework matching the filter criteria." + }, + "absences": { + "title": "Absences", + "date": "Date", + "lesson": "Lesson", + "type": "Type", + "justified": "Justified", + "unjustified": "Unjustified", + "filter_title": "Filter", + "all_subjects": "All subjects", + "all_types": "All types", + "pending": "Pending justification", + "subject": "Subject", + "justification": "Justification", + "hours": "hours", + "page_transform_error": "An error occurred while transforming the page" + }, + "profile": { + "title": "Profile", + "name": "Name", + "class": "Class", + "school": "School", + "student_id": "Student ID", + "settings_title": "Profile settings", + "tab_settings": "Settings", + "tab_password": "Change password", + "tab_security": "Security settings", + "tab_contacts": "Contact information", + "two_factor_description": "To use two-factor authentication, install a time-based one-time password (TOTP) application:", + "android": "Android", + "iphone": "iPhone", + "enable_2fa": "Enable two-factor authentication", + "security_key": "Security key:", + "verification_code_label": "Verification code", + "verification_code_help": "Enter the 6-digit code shown in your authenticator app.", + "verification_code_placeholder": "123456", + "verify_and_activate": "Verify and activate", + "backup_codes_description": "You can use the following backup codes to log in if you don't have access to your authenticator app. Each code can only be used once.", + "email_label": "Email address", + "email_help": "Email address is required for password recovery.", + "phone_label": "Phone number", + "phone_help": "Phone number is optional.", + "phone_placeholder": "+1 xxx xxx xxxx", + "current_password": "Current password", + "new_password": "New password", + "new_password_help": "Password must be at least 8 characters long.", + "confirm_password": "Confirm new password", + "change_password": "Change password", + "show_tips": "Show tips", + "show_tips_help": "Toggle display of tips on/off.", + "email_required": "Email address is required!", + "email_invalid": "Please enter a valid email address!", + "phone_invalid": "Please enter a valid phone number!", + "contacts_saved": "Contact information saved successfully!", + "contacts_save_error": "An error occurred while saving. Please try again later.", + "settings_saved": "Settings saved successfully! Please log in again for changes to take effect.", + "settings_save_error": "An error occurred while saving. Please try again later.", + "password_fields_required": "Please fill in all fields!", + "passwords_not_match": "New passwords do not match!", + "password_too_short": "New password must be at least 8 characters long!", + "password_changed": "Password changed successfully!", + "password_change_error": "An error occurred while changing password. Please try again later." + }, + "login": { + "title": "Login", + "username": "Username", + "password": "Password", + "login_button": "Login", + "forgot_password": "Forgot password", + "two_factor_title": "Two-factor authentication", + "verification_code": "Verification code", + "username_placeholder": "Username", + "password_placeholder": "Password", + "username_required": "Please enter your username.", + "password_required": "Please enter your password.", + "help_login": "Can't log in?", + "help_link": "Help", + "system_message": "System message", + "privacy_policy": "Privacy policy", + "kreta_id": "KRÉTA ID", + "system_notification": "System notification" + }, + "forgot_password": { + "title": "Forgot password", + "om_id": "OM ID", + "email": "Email address", + "om_id_placeholder": "Enter your OM ID", + "email_placeholder": "Enter your email address", + "om_id_required": "Please enter your OM ID.", + "email_required": "Please enter your email address." + }, + "two_factor": { + "title": "Two-factor authentication", + "code_placeholder": "One-time password", + "code_required": "Please enter the one-time password.", + "verify_button": "Verify code", + "verifying": "Verifying...", + "trust_device": "Trust this device" + }, + "logout": { + "title": "Logout", + "message": "Are you sure you want to logout?", + "confirm": "Yes", + "cancel": "Cancel", + "success": "Successfully logged out!", + "continue": "Continue" + }, + "common": { + "save": "Save", + "cancel": "Cancel", + "close": "Close", + "loading": "Loading...", + "error": "Error", + "success": "Success", + "warning": "Warning", + "info": "Information", + "yes": "Yes", + "no": "No", + "continue": "Continue", + "back": "Back", + "next": "Next", + "previous": "Previous", + "all": "All", + "none": "None", + "filter": "Filter", + "search": "Search", + "select": "Select", + "required": "Required", + "optional": "Optional", + "api_error": "API error", + "api_load_error": "API load error", + "monday": "Monday", + "tuesday": "Tuesday", + "wednesday": "Wednesday", + "thursday": "Thursday", + "friday": "Friday", + "saturday": "Saturday", + "sunday": "Sunday", + "today": "Today", + "tomorrow": "Tomorrow" + }, + "months": { + "january": "January", + "february": "February", + "march": "March", + "april": "April", + "may": "May", + "june": "June", + "july": "July", + "august": "August", + "september": "September", + "october": "October", + "november": "November", + "december": "December" + }, + "search": { + "title": "Choose school", + "select_institution": "Please select an institution to continue!" + }, + "roleselect": { + "student_book": "Student Book", + "student_description": "View grades, absences, timetable and other information.", + "dkt_title": "Digital Collaboration Space (DKT)", + "dkt_description": "Classroom communication and assignments.", + "logout_title": "Logout", + "logout_description": "Log out of the system", + "role_change_error": "An error occurred while changing roles." + }, + "maintenance": { + "title": "Maintenance", + "message1": "The KRÉTA system is currently being updated and will be available again soon.", + "message2": "Thank you for your patience and understanding!", + "team": "KRÉTA Team" + }, + "about": { + "title": "About", + "description": "Firka is an open source project that creates a custom user interface for the KRÉTA system.", + "support_title": "Support", + "support_description": "If you like our work and want to support development, you can do so in the following way:", + "version": "v1.1.0" + }, + "app": { + "title": "Firka - KRÉTA", + "settings_title": "Firka - Settings" + } +} \ No newline at end of file diff --git a/i18n/hu.json b/i18n/hu.json new file mode 100644 index 0000000..6e6d54b --- /dev/null +++ b/i18n/hu.json @@ -0,0 +1,319 @@ +{ + "loading": { + "text": "Betöltés alatt...", + "subtext": "Kis türelmet!" + }, + "settings": { + "title": "Beállítások", + "theme": "Téma", + "language": "Nyelv", + "themes": { + "light_blue": "Világos Kék", + "light_green": "Világos Zöld", + "dark_blue": "Sötét Kék", + "dark_green": "Sötét Zöld", + "dark_red": "Sötét Piros", + "dark_purple": "Sötét Lila", + "dark_orange": "Sötét Narancs", + "dark_pink": "Sötét Rózsaszín", + "dark_yellow": "Sötét Sárga", + "dark_cyan": "Sötét Cián", + "dark_lime": "Sötét Lime", + "dark_indigo": "Sötét Indigó" + }, + "languages": { + "hu": "Magyar", + "en": "English" + }, + "about": { + "title": "Névjegy", + "description": "A Firka egy nyílt forráskódú projekt, amely a KRÉTA rendszerhez készít saját felhasználói felületet.", + "github": "GitHub" + }, + "support": { + "title": "Támogatás", + "description": "Ha tetszik a munkánk és szeretnéd támogatni a fejlesztést, az alábbi módon teheted meg:", + "kofi": "Ko-Fi" + } + }, + "navigation": { + "dashboard": "Kezdőlap", + "timetable": "Órarend", + "grades": "Jegyek", + "homework": "Házi feladatok", + "absences": "Mulasztások", + "other": "Egyéb", + "profile": "Profil", + "settings": "Beállítások", + "logout": "Kijelentkezés", + "nav_toggle": "Navigáció megnyitása" + }, + "dashboard": { + "welcome": "Üdvözöljük", + "recent_grades": "Legutóbbi jegyek", + "upcoming_lessons": "Következő órák", + "homework_due": "Esedékes házi feladatok", + "news": "Hírek", + "grades": "Értékeléseid", + "absences": "Mulasztások", + "notes": "Feljegyzések", + "exams": "Bejelentett dolgozatok", + "all_news": "Összes hír", + "all_grades": "Összes jegyed", + "all_absences": "Összes mulasztás", + "all_messages": "Összes üzeneted", + "all_exams": "Összes dolgozat", + "not_supported": "Jelenleg nincsen adat amivel fel lehetne tölteni", + "evaluation": "Értékelés" + }, + "grades": { + "title": "Jegyek", + "subject": "Tantárgy", + "grade": "Jegy", + "date": "Dátum", + "teacher": "Tanár", + "average": "Átlag", + "chart_title": "Jegyek", + "semester_evaluations": "Félévi értékelések", + "semester_average": "Félévi átlag", + "no_grades": "Nincsenek jegyek", + "september": "Szeptember", + "october": "Oktober", + "november": "November", + "december": "December", + "january_1": "JanuarI", + "january_2": "JanuarII", + "february": "Februar", + "march": "Marcius", + "april": "Aprilis", + "may": "Majus", + "june_1": "JuniusI", + "june_2": "JuniusII" + }, + "timetable": { + "title": "Órarend", + "lesson": "Óra", + "time": "Idő", + "subject": "Tantárgy", + "teacher": "Tanár", + "classroom": "Terem", + "homework_indicator": "Házi feladat", + "test_indicator": "Számonkérés", + "teacher_label": "Tanár:", + "substitute_teacher_label": "Helyettesítő tanár:", + "classroom_label": "Terem:", + "time_label": "Időpont:", + "status_label": "Állapot:", + "substitution": "Helyettesítés", + "cancelled": "Elmarad", + "has_homework": "Van házi feladat", + "no_lessons_this_week": "Nincsenek órák ezen a héten vagy időtúllépés történt", + "monday": "Hétfő", + "tuesday": "Kedd", + "wednesday": "Szerda", + "thursday": "Csütörtök", + "friday": "Péntek", + "found_current_week": "Megtalált jelenlegi hét" + }, + "homework": { + "title": "Házi feladatok", + "due_date": "Határidő", + "subject": "Tantárgy", + "description": "Leírás", + "filter_title": "Szűrés", + "all_subjects": "Összes tantárgy", + "all_teachers": "Összes tanár", + "all_deadlines": "Összes határidő", + "tomorrow_deadline": "Holnapi határidő", + "this_week": "Ezen a héten", + "next_week": "Jövő héten", + "no_homework": "Nincs megjeleníthető házi feladat.", + "no_filtered_homework": "Nincs a szűrési feltételeknek megfelelő házi feladat.", + "teacher": "Tanár", + "no_matching_homework": "Nincs a szűrési feltételeknek megfelelő házi feladat." + }, + "absences": { + "title": "Hiányzások", + "date": "Dátum", + "lesson": "Óra", + "type": "Típus", + "justified": "Igazolt", + "unjustified": "Igazolatlan", + "filter_title": "Szűrés", + "all_subjects": "Minden tantárgy", + "all_types": "Mindegy", + "pending": "Igazolásra vár", + "subject": "Tantárgy", + "justification": "Igazolás", + "hours": "óra", + "page_transform_error": "Hiba történt az oldal átalakítása során" + }, + "profile": { + "title": "Profil", + "name": "Név", + "class": "Osztály", + "school": "Iskola", + "student_id": "Diák azonosító", + "settings_title": "Profil beállítások", + "tab_settings": "Beállítások", + "tab_password": "Jelszó módosítása", + "tab_security": "Biztonsági beállítások", + "tab_contacts": "Elérhetőségek", + "two_factor_description": "A kétfaktoros hitelesítés használatához telepítsen egy időalapú, egyszer használatos jelszó (TOTP) alkalmazást:", + "android": "Android", + "iphone": "iPhone", + "enable_2fa": "Kétfaktoros azonosítás bekapcsolása", + "security_key": "Biztonsági kulcs:", + "verification_code_label": "Ellenőrző kód", + "verification_code_help": "Adja meg a hitelesítő alkalmazásban megjelenő 6 számjegyű kódot.", + "verification_code_placeholder": "123456", + "verify_and_activate": "Ellenőrzés és aktiválás", + "backup_codes_description": "Az alábbi biztonsági kódokat használhatja bejelentkezéshez, ha nem fér hozzá a hitelesítő alkalmazásához. Minden kód csak egyszer használható.", + "email_label": "E-mail cím", + "email_help": "Az e-mail cím megadása a jelszó emlékeztető miatt szükséges.", + "phone_label": "Telefonszám", + "phone_help": "A telefonszám megadása nem kötelező.", + "phone_placeholder": "+36 xx xxx xxxx", + "current_password": "Jelenlegi jelszó", + "new_password": "Új jelszó", + "new_password_help": "A jelszónak legalább 8 karakter hosszúnak kell lennie.", + "confirm_password": "Új jelszó megerősítése", + "change_password": "Jelszó módosítása", + "show_tips": "Tippek megjelenítése", + "show_tips_help": "A tippek megjelenítésének ki/be kapcsolása.", + "email_required": "Az e-mail cím megadása kötelező!", + "email_invalid": "Kérjük, adjon meg egy érvényes e-mail címet!", + "phone_invalid": "Kérjük, adjon meg egy érvényes telefonszámot!", + "contacts_saved": "Elérhetőségek sikeresen mentve!", + "contacts_save_error": "Hiba történt a mentés során. Kérjük, próbálja újra később.", + "settings_saved": "Beállítások sikeresen mentve! A változtatások érvényesítéséhez jelentkezzen be újra.", + "settings_save_error": "Hiba történt a mentés során. Kérjük, próbálja újra később.", + "password_fields_required": "Kérjük, töltse ki az összes mezőt!", + "passwords_not_match": "Az új jelszavak nem egyeznek!", + "password_too_short": "Az új jelszónak legalább 8 karakter hosszúnak kell lennie!", + "password_changed": "Jelszó sikeresen módosítva!", + "password_change_error": "Hiba történt a jelszó módosítása során. Kérjük, próbálja újra később." + }, + "login": { + "title": "Bejelentkezés", + "username": "Felhasználónév", + "password": "Jelszó", + "login_button": "Bejelentkezés", + "forgot_password": "Elfelejtett jelszó", + "two_factor_title": "Kétfaktoros hitelesítés", + "verification_code": "Ellenőrző kód", + "username_placeholder": "Felhasználónév", + "password_placeholder": "Jelszó", + "username_required": "Kérjük, add meg a felhasználóneved.", + "password_required": "Kérjük, add meg a jelszavad.", + "help_login": "Nem tudsz bejelentkezni?", + "help_link": "Segítség", + "system_message": "Rendszerüzenet", + "privacy_policy": "Adatvédelmi szabályzat", + "kreta_id": "KRÉTA azonosító", + "system_notification": "Rendszerértesítés" + }, + "forgot_password": { + "title": "Elfelejtett jelszó", + "om_id": "OM azonosítód", + "email": "E-mail cím", + "om_id_placeholder": "Add meg az OM azonosítód", + "email_placeholder": "Add meg az e-mail címed", + "om_id_required": "Kérjük, add meg az OM azonosítód.", + "email_required": "Kérjük, add meg az e-mail címed." + }, + "two_factor": { + "title": "Kétfaktoros azonosítás", + "code_placeholder": "Egyszeri jelszó", + "code_required": "Kérjük, add meg az egyszeri jelszót.", + "verify_button": "Kód ellenőrzése", + "verifying": "Ellenőrzés...", + "trust_device": "Eszköz megbízhatónak jelölése" + }, + "logout": { + "title": "Kijelentkezés", + "message": "Biztosan ki szeretne jelentkezni?", + "confirm": "Igen", + "cancel": "Mégse", + "success": "Sikeres kijelentkezés!", + "continue": "Tovább" + }, + "common": { + "save": "Mentés", + "cancel": "Mégse", + "close": "Bezárás", + "loading": "Betöltés...", + "error": "Hiba", + "success": "Sikeres", + "warning": "Figyelmeztetés", + "info": "Információ", + "yes": "Igen", + "no": "Nem", + "continue": "Tovább", + "back": "Vissza", + "next": "Következő", + "previous": "Előző", + "all": "Összes", + "none": "Nincs", + "filter": "Szűrés", + "search": "Keresés", + "select": "Válassz", + "required": "Kötelező", + "optional": "Opcionális", + "api_error": "API hiba", + "api_load_error": "API betöltési hiba", + "monday": "hétfő", + "tuesday": "kedd", + "wednesday": "szerda", + "thursday": "csütörtök", + "friday": "péntek", + "saturday": "szombat", + "sunday": "vasárnap", + "today": "Ma", + "tomorrow": "Holnap" + }, + "months": { + "january": "január", + "february": "február", + "march": "március", + "april": "április", + "may": "május", + "june": "június", + "july": "július", + "august": "augusztus", + "september": "szeptember", + "october": "október", + "november": "november", + "december": "december" + }, + "search": { + "title": "Válassz iskolát", + "select_institution": "Kérjük, válasszon egy intézményt a folytatáshoz!" + }, + "roleselect": { + "student_book": "Ellenőrzőkönyv", + "student_description": "Jegyek, hiányzások, órarended és egyéb információk megtekintése.", + "dkt_title": "Digitális Kollaborációs Tér (DKT)", + "dkt_description": "Osztálytermi kommunikáció és feladatok.", + "logout_title": "Kijelentkezés", + "logout_description": "Kilépés a rendszerből", + "role_change_error": "Hiba történt a szerepkör váltása közben." + }, + "maintenance": { + "title": "Karbantartás", + "message1": "A KRÉTA rendszer jelenleg frissítés alatt van, hamarosan újra elérhetővé válik.", + "message2": "Köszönjük türelmüket és megértésüket!", + "team": "KRÉTA Csapat" + }, + "about": { + "title": "Névjegy", + "description": "A Firka egy nyílt forráskódú projekt, amely a KRÉTA rendszerhez készít saját felhasználói felületet.", + "support_title": "Támogatás", + "support_description": "Ha tetszik a munkánk és szeretnéd támogatni a fejlesztést, az alábbi módon teheted meg:", + "version": "v1.1.0" + }, + "app": { + "title": "Firka - KRÉTA", + "settings_title": "Firxa - Beállítások" + } +} \ No newline at end of file diff --git a/images/cactus.png b/images/cactus.png new file mode 100644 index 0000000000000000000000000000000000000000..e0250993468a540f8b166709ad30553b1d6d44fc GIT binary patch literal 43397 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 literal 0 HcmV?d00001 diff --git a/login/login.js b/login/login.js index 8103197..cb013f1 100644 --- a/login/login.js +++ b/login/login.js @@ -27,7 +27,7 @@ async function transformLoginPage() { if (spanElement) { const lines = spanElement.textContent?.split('\n').map(line => line.trim()) || []; schoolInfo.kretaId = lines[0] || ''; - schoolInfo.omCode = (lines[1] || '').replace('KRÉTA azonosító: ', ''); + schoolInfo.omCode = (lines[1] || '').replace(`${LanguageManager.t('login.kreta_id')}: `, ''); } const rawSystemMessage = document.querySelector('.alert-primary')?.textContent?.trim() || ''; @@ -44,7 +44,7 @@ async function transformLoginPage() {

${schoolInfo.name}

${schoolInfo.kretaId ? `
${schoolInfo.kretaId}
` : ''} - ${schoolInfo.omCode ? `
KRÉTA azonosító: ${schoolInfo.omCode}
` : ''} + ${schoolInfo.omCode ? `
${LanguageManager.t('login.kreta_id')}: ${schoolInfo.omCode}
` : ''}
@@ -57,27 +57,27 @@ async function transformLoginPage() {
-
Kérjük, add meg a felhasználóneved.
+ placeholder="${LanguageManager.t('login.username_placeholder')}" maxlength="256" autocomplete="username" required value="${formData.userName}"> +
${LanguageManager.t('login.username_required')}
- -
Kérjük, add meg a jelszavad.
+
${LanguageManager.t('login.password_required')}
@@ -85,14 +85,14 @@ async function transformLoginPage() { ${systemMessage ? `
-

Rendszerértesítés

+

${LanguageManager.t('login.system_message')}

${systemMessage}

` : ''}
`; diff --git a/login/twofactor.js b/login/twofactor.js index 2fc2609..ea50ab8 100644 --- a/login/twofactor.js +++ b/login/twofactor.js @@ -29,7 +29,7 @@ async function transformTwoFactorPage() { Firka

-

Kétfaktoros azonosítás

+

${LanguageManager.t('twofactor.title')}

@@ -41,30 +41,30 @@ async function transformTwoFactorPage() {
- -
Kérjük, add meg az egyszeri jelszót.
+
${LanguageManager.t('twofactor.code_required')}
- +
- Nem fér hozzá eszközéhez? Lépjen be + ${LanguageManager.t('twofactor.no_access')}
@@ -73,7 +73,7 @@ async function transformTwoFactorPage() {
`; @@ -162,7 +162,7 @@ function handleSubmit(event) { const submitButton = form.querySelector('.btn-kreta'); if (submitButton) { submitButton.disabled = true; - submitButton.innerHTML = 'Ellenőrzés...'; + submitButton.innerHTML = `${LanguageManager.t('twofactor.verifying')}`; } form.submit(); diff --git a/manifest.json b/manifest.json index fd50abe..b4a2e8d 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 3, "name": "Firxa", - "version": "1.2.1", + "version": "1.2.2", "description": "KRÉTA webes verziójának újraírása", "icons": { "128": "images/firka_logo_128.png" @@ -15,10 +15,13 @@ "web_accessible_resources": [{ "resources": [ "settings/*", + "global/language.js", "images/*", "fonts/*.woff2", "icons/*.svg", - "grades/chart.js" + "grades/chart.js", + "i18n/*.json", + "tools/cookieManager.js" ], "matches": ["https://*.e-kreta.hu/*", "https://idp.e-kreta.hu/*"] }], @@ -28,7 +31,7 @@ "https://*.e-kreta.hu/*" ], "js": ["tools/cookieManager.js", "tools/helper.js", "tools/loadingScreen.js", "tools/createTemplate.js", - "global/maintenance.js", "global/theme.js", "global/navigation.js"], + "global/language.js", "global/maintenance.js", "global/theme.js", "global/navigation.js"], "css": ["tools/loadingScreen.css", "global/theme.css", "global/navigation.css"], "run_at": "document_start" }, diff --git a/profile/profile.js b/profile/profile.js index a5de3f6..d9fb01f 100644 --- a/profile/profile.js +++ b/profile/profile.js @@ -174,17 +174,17 @@ const phone = phoneInput.value.trim(); if (!email) { - alert('Az e-mail cím megadása kötelező!'); + alert(LanguageManager.t('profile.email_required')); return; } if (email && !isValidEmail(email)) { - alert('Kérjük, adjon meg egy érvényes e-mail címet!'); + alert(LanguageManager.t('profile.invalid_email')); return; } if (phone && !isValidPhone(phone)) { - alert('Kérjük, adjon meg egy érvényes telefonszámot!'); + alert(LanguageManager.t('profile.invalid_phone')); return; } @@ -199,13 +199,13 @@ }); if (response.ok) { - alert('Elérhetőségek sikeresen mentve!'); + alert(LanguageManager.t('profile.contacts_saved')); } else { - throw new Error('Hiba történt a mentés során.'); + throw new Error(LanguageManager.t('profile.contacts_save_error')); } } catch (error) { console.error('Error saving contacts:', error); - alert('Hiba történt a mentés során. Kérjük, próbálja újra később.'); + alert(LanguageManager.t('profile.save_error')); } }); } @@ -259,13 +259,13 @@ }); if (response.ok) { - alert('Beállítások sikeresen mentve! A változtatások érvényesítéséhez jelentkezzen be újra.'); + alert(LanguageManager.t('profile.settings_saved')); } else { - throw new Error('Hiba történt a mentés során.'); + throw new Error(LanguageManager.t('profile.settings_save_error')); } } catch (error) { console.error('Error saving settings:', error); - alert('Hiba történt a mentés során. Kérjük, próbálja újra később.'); + alert(LanguageManager.t('profile.save_error')); } }); @@ -276,17 +276,17 @@ const confirmPassword = document.getElementById('confirmPassword').value; if (!currentPassword || !newPassword || !confirmPassword) { - alert('Kérjük, töltse ki az összes mezőt!'); + alert(LanguageManager.t('profile.fill_all_fields')); return; } if (newPassword !== confirmPassword) { - alert('Az új jelszavak nem egyeznek!'); + alert(LanguageManager.t('profile.passwords_not_match')); return; } if (newPassword.length < 8) { - alert('Az új jelszónak legalább 8 karakter hosszúnak kell lennie!'); + alert(LanguageManager.t('profile.password_min_length')); return; } @@ -305,16 +305,16 @@ }); if (response.ok) { - alert('Jelszó sikeresen módosítva!'); + alert(LanguageManager.t('profile.password_changed')); document.getElementById('currentPassword').value = ''; document.getElementById('newPassword').value = ''; document.getElementById('confirmPassword').value = ''; } else { - throw new Error('Hiba történt a jelszó módosítása során.'); + throw new Error(LanguageManager.t('profile.password_change_error')); } } catch (error) { console.error('Error changing password:', error); - alert('Hiba történt a jelszó módosítása során. Kérjük, próbálja újra később.'); + alert(LanguageManager.t('profile.password_change_error')); } }); diff --git a/roleselect/roleselect.js b/roleselect/roleselect.js index 02c1f53..5b70033 100644 --- a/roleselect/roleselect.js +++ b/roleselect/roleselect.js @@ -34,7 +34,7 @@ } } catch (error) { console.error('Error changing role:', error); - alert('Hiba történt a szerepkör váltása közben.'); + alert(LanguageManager.t('roleselect.role_change_error')); } }; @@ -66,8 +66,8 @@ Napló ikon
- Ellenőrzőkönyv -
Jegyek, hiányzások, órarended és egyéb információk megtekintése.
+ ${LanguageManager.t('roleselect.student_book')} +
${LanguageManager.t('roleselect.student_description')}
@@ -78,8 +78,8 @@ DKT ikon
- Digitális Kollaborációs Tér (DKT) -
Osztálytermi kommunikáció és feladatok.
+ ${LanguageManager.t('roleselect.dkt_title')} +
${LanguageManager.t('roleselect.dkt_description')}
@@ -88,8 +88,8 @@ Kijelentkezés ikon
- Kijelentkezés -
Kilépés a rendszerből
+ ${LanguageManager.t('roleselect.logout_title')} +
${LanguageManager.t('roleselect.logout_description')}
@@ -113,7 +113,7 @@ const schoolSubdomain = window.location.hostname.split('.')[0]; const userNameEl = document.querySelector('.UserName'); - const userName = userNameEl?.textContent.trim() || 'Felhasználónév'; + const userName = userNameEl?.textContent.trim() || LanguageManager.t('common.username'); if (schoolCode && fullSchoolName) { diff --git a/search/search.js b/search/search.js index 46c6173..298f7c5 100644 --- a/search/search.js +++ b/search/search.js @@ -64,7 +64,7 @@ function applyFirkaStyling() { redirectButton.addEventListener('click', function(event) { if (!instituteCodeInput.value) { event.preventDefault(); - alert('Kérjük, válasszon egy intézményt a folytatáshoz!'); + alert(LanguageManager.t('search.select_institution')); } }); } diff --git a/settings/index.css b/settings/index.css index f64ab79..7f7a997 100644 --- a/settings/index.css +++ b/settings/index.css @@ -88,12 +88,14 @@ h2 { } -.theme-grid { +.theme-grid, +.language-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px; } -.theme-option { +.theme-option, +.language-option { background: none; border: none; padding: 0; @@ -104,13 +106,18 @@ h2 { gap: 8px; transition: transform 0.2s ease; } -.theme-option:hover { +.theme-option:hover, +.language-option:hover { transform: translateY(-2px); } .theme-option.active .theme-preview { outline: 2px solid var(--accent-accent); outline-offset: 2px; } +.language-option.active .language-preview { + outline: 2px solid var(--accent-accent); + outline-offset: 2px; +} .theme-preview { width: 100%; height: 100px; @@ -135,6 +142,30 @@ h2 { border-radius: 8px; } +.language-option { + background: var(--card-card); + border: 1px solid var(--border-border); + border-radius: 8px; + padding: 12px; + transition: all 0.2s ease; +} + +.language-option:hover { + background: var(--card-hover); + border-color: var(--accent-accent); +} + +.language-option.active { + background: var(--accent-accent); + border-color: var(--accent-accent); + color: white; +} + +.language-name { + font-weight: 500; + font-size: 14px; +} + .theme-preview.light-blue { background: #DAE4F7; diff --git a/settings/index.html b/settings/index.html index f14de9d..f0653a0 100644 --- a/settings/index.html +++ b/settings/index.html @@ -23,12 +23,12 @@
-

Beállítások

+

Beállítások

palette - Téma + Téma
- Világos Kék + Világos Kék
- Világos Zöld + Világos Zöld
- Sötét Kék + Sötét Kék
- Sötét Zöld + Sötét Zöld + +
+
+ +
+
+ language + Nyelv +
+
+ +
@@ -73,24 +88,24 @@
-

Névjegy

+

Névjegy

-

A Firka egy nyílt forráskódú projekt, amely a KRÉTA rendszerhez készít saját felhasználói felületet.

+

A Firka egy nyílt forráskódú projekt, amely a KRÉTA rendszerhez készít saját felhasználói felületet.

code - GitHub + GitHub
-

Támogatás

+

Támogatás

-

Ha tetszik a munkánk és szeretnéd támogatni a fejlesztést, az alábbi módon teheted meg:

+

Ha tetszik a munkánk és szeretnéd támogatni a fejlesztést, az alábbi módon teheted meg:

@@ -100,6 +115,8 @@
v1.1.0
- + + + \ No newline at end of file diff --git a/settings/index.js b/settings/index.js index 3acaeb5..203b127 100644 --- a/settings/index.js +++ b/settings/index.js @@ -1,4 +1,7 @@ document.addEventListener('DOMContentLoaded', async () => { + while (typeof window.LanguageManager === 'undefined') { + await new Promise(resolve => setTimeout(resolve, 10)); + } function isThemeDisabled(theme) { const blueThemesUnlocked = localStorage.getItem('blueThemesUnlocked') === 'true'; @@ -59,6 +62,35 @@ document.addEventListener('DOMContentLoaded', async () => { }); updateThemeAvailability(); } + + function getCurrentLanguage() { + return localStorage.getItem('languagePreference') || + getCookie('languagePreference') || + 'hu'; + } + + function updateLanguageButtons(currentLanguage) { + document.querySelectorAll('.language-option').forEach(button => { + const language = button.dataset.language; + button.classList.toggle('active', language === currentLanguage); + }); + } + + async function applyLanguage(language) { + setCookie('languagePreference', language); + localStorage.setItem('languagePreference', language); + + updateLanguageButtons(language); + + const tabs = await chrome.tabs.query({}); + tabs.forEach(tab => { + chrome.tabs.sendMessage(tab.id, { + action: 'changeLanguage', + language: language + }).catch(() => { + }); + }); + } async function applyTheme(theme) { setCookie('themePreference', theme); @@ -77,8 +109,6 @@ document.addEventListener('DOMContentLoaded', async () => { action: 'changeTheme', theme: theme }).catch(() => { - - console.log('Tab not ready for theme change:', tab.id); }); }); } @@ -91,7 +121,7 @@ document.addEventListener('DOMContentLoaded', async () => { if (button.hasAttribute('disabled')) { - alert('Ez a téma jelenleg nem elérhető.'); + alert(window.LanguageManager.t('common.warning') + ': ' + window.LanguageManager.t('settings.theme_not_available')); return; } @@ -99,6 +129,14 @@ document.addEventListener('DOMContentLoaded', async () => { }); }); + const languageButtons = document.querySelectorAll('.language-option'); + languageButtons.forEach(button => { + button.addEventListener('click', () => { + const language = button.dataset.language; + applyLanguage(language); + }); + }); + let initialTheme = getCurrentTheme(); @@ -111,6 +149,9 @@ document.addEventListener('DOMContentLoaded', async () => { updateThemeAvailability(); await applyTheme(initialTheme); + const initialLanguage = getCurrentLanguage(); + updateLanguageButtons(initialLanguage); + chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { if (message.action === 'themeChanged') { @@ -148,7 +189,7 @@ document.addEventListener('DOMContentLoaded', async () => { box-shadow: 0 4px 12px rgba(0,0,0,0.15); animation: slideIn 0.3s ease-out; `; - notification.textContent = 'Kék témák feloldva! 🎉'; + notification.textContent = window.LanguageManager.t('common.success') + ': ' + window.LanguageManager.t('settings.blue_themes_unlocked'); const style = document.createElement('style'); @@ -184,4 +225,18 @@ document.addEventListener('DOMContentLoaded', async () => { button.style.transform = 'translateY(0)'; }); }); + + languageButtons.forEach(button => { + button.addEventListener('mouseover', () => { + button.style.transform = 'translateY(-2px)'; + }); + + button.addEventListener('mouseout', () => { + button.style.transform = 'translateY(0)'; + }); + }); + + window.addEventListener('languageChanged', (event) => { + updateLanguageButtons(event.detail.language); + }); }); \ No newline at end of file diff --git a/timetable/timetable.css b/timetable/timetable.css index 83af1be..b3a51ff 100644 --- a/timetable/timetable.css +++ b/timetable/timetable.css @@ -55,7 +55,6 @@ body { flex-direction: column; } -/* Update header styles to match dashboard */ .kreta-header { padding: clamp(1rem, 3vw, 2rem); display: grid; @@ -140,7 +139,6 @@ body { } } -/* User profile styles */ .user-profile { position: relative; justify-self: flex-end; @@ -399,24 +397,52 @@ body { .week-controls { display: flex; - gap: 16px; + flex-direction: column; align-items: center; justify-content: center; margin: 16px auto; background: var(--card-card); border-radius: 24px; - max-width: 400px; + max-width: 800px; + padding: 20px; } -.week-select { - flex: 1; - padding: 12px; +.week-selector-container { + width: 100%; + position: relative; +} + +.expand-week-view-btn { + display: flex; + align-items: center; + justify-content: center; + width: 40px; + height: 40px; border: none; - border-radius: 12px; + border-radius: 8px; background: var(--button-secondaryFill); color: var(--text-primary); - font-family: inherit; cursor: pointer; + transition: all 0.2s ease; + margin-left: 12px; +} + +.expand-week-view-btn:hover { + background: var(--accent-15); + color: var(--accent-accent); + transform: scale(1.05); +} + +.expand-week-view-btn .material-icons-round { + font-size: 18px; +} + +.week-selector { + display: flex; + align-items: center; + justify-content: center; + gap: 12px; + max-width: 100%; } .week-nav-btn { @@ -426,19 +452,292 @@ body { width: 40px; height: 40px; border: none; - border-radius: 12px; + border-radius: 8px; background: var(--button-secondaryFill); - color: var(--text-secondary); + color: var(--text-primary); cursor: pointer; transition: all 0.2s ease; } -.week-nav-btn:hover { +.week-nav-btn:hover:not(:disabled) { + background: var(--accent-15); + color: var(--accent-accent); + transform: scale(1.05); +} + +.week-nav-btn .material-icons-round { + font-size: 20px; +} + +.week-display { + display: flex; + gap: 8px; + align-items: center; +} + +.week-cell { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + width: 60px; + height: 50px; + border: 2px solid var(--button-secondaryFill); + border-radius: 8px; + background: var(--button-secondaryFill); + color: var(--text-primary); + font-family: inherit; + font-weight: 500; + cursor: pointer; + transition: all 0.2s ease; + position: relative; + gap: 2px; +} + +.week-cell:hover { + background: var(--accent-15); + border-color: var(--accent-accent); + color: var(--accent-accent); + transform: scale(1.05); +} + +.week-cell.selected { + background: var(--accent-accent); + border-color: var(--accent-accent); + color: white; + font-weight: 600; +} + +.week-cell.selected:hover { + background: var(--accent-accent); + transform: scale(1.05); +} + +.week-cell.current-week { + border-color: var(--accent-accent); + box-shadow: 0 0 0 1px var(--accent-accent); +} + +.week-cell.current-week.selected { + box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.5); +} + +.week-number { + font-size: 14px; + font-weight: inherit; +} + +.current-indicator { + font-size: 8px; + color: var(--accent-accent); + line-height: 1; +} + +.week-cell.selected .current-indicator { + color: white; +} + +.week-tooltip { + position: absolute; + background: var(--card-card); + border: 1px solid var(--border-border); + border-radius: 8px; + padding: 8px 12px; + font-size: 14px; + color: var(--text-primary); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); + pointer-events: none; + opacity: 0; + transition: opacity 0.2s ease; + z-index: 1000; + white-space: nowrap; +} + +.week-tooltip.show { + opacity: 1; +} + +.week-modal { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.5); + z-index: 10000; + display: flex; + align-items: center; + justify-content: center; + padding: 20px; +} + +.week-modal-content { + background: var(--card-card); + border-radius: 16px; + max-width: 1000px; + max-height: 70vh; + width: 100%; + overflow: hidden; + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3); +} + +.week-modal-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 20px 24px; + border-bottom: 1px solid var(--border-border); +} + +.week-modal-grid { + display: grid; + 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; + font-size: 12px; +} + + + +@media (max-width: 768px) { + .week-modal-content { + max-width: 95vw; + max-height: 80vh; + } + + .week-modal-grid { + grid-template-columns: repeat(10, 1fr); + gap: 6px; + padding: 16px; + max-height: calc(80vh - 80px); + } + + .modal-week-cell { + width: 35px; + height: 35px; + font-size: 11px; + } +} + +.week-modal-header h3 { + margin: 0; + color: var(--text-primary); + font-size: 20px; + font-weight: 600; +} + +.week-modal-close { + display: flex; + align-items: center; + justify-content: center; + width: 40px; + height: 40px; + border: none; + border-radius: 8px; + background: var(--button-secondaryFill); + color: var(--text-primary); + cursor: pointer; + transition: all 0.2s ease; +} + +.week-modal-close:hover { background: var(--accent-15); color: var(--accent-accent); } -/* Day navigation for mobile */ +.week-modal-close .material-icons-round { + font-size: 24px; +} + +.week-modal-grid { + display: grid; + grid-template-columns: repeat(13, 1fr); + gap: 8px; + padding: 24px; + max-height: 70vh; + overflow-y: auto; +} + +.week-modal-grid .week-cell { + width: 60px; + height: 50px; + font-size: 14px; +} + +@media (max-width: 1200px) { + .week-modal-grid { + grid-template-columns: repeat(10, 1fr); + } +} + +@media (max-width: 768px) { + .week-modal-grid { + grid-template-columns: repeat(7, 1fr); + gap: 6px; + } + + .week-modal-grid .week-cell { + width: 50px; + height: 45px; + font-size: 12px; + } + + .week-modal-content { + margin: 10px; + } + + .week-modal-header { + padding: 16px 20px; + } + + .week-modal-grid { + padding: 20px; + } +} + +@media (max-width: 768px) { + .week-grid { + grid-template-columns: repeat(10, 1fr); + gap: 6px; + } + + .week-cell { + width: 40px; + height: 35px; + font-size: 12px; + } + + .week-controls { + max-width: 600px; + padding: 15px; + } +} + +@media (max-width: 480px) { + .week-grid { + grid-template-columns: repeat(8, 1fr); + gap: 4px; + } + + .week-cell { + width: 35px; + height: 30px; + font-size: 11px; + } + + .week-controls { + max-width: 400px; + padding: 12px; + } +} + .day-navigation { display: none; align-items: center; @@ -465,10 +764,7 @@ body { color: var(--accent-accent); } -.day-nav-btn:disabled { - opacity: 0.5; - cursor: not-allowed; -} + .current-day-info { text-align: center; @@ -676,7 +972,6 @@ body { display: none; } -/* Animations */ @keyframes fadeIn { from { opacity: 0; @@ -710,7 +1005,6 @@ body { } } -/* Responsive adjustments */ @media (max-width: 768px) { .kreta-header { flex-direction: column; @@ -726,7 +1020,6 @@ body { } } -/* Scrollbar styling */ ::-webkit-scrollbar { width: 8px; height: 8px; @@ -745,7 +1038,6 @@ body { background: var(--text-primary); } -/* Speciális napok stílusai */ .grid-header.special-day { background: linear-gradient(135deg, #F99F50, #FF8C42); color: white; @@ -790,7 +1082,6 @@ body { font-weight: 400; } -/* Week selector improvements */ .week-select { min-width: 200px; max-width: 300px; diff --git a/timetable/timetable.js b/timetable/timetable.js index 5c6cd07..2f35227 100644 --- a/timetable/timetable.js +++ b/timetable/timetable.js @@ -13,12 +13,12 @@ }); if (!response.ok) { - throw new Error(`API hiba: ${response.status}`); + throw new Error(`${LanguageManager.t('common.api_error')}: ${response.status}`); } return await response.json(); } catch (error) { - console.error('Hiba az API adatok betöltése során:', error); + console.error(LanguageManager.t('common.api_load_error'), error); return []; } } @@ -26,7 +26,7 @@ function generateWeekDates(startDate) { const start = new Date(startDate); const dates = []; - const dayNames = ['Hétfő', 'Kedd', 'Szerda', 'Csütörtök', 'Péntek']; + const dayNames = [LanguageManager.t('common.monday'), LanguageManager.t('common.tuesday'), LanguageManager.t('common.wednesday'), LanguageManager.t('common.thursday'), LanguageManager.t('common.friday')]; for (let i = 0; i < 5; i++) { const date = new Date(start); @@ -47,238 +47,72 @@ function convertAPIDataToLessons(apiData, weekDates) { const lessons = []; - - apiData.forEach(event => { - const eventDate = new Date(event.start); - const dayIndex = weekDates.findIndex(date => - new Date(date.fullDate).toDateString() === eventDate.toDateString() - ); - - if (dayIndex === -1) { - console.log('Nem található nap az eseményhez:', event.start); - return; - } - - if (event.oraType === 5) { - lessons.push({ - startTime: 'Egész nap', - endTime: '', - subject: event.title, - teacher: '', - originalTeacher: '', - room: '', - day: dayIndex, - isSubstituted: false, - isCancelled: false, - hasHomework: false, - testInfo: event.Tema || '', - homeworkDetails: '', - isSpecialDay: true, - color: event.color - }); - } else if (event.oraType === 2) { - const startTime = new Date(event.start); - const endTime = new Date(event.end); - const startTimeStr = startTime.toLocaleTimeString('hu-HU', { hour: '2-digit', minute: '2-digit' }); - const endTimeStr = endTime.toLocaleTimeString('hu-HU', { hour: '2-digit', minute: '2-digit' }); - const titleParts = event.title.split('\n'); - const teacher = titleParts[1] || ''; - const room = titleParts[2] ? titleParts[2].replace(/[()]/g, '') : ''; + apiData.forEach((event, index) => { + try { + const eventDate = new Date(event.start); + const dayIndex = weekDates.findIndex(date => + new Date(date.fullDate).toDateString() === eventDate.toDateString() + ); - lessons.push({ - startTime: startTimeStr, - endTime: endTimeStr, - subject: event.Tantargy || event.TantargyKategoria, - teacher: teacher, - originalTeacher: event.helyettesitoId ? teacher : '', - room: room, - day: dayIndex, - isSubstituted: !!event.helyettesitoId, - isCancelled: event.isElmaradt || false, - hasHomework: event.hasHaziFeladat || false, - testInfo: event.hasBejelentettSzamonkeres ? (event.Tema || 'Számonkérés') : '', - homeworkDetails: '', - isSpecialDay: false, - color: event.color - }); - } - }); - - return lessons; - } - - async function collectTimetableData() { - await helper.waitForElement('#Calendar'); - await helper.waitForElement('.modalBckgroundMain:not(.isOverlayActiv)'); - - const dates = Array.from(document.querySelectorAll('.fc-day-header')).map(header => { - const fullText = header.textContent.trim(); - const dateText = fullText.replace(/^(hétfő|kedd|szerda|csütörtök|péntek)/, '').trim(); - return { - date: fullText, - formattedDate: dateText - }; - }); - if (dates.length === 4) { - const wedDate = dates[2].formattedDate; - const [month, day] = wedDate.split(' '); - const dayNum = parseInt(day.replace('.', '')); - const thursdayDate = `${month} ${dayNum + 1}.`; - - dates.splice(3, 0, { - date: `csütörtök${thursdayDate}`, - formattedDate: thursdayDate - }); - } - const weekOptions = Array.from(document.querySelectorAll('#Calendar_tanevHetek_listbox li')); - - const kendoCombo = document.querySelector('#Calendar_tanevHetek')?.__kendoWidget; - let currentWeekIndex = 0; - - if (kendoCombo) { - const currentValue = kendoCombo.value(); - const selectedIndex = kendoCombo.selectedIndex; - - console.log('Kendo widget értékek:', { currentValue, selectedIndex, weekOptionsLength: weekOptions.length }); - - if (currentValue !== null && currentValue !== undefined && currentValue !== '') { - currentWeekIndex = parseInt(currentValue); - } else if (selectedIndex !== -1) { - currentWeekIndex = selectedIndex; - } else { - const today = new Date(); - let foundWeekIndex = -1; - - for (let i = 0; i < weekOptions.length; i++) { - const weekText = weekOptions[i].textContent.trim(); - console.log(`Vizsgált hét ${i}: ${weekText}`); + if (dayIndex === -1) { + return; + } + + if (event.oraType === 5) { + const lesson = { + startTime: LanguageManager.t('timetable.all_day'), + endTime: '', + subject: event.title || 'Különleges nap', + teacher: '', + originalTeacher: '', + room: '', + day: dayIndex, + isSubstituted: false, + isCancelled: false, + hasHomework: false, + testInfo: event.Tema || '', + homeworkDetails: '', + isSpecialDay: true, + color: event.color + }; + lessons.push(lesson); + } else if (event.oraType === 2 || event.oraType === 1 || event.oraType === 3 || event.oraType === 4) { + const startTime = new Date(event.start); + const endTime = new Date(event.end); + const startTimeStr = startTime.toLocaleTimeString('hu-HU', { hour: '2-digit', minute: '2-digit' }); + const endTimeStr = endTime.toLocaleTimeString('hu-HU', { hour: '2-digit', minute: '2-digit' }); + const titleParts = event.title ? event.title.split('\n') : []; + const teacher = titleParts[1] || ''; + const room = titleParts[2] ? titleParts[2].replace(/[()]/g, '') : ''; + const subject = event.Tantargy || event.TantargyKategoria || titleParts[0] || 'Ismeretlen tantárgy'; - let dateMatch = null; - let year, startMonth, startDay, endMonth, endDay; - - dateMatch = weekText.match(/(\d{4})\. (\w+) (\d{1,2})\. - (\w+) (\d{1,2})\. \((\d+)\. hét\)/); - if (dateMatch) { - [, year, startMonth, startDay, endMonth, endDay] = dateMatch; + if (startTimeStr && subject) { + const lesson = { + startTime: startTimeStr, + endTime: endTimeStr, + subject: subject, + teacher: teacher, + originalTeacher: event.helyettesitoId ? teacher : '', + room: room, + day: dayIndex, + isSubstituted: !!event.helyettesitoId, + isCancelled: event.isElmaradt || false, + hasHomework: event.hasHaziFeladat || false, + testInfo: event.hasBejelentettSzamonkeres ? (event.Tema || LanguageManager.t('timetable.test_indicator')) : '', + homeworkDetails: '', + isSpecialDay: false, + color: event.color + }; + lessons.push(lesson); } else { - dateMatch = weekText.match(/(\d{4})\.(\d{2})\.(\d{2})\. - (\d{4})\.(\d{2})\.(\d{2})\. \((\d+)\. hét\)/); - if (dateMatch) { - const [, startYear, startMonthNum, startDayNum, endYear, endMonthNum, endDayNum] = dateMatch; - year = startYear; - startMonth = parseInt(startMonthNum) - 1; - startDay = startDayNum; - endMonth = parseInt(endMonthNum) - 1; - endDay = endDayNum; - } else { - dateMatch = weekText.match(/(\d+)\. hét \((\d{4})\.(\d{2})\.(\d{2})\. - (\d{4})\.(\d{2})\.(\d{2})\.\)/); - if (dateMatch) { - const [, weekNum, startYear, startMonthNum, startDayNum, endYear, endMonthNum, endDayNum] = dateMatch; - year = startYear; - startMonth = parseInt(startMonthNum) - 1; - startDay = startDayNum; - endMonth = parseInt(endMonthNum) - 1; - endDay = endDayNum; - } - } } - - if (dateMatch) { - if (typeof startMonth === 'string') { - const monthNames = { - 'január': 0, 'február': 1, 'március': 2, 'április': 3, 'május': 4, 'június': 5, - 'július': 6, 'augusztus': 7, 'szeptember': 8, 'október': 9, 'november': 10, 'december': 11 - }; - - const startMonthNum = monthNames[startMonth.toLowerCase()]; - const endMonthNum = monthNames[endMonth.toLowerCase()]; - - if (startMonthNum !== undefined && endMonthNum !== undefined) { - startMonth = startMonthNum; - endMonth = endMonthNum; - } else { - continue; - } - } - - const weekStart = new Date(parseInt(year), startMonth, parseInt(startDay)); - const weekEnd = new Date(parseInt(year), endMonth, parseInt(endDay)); - - if (today >= weekStart && today <= weekEnd) { - foundWeekIndex = i; - console.log(`Megtalált jelenlegi hét: ${i}`); - break; - } - } - } - - if (foundWeekIndex !== -1) { - currentWeekIndex = foundWeekIndex; } else { - currentWeekIndex = Math.min(41, weekOptions.length - 1); - console.log(`Nem találtuk a jelenlegi hetet, fallback: ${currentWeekIndex}`); } - - kendoCombo.value(currentWeekIndex.toString()); - kendoCombo.trigger('change'); + } catch (error) { + console.error(`Hiba az API feldolgozása során (${index}):`, error, event); } - - console.log('Beállított currentWeekIndex:', currentWeekIndex); - } - const timetableData = { - schoolInfo: { - name: cookieManager.get('schoolName') || 'Iskola', - id: cookieManager.get('schoolCode') || '' - }, - userData: { - name: cookieManager.get('userName') || 'Felhasználó', - time: document.querySelector('.usermenu_timer')?.textContent?.trim() || '45:00' - }, - weekInfo: { - title: document.querySelector('.fc-center h2')?.textContent?.trim() || 'Hét', - options: Array.from(document.querySelectorAll('#Calendar_tanevHetek_listbox li')) - .map((li, i) => ({ - text: li.textContent.trim(), - value: i.toString(), - selected: i === currentWeekIndex - })) - }, - weekDates: dates, - lessons: [] - }; - - for (const event of document.querySelectorAll('.fc-event')) { - const timeEl = event.querySelector('.fc-time'); - const titleEl = event.querySelector('.fc-title'); - - if (timeEl && titleEl) { - const [startTime, endTime] = (timeEl.getAttribute('data-full') || timeEl.textContent || '').split(' - '); - const [fullSubject, teacher, room] = titleEl.innerHTML.split('
').map(str => str.trim()); - const subject = fullSubject.split('-')[0].trim(); - - let originalTeacher = ''; - if (teacher.startsWith('Helyettesítő:')) { - event.click(); - originalTeacher = await helper.waitForElement("#OraAdatokDetailTabStrip-1 > div > div:nth-child(3) > div:nth-child(2)"); - originalTeacher = originalTeacher.innerText; - document.querySelector("body > div.k-widget.k-window > div.k-window-titlebar.k-header > div > a:nth-child(2)").click(); - } - - timetableData.lessons.push({ - startTime, - endTime, - subject: subject || '', - teacher: teacher || '', - originalTeacher: originalTeacher || '', - room: (room || '').replace(/[()]/g, ''), - day: event.closest('td').cellIndex - 1, - isSubstituted: event.querySelector('.fc-bg2') !== null, - isCancelled: event.classList.contains('fc-textline-through'), - hasHomework: titleEl.querySelector('.hasCalendarIcon') !== null, - testInfo: event.getAttribute('data-tooltiptext') || '', - homeworkDetails: event.getAttribute('data-homework') || '' - }); - } - } - return timetableData; + }); + return lessons; } function generateTimeGrid(lessons, weekDates) { @@ -290,7 +124,13 @@ const timeB = helper.convertTimeToMinutes(b); return timeA - timeB; }); - const days = ['Hétfő', 'Kedd', 'Szerda', 'Csütörtök', 'Péntek']; + const days = [ + LanguageManager.t('timetable.monday'), + LanguageManager.t('timetable.tuesday'), + LanguageManager.t('timetable.wednesday'), + LanguageManager.t('timetable.thursday'), + LanguageManager.t('timetable.friday') + ]; return `
@@ -324,17 +164,17 @@
${lesson.teacher}
${lesson.room}
-
${lesson.isCancelled ? 'Elmarad' : lesson.startTime}
+
${lesson.isCancelled ? LanguageManager.t('timetable.cancelled') : lesson.startTime}
${lesson.hasHomework || lesson.testInfo ? `
${lesson.hasHomework ? ` - + assignment ` : ''} ${lesson.testInfo ? ` - + quiz ` : ''} @@ -362,31 +202,31 @@