56 Commits

Author SHA1 Message Date
Zan1456
eef39c28d2 removed unused 2025-06-13 14:49:40 +02:00
Zan1456
89266bc3c9 Fixed week select 2025-06-13 14:41:21 +02:00
Zan1456
5408a1e08a . 2025-06-13 14:02:54 +02:00
Zan1456
107e20f5c1 . 2025-06-13 13:26:21 +02:00
Zan1456
cb54d51b8c Added store logos 2025-06-10 22:58:14 +02:00
Zan1456
8eb7256f5b . 2025-06-10 22:54:27 +02:00
Zan
3510288181 Added firefox addonstore 2025-06-10 22:37:28 +02:00
Zan1456
8c9e1b215f version bump 2025-06-09 16:13:19 +02:00
Zan1456
e930706eb1 Fully working 2025-06-09 16:11:15 +02:00
Zan1456
5340c1dff0 Full rework 2025-06-09 16:10:15 +02:00
Zan1456
f3439a93d0 Merge branch 'main' of https://github.com/QwIT-Development/firka-extension 2025-06-09 13:46:42 +02:00
Zan1456
3e7b7089dd better scrolling 2025-06-09 13:33:44 +02:00
Anchietae
185f486a62 typo 2025-06-09 09:52:03 +02:00
Zan
048f249dc5 Update README.md 2025-06-08 23:29:44 +02:00
Zan1456
5d9ae6971b version dump 2025-06-08 23:24:06 +02:00
Zan1456
42bfa0912f . 2025-06-08 22:47:25 +02:00
Zan1456
0322b31146 Fixed user dropdown 2025-06-08 22:36:02 +02:00
Zan1456
52fc99ca99 navbar fixes 2025-06-08 22:24:13 +02:00
Zan1456
0a65ed3dda Removed logs 2025-06-08 22:23:25 +02:00
Zan1456
5a836947e1 rem cap 2025-06-08 21:42:58 +02:00
Zan1456
ebb291ee87 Title and icon 2025-06-08 21:42:51 +02:00
Zan1456
56dcef30e0 Load back the username at login 2025-06-08 21:32:37 +02:00
Zan1456
cfe6e0fccf search fix 2025-06-04 17:10:55 +02:00
Zan1456
bacf77b506 2fa support 2025-06-04 16:56:54 +02:00
Zan1456
3ee34b90e6 captcha 2025-06-04 16:44:42 +02:00
Zan1456
0e1f97eaaa search 2025-06-04 16:43:32 +02:00
Zan1456
699d2604c6 Update .gitignore 2025-06-04 16:40:14 +02:00
Zan1456
d8f1622b5b search 2025-06-04 16:40:10 +02:00
Zan1456
b69c9f3f6a search 2025-06-04 16:39:50 +02:00
Zan1456
ebf6aa8d61 Revert "typo"
This reverts commit ea5e01ce9c.
2025-06-04 16:38:18 +02:00
Zan1456
0048ebb932 hover fix 2025-06-04 16:35:06 +02:00
Zan1456
a55e40ecf2 rm 2025-06-04 16:30:29 +02:00
Zan1456
ea5e01ce9c typo 2025-06-04 16:29:32 +02:00
Zan1456
c0e738fd8b Merge branch 'main' of https://github.com/QwIT-Development/firka-extension 2025-06-04 16:29:12 +02:00
Zan
842588a0bb Merge pull request #11 from JeromeSchmied/releases-link
ci: add link checker
2025-04-22 15:16:55 +02:00
Jeromos Kovács
be2e7a192b Merge branch 'main' into releases-link 2025-04-21 17:39:09 +02:00
Zan
60ad73ffae Merge pull request #13 from MrS-1302/patch-2
Update build.yml
2025-04-21 09:23:24 +02:00
Mr.S
3a9bbbcb2a Update build.yml 2025-04-03 21:52:39 +02:00
Jeromos Kovács
afb7a3d79b ci: add link checker 2025-03-26 09:40:21 +01:00
Zan
95d50e9297 Merge pull request #12 from JeromeSchmied/firefox-install-docs
add installation instructions for firefox-based browsers
2025-03-25 21:56:33 +01:00
Zan
e88566369a Update README.md 2025-03-25 21:56:04 +01:00
Jeromos Kovács
b9985ccf09 docs(installation): add instructions for firefox-based browsers 2025-03-25 17:46:29 +01:00
Zan
88706017f9 Merge branch 'main' of https://github.com/QwIT-Development/firka-extension 2025-03-25 17:04:44 +01:00
Zan
0aef3950e7 Merge pull request #10 from JeromeSchmied/main
docs: GNU GPLv3 is **not** GNU AGPLv3
2025-03-25 17:01:54 +01:00
Zan
db74cb1a37 Merge pull request #9 from balint1414/main
Hibás fiók linkek szerkesztése
2025-03-25 16:57:05 +01:00
Jeromos Kovács
0d71510c7f docs: GNU GPLv3 is **not** GNU AGPLv3 2025-03-25 16:18:02 +01:00
ee26b6d966 Hibás fiók linkek szerkesztése 2025-03-22 17:12:28 +01:00
Zan
731511d28a gh token fix needed 2025-03-17 21:43:46 +01:00
Zan
79b09cb1ab Automatikus buildelés 2025-03-17 20:49:50 +01:00
Zan
1730c108df Merge pull request #5 from MrS-1302/remove-duplications
duplication fix in css files is not started
removed some enter
2025-03-17 20:34:46 +01:00
MrS-1302
f990e2e899 remove some duplication & faster loading page
duplication fix in css files is not started
removed some enter
2025-03-17 20:03:46 +01:00
Zan
2feef43ef2 Merge pull request #2 from MrS-1302/dev
chart
- fixed incorrect behavior when value is NaN
- the height is don't same with semester grades
subject cards
- i added max height for better appearance
hotfix for grades-overview
- made it responsive
2025-03-17 16:26:41 +01:00
MrS-1302
dfc0c362c7 you can see original teacher too when a substitute is assigned 2025-03-14 22:03:51 +01:00
MrS-1302
367547f67f fix multiple classes in the same hour problem 2025-03-14 19:47:06 +01:00
MrS-1302
003db8569e Fixes for grades page
- chart
   - fixed incorrect behavior when value is NaN
   - the height is don't same with semester grades
- subject cards
  - i added max height for better appearance
- hotfix for grades-overview
  - made it responsive
2025-03-14 18:29:54 +01:00
Zan
4eee8fa7e6 Update README.md 2025-03-14 15:06:41 +01:00
51 changed files with 5697 additions and 3325 deletions

71
.github/workflows/build.yml vendored Normal file
View File

@@ -0,0 +1,71 @@
name: Package and Release Extension
on:
workflow_dispatch:
push:
branches:
- test
jobs:
build-and-release:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Get current date
id: date
run: |
echo "DATE=$(date +'%Y.%m.%d. %H:%M')" >> $GITHUB_ENV
echo "DATE_FOR_ZIP=$(date +'%Y%m%d-%H%M')" >> $GITHUB_ENV
- name: Create ZIP file
run: |
zip -r "pre-firxa-${{ env.DATE_FOR_ZIP }}.zip" . -x "*.git*" "*.github*" "*.idea*"
- name: Delete previous pre-release
uses: dev-drprasad/delete-tag-and-release@v0.2.1
with:
delete_release: true
tag_name: pre-release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
continue-on-error: true
- name: Create new pre-release
id: create_release
uses: softprops/action-gh-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: pre-release
release_name: Fejlesztői build
body: |
Ez egy kiadás előtti build, amely minden egyes commit után frissül!
A build automatikusan készült ekkor: ${{ env.DATE }}
draft: false
prerelease: true
- name: Upload ZIP to release
if: steps.create_release.outputs.upload_url != ''
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./pre-firxa-${{ env.DATE_FOR_ZIP }}.zip
asset_name: pre-firxa-${{ env.DATE_FOR_ZIP }}.zip
asset_content_type: application/zip
check-links:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Link Checker
id: lychee
uses: lycheeverse/lychee-action@v2
with:
fail: false

2
.gitignore vendored
View File

@@ -7,3 +7,5 @@ META-INF/cose.sig
META-INF/manifest.mf
META-INF/mozilla.sf
dashboard/KRÉTA Iskolai Alaprendszer.html
*.xml
*.xml

View File

@@ -1,9 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

6
.idea/misc.xml generated
View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

8
.idea/modules.xml generated
View File

@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/Firkaextension.iml" filepath="$PROJECT_DIR$/.idea/Firkaextension.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml generated
View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

45
.idea/workspace.xml generated
View File

@@ -1,45 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AutoImportSettings">
<option name="autoReloadType" value="NONE" />
</component>
<component name="ChangeListManager">
<list default="true" id="2540850d-5586-4ef8-a1c0-838ec2731d44" name="Changes" comment="" />
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="ClangdSettings">
<option name="formatViaClangd" value="false" />
</component>
<component name="ProjectColorInfo"><![CDATA[{
"associatedIndex": 0
}]]></component>
<component name="ProjectId" id="2uHD20KrXgQVbKPvZ8rqEw44Sjy" />
<component name="ProjectViewState">
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent"><![CDATA[{
"keyToString": {
"RunOnceActivity.ShowReadmeOnStart": "true",
"RunOnceActivity.cidr.known.project.marker": "true",
"RunOnceActivity.readMode.enableVisualFormatting": "true",
"cf.first.check.clang-format": "false",
"cidr.known.project.marker": "true",
"kotlin-language-version-configured": "true"
}
}]]></component>
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="2540850d-5586-4ef8-a1c0-838ec2731d44" name="Changes" comment="" />
<created>1741896394934</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1741896394934</updated>
</task>
<servers />
</component>
</project>

View File

@@ -8,14 +8,23 @@
</p>
<p align="center">
<a href="https://github.com/Zan1456/Firkaextension/releases">
<img src="https://img.shields.io/github/downloads-pre/Zan1456/Firkaextension/latest/total?style=for-the-badge&logo=github&logoColor=EAF7CC&label=Let%C3%B6lt%C3%A9sek&labelColor=141905&color=A7DC22" alt="Downloads">
<a href="https://github.com/QwIT-Development/firka-extension/releases">
<img src="https://img.shields.io/github/downloads-pre/QwIT-Development/firka-extension/latest/total?style=for-the-badge&logo=github&logoColor=EAF7CC&label=Let%C3%B6lt%C3%A9sek&labelColor=141905&color=A7DC22" alt="Downloads">
</a>
<a href="https://discord.gg/6DvjyPAw2T">
<img src="https://img.shields.io/discord/1111649116020285532?style=for-the-badge&logo=discord&logoColor=EAF7CC&label=Discord&labelColor=0D1202&color=A7DC22" alt="Discord">
</a>
<a href="https://github.com/Zan1456/Firkaextension/blob/main/LICENSE">
<img src="https://img.shields.io/github/license/Zan1456/Firkaextension?style=for-the-badge&logo=discord&logoColor=EAF7CC&label=Discord&labelColor=0D1202&color=A7DC22" alt="License">
<a href="https://github.com/QwIT-Development/firka-extension/blob/main/LICENSE">
<img src="https://img.shields.io/github/license/QwIT-Development/firka-extension?style=for-the-badge&logo=discord&logoColor=EAF7CC&label=Discord&labelColor=0D1202&color=A7DC22" alt="License">
</a>
</p>
<p align="center">
<a href="https://chromewebstore.google.com/detail/firxa/emafoaifbfppcccgfmpcoheonhjnpldj?hl=hu">
<img src="https://github.com/QwIT-Development/firka-extension/blob/main/images/chrome.png?raw=true" alt="Elérhető a Chrome Web Store-on" width="200">
</a>
<a href="https://addons.mozilla.org/hu/firefox/addon/firxa/">
<img src="https://github.com/QwIT-Development/firka-extension/blob/main/images/firefox.png?raw=true" alt="Elérhető a Firefox add-ons oldalon" width="200">
</a>
</p>
@@ -34,13 +43,7 @@
## 🚀 Telepítés
1. Töltsd le a legfrissebb verziót a [Releases](https://github.com/Zan1456/Firkaextension/releases) oldalról
2. Csomagold ki a letöltött fájlt
3. Chrome böngészőben navigálj a `chrome://extensions` oldalra
4. Kapcsold be a "Fejlesztői mód"-ot a jobb felső sarokban
5. Kattints a "Kicsomagolt bővítmény betöltése" gombra
6. Válaszd ki a kicsomagolt mappát
7. Kész! A bővítmény automatikusan működésbe lép, amikor megnyitod az e-KRÉTA oldalt
1. Töltsd le a legfrissebb verziót a fenti gombok segítségével a böngésződnek megfelelően.
## ⚙️ Beállítások
@@ -61,12 +64,13 @@ A bővítmény jelenleg az alábbi e-KRÉTA oldalakat támogatja:
- Hiányzások
- Házi feladatok
- Jegyek
- Intézménykereső
## 👥 Csapat
- **[Zan1456](https://github.com/Zan1456)** - Vezető Fejlesztő
- **[BalazsManus](https://github.com/BalazsManus)** - Fejlesztő
- **[Xou](https://github.com/Xou)** - Designer
- **[BalazsManus](https://github.com/olajcsere)** - Fejlesztő
- **[Xou](https://yoursit.ee/xou)** - Designer
## 🤝 Közreműködés
@@ -80,7 +84,7 @@ A bővítmény jelenleg az alábbi e-KRÉTA oldalakat támogatja:
## 📝 Licensz
A projekt [GNU General Public License v3.0](LICENSE) alatt jelent meg. További információért lásd a LICENSE fájlt.
A projekt [GNU Affero General Public License v3.0](LICENSE) alatt jelent meg. További információért lásd a LICENSE fájlt.
## 💬 Kapcsolat

View File

@@ -42,10 +42,10 @@ body {
@media (max-width: 768px) {
.kreta-header {
grid-template-columns: 1fr auto;
grid-template-columns: 1fr auto auto;
grid-template-areas:
"school user"
"nav nav";
"school toggle user"
"nav nav nav";
padding: 1rem;
gap: 0.5rem;
}
@@ -110,77 +110,6 @@ body {
}
}
.kreta-nav {
padding: 0 clamp(0.5rem, 3vw, 1.5rem);
position: sticky;
top: 0;
z-index: 100;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
scrollbar-width: none;
-ms-overflow-style: none;
display: flex;
justify-content: center;
}
@media (max-width: 768px) {
.kreta-nav {
grid-area: nav;
padding: 0;
margin-top: 0.5rem;
}
}
.nav-links {
display: flex;
gap: clamp(0.5rem, 2vw, 1rem);
padding: 0.25rem;
justify-content: center;
}
@media (max-width: 768px) {
.nav-links {
justify-content: flex-start;
width: 100%;
gap: 0.25rem;
}
}
.nav-links a {
color: var(--text-secondary);
text-decoration: none;
padding: clamp(0.5rem, 1.5vw, 1rem) 0.5rem;
font-weight: 500;
white-space: nowrap;
transition: all 0.2s ease;
display: flex;
align-items: center;
gap: 0.5rem;
border-radius: 8px;
}
@media (max-width: 768px) {
.nav-links a {
padding: 0.5rem;
font-size: 13px;
}
.nav-links a .material-icons-round {
font-size: 20px;
}
}
.nav-links a:hover {
color: var(--text-primary);
background-color: var(--card-card);
}
.nav-links a.active {
color: var(--accent-accent);
}
.user-profile {
position: relative;
justify-self: flex-end;
@@ -212,19 +141,6 @@ body {
text-align: right;
}
.user-name {
display: block;
color: var(--text-primary);
font-size: 16px;
font-weight: 500;
}
.user-time {
display: block;
color: var(--text-secondary);
font-size: 14px;
}
.user-dropdown {
position: absolute;
top: 100%;

View File

@@ -1,65 +1,14 @@
function getCookie(name) {
const cookieName = `${name}=`;
const decodedCookie = decodeURIComponent(document.cookie);
const cookieArray = decodedCookie.split(';');
for(let i = 0; i < cookieArray.length; i++) {
let cookie = cookieArray[i];
while (cookie.charAt(0) === ' ') {
cookie = cookie.substring(1);
}
if (cookie.indexOf(cookieName) === 0) {
return cookie.substring(cookieName.length, cookie.length);
}
}
return null;
}
function shortenSchoolName(name, maxLength = 50) {
if (!name) return '';
if (name.length <= maxLength) return name;
const parts = name.split(' - ');
if (parts.length === 2) {
const [code, fullName] = parts;
if (fullName.length > maxLength - code.length - 3) {
return `${code} - ${fullName.substring(0, maxLength - code.length - 6)}...`;
}
}
return name.substring(0, maxLength - 3) + '...';
}
async function waitForElement(selector) {
return new Promise(resolve => {
if (document.querySelector(selector)) {
return resolve(document.querySelector(selector));
}
const observer = new MutationObserver(mutations => {
if (document.querySelector(selector)) {
observer.disconnect();
resolve(document.querySelector(selector));
}
});
observer.observe(document.body, {
childList: true,
subtree: true
});
});
}
async function collectAbsencesData() {
await waitForElement('#HianyzasGrid');
await helper.waitForElement('#HianyzasGrid');
await new Promise(resolve => setTimeout(resolve, 1000));
const basicData = {
schoolInfo: {
name: getCookie('schoolName') || 'Iskola',
id: getCookie('schoolCode') || ''
name: cookieManager.get('schoolName') || 'Iskola',
id: cookieManager.get('schoolCode') || ''
},
userData: {
name: getCookie('userName') || 'Felhasználó',
name: cookieManager.get('userName') || 'Felhasználó',
time: document.querySelector('.usermenu_timer')?.textContent?.trim() || '45:00'
}
};
@@ -95,119 +44,33 @@ async function collectAbsencesData() {
return { basicData, absences, groupedAbsences };
}
function showLoadingScreen() {
const loadingHTML = `
<div class="loading-overlay">
<div class="loading-container">
<img src="https://i.imgur.com/JE3LzRc.gif" alt="Firka" class="loading-logo"><!--logó csere-->
<div class="loading-text">Betöltés alatt...</div>
<p class="loading-text2">Kis türelmet</p>
</div>
</div>
`;
document.body.insertAdjacentHTML('beforeend', loadingHTML);
}
function hideLoadingScreen() {
const loadingOverlay = document.querySelector('.loading-overlay');
if (loadingOverlay) {
loadingOverlay.style.opacity = '0';
loadingOverlay.style.transition = 'opacity 0.3s ease';
setTimeout(() => loadingOverlay.remove(), 300);
}
}
async function transformAbsencesPage() {
showLoadingScreen();
const { basicData, absences, groupedAbsences } = await collectAbsencesData();
const schoolNameFull = `${basicData.schoolInfo.id} - ${basicData.schoolInfo.name}`;
const shortenedSchoolName = shortenSchoolName(schoolNameFull);
document.body.innerHTML = `
<div class="kreta-container">
<header class="kreta-header">
<div class="school-info">
<p class="logo-text">
<img src="${chrome.runtime.getURL('images/firka_logo.png')}" alt="Firka" class="logo">
Firka
</p>
<div class="school-details" title="${schoolNameFull}">
${shortenedSchoolName}
</div>
</div>
<nav class="kreta-nav">
<div class="nav-links">
<a href="/Intezmeny/Faliujsag" data-page="dashboard" class="nav-item">
<img src="${chrome.runtime.getURL('icons/dashboard-inactive.svg')}" alt="Kezdőlap">
Kezdőlap
</a>
<a href="/TanuloErtekeles/Osztalyzatok" data-page="grades" class="nav-item">
<img src="${chrome.runtime.getURL('icons/grades-inactive.svg')}" alt="Jegyek">
Jegyek
</a>
<a href="/Orarend/InformaciokOrarend" data-page="timetable" class="nav-item">
<img src="${chrome.runtime.getURL('icons/timetable-inactive.svg')}" alt="Órarend">
Órarend
</a>
<a href="/Hianyzas/Hianyzasok" data-page="absences" class="nav-item active">
<img src="${chrome.runtime.getURL('icons/absences-active.svg')}" alt="Mulasztások">
Mulasztások
</a>
<a href="/Tanulo/TanuloHaziFeladat" data-page="other" class="nav-item">
<img src="${chrome.runtime.getURL('icons/others.svg')}" alt="Egyéb">
Egyéb
</a>
</div>
</nav>
<div class="user-profile">
<button class="user-dropdown-btn">
<div class="user-info">
<span class="user-name">${basicData.userData.name}</span>
<span class="nav-logout-timer" id="logoutTimer">${basicData.userData.time}</span>
</div>
</button>
<div class="user-dropdown">
<a href="/Adminisztracio/Profil" data-page="profile" class="dropdown-item">
<img src="${chrome.runtime.getURL('icons/profile.svg')}" alt="Profil">
Profil
</a>
<a href="#" class="dropdown-item" id="settingsBtn">
<img src="${chrome.runtime.getURL('icons/settings.svg')}" alt="Beállítások">
Beállítások
</a>
<a href="/Home/Logout" data-page="logout" class="dropdown-item">
<img src="${chrome.runtime.getURL('icons/logout.svg')}" alt="Kijelentkezés">
Kijelentkezés
</a>
</div>
</div>
</header>
${createTemplate.header()}
<main class="kreta-main">
<div class="filter-card">
<div class="filter-header">
<h2>Szűrés</h2>
<h2>${LanguageManager.t('absences.filter_title')}</h2>
</div>
<div class="filter-content">
<div class="filter-group">
<label>
<span class="material-icons-round">date_range</span>
Dátum
${LanguageManager.t('absences.date')}
</label>
<input type="date" id="dateFilter" class="filter-input" disabled>
</div>
<div class="filter-group">
<label>
<span class="material-icons-round">school</span>
Tantárgy
${LanguageManager.t('absences.subject')}
</label>
<select id="subjectFilter" class="filter-input">
<option value="">Minden tantárgy</option>
<option value="">${LanguageManager.t('absences.all_subjects')}</option>
${[...new Set(absences.map(a => a.subject))].sort().map(subject =>
`<option value="${subject}">${subject}</option>`
).join('')}
@@ -216,13 +79,13 @@ async function transformAbsencesPage() {
<div class="filter-group">
<label>
<span class="material-icons-round">check_circle</span>
Igazolás
${LanguageManager.t('absences.justification')}
</label>
<select id="justificationFilter" class="filter-input">
<option value="">Mindegy</option>
<option value="justified">Igazolt</option>
<option value="unjustified">Igazolatlan</option>
<option value="pending">Igazolásra vár</option>
<option value="">${LanguageManager.t('absences.all_types')}</option>
<option value="justified">${LanguageManager.t('absences.justified')}</option>
<option value="unjustified">${LanguageManager.t('absences.unjustified')}</option>
<option value="pending">${LanguageManager.t('absences.pending')}</option>
</select>
</div>
</div>
@@ -234,7 +97,7 @@ async function transformAbsencesPage() {
<div class="absence-date">
<span class="material-icons-round">event</span>
${date}
<span class="absence-count">${dayAbsences.length} óra</span>
<span class="absence-count">${dayAbsences.length} ${LanguageManager.t('absences.hours')}</span>
</div>
<div class="absence-list">
${dayAbsences.map(absence => `
@@ -243,7 +106,7 @@ async function transformAbsencesPage() {
data-justified="${absence.justified}">
<div class="absence-time">
<span class="material-icons-round">schedule</span>
${absence.lesson}. óra
${absence.lesson}. ${LanguageManager.t('absences.lesson').toLowerCase()}
</div>
<div class="absence-details">
<div class="absence-subject">${absence.subject}</div>
@@ -251,10 +114,10 @@ async function transformAbsencesPage() {
</div>
<div class="absence-status ${absence.justificationStatus}">
${absence.justificationStatus === 'justified' ?
`Igazolt <span class="material-icons-round">check_circle</span>` :
`${LanguageManager.t('absences.justified')} <span class="material-icons-round">check_circle</span>` :
absence.justificationStatus === 'unjustified' ?
`Igazolatlan <span class="material-icons-round">cancel</span>` :
`Igazolásra vár <span class="material-icons-round">pending</span>`}
`${LanguageManager.t('absences.unjustified')} <span class="material-icons-round">cancel</span>` :
`${LanguageManager.t('absences.pending')} <span class="material-icons-round">pending</span>`}
</div>
</div>
`).join('')}
@@ -266,69 +129,17 @@ async function transformAbsencesPage() {
</div>
`;
const links = [
{ rel: 'preconnect', href: 'https://fonts.googleapis.com' },
{ rel: 'preconnect', href: 'https://fonts.gstatic.com', crossorigin: true },
{ rel: 'stylesheet', href: 'https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&display=swap' },
{ rel: 'stylesheet', href: 'https://fonts.googleapis.com/icon?family=Material+Icons+Round' }
];
links.forEach(link => {
const linkElement = document.createElement('link');
Object.entries(link).forEach(([key, value]) => {
linkElement[key] = value;
});
document.head.appendChild(linkElement);
});
createTemplate.importFonts();
setupUserDropdown();
setupMobileNavigation();
setupEventListeners();
setupFilters();
hideLoadingScreen();
loadingScreen.hide();
}
function setupEventListeners(data) {
const userBtn = document.querySelector('.user-dropdown-btn');
const userDropdown = document.querySelector('.user-dropdown');
userBtn?.addEventListener('click', (e) => {
e.stopPropagation();
userDropdown.classList.toggle('show');
});
document.addEventListener('click', () => {
userDropdown?.classList.remove('show');
});
const timerEl = document.getElementById('logoutTimer');
if (timerEl) {
const startTime = parseInt(timerEl.textContent?.match(/\d+/)?.[0] || "30");
let timeLeft = startTime * 60;
const updateTimer = () => {
const minutes = Math.floor(timeLeft / 60);
const seconds = timeLeft % 60;
timerEl.textContent = `${minutes}:${seconds.toString().padStart(2, '0')}`;
if (timeLeft <= 0) {
window.location.href = '/Home/Logout';
} else {
timeLeft--;
}
};
updateTimer();
setInterval(updateTimer, 1000);
}
document.getElementById('settingsBtn')?.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
const url = chrome.runtime.getURL('settings/index.html');
window.open(url, '_blank', 'width=400,height=600');
});
function setupEventListeners() {
}
function setupFilters() {
@@ -386,16 +197,6 @@ function setupFilters() {
let showGroup = true;
// if (selectedDate && dateFilterValue) {
// // Compare year, month, and day to ignore time
// showGroup = groupDate.getFullYear() === selectedDate.getFullYear() &&
// groupDate.getMonth() === selectedDate.getMonth() &&
// groupDate.getDate() === selectedDate.getDate();
//
// console.log(`Comparing dates: ${groupDate.toDateString()} vs ${selectedDate.toDateString()}, match: ${showGroup}`);
// }
const absenceItems = group.querySelectorAll('.absence-item');
let visibleItems = 0;
@@ -422,15 +223,9 @@ function setupFilters() {
};
// if (!filters.dateFilter.value) {
// const today = new Date();
// filters.dateFilter.value = today.toISOString().split('T')[0]; // Set date to today by default
// }
Object.values(filters).forEach(filter => {
try {
if (filter && filter !== filters.dateFilter) { // Don't add event listener to dateFilter
if (filter && filter !== filters.dateFilter) {
filter.addEventListener('change', filterAbsences);
}
} catch (err) {
@@ -457,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);
});
}

View File

@@ -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;
@@ -120,6 +283,7 @@ body {
.grade-3 {color: var(--grades-3); background-color: var(--grades-background-3);}
.grade-4 {color: var(--grades-4); background-color: var(--grades-background-4);}
.grade-5 {color: var(--grades-5); background-color: var(--grades-background-5);}
.grade-Sz {color: var(--grades-3); background-color: var(--grades-background-3);}
@keyframes fadeIn {
from {
@@ -179,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;
@@ -211,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 {

View File

@@ -1,52 +1,49 @@
const utils = {
shortenSchoolName(name, maxLength = 50) {
if (!name || name.length <= maxLength) return name || '';
const [code, fullName] = name.split(' - ');
if (fullName) {
const maxFullNameLength = maxLength - code.length - 3;
if (fullName.length > maxFullNameLength) {
return `${code} - ${fullName.substring(0, maxFullNameLength)}...`;
}
}
return `${name.substring(0, maxLength - 3)}...`;
},
const DashboardUtils = {
formatGradeValue(value) {
return value?.trim() || '';
const trimmedValue = value?.trim() || '';
if (trimmedValue.toLowerCase() === 'szöveges') {
return 'Sz';
}
return trimmedValue;
},
parseDate(dateStr) {
return dateStr?.trim() || '';
},
formatHungarianDate(dateStr) {
if (!dateStr) return '';
const dateParts = dateStr.trim().split('.');
if (dateParts.length < 3) return dateStr;
const month = parseInt(dateParts[1], 10);
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: [],
@@ -55,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;
@@ -107,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;
@@ -122,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;
@@ -138,309 +158,293 @@ 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,
schoolInfo: {
name: cookies.getCookie(COOKIE_KEYS.SCHOOL_NAME) || DEFAULT_VALUES.SCHOOL,
id: cookies.getCookie(COOKIE_KEYS.SCHOOL_CODE) || ''
name: cookieManager.get(COOKIE_KEYS.SCHOOL_NAME) || DEFAULT_VALUES.SCHOOL,
id: cookieManager.get(COOKIE_KEYS.SCHOOL_CODE) || ''
},
userData: {
name: cookies.getCookie(COOKIE_KEYS.USER_NAME) || DEFAULT_VALUES.USER,
name: cookieManager.get(COOKIE_KEYS.USER_NAME) || DEFAULT_VALUES.USER,
time: document.querySelector('.usermenu_timer')?.textContent?.trim() || DEFAULT_VALUES.TIMER
}
};
this.schoolNameFull = `${this.data.schoolInfo.id} - ${this.data.schoolInfo.name}`;
this.shortenedSchoolName = utils.shortenSchoolName(this.schoolNameFull);
this.shortenedSchoolName = helper.shortenSchoolName(this.schoolNameFull);
}
static generateHeaderHTML(data, schoolNameFull, shortenedSchoolName) {
return `
<header class="kreta-header">
<div class="school-info">
<p class="logo-text">
<img src="${chrome.runtime.getURL('images/firka_logo.png')}" alt="Firka" class="logo">
Firka
</p>
<div class="school-details" title="${schoolNameFull}">
${shortenedSchoolName}
</div>
</div>
<nav class="kreta-nav">
<div class="nav-links">
<a href="/Intezmeny/Faliujsag" data-page="dashboard" class="nav-item active">
<img src="${chrome.runtime.getURL('icons/dashboard-active.svg')}" alt="Kezdőlap">
Kezdőlap
</a>
<a href="/TanuloErtekeles/Osztalyzatok" data-page="grades" class="nav-item">
<img src="${chrome.runtime.getURL('icons/grades-inactive.svg')}" alt="Jegyek">
Jegyek
</a>
<a href="/Orarend/InformaciokOrarend" data-page="timetable" class="nav-item">
<img src="${chrome.runtime.getURL('icons/timetable-inactive.svg')}" alt="Órarend">
Órarend
</a>
<a href="/Hianyzas/Hianyzasok" data-page="absences" class="nav-item">
<img src="${chrome.runtime.getURL('icons/absences-inactive.svg')}" alt="Mulasztások">
Mulasztások
</a>
<a href="/Tanulo/TanuloHaziFeladat" data-page="other" class="nav-item">
<img src="${chrome.runtime.getURL('icons/others.svg')}" alt="Egyéb">
Egyéb
</a>
</div>
</nav>
<div class="user-profile">
<button class="user-dropdown-btn">
<div class="user-info">
<span class="user-name">${data.userData.name}</span>
<span class="nav-logout-timer" id="logoutTimer">${data.userData.time}</span>
</div>
</button>
<div class="user-dropdown">
<a href="/Adminisztracio/Profil" data-page="profile" class="dropdown-item">
<img src="${chrome.runtime.getURL('icons/profile.svg')}" alt="Profil">
Profil
</a>
<a href="#" class="dropdown-item" id="settingsBtn">
<img src="${chrome.runtime.getURL('icons/settings.svg')}" alt="Beállítások">
Beállítások
</a>
<a href="/Home/Logout" data-page="logout" class="dropdown-item">
<img src="${chrome.runtime.getURL('icons/logout.svg')}" alt="Kijelentkezés">
Kijelentkezés
</a>
</div>
</div>
</header>
`;
}
generateMainContentHTML() {
generateMainContent() {
return `
<main class="kreta-main">
<div class="grid-container">
${this.generateGradeCard()}
${this.generateAbsenceCard()}
${this.generateNoteCard()}
${this.generateExamCard()}
${this.generateNewsCard()}
${this.createGradeCard()}
${this.createAbsenceCard()}
${this.createNoteCard()}
${this.createExamCard()}
${this.createNewsCard()}
</div>
</main>
`;
}
generateNewsCard() {
createNewsCard() {
const newsItems = this.data.news.map(news => `
<div class="news-item">
<div class="news-header">
<div class="news-date">${news.date}</div>
${news.author ? `<div class="news-author">${news.author}</div>` : ''}
</div>
<div class="news-details">
<h3 class="news-title">${news.title}</h3>
<div class="news-content">${news.content}</div>
</div>
</div>
`).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 => `
<div class="grade-item">
<div class="grade-row">
<div class="grade grade-${grade.value}">${grade.value}</div>
<div class="grade-details">
<div class="subject-name">${grade.subject}</div>
<div class="grade-type">${grade.type}</div>
<div class="widget-item news-item">
<div class="widget-row">
<div class="widget-details news-details">
<div class="widget-title news-title">${news.title}</div>
<div class="widget-content news-content">${news.content}</div>
</div>
<div class="widget-meta">
${news.date ? `<div class="widget-date news-date">${news.date}</div>` : ''}
${news.author ? `<div class="widget-author news-author">${news.author}</div>` : ''}
</div>
${grade.date ? `<div class="grade-date">${grade.date}</div>` : ''}
</div>
</div>
`).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 => `
<div class="widget-item grade-item">
<div class="widget-row grade-row">
<div class="grade grade-${grade.value}">${grade.value}</div>
<div class="widget-details grade-details">
<div class="widget-title subject-name">${grade.subject}</div>
<div class="grade-type-with-date">
<div class="widget-subtitle grade-type">${grade.type}</div>
${grade.dateInSubject || grade.date ? `<div class="widget-subtitle grade-date">${grade.dateInSubject || grade.date}</div>` : ''}
</div>
</div>
</div>
</div>
`).join('');
return this.createCard(LanguageManager.t('dashboard.grades'), gradeItems, '/TanuloErtekeles/Osztalyzatok', LanguageManager.t('dashboard.all_grades'));
}
createAbsenceCard() {
const absenceItems = this.data.absences.map(absence => `
<div class="absence-item">
<div class="absence-details">
<div class="absence-type">${absence.type}</div>
<div class="absence-date">${absence.date}</div>
<div class="widget-item absence-item">
<div class="widget-row">
<div class="widget-details absence-details">
<div class="widget-title absence-type">${absence.type}</div>
<div class="widget-subtitle absence-date">${absence.date}</div>
</div>
${absence.day ? `<div class="widget-date">${absence.day}</div>` : ''}
</div>
</div>
`).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 => `
<div class="note-item">
<div class="note-details">
<div class="note-title">${note.title}</div>
<div class="note-date">${note.date}</div>
<div class="widget-item note-item">
<div class="widget-row">
<div class="widget-details note-details">
<div class="widget-title note-title">${note.title}</div>
<div class="widget-subtitle note-date">${note.date}</div>
</div>
${note.author ? `<div class="widget-author note-author">${note.author}</div>` : ''}
</div>
</div>
`).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 => `
<div class="exam-item">
<div class="exam-details">
<div class="exam-subject">${exam.subject}</div>
<div class="exam-date">${utils.formatHungarianDate(exam.date)}</div>
<div class="widget-item exam-item">
<div class="widget-row">
<div class="widget-details exam-details">
<div class="widget-title exam-subject">${exam.subject}</div>
<div class="widget-subtitle exam-type">${exam.type || ''}</div>
</div>
<div class="widget-date exam-date">${DashboardUtils.formatHungarianDate(exam.date)}</div>
</div>
</div>
`).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 `
<div class="card">
<h2>${title}</h2>
<div class="card-content">
${content || `Jelenleg ez egy nem támogatott funkció`}
<a href="${linkHref}" class="more-link">
<div class="widget-card card">
<div class="widget-header">
<h2 class="widget-card-title">${title}</h2>
</div>
<div class="widget-content card-content">
${content || `<div class="widget-empty">${LanguageManager.t('dashboard.not_supported')}</div>`}
</div>
<div class="widget-footer">
<a href="${linkHref}" class="widget-link more-link">
${linkText}
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="17" viewBox="0 0 16 17" fill="none">
<path d="M11.2997 5.19947L5.64282 5.19947M11.2997 5.19947L11.2997 10.8563M11.2997 5.19947L4.70001 11.7991" stroke="var(--accent-accent)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</svg>
</a>
</div>
</div>
`;
}
render() {
document.body.innerHTML = `
<div class="kreta-container">
${DashboardUI.generateHeaderHTML(this.data, this.schoolNameFull, this.shortenedSchoolName)}
${this.generateMainContentHTML()}
${createTemplate.header()}
${this.generateMainContent()}
</div>
`;
setupUserDropdown();
setupLogoutTimer();
}
}
function setupLogoutTimer() {
const timerElement = document.querySelector('.nav-logout-timer');
if (!timerElement) return;
const timeString = timerElement.textContent;
const startTime = parseInt(timeString?.match(/\d+/)?.[0] || "45");
let timeLeft = startTime * 60;
const updateTimer = () => {
const minutes = Math.floor(timeLeft / 60);
const seconds = timeLeft % 60;
timerElement.textContent = `${minutes}:${seconds.toString().padStart(2, '0')}`;
if (timeLeft <= 0) {
window.location.href = '/Home/Logout';
}
timeLeft--;
};
updateTimer();
setInterval(updateTimer, 1000);
}
class FontLoader {
static loadFonts() {
const links = [
{ rel: 'preconnect', href: 'https://fonts.googleapis.com' },
{ rel: 'preconnect', href: 'https://fonts.gstatic.com', crossorigin: true },
{ rel: 'stylesheet', href: 'https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&display=swap' },
{ rel: 'stylesheet', href: 'https://fonts.googleapis.com/icon?family=Material+Icons+Round' }
];
links.forEach(link => {
const linkElement = document.createElement('link');
Object.entries(link).forEach(([key, value]) => {
linkElement[key] = value;
});
document.head.appendChild(linkElement);
});
}
}
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();
FontLoader.loadFonts();
const ui = new DashboardUI(dashboardData);
ui.render();
const dataManager = new DashboardDataManager();
const dashboardData = await dataManager.extractAllData();
createTemplate.importFonts();
const renderer = new DashboardRenderer(dashboardData);
renderer.render();
} catch (error) {
console.error('Error initializing dashboard:', error);
}
}
}
new DashboardApp();
new DashboardApplication();

View File

@@ -1,202 +1,176 @@
:root {
--primary: #050B15;
--secondary: #3F444F;
--tertiary: #1C469A;
--icon: #0A2456;
--accent: #3673EE;
--bg: #DAE4F7;
--card: #EDF3FF;
--cardsec: #FBFCFF;
--error: #E32D2D;
--error-bg: rgba(227, 45, 45, 0.05);
}
:root[data-theme="dark"] {
--primary: #EBF1FD;
--secondary: #CFD8E9;
--tertiary: #AEC8FC;
--icon: #BAD1FF;
--accent: #3673ed;
--bg: #070A0E;
--card: #0F131B;
--cardsec: #131822;
--error: #FF4444;
--error-bg: rgba(255, 68, 68, 0.1);
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
margin: 0;
padding: 0;
color: var(--primary);
background-color: var(--bg) !important;
font-family: "Montserrat", serif !important;
min-height: 100vh;
font-size: 16px;
display: flex;
align-items: center;
justify-content: center;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
margin: 0;
padding: 0;
color: var(--primary);
background-color: var(--bg) !important;
font-family: "Montserrat", serif !important;
min-height: 100vh;
font-size: 16px;
display: flex;
align-items: center;
justify-content: center;
}
.forgot-container {
width: 100%;
max-width: 500px;
padding: 20px;
margin: 0 auto;
}
.forgot-header {
text-align: center;
margin-bottom: 24px;
}
.logo-text {
color: var(--icon);
font-size: 24px;
font-weight: 600;
margin: 16px 0;
display: flex;
align-items: center;
justify-content: center;
}
.logo {
width: 24px;
border-radius: 8px;
margin-right: 8px;
}
.forgot-card {
background: var(--card);
padding: 24px;
border-radius: 24px;
margin-bottom: 16px;
}
.forgot-title {
font-size: 18px;
font-weight: 600;
color: var(--primary);
margin-bottom: 24px;
text-align: center;
}
.form-group {
margin-bottom: 16px;
}
.form-label {
display: block;
color: var(--secondary);
font-size: 14px;
margin-bottom: 8px;
}
.form-control {
width: 100%;
padding: 12px 16px;
border: 2px solid transparent;
border-radius: 12px;
font-size: 16px;
font-family: "Montserrat", serif;
background: var(--cardsec);
color: var(--primary);
transition: all 0.2s ease;
}
.form-control:focus {
outline: none;
border-color: var(--accent);
}
.form-control::placeholder {
color: var(--secondary);
}
.form-actions {
margin-top: 24px;
display: flex;
justify-content: space-between;
align-items: center;
}
.help-link {
color: var(--accent);
text-decoration: none;
font-size: 14px;
transition: color 0.2s ease;
}
.help-link:hover {
color: var(--tertiary);
}
.btn-submit {
padding: 12px 24px;
background: var(--accent);
color: white;
border: none;
border-radius: 12px;
font-size: 16px;
font-weight: 600;
font-family: "Montserrat", serif;
cursor: pointer;
transition: background 0.2s ease;
}
.btn-submit:hover {
background: var(--tertiary);
}
.btn-submit:disabled {
opacity: 0.7;
cursor: not-allowed;
}
.error-message {
color: var(--error);
font-size: 14px;
margin-top: 4px;
display: none;
}
.error-message.show {
display: block;
animation: fadeIn 0.2s ease;
}
.g-recaptcha {
margin-top: 24px;
display: flex;
justify-content: center;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(-4px); }
to { opacity: 1; transform: translateY(0); }
}
@media (max-width: 600px) {
.forgot-container {
width: 100%;
max-width: 500px;
padding: 20px;
margin: 0 auto;
}
.forgot-header {
text-align: center;
margin-bottom: 24px;
}
.logo-text {
color: var(--icon);
font-size: 24px;
font-weight: 600;
margin: 16px 0;
display: flex;
align-items: center;
justify-content: center;
}
.logo {
width: 24px;
border-radius: 8px;
margin-right: 8px;
padding: 16px;
}
.forgot-card {
background: var(--card);
padding: 24px;
border-radius: 24px;
margin-bottom: 16px;
}
.forgot-title {
font-size: 18px;
font-weight: 600;
color: var(--primary);
margin-bottom: 24px;
text-align: center;
}
.form-group {
margin-bottom: 16px;
}
.form-label {
display: block;
color: var(--secondary);
font-size: 14px;
margin-bottom: 8px;
}
.form-control {
width: 100%;
padding: 12px 16px;
border: 2px solid transparent;
border-radius: 12px;
font-size: 16px;
font-family: "Montserrat", serif;
background: var(--cardsec);
color: var(--primary);
transition: all 0.2s ease;
}
.form-control:focus {
outline: none;
border-color: var(--accent);
}
.form-control::placeholder {
color: var(--secondary);
padding: 20px;
}
.form-actions {
margin-top: 24px;
display: flex;
justify-content: space-between;
align-items: center;
}
.help-link {
color: var(--accent);
text-decoration: none;
font-size: 14px;
transition: color 0.2s ease;
}
.help-link:hover {
color: var(--tertiary);
flex-direction: column;
gap: 16px;
align-items: stretch;
}
.btn-submit {
padding: 12px 24px;
background: var(--accent);
color: white;
border: none;
border-radius: 12px;
font-size: 16px;
font-weight: 600;
font-family: "Montserrat", serif;
cursor: pointer;
transition: background 0.2s ease;
width: 100%;
}
.btn-submit:hover {
background: var(--tertiary);
}
.btn-submit:disabled {
opacity: 0.7;
cursor: not-allowed;
}
.error-message {
color: var(--error);
font-size: 14px;
margin-top: 4px;
display: none;
}
.error-message.show {
display: block;
animation: fadeIn 0.2s ease;
}
.g-recaptcha {
margin-top: 24px;
display: flex;
justify-content: center;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(-4px); }
to { opacity: 1; transform: translateY(0); }
}
@media (max-width: 600px) {
.forgot-container {
padding: 16px;
}
.forgot-card {
padding: 20px;
}
.form-actions {
flex-direction: column;
gap: 16px;
align-items: stretch;
}
.btn-submit {
width: 100%;
}
}
}

View File

@@ -1,9 +1,7 @@
(() => {
const transformForgotPasswordPage = () => {
const isDarkMode = localStorage.getItem('darkMode') === 'true';
document.documentElement.setAttribute('data-theme', isDarkMode ? 'dark' : 'light');
chrome.runtime.onMessage.addListener((message) => {
if (message.action === 'toggleTheme') {
@@ -11,7 +9,6 @@
localStorage.setItem('darkMode', message.darkMode);
}
});
document.body.innerHTML = `
<div class="forgot-container">
@@ -28,31 +25,31 @@
</header>
<div class="forgot-card">
<h1 class="forgot-title">Elfelejtett jelszó</h1>
<h1 class="forgot-title">${LanguageManager.t('forgotpassword.title')}</h1>
<form id="forgotForm" novalidate>
<div class="form-group">
<label class="form-label" for="username">OM azonosítód</label>
<label class="form-label" for="username">${LanguageManager.t('forgotpassword.om_id_label')}</label>
<input type="text" id="username" name="username" class="form-control"
placeholder="Add meg az OM azonosítód" required>
<div class="error-message">Kérjük, add meg az OM azonosítód.</div>
placeholder="${LanguageManager.t('forgotpassword.om_id_placeholder')}" required>
<div class="error-message">${LanguageManager.t('forgotpassword.om_id_required')}</div>
</div>
<div class="form-group">
<label class="form-label" for="email">E-mail cím</label>
<label class="form-label" for="email">${LanguageManager.t('forgotpassword.email_label')}</label>
<input type="email" id="email" name="email" class="form-control"
placeholder="Add meg az e-mail címed" required>
<div class="error-message">Kérjük, add meg az e-mail címed.</div>
placeholder="${LanguageManager.t('forgotpassword.email_placeholder')}" required>
<div class="error-message">${LanguageManager.t('forgotpassword.email_required')}</div>
</div>
<div class="g-recaptcha" data-sitekey="6LcmPB8dAAAAACJPQBj7WfpBoBsEfyibZeIG5Vbl"></div>
<div class="form-actions">
<a href="/Adminisztracio/Login" class="help-link">
Vissza a bejelentkezéshez
${LanguageManager.t('forgotpassword.back_to_login')}
</a>
<button type="submit" class="btn-submit">
Jelszó visszaállítása
${LanguageManager.t('forgotpassword.reset_button')}
</button>
</div>
</form>
@@ -103,7 +100,6 @@
const form = event.target;
const inputs = form.querySelectorAll('.form-control[required]');
let isValid = true;
inputs.forEach(input => {
if (!validateInput(input, true)) {
@@ -134,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) {
@@ -144,7 +140,6 @@
submitButton.disabled = false;
}
};
if (window.location.href.includes('/Adminisztracio/ElfelejtettJelszo')) {
transformForgotPasswordPage();

131
global/language.js Normal file
View File

@@ -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' }
]
};
})();

View File

@@ -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;
}

View File

@@ -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);
}
}

View File

@@ -191,16 +191,42 @@
}
}
/* Hamburger menu styles */
.nav-toggle {
display: none;
background: none;
border: none;
cursor: pointer;
padding: 0.5rem;
border-radius: 8px;
transition: background-color 0.2s;
}
.nav-toggle:hover {
background: var(--hover);
}
.nav-toggle svg {
width: 24px;
height: 24px;
fill: var(--text-primary);
}
@media (max-width: 768px) {
.kreta-header {
grid-template-columns: 1fr auto;
grid-template-columns: 1fr auto auto;
grid-template-areas:
"school user"
"nav nav";
"school toggle user"
"nav nav nav";
padding: 1rem;
gap: 0.5rem;
}
.nav-toggle {
display: block;
grid-area: toggle;
}
.school-info {
grid-area: school;
max-width: none;
@@ -211,21 +237,24 @@
.logo-text {
margin: 0;
font-size: 20px;
font-size: 18px;
}
.school-details {
font-size: 12px;
font-size: 11px;
max-width: 200px;
}
.kreta-nav {
grid-area: nav;
padding: 0;
margin-top: 0.5rem;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
scrollbar-width: none;
-ms-overflow-style: none;
display: none;
}
.kreta-nav.show {
display: flex;
animation: slideDown 0.3s ease;
}
.kreta-nav::-webkit-scrollbar {
@@ -233,25 +262,140 @@
}
.nav-links {
justify-content: flex-start;
flex-direction: column;
width: 100%;
gap: 0.25rem;
gap: 0.5rem;
background: var(--card-card);
border-radius: 12px;
padding: 1rem;
box-shadow: 0px 1px var(--shadow-blur) 0px var(--accent-shadow);
}
.nav-item {
padding: 0.5rem;
font-size: 13px;
width: 100%;
justify-content: flex-start;
padding: 0.75rem;
font-size: 14px;
}
.user-profile {
grid-area: user;
}
.user-info {
text-align: right;
max-width: 120px;
}
.user-name {
font-size: 14px;
font-size: 13px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.nav-logout-timer {
font-size: 11px;
}
}
@media (max-width: 480px) {
.kreta-header {
grid-template-columns: 1fr auto auto;
grid-template-areas:
"school toggle user"
"nav nav nav";
padding: 0.75rem;
gap: 0.25rem;
}
.school-info {
min-width: 0;
flex: 1;
}
.logo-text {
font-size: 16px;
}
.school-details {
font-size: 10px;
max-width: 150px;
}
.kreta-nav {
display: none;
}
.kreta-nav.show {
display: flex;
animation: slideDown 0.3s ease;
}
.nav-links {
flex-direction: column;
width: 100%;
gap: 0.5rem;
background: var(--card-card);
border-radius: 12px;
padding: 1rem;
box-shadow: 0px 1px var(--shadow-blur) 0px var(--accent-shadow);
}
.nav-item {
width: 100%;
justify-content: flex-start;
padding: 0.75rem;
font-size: 14px;
}
.user-info {
max-width: 100px;
}
.user-name {
font-size: 12px;
}
.nav-logout-timer {
font-size: 10px;
}
}
@media (max-width: 360px) {
.kreta-header {
padding: 0.5rem;
}
.logo-text {
font-size: 14px;
}
.school-details {
font-size: 9px;
max-width: 120px;
}
.user-info {
max-width: 80px;
}
.user-name {
font-size: 11px;
}
.nav-logout-timer {
font-size: 9px;
}
}
@keyframes slideDown {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}

View File

@@ -6,31 +6,20 @@ 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'
};
const cookies = {
getCookie(name) {
const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`);
return parts.length === 2 ? parts.pop().split(';').shift() : null;
}
};
function updateHeaderInfo() {
const schoolName = document.querySelector('.nav-school-name');
const userName = document.querySelector('.nav-user-name');
const logoutTimer = document.querySelector('.nav-logout-timer');
const userData = {
schoolName: cookies.getCookie(COOKIE_KEYS.SCHOOL_NAME) || DEFAULT_VALUES.SCHOOL,
schoolId: cookies.getCookie(COOKIE_KEYS.SCHOOL_CODE) || '',
name: cookies.getCookie(COOKIE_KEYS.USER_NAME) || DEFAULT_VALUES.USER,
schoolName: cookieManager.get(COOKIE_KEYS.SCHOOL_NAME) || DEFAULT_VALUES.SCHOOL,
schoolId: cookieManager.get(COOKIE_KEYS.SCHOOL_CODE) || '',
name: cookieManager.get(COOKIE_KEYS.USER_NAME) || DEFAULT_VALUES.USER,
time: document.querySelector('.usermenu_timer')?.textContent?.trim() || DEFAULT_VALUES.TIMER
};
@@ -95,9 +84,39 @@ function setupSettingsButton() {
});
}
function setupMobileNavigation() {
setTimeout(() => {
const navToggle = document.querySelector('.nav-toggle');
const nav = document.querySelector('.kreta-nav');
if (!navToggle || !nav) {
return;
}
navToggle.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
nav.classList.toggle('show');
});
document.addEventListener('click', (e) => {
if (!nav.contains(e.target) && !navToggle.contains(e.target)) {
nav.classList.remove('show');
}
});
const navItems = document.querySelectorAll('.nav-item');
navItems.forEach(item => {
item.addEventListener('click', () => {
nav.classList.remove('show');
});
});
}, 100);
}
document.addEventListener('DOMContentLoaded', () => {
updateHeaderInfo();
setupUserDropdown();
setupSettingsButton();
setupMobileNavigation();
});

View File

@@ -1,87 +1,69 @@
(() => {
function setCookie(name, value, days = 365) {
const date = new Date();
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
const expires = `expires=${date.toUTCString()}`;
document.cookie = `${name}=${value}; ${expires}; path=/; domain=.e-kreta.hu`;
}
function getCookie(name) {
const cookieName = `${name}=`;
const decodedCookie = decodeURIComponent(document.cookie);
const cookieArray = decodedCookie.split(';');
for(let i = 0; i < cookieArray.length; i++) {
let cookie = cookieArray[i];
while (cookie.charAt(0) === ' ') {
cookie = cookie.substring(1);
}
if (cookie.indexOf(cookieName) === 0) {
return cookie.substring(cookieName.length, cookie.length);
}
}
return null;
}
function setTheme(theme) {
try {
const actualTheme = theme === 'default' ? 'light-blue' : theme;
document.documentElement.setAttribute('data-theme', actualTheme);
setCookie('themePreference', actualTheme);
cookieManager.set('themePreference', actualTheme);
localStorage.setItem('themePreference', actualTheme);
chrome.runtime.sendMessage({
action: 'themeChanged',
theme: actualTheme
}).catch(() => {
console.log('Extension context not available for theme sync');
});
console.log('Theme set to:', actualTheme);
} catch (error) {
console.error('Error setting theme:', error);
}
}
function setPageTitleAndFavicon() {
try {
document.title = 'Firka - KRÉTA';
const existingFavicons = document.querySelectorAll('link[rel="icon"], link[rel="shortcut icon"]');
existingFavicons.forEach(link => link.remove());
if (typeof chrome !== 'undefined' && chrome.runtime && chrome.runtime.getURL) {
const favicon = document.createElement('link');
favicon.rel = 'icon';
favicon.type = 'image/png';
favicon.href = chrome.runtime.getURL('images/firka_logo_128.png');
document.head.appendChild(favicon);
const shortcutIcon = document.createElement('link');
shortcutIcon.rel = 'shortcut icon';
shortcutIcon.type = 'image/png';
shortcutIcon.href = chrome.runtime.getURL('images/firka_logo_128.png');
document.head.appendChild(shortcutIcon);
}
} catch (error) {
console.error('Error setting page title and favicon:', error);
}
}
function initializeTheme() {
const cookieTheme = getCookie('themePreference');
const cookieTheme = cookieManager.get('themePreference');
const localStorageTheme = localStorage.getItem('themePreference');
const theme = cookieTheme || localStorageTheme || 'light-blue';
const theme = cookieTheme || localStorageTheme || 'light-green';
setTheme(theme);
setPageTitleAndFavicon();
if (cookieTheme !== localStorageTheme) {
if (cookieTheme) {
localStorage.setItem('themePreference', cookieTheme);
} else if (localStorageTheme) {
setCookie('themePreference', localStorageTheme);
cookieManager.set('themePreference', localStorageTheme);
}
}
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => {
initializeTheme();
});
initializeTheme();
document.addEventListener('DOMContentLoaded', initializeTheme);
} else {
initializeTheme();
}
@@ -93,35 +75,58 @@
}
if (message.action === 'getTheme') {
const currentTheme = document.documentElement.getAttribute('data-theme') || 'light-blue';
const currentTheme = document.documentElement.getAttribute('data-theme') || 'light-green';
sendResponse({ theme: currentTheme });
}
return true;
});
let titleCheckTimeout;
const observer = new MutationObserver((mutations) => {
const currentTheme = document.documentElement.getAttribute('data-theme');
const savedTheme = getCookie('themePreference') || localStorage.getItem('themePreference');
const savedTheme = cookieManager.get('themePreference') || localStorage.getItem('themePreference');
if ((!currentTheme && savedTheme) || (currentTheme !== savedTheme && savedTheme)) {
setTheme(savedTheme);
}
const titleChanged = mutations.some(mutation =>
mutation.type === 'childList' &&
mutation.target === document.head &&
Array.from(mutation.addedNodes).some(node => node.tagName === 'TITLE')
);
if (titleChanged || document.title !== 'Firka - KRÉTA') {
clearTimeout(titleCheckTimeout);
titleCheckTimeout = setTimeout(() => {
if (document.title !== 'Firka - KRÉTA') {
setPageTitleAndFavicon();
}
}, 100);
}
});
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => {
observer.observe(document.documentElement, {
attributes: true,
attributeFilter: ['data-theme']
});
observer.observe(document.head, {
childList: true,
subtree: true
});
});
} else {
observer.observe(document.documentElement, {
attributes: true,
attributeFilter: ['data-theme']
});
observer.observe(document.head, {
childList: true,
subtree: true
});
}
})();

File diff suppressed because it is too large Load Diff

View File

@@ -1,201 +1,170 @@
(() => {
function getCookie(name) {
const cookieName = `${name}=`;
const decodedCookie = decodeURIComponent(document.cookie);
const cookieArray = decodedCookie.split(';');
for(let i = 0; i < cookieArray.length; i++) {
let cookie = cookieArray[i];
while (cookie.charAt(0) === ' ') {
cookie = cookie.substring(1);
}
if (cookie.indexOf(cookieName) === 0) {
return cookie.substring(cookieName.length, cookie.length);
}
async function transformGradesPage() {
try {
await helper.waitForElement('#Osztalyzatok_7895TanuloErtekelesByTanuloGrid');
await new Promise(resolve => setTimeout(resolve, 1000));
const gradesData = extractGradesData();
const studentAverage = calculateOverallAverage(gradesData.subjects);
const classAverage = calculateOverallClassAverage(gradesData.subjects);
document.body.innerHTML = generatePageHTML(gradesData, studentAverage, classAverage);
createTemplate.importFonts();
setupUserDropdown();
setupMobileNavigation();
const script = document.createElement('script');
script.src = chrome.runtime.getURL('grades/chart.js');
document.head.appendChild(script);
script.onload = () => {
setupGradesChart(gradesData.subjects);
};
setupEventListeners();
setupGradesListScrolling();
loadingScreen.hide();
} catch (error) {
loadingScreen.hide();
}
}
return null;
}
function showLoadingScreen() {
const existingLoadingScreen = document.querySelector('.loading-screen');
if (existingLoadingScreen) return;
const loadingScreen = document.createElement('div');
loadingScreen.className = 'loading-screen';
loadingScreen.innerHTML = `
<div class="loading-content">
<img src="${chrome.runtime.getURL('images/firka_logo.png')}" alt="Firka" class="loading-logo">
<div class="loading-text">Betöltés alatt...</div>
<div class="loading-text2">Kis türelmet!</div>
</div>
`;
document.body.appendChild(loadingScreen);
}
function hideLoadingScreen() {
const loadingScreen = document.querySelector('.loading-screen');
if (loadingScreen) {
loadingScreen.style.opacity = '0';
loadingScreen.addEventListener('transitionend', () => {
loadingScreen.remove();
});
}
}
function extractGradesData() {
const subjects = [];
const rows = document.querySelectorAll('#Osztalyzatok_7895TanuloErtekelesByTanuloGrid tbody tr');
async function transformGradesPage() {
try {
showLoadingScreen();
rows.forEach(row => {
const cells = row.querySelectorAll('td');
if (cells.length >= 17) {
const subjectName = cells[2].textContent.trim();
if (subjectName && subjectName !== 'Magatartás/Szorgalom') {
const grades = [];
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')
];
await waitForElement('#Osztalyzatok_7895TanuloErtekelesByTanuloGrid');
await new Promise(resolve => setTimeout(resolve, 1000));
months.forEach((month, index) => {
const gradeElements = cells[index + 3].querySelectorAll('span[data-tanuloertekelesid]');
gradeElements.forEach(element => {
const gradeText = element.textContent.trim();
if (gradeText && gradeText !== '-') {
grades.push({
value: gradeText,
date: element.getAttribute('data-datum'),
type: element.getAttribute('data-tipusmod'),
theme: element.getAttribute('data-ertekelestema').replace('Téma: ', ''),
weight: element.getAttribute('data-suly'),
teacher: element.getAttribute('data-ertekelonyomtatasinev'),
isSemesterGrade: (element.getAttribute('data-tipusmod') || '').toLowerCase().includes('félévi') ||
(element.getAttribute('data-ertekelestema') || '').toLowerCase().includes('félévi') ||
(element.getAttribute('data-tipus') || '').toLowerCase().includes('félévi')
});
}
});
});
const gradesData = extractGradesData();
const studentAverage = calculateOverallAverage(gradesData.subjects);
const classAverage = calculateOverallClassAverage(gradesData.subjects);
document.body.innerHTML = generatePageHTML(gradesData, studentAverage, classAverage);
const avgText = cells[16].textContent.trim();
const classAvgText = cells[17].textContent.trim();
const links = [
{ rel: 'preconnect', href: 'https://fonts.googleapis.com' },
{ rel: 'preconnect', href: 'https://fonts.gstatic.com', crossorigin: true },
{ rel: 'stylesheet', href: 'https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&display=swap' },
{ rel: 'stylesheet', href: 'https://fonts.googleapis.com/icon?family=Material+Icons+Round' }
];
const average = avgText !== '-' ? parseFloat(avgText.replace(',', '.')) : 0;
const classAvg = classAvgText !== '-' ? parseFloat(classAvgText.replace(',', '.')) : 0;
const script = document.createElement('script');
script.src = chrome.runtime.getURL('grades/chart.js');
document.head.appendChild(script);
if (grades.length > 0) {
links.forEach(link => {
const linkElement = document.createElement('link');
Object.entries(link).forEach(([key, value]) => {
linkElement[key] = value;
});
document.head.appendChild(linkElement);
subjects.push({
name: subjectName,
grades: grades,
average: average || 0,
classAverage: classAvg || 0
});
}
}
}
});
script.onload = () => {
setupGradesChart(gradesData.subjects);
return {
schoolInfo: {
id: cookieManager.get('schoolCode') || '',
name: cookieManager.get('schoolName') || 'Iskola'
},
userData: {
name: cookieManager.get('userName') || 'Felhasználó',
time: document.querySelector('.usermenu_timer')?.textContent?.trim() || '45:00'
},
subjects: subjects
};
setupEventListeners();
hideLoadingScreen();
} catch (error) {
console.error('Error transforming grades page:', error);
hideLoadingScreen();
}
}
function extractGradesData() {
const subjects = [];
const rows = document.querySelectorAll('#Osztalyzatok_7895TanuloErtekelesByTanuloGrid tbody tr');
rows.forEach(row => {
const cells = row.querySelectorAll('td');
if (cells.length >= 17) {
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'];
months.forEach((month, index) => {
const gradeElements = cells[index + 3].querySelectorAll('span[data-tanuloertekelesid]');
gradeElements.forEach(element => {
const gradeText = element.textContent.trim();
if (gradeText && gradeText !== '-') {
grades.push({
value: gradeText,
date: element.getAttribute('data-datum'),
type: element.getAttribute('data-tipusmod'),
theme: element.getAttribute('data-ertekelestema').replace('Téma: ', ''),
weight: element.getAttribute('data-suly'),
teacher: element.getAttribute('data-ertekelonyomtatasinev'),
isSemesterGrade: (element.getAttribute('data-tipusmod') || '').toLowerCase().includes('félévi') ||
(element.getAttribute('data-ertekelestema') || '').toLowerCase().includes('félévi') ||
(element.getAttribute('data-tipus') || '').toLowerCase().includes('félévi')
});
}
});
});
function calculateOverallAverage(subjects) {
const validSubjects = subjects.filter(s => s.average > 0);
if (validSubjects.length === 0) return 0;
const avgText = cells[16].textContent.trim();
const classAvgText = cells[17].textContent.trim();
const average = avgText !== '-' ? parseFloat(avgText.replace(',', '.')) : 0;
const classAvg = classAvgText !== '-' ? parseFloat(classAvgText.replace(',', '.')) : 0;
if (grades.length > 0) {
const weightedSum = validSubjects.reduce((sum, subject) => {
const validGrades = subject.grades.filter(grade => !isNaN(parseInt(grade.value)));
const subjectWeightedSum = validGrades.reduce((gradeSum, grade) => {
const value = parseInt(grade.value);
const weight = parseInt(grade.weight?.match(/\d+/)?.[0] || '100') / 100;
return gradeSum + (value * weight);
}, 0);
subjects.push({
name: subjectName,
grades: grades,
average: average || 0,
classAverage: classAvg || 0
});
}
}
}
});
const totalWeight = validGrades.reduce((weightSum, grade) => {
const weight = parseInt(grade.weight?.match(/\d+/)?.[0] || '100') / 100;
return weightSum + weight;
}, 0);
return {
schoolInfo: {
id: getCookie('schoolCode') || '',
name: getCookie('schoolName') || 'Iskola'
},
userData: {
name: getCookie('userName') || 'Felhasználó',
time: document.querySelector('.usermenu_timer')?.textContent?.trim() || '45:00'
},
subjects: subjects
};
}
return sum + (subjectWeightedSum / totalWeight);
}, 0);
function calculateOverallAverage(subjects) {
const validSubjects = subjects.filter(s => s.average > 0);
if (validSubjects.length === 0) return 0;
const weightedSum = validSubjects.reduce((sum, subject) => {
const validGrades = subject.grades.filter(grade => !isNaN(parseInt(grade.value)));
const subjectWeightedSum = validGrades.reduce((gradeSum, grade) => {
const value = parseInt(grade.value);
const weight = parseInt(grade.weight?.match(/\d+/)?.[0] || '100') / 100;
return gradeSum + (value * weight);
}, 0);
const totalWeight = validGrades.reduce((weightSum, grade) => {
const weight = parseInt(grade.weight?.match(/\d+/)?.[0] || '100') / 100;
return weightSum + weight;
}, 0);
return sum + (subjectWeightedSum / totalWeight);
}, 0);
return weightedSum / validSubjects.length;
}
return weightedSum / validSubjects.length;
}
function calculateOverallClassAverage(subjects) {
const validSubjects = subjects.filter(s => s.classAverage > 0);
if (validSubjects.length === 0) return 0;
return validSubjects.reduce((sum, s) => sum + s.classAverage, 0) / validSubjects.length;
}
function calculateOverallClassAverage(subjects) {
const validSubjects = subjects.filter(s => s.classAverage > 0);
if (validSubjects.length === 0) return 0;
return validSubjects.reduce((sum, s) => sum + s.classAverage, 0) / validSubjects.length;
}
function shortenEvaluationName(name, maxLength = 30) {
if (!name) return '';
if (name.length <= maxLength) return name;
return name.substring(0, maxLength - 3) + '...';
}
function shortenEvaluationName(name, maxLength = 30) {
if (!name) return '';
if (name.length <= maxLength) return name;
return name.substring(0, maxLength - 3) + '...';
}
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 formattedDate = `${monthNames[dateObj.getMonth()]} ${dateObj.getDate()}`;
const shortenedTheme = shortenEvaluationName(grade.theme);
return `
function generateGradeItem(grade) {
const semesterClass = grade.isSemesterGrade ? 'semester-grade' : '';
const dateObj = new Date(grade.date);
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 `
<div class="grade-item grade-${grade.value} ${semesterClass}">
<div class="grade-value">${grade.value}</div>
<div class="grade-details">
@@ -205,95 +174,41 @@
<div class="grade-date">${formattedDate}</div>
</div>
`;
}
}
function calculateGradeDistribution(subjects) {
const distribution = {1: 0, 2: 0, 3: 0, 4: 0, 5: 0};
subjects.forEach(subject => {
subject.grades.forEach(grade => {
const value = parseInt(grade.value);
if (value >= 1 && value <= 5) {
distribution[value]++;
}
function calculateGradeDistribution(subjects) {
const distribution = {1: 0, 2: 0, 3: 0, 4: 0, 5: 0};
subjects.forEach(subject => {
subject.grades.forEach(grade => {
const value = parseInt(grade.value);
if (value >= 1 && value <= 5) {
distribution[value]++;
}
});
});
});
return distribution;
}
return distribution;
}
function generatePageHTML(data, studentAverage, classAverage) {
const totalGrades = data.subjects.reduce((sum, subject) => sum + subject.grades.length, 0);
const gradeDistribution = calculateGradeDistribution(data.subjects);
const semesterGrades = extractSemesterGrades(data.subjects);
const studentGradeLevel = Math.floor(studentAverage) || 0;
const classGradeLevel = Math.floor(classAverage) || 0;
function generatePageHTML(data, studentAverage, classAverage) {
const totalGrades = data.subjects.reduce((sum, subject) => sum + subject.grades.length, 0);
const gradeDistribution = calculateGradeDistribution(data.subjects);
const semesterGrades = extractSemesterGrades(data.subjects);
return `
const studentGradeLevel = Math.floor(studentAverage) || 0;
const classGradeLevel = Math.floor(classAverage) || 0;
schoolNameFull = `${data.schoolInfo.id} - ${data.schoolInfo.name}`;
shortenedSchoolName = helper.shortenSchoolName(schoolNameFull);
return `
<div class="kreta-container">
<header class="kreta-header">
<div class="school-info">
<p class="logo-text">
<img src="${chrome.runtime.getURL('images/firka_logo.png')}" alt="Firka" class="logo">
Firka
</p>
<div class="school-details">
<span>${data.schoolInfo.id} - ${data.schoolInfo.name}</span>
</div>
</div>
<nav class="kreta-nav">
<div class="nav-links">
<a href="/Intezmeny/Faliujsag" data-page="dashboard" class="nav-item">
<img src="${chrome.runtime.getURL('icons/dashboard-inactive.svg')}" alt="Kezdőlap">
Kezdőlap
</a>
<a href="/TanuloErtekeles/Osztalyzatok" data-page="grades" class="nav-item active">
<img src="${chrome.runtime.getURL('icons/grades-active.svg')}" alt="Jegyek">
Jegyek
</a>
<a href="/Orarend/InformaciokOrarend" data-page="timetable" class="nav-item">
<img src="${chrome.runtime.getURL('icons/timetable-inactive.svg')}" alt="Órarend">
Órarend
</a>
<a href="/Hianyzas/Hianyzasok" data-page="absences" class="nav-item">
<img src="${chrome.runtime.getURL('icons/absences-inactive.svg')}" alt="Mulasztások">
Mulasztások
</a>
<a href="/Tanulo/TanuloHaziFeladat" data-page="other" class="nav-item">
<img src="${chrome.runtime.getURL('icons/others.svg')}" alt="Egyéb">
Egyéb
</a>
</div>
</nav>
<div class="user-profile">
<button class="user-dropdown-btn">
<div class="user-info">
<span class="user-name">${data.userData.name}</span>
<span class="nav-logout-timer" id="logoutTimer">${data.userData.time}</span>
</div>
</button>
<div class="user-dropdown">
<a href="/Adminisztracio/Profil" data-page="profile" class="dropdown-item">
<img src="${chrome.runtime.getURL('icons/profile.svg')}" alt="Profil">
Profil
</a>
<a href="#" class="dropdown-item" id="settingsBtn">
<img src="${chrome.runtime.getURL('icons/settings.svg')}" alt="Beállítások">
Beállítások
</a>
<a href="/Home/Logout" data-page="logout" class="dropdown-item">
<img src="${chrome.runtime.getURL('icons/logout.svg')}" alt="Kijelentkezés">
Kijelentkezés
</a>
</div>
</div>
</header>
${createTemplate.header()}
<main class="kreta-main">
<div class="grades-overview">
<div class="overall-averages card">
<div class="chart-header">
<div class="chart-title">Jegyek (${totalGrades}db)</div>
<div class="chart-title">${LanguageManager.t('grades.chart_title')} (${totalGrades}db)</div>
<div class="chart-averages">
<div class="average-circle my-average" data-grade="${studentGradeLevel}">
<span class="average-value ${studentAverage < 2 && studentAverage > 0 ? 'warning' : ''}">${studentAverage > 0 ? studentAverage.toFixed(2) : '-'}</span>
@@ -320,7 +235,7 @@ function generatePageHTML(data, studentAverage, classAverage) {
</div>
${semesterGrades.length > 0 ? `
<div class="semester-grades card">
<h3>Félévi értékelések</h3>
<h3>${LanguageManager.t('grades.semester_evaluations')}</h3>
<div class="semester-grades-list">
${semesterGrades.map(grade => `
<div class="semester-grade-item grade-${grade.value}">
@@ -338,166 +253,168 @@ function generatePageHTML(data, studentAverage, classAverage) {
</main>
</div>
`;
}
}
function extractSemesterGrades(subjects) {
const semesterGrades = [];
subjects.forEach(subject => {
const semesterGrade = subject.grades.find(grade => grade.isSemesterGrade);
if (semesterGrade) {
semesterGrades.push({
subject: subject.name,
value: semesterGrade.value,
date: semesterGrade.date
});
}
});
return semesterGrades;
}
function extractSemesterGrades(subjects) {
const semesterGrades = [];
subjects.forEach(subject => {
const semesterGrade = subject.grades.find(grade => grade.isSemesterGrade);
if (semesterGrade) {
semesterGrades.push({
subject: subject.name,
value: semesterGrade.value,
date: semesterGrade.date
});
}
});
return semesterGrades;
}
function calculateGradePoints(subjects) {
const allGrades = [];
subjects.forEach(subject => {
subject.grades.forEach(grade => {
const date = new Date(grade.date);
const value = parseInt(grade.value);
const weight = parseInt(grade.weight?.match(/\d+/)?.[0] || '100') / 100;
allGrades.push({
date,
value,
weight
});
});
});
function calculateGradePoints(subjects) {
const allGrades = [];
allGrades.sort((a, b) => a.date - b.date);
subjects.forEach(subject => {
subject.grades.forEach(grade => {
const date = new Date(grade.date);
const value = parseInt(grade.value);
const weight = parseInt(grade.weight?.match(/\d+/)?.[0] || '100') / 100;
if (date && value && weight) {
allGrades.push({
date,
value,
weight
});
}
});
});
let totalWeight = 0;
let weightedSum = 0;
return allGrades.map(grade => {
totalWeight += grade.weight;
weightedSum += grade.value * grade.weight;
return {
date: grade.date.toISOString(),
grade: grade.value,
average: weightedSum / totalWeight
};
});
}
function setupGradesChart(subjects) {
const ctx = document.getElementById('gradesChart');
if (!ctx) return;
allGrades.sort((a, b) => a.date - b.date);
const gradePoints = calculateGradePoints(subjects);
new Chart(ctx, {
type: 'line',
data: {
labels: gradePoints.map((_, index) => ''),
datasets: [{
label: 'Átlag',
data: gradePoints.map(p => p.average),
borderWidth: 5,
tension: 0.5,
segment: {
borderColor: ctx => {
const curr = ctx.p1.parsed.y;
if (!curr) return 'transparent';
const color = getComputedStyle(document.documentElement).getPropertyValue(
curr < 2 ? '--grades-1' :
curr < 2.5 ? '--grades-2' :
curr < 3.5 ? '--grades-3' :
curr < 4.5 ? '--grades-4' : '--grades-5'
).trim() + '80';
return color;
}
},
fill: true,
backgroundColor: function(context) {
const chart = context.chart;
const {ctx, chartArea} = chart;
if (!chartArea) return null;
const gradientBg = ctx.createLinearGradient(0, chartArea.bottom, 0, chartArea.top);
gradientBg.addColorStop(0, getComputedStyle(document.documentElement).getPropertyValue('--grades-1').trim() + '30');
gradientBg.addColorStop(0.2, getComputedStyle(document.documentElement).getPropertyValue('--grades-2').trim() + '30');
gradientBg.addColorStop(0.4, getComputedStyle(document.documentElement).getPropertyValue('--grades-3').trim() + '30');
gradientBg.addColorStop(0.6, getComputedStyle(document.documentElement).getPropertyValue('--grades-4').trim() + '30');
gradientBg.addColorStop(0.8, getComputedStyle(document.documentElement).getPropertyValue('--grades-5').trim() + '30');
return gradientBg;
},
pointBackgroundColor: context => {
const value = context.raw;
return getComputedStyle(document.documentElement).getPropertyValue(
value < 2 ? '--grades-1' :
value < 2.5 ? '--grades-2' :
value < 3.5 ? '--grades-3' :
value < 4.5 ? '--grades-4' : '--grades-5'
).trim();
},
pointRadius: 0,
pointHoverRadius: 0
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
y: {
min: 1,
max: 5,
ticks: {
stepSize: 1,
color: getComputedStyle(document.documentElement).getPropertyValue('--text-secondary')
},
grid: {
color: getComputedStyle(document.documentElement).getPropertyValue('--text-teritary') + '20',
lineWidth: 1,
borderDash: [5, 5]
}
},
x: {
display: false
}
},
plugins: {
legend: {
display: false
},
tooltip: {
backgroundColor: getComputedStyle(document.documentElement).getPropertyValue('--card-card'),
titleColor: getComputedStyle(document.documentElement).getPropertyValue('--text-primary'),
bodyColor: getComputedStyle(document.documentElement).getPropertyValue('--text-primary'),
borderColor: getComputedStyle(document.documentElement).getPropertyValue('--text-teritary') + '20',
borderWidth: 1,
padding: 12,
displayColors: false,
callbacks: {
title: () => '',
label: context => `Átlag: ${context.raw.toFixed(2)}`
}
}
}
}
});
}
function generateSubjectCards(subjects) {
const sortedSubjects = [...subjects].sort((a, b) => a.grades.length - b.grades.length);
let totalWeight = 0;
let weightedSum = 0;
return allGrades.map(grade => {
totalWeight += grade.weight;
weightedSum += grade.value * grade.weight;
return {
date: grade.date.toISOString(),
grade: grade.value,
average: weightedSum / totalWeight
};
});
}
return sortedSubjects.map(subject => {
const regularGrades = subject.grades.filter(grade => !grade.isSemesterGrade);
const myGrade = Math.floor(subject.average) || 0;
const classGrade = Math.floor(subject.classAverage) || 0;
return `
function setupGradesChart(subjects) {
const ctx = document.getElementById('gradesChart');
if (!ctx) return;
const gradePoints = calculateGradePoints(subjects);
new Chart(ctx, {
type: 'line',
data: {
labels: gradePoints.map((_, index) => ''),
datasets: [{
label: 'Átlag',
data: gradePoints.map(p => p.average),
borderWidth: 5,
tension: 0.5,
segment: {
borderColor: ctx => {
const curr = ctx.p1.parsed.y;
if (!curr) return 'transparent';
const color = getComputedStyle(document.documentElement).getPropertyValue(
curr < 2 ? '--grades-1' :
curr < 2.5 ? '--grades-2' :
curr < 3.5 ? '--grades-3' :
curr < 4.5 ? '--grades-4' : '--grades-5'
).trim() + '80';
return color;
}
},
fill: true,
backgroundColor: function(context) {
const chart = context.chart;
const {ctx, chartArea} = chart;
if (!chartArea) return null;
const gradientBg = ctx.createLinearGradient(0, chartArea.bottom, 0, chartArea.top);
gradientBg.addColorStop(0, getComputedStyle(document.documentElement).getPropertyValue('--grades-1').trim() + '30');
gradientBg.addColorStop(0.2, getComputedStyle(document.documentElement).getPropertyValue('--grades-2').trim() + '30');
gradientBg.addColorStop(0.4, getComputedStyle(document.documentElement).getPropertyValue('--grades-3').trim() + '30');
gradientBg.addColorStop(0.6, getComputedStyle(document.documentElement).getPropertyValue('--grades-4').trim() + '30');
gradientBg.addColorStop(0.8, getComputedStyle(document.documentElement).getPropertyValue('--grades-5').trim() + '30');
return gradientBg;
},
pointBackgroundColor: context => {
const value = context.raw;
return getComputedStyle(document.documentElement).getPropertyValue(
value < 2 ? '--grades-1' :
value < 2.5 ? '--grades-2' :
value < 3.5 ? '--grades-3' :
value < 4.5 ? '--grades-4' : '--grades-5'
).trim();
},
pointRadius: 0,
pointHoverRadius: 0
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
y: {
min: 1,
max: 5,
ticks: {
stepSize: 1,
color: getComputedStyle(document.documentElement).getPropertyValue('--text-secondary')
},
grid: {
color: getComputedStyle(document.documentElement).getPropertyValue('--text-teritary') + '20',
lineWidth: 1,
borderDash: [5, 5]
}
},
x: {
display: false
}
},
plugins: {
legend: {
display: false
},
tooltip: {
backgroundColor: getComputedStyle(document.documentElement).getPropertyValue('--card-card'),
titleColor: getComputedStyle(document.documentElement).getPropertyValue('--text-primary'),
bodyColor: getComputedStyle(document.documentElement).getPropertyValue('--text-primary'),
borderColor: getComputedStyle(document.documentElement).getPropertyValue('--text-teritary') + '20',
borderWidth: 1,
padding: 12,
displayColors: false,
callbacks: {
title: () => '',
label: context => `Átlag: ${context.raw.toFixed(2)}`
}
}
}
}
});
}
function generateSubjectCards(subjects) {
const sortedSubjects = [...subjects].sort((a, b) => a.grades.length - b.grades.length);
return sortedSubjects.map(subject => {
const regularGrades = subject.grades.filter(grade => !grade.isSemesterGrade).reverse();
const myGrade = Math.floor(subject.average) || 0;
const classGrade = Math.floor(subject.classAverage) || 0;
return `
<div class="subject-card card">
<div class="subject-header">
<div class="subject-title">
@@ -519,68 +436,59 @@ function generateSubjectCards(subjects) {
</div>
</div>
`;
}).join('');
}
function setupEventListeners() {
const userBtn = document.querySelector('.user-dropdown-btn');
const userDropdown = document.querySelector('.user-dropdown');
userBtn?.addEventListener('click', (e) => {
e.stopPropagation();
userDropdown.classList.toggle('show');
});
document.addEventListener('click', () => {
userDropdown?.classList.remove('show');
});
const timerEl = document.getElementById('logoutTimer');
if (timerEl) {
const startTime = parseInt(timerEl.textContent?.match(/\d+/)?.[0] || "45");
let timeLeft = startTime * 60;
const updateTimer = () => {
const minutes = Math.floor(timeLeft / 60);
const seconds = timeLeft % 60;
timerEl.textContent = `${minutes}:${seconds.toString().padStart(2, '0')}`;
if (timeLeft <= 0) {
window.location.href = '/Home/Logout';
} else {
timeLeft--;
}
};
updateTimer();
setInterval(updateTimer, 1000);
}).join('');
}
}
function waitForElement(selector) {
return new Promise(resolve => {
if (document.querySelector(selector)) {
return resolve(document.querySelector(selector));
}
function setupEventListeners() {
const timerEl = document.getElementById('logoutTimer');
if (timerEl) {
const startTime = parseInt(timerEl.textContent?.match(/\d+/)?.[0] || "45");
let timeLeft = startTime * 60;
const observer = new MutationObserver(mutations => {
if (document.querySelector(selector)) {
observer.disconnect();
resolve(document.querySelector(selector));
const updateTimer = () => {
const minutes = Math.floor(timeLeft / 60);
const seconds = timeLeft % 60;
timerEl.textContent = `${minutes}:${seconds.toString().padStart(2, '0')}`;
timeLeft--;
if (timeLeft < 0) {
window.location.href = chrome.runtime.getURL('logout/logout.html');
}
};
setInterval(updateTimer, 1000);
}
});
}
observer.observe(document.body, {
childList: true,
subtree: true
});
});
}
function setupGradesListScrolling() {
const gradesLists = document.querySelectorAll('.grades-list');
gradesLists.forEach(list => {
const checkScrollable = () => {
if (list.scrollHeight > list.clientHeight) {
list.classList.add('scrollable', 'has-more');
const handleScroll = () => {
const isAtBottom = list.scrollTop + list.clientHeight >= list.scrollHeight - 5;
if (isAtBottom) {
list.classList.remove('has-more');
} else {
list.classList.add('has-more');
}
};
list.addEventListener('scroll', handleScroll);
handleScroll();
} else {
list.classList.remove('scrollable', 'has-more');
}
};
checkScrollable();
});
}
if (window.location.href.includes('/TanuloErtekeles/Osztalyzatok')) {
transformGradesPage();
}
if (window.location.href.includes('/TanuloErtekeles/Osztalyzatok')) {
transformGradesPage();
}
})();

View File

@@ -1,28 +1,3 @@
@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;
}
* {
box-sizing: border-box;
margin: 0;
@@ -67,10 +42,10 @@ body {
@media (max-width: 768px) {
.kreta-header {
grid-template-columns: 1fr auto;
grid-template-columns: 1fr auto auto;
grid-template-areas:
"school user"
"nav nav";
"school toggle user"
"nav nav nav";
padding: 1rem;
gap: 0.5rem;
}
@@ -135,77 +110,6 @@ body {
}
}
.kreta-nav {
padding: 0 clamp(0.5rem, 3vw, 1.5rem);
position: sticky;
top: 0;
z-index: 100;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
scrollbar-width: none;
-ms-overflow-style: none;
display: flex;
justify-content: center;
}
@media (max-width: 768px) {
.kreta-nav {
grid-area: nav;
padding: 0;
margin-top: 0.5rem;
}
}
.nav-links {
display: flex;
gap: clamp(0.5rem, 2vw, 1rem);
padding: 0.25rem;
justify-content: center;
}
@media (max-width: 768px) {
.nav-links {
justify-content: flex-start;
width: 100%;
gap: 0.25rem;
}
}
.nav-links a {
color: var(--text-secondary);
text-decoration: none;
padding: clamp(0.5rem, 1.5vw, 1rem) 0.5rem;
font-weight: 500;
white-space: nowrap;
transition: all 0.2s ease;
display: flex;
align-items: center;
gap: 0.5rem;
border-radius: 8px;
}
@media (max-width: 768px) {
.nav-links a {
padding: 0.5rem;
font-size: 13px;
}
.nav-links a .material-icons-round {
font-size: 20px;
}
}
.nav-links a:hover {
color: var(--text-primary);
background-color: var(--card-card);
}
.nav-links a.active {
color: var(--accent-accent);
}
.user-profile {
position: relative;
justify-self: flex-end;
@@ -235,31 +139,22 @@ body {
gap: 0.25rem;
}
.user-name {
font-weight: 600;
font-size: 14px;
}
.nav-logout-timer {
font-size: 12px;
color: var(--text-secondary);
}
.user-dropdown {
position: absolute;
top: 100%;
right: 0;
margin-top: 0.5rem;
background: var(--card-card);
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
min-width: 200px;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
width: 200px;
display: none;
z-index: 1000;
overflow: hidden;
}
.user-dropdown.show {
display: block;
animation: dropdownShow 0.2s ease;
}
.dropdown-item {
@@ -461,47 +356,6 @@ body {
font-style: italic;
}
.loading-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.7);
display: flex;
justify-content: center;
align-items: center;
z-index: 9999;
}
.loading-container {
background-color: var(--background);
padding: 2rem;
border-radius: 16px;
text-align: center;
max-width: 90%;
width: 300px;
}
.loading-logo {
width: 80px;
margin-bottom: 1rem;
}
.loading-text {
font-size: 18px;
font-weight: 600;
margin-bottom: 0.5rem;
color: var(--text-primary);
}
.loading-text2 {
font-size: 14px;
color: var(--text-secondary);
}
.empty-state {
text-align: center;
padding: 2rem;
@@ -524,4 +378,15 @@ body {
flex-direction: column;
gap: 0.25rem;
}
}
@keyframes dropdownShow {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}

View File

@@ -1,89 +1,63 @@
function getCookie(name) {
const cookieName = `${name}=`;
const decodedCookie = decodeURIComponent(document.cookie);
const cookieArray = decodedCookie.split(';');
for(let i = 0; i < cookieArray.length; i++) {
let cookie = cookieArray[i];
while (cookie.charAt(0) === ' ') {
cookie = cookie.substring(1);
}
if (cookie.indexOf(cookieName) === 0) {
return cookie.substring(cookieName.length, cookie.length);
}
}
return null;
}
function shortenSchoolName(name, maxLength = 50) {
if (!name) return '';
if (name.length <= maxLength) return name;
const parts = name.split(' - ');
if (parts.length === 2) {
const [code, fullName] = parts;
if (fullName.length > maxLength - code.length - 3) {
return `${code} - ${fullName.substring(0, maxLength - code.length - 6)}...`;
}
}
return name.substring(0, maxLength - 3) + '...';
}
async function waitForElement(selector) {
return new Promise(resolve => {
if (document.querySelector(selector)) {
return resolve(document.querySelector(selector));
}
const observer = new MutationObserver(mutations => {
if (document.querySelector(selector)) {
observer.disconnect();
resolve(document.querySelector(selector));
async function fetchHomeworkData() {
try {
const currentDomain = window.location.hostname;
const apiUrl = `https://${currentDomain}/api/TanuloHaziFeladatApi/GetTanulotHaziFeladatGrid?sort=HaziFeladatHatarido-asc&page=1&pageSize=100&group=&filter=&data=%7B%22RegiHaziFeladatokElrejtese%22%3Afalse%7D&_=${Date.now()}`;
const response = await fetch(apiUrl, {
method: 'GET',
credentials: 'include',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
});
observer.observe(document.body, {
childList: true,
subtree: true
});
});
if (!response.ok) {
throw new Error('Network response was not ok');
}
const data = await response.json();
return data;
} catch (error) {
console.error('Error fetching homework data:', error);
return { Data: [], Total: 0 };
}
}
async function collectHomeworkData() {
await waitForElement('#TanulotHaziFeladatkGrid');
await new Promise(resolve => setTimeout(resolve, 1000));
const apiData = await fetchHomeworkData();
const basicData = {
schoolInfo: {
name: getCookie('schoolName') || 'Iskola',
id: getCookie('schoolCode') || ''
name: cookieManager.get('schoolName') || 'Iskola',
id: cookieManager.get('schoolCode') || ''
},
userData: {
name: getCookie('userName') || 'Felhasználó',
name: cookieManager.get('userName') || 'Felhasználó',
time: document.querySelector('.usermenu_timer')?.textContent?.trim() || '45:00'
}
};
const homeworkItems = [];
const rows = document.querySelectorAll('#TanulotHaziFeladatkGrid .k-grid-content tr');
rows.forEach(row => {
const cells = row.querySelectorAll('td');
if (cells.length >= 7) {
homeworkItems.push({
subject: cells[3]?.textContent?.trim() || '',
teacher: cells[4]?.textContent?.trim() || '',
description: cells[5]?.textContent?.trim() || '',
createdDate: cells[6]?.textContent?.trim() || '',
deadline: cells[7]?.textContent?.trim() || ''
});
}
});
if (apiData.Data && Array.isArray(apiData.Data)) {
apiData.Data.forEach(item => {
homeworkItems.push({
id: item.ID,
subject: item.TantargyNev || '',
teacher: item.TanarNeve || '',
description: item.HaziFeladatSzoveg || '',
createdDate: formatApiDate(item.HaziFeladatRogzitesDatuma),
deadline: formatApiDate(item.HaziFeladatHatarido),
completed: item.MegoldottHF_BOOL || false,
classGroup: item.OsztalyCsoport || ''
});
});
}
const groupedHomework = {};
homeworkItems.forEach(homework => {
const deadlineDate = homework.deadline.split(' ').slice(0, 3).join(' ');
const deadlineDate = homework.deadline.split(' ')[0];
if (!groupedHomework[deadlineDate]) {
groupedHomework[deadlineDate] = [];
}
@@ -93,26 +67,28 @@ async function collectHomeworkData() {
return { basicData, homeworkItems, groupedHomework };
}
function showLoadingScreen() {
const loadingHTML = `
<div class="loading-overlay">
<div class="loading-container">
<img src="${chrome.runtime.getURL('images/firka_logo.png')}" alt="Firka" class="loading-logo">
<div class="loading-text">Betöltés alatt...</div>
<p class="loading-text2">Kis türelmet</p>
</div>
</div>
`;
function formatApiDate(dateString) {
if (!dateString) return '';
document.body.insertAdjacentHTML('beforeend', loadingHTML);
}
function hideLoadingScreen() {
const loadingOverlay = document.querySelector('.loading-overlay');
if (loadingOverlay) {
loadingOverlay.style.opacity = '0';
loadingOverlay.style.transition = 'opacity 0.3s ease';
setTimeout(() => loadingOverlay.remove(), 300);
try {
const date = new Date(dateString);
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
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})`;
} catch (error) {
return dateString;
}
}
@@ -124,7 +100,7 @@ function isTomorrow(dateStr) {
if (parts.length < 3) return false;
const year = parseInt(parts[0].trim());
const month = parseInt(parts[1].trim()) - 1; // JS months are 0-indexed
const month = parseInt(parts[1].trim()) - 1;
const day = parseInt(parts[2].trim());
const homeworkDate = new Date(year, month, day);
@@ -143,88 +119,25 @@ function isTomorrow(dateStr) {
}
async function transformHomeworkPage() {
showLoadingScreen();
const { basicData, homeworkItems, groupedHomework } = await collectHomeworkData();
const schoolNameFull = `${basicData.schoolInfo.id} - ${basicData.schoolInfo.name}`;
const shortenedSchoolName = shortenSchoolName(schoolNameFull);
document.body.innerHTML = `
<div class="kreta-container">
<header class="kreta-header">
<div class="school-info">
<p class="logo-text">
<img src="${chrome.runtime.getURL('images/firka_logo.png')}" alt="Firka" class="logo">
Firka
</p>
<div class="school-details" title="${schoolNameFull}">
${shortenedSchoolName}
</div>
</div>
<nav class="kreta-nav">
<div class="nav-links">
<a href="/Intezmeny/Faliujsag" data-page="dashboard" class="nav-item">
<img src="${chrome.runtime.getURL('icons/dashboard-inactive.svg')}" alt="Kezdőlap">
Kezdőlap
</a>
<a href="/TanuloErtekeles/Osztalyzatok" data-page="grades" class="nav-item">
<img src="${chrome.runtime.getURL('icons/grades-inactive.svg')}" alt="Jegyek">
Jegyek
</a>
<a href="/Orarend/InformaciokOrarend" data-page="timetable" class="nav-item">
<img src="${chrome.runtime.getURL('icons/timetable-inactive.svg')}" alt="Órarend">
Órarend
</a>
<a href="/Hianyzas/Hianyzasok" data-page="absences" class="nav-item">
<img src="${chrome.runtime.getURL('icons/absences-inactive.svg')}" alt="Mulasztások">
Mulasztások
</a>
<a href="/Tanulo/TanuloHaziFeladat" data-page="other" class="nav-item active">
<img src="${chrome.runtime.getURL('icons/others.svg')}" alt="Egyéb">
Egyéb
</a>
</div>
</nav>
<div class="user-profile">
<button class="user-dropdown-btn">
<div class="user-info">
<span class="user-name">${basicData.userData.name}</span>
<span class="nav-logout-timer" id="logoutTimer">${basicData.userData.time}</span>
</div>
</button>
<div class="user-dropdown">
<a href="/Adminisztracio/Profil" data-page="profile" class="dropdown-item">
<img src="${chrome.runtime.getURL('icons/profile.svg')}" alt="Profil">
Profil
</a>
<a href="#" class="dropdown-item" id="settingsBtn">
<img src="${chrome.runtime.getURL('icons/settings.svg')}" alt="Beállítások">
Beállítások
</a>
<a href="/Home/Logout" data-page="logout" class="dropdown-item">
<img src="${chrome.runtime.getURL('icons/logout.svg')}" alt="Kijelentkezés">
Kijelentkezés
</a>
</div>
</div>
</header>
${createTemplate.header()}
<main class="kreta-main">
<div class="filter-card">
<div class="filter-header">
<h2>Szűrés</h2>
<h2>${LanguageManager.t('homework.filter_title')}</h2>
</div>
<div class="filter-content">
<div class="filter-group">
<label>
<!--<span class="material-icons-round">subject</span>-->
Tantárgy
${LanguageManager.t('homework.subject')}
</label>
<select id="subjectFilter">
<option value="">Összes tantárgy</option>
<option value="">${LanguageManager.t('homework.all_subjects')}</option>
${[...new Set(homeworkItems.map(item => item.subject))]
.sort()
.map(subject => `<option value="${subject}">${subject}</option>`)
@@ -234,10 +147,10 @@ async function transformHomeworkPage() {
<div class="filter-group">
<label>
<!--<span class="material-icons-round">person</span>-->
Tanár
${LanguageManager.t('homework.teacher')}
</label>
<select id="teacherFilter">
<option value="">Összes tanár</option>
<option value="">${LanguageManager.t('homework.all_teachers')}</option>
${[...new Set(homeworkItems.map(item => item.teacher))]
.sort()
.map(teacher => `<option value="${teacher}">${teacher}</option>`)
@@ -247,13 +160,13 @@ async function transformHomeworkPage() {
<div class="filter-group">
<label>
<!--<span class="material-icons-round">date_range</span>-->
Határidő
${LanguageManager.t('homework.due_date')}
</label>
<select id="deadlineFilter">
<option value="">Összes határidő</option>
<option value="tomorrow">Holnapi határidő</option>
<option value="thisWeek">Ezen a héten</option>
<option value="nextWeek">Jövő héten</option>
<option value="">${LanguageManager.t('homework.all_deadlines')}</option>
<option value="tomorrow">${LanguageManager.t('homework.tomorrow_deadline')}</option>
<option value="thisWeek">${LanguageManager.t('homework.this_week')}</option>
<option value="nextWeek">${LanguageManager.t('homework.next_week')}</option>
</select>
</div>
</div>
@@ -268,22 +181,36 @@ async function transformHomeworkPage() {
setupFilters(homeworkItems, groupedHomework);
setupUserDropdown();
setupLogoutTimer();
hideLoadingScreen();
setupMobileNavigation();
loadingScreen.hide();
}
function renderHomeworkList(groupedHomework) {
const sortedDates = Object.keys(groupedHomework).sort((a, b) => {
const dateA = new Date(a.replace(/\./g, ''));
const dateB = new Date(b.replace(/\./g, ''));
return dateA - dateB;
const partsA = a.split('.');
const partsB = b.split('.');
const monthA = parseInt(partsA[0]) - 1;
const dayA = parseInt(partsA[1]);
const monthB = parseInt(partsB[0]) - 1;
const dayB = parseInt(partsB[1]);
const currentYear = new Date().getFullYear();
const currentMonth = new Date().getMonth();
const yearA = monthA < currentMonth ? currentYear + 1 : currentYear;
const yearB = monthB < currentMonth ? currentYear + 1 : currentYear;
const dateA = new Date(yearA, monthA, dayA);
const dateB = new Date(yearB, monthB, dayB);
return dateB - dateA;
});
if (sortedDates.length === 0) {
return `
<div class="empty-state">
<p>Nincs megjeleníthető házi feladat.</p>
<p>${LanguageManager.t('homework.no_homework')}</p>
</div>
`;
}
@@ -303,7 +230,7 @@ function renderHomeworkList(groupedHomework) {
<div class="homework-item ${isTomorrowClass}" data-subject="${homework.subject}" data-teacher="${homework.teacher}">
<div class="homework-header">
<div class="homework-subject">${homework.subject}</div>
<div class="homework-deadline ${urgentClass}">${formatDeadline(homework.deadline)}</div>
<div class="homework-deadline ${urgentClass}">${homework.deadline}</div>
</div>
<div class="homework-content">${formatHomeworkDescription(homework.description)}</div>
<div class="homework-footer">
@@ -327,37 +254,32 @@ function formatDateHeader(dateStr) {
tomorrow.setDate(tomorrow.getDate() + 1);
const parts = dateStr.split('.');
if (parts.length < 3) return dateStr;
if (parts.length < 2) return dateStr;
const year = parseInt(parts[0].trim());
const month = parseInt(parts[1].trim()) - 1;
const day = parseInt(parts[2].trim());
const date = new Date(year, month, day);
const month = parseInt(parts[0].trim()) - 1;
const day = parseInt(parts[1].trim());
const currentYear = today.getFullYear();
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}`;
}
function formatDeadline(dateStr) {
if (!dateStr) return '';
if (isTomorrow(dateStr)) {
return `Határidő: ${dateStr} (holnap!)`;
}
return `Határidő: ${dateStr}`;
}
function formatDate(dateStr) {
if (!dateStr) return '';
return dateStr;
@@ -366,14 +288,6 @@ function formatDate(dateStr) {
function formatHomeworkDescription(description) {
if (!description) return '';
description = description.replace(/(\d+\.)\s*(\w[^\n.]*)/g, '<strong>$1 $2</strong>');
description = description.replace(/(Határidő:)\s*([^\n]+)/g, '<div class="homework-requirement"><span class="requirement-label">$1</span> $2</div>');
description = description.replace(/(MS\s+[^\n.]+szerint\s+adható\s+be\.)/g, '<div class="homework-requirement"><span class="requirement-label">Beadás:</span> $1</div>');
description = description.replace(/\n/g, '<br>');
return description;
@@ -437,11 +351,11 @@ function setupFilters(homeworkItems, groupedHomework) {
const startOfWeek = new Date(today);
const dayOfWeek = today.getDay() || 7; // Convert Sunday from 0 to 7
startOfWeek.setDate(today.getDate() - dayOfWeek + 1); // Monday
const dayOfWeek = today.getDay() || 7;
startOfWeek.setDate(today.getDate() - dayOfWeek + 1);
const endOfWeek = new Date(startOfWeek);
endOfWeek.setDate(startOfWeek.getDate() + 6); // Sunday
endOfWeek.setDate(startOfWeek.getDate() + 6);
const startOfNextWeek = new Date(endOfWeek);
@@ -478,7 +392,7 @@ function setupFilters(homeworkItems, groupedHomework) {
if (!emptyState) {
emptyState = document.createElement('div');
emptyState.className = 'empty-state';
emptyState.innerHTML = '<p>Nincs a szűrési feltételeknek megfelelő házi feladat.</p>';
emptyState.innerHTML = `<p>${LanguageManager.t('homework.no_matching_homework')}</p>`;
homeworkList.appendChild(emptyState);
}
@@ -519,50 +433,6 @@ function setupFilters(homeworkItems, groupedHomework) {
deadlineFilter.addEventListener('change', applyFilters);
}
function setupUserDropdown() {
const userBtn = document.querySelector('.user-dropdown-btn');
const userDropdown = document.querySelector('.user-dropdown');
userBtn?.addEventListener('click', (e) => {
e.stopPropagation();
userDropdown?.classList.toggle('show');
});
document.addEventListener('click', () => {
userDropdown?.classList.remove('show');
});
document.getElementById('settingsBtn')?.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
const url = chrome.runtime.getURL('settings/index.html');
window.open(url, '_blank', 'width=400,height=600');
});
}
function setupLogoutTimer() {
const timerElement = document.querySelector('.nav-logout-timer');
if (!timerElement) return;
const timeString = timerElement.textContent;
const startTime = parseInt(timeString?.match(/\d+/)?.[0] || "45");
let timeLeft = startTime * 60;
const updateTimer = () => {
const minutes = Math.floor(timeLeft / 60);
const seconds = timeLeft % 60;
timerElement.textContent = `${minutes}:${seconds.toString().padStart(2, '0')}`;
if (timeLeft <= 0) {
window.location.href = '/Home/Logout';
}
timeLeft--;
};
updateTimer();
setInterval(updateTimer, 1000);
}
if (window.location.href.includes('/Tanulo/TanuloHaziFeladat')) {

319
i18n/en.json Normal file
View File

@@ -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"
}
}

319
i18n/hu.json Normal file
View File

@@ -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"
}
}

BIN
images/cactus.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

BIN
images/chrome.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
images/firefox.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

View File

@@ -1,28 +1,3 @@
@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;
}
:root {
--icon-invert: 0.1;
--icon-sepia: 0.1;

View File

@@ -1,21 +1,20 @@
async function transformLoginPage() {
try {
if (document.readyState !== 'complete') {
await new Promise(resolve => {
window.addEventListener('load', resolve);
});
}
const existingForm = document.querySelector('form');
const formData = {
action: existingForm?.getAttribute('action') || '',
returnUrl: document.querySelector('#ReturnUrl')?.value || '',
instituteCode: document.querySelector('#instituteCode')?.value || '',
requestToken: document.querySelector('input[name="__RequestVerificationToken"]')?.value || ''
requestToken: document.querySelector('input[name="__RequestVerificationToken"]')?.value || '',
userName: document.querySelector('#UserName')?.value || '',
password: document.querySelector('#Password')?.value || ''
};
const titleElement = document.querySelector('.page-title');
const schoolInfo = {
@@ -23,19 +22,16 @@ async function transformLoginPage() {
kretaId: '',
omCode: ''
};
const spanElement = titleElement?.querySelector('span');
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() || '';
const systemMessage = rawSystemMessage.replace('Rendszerértesítés', '').trim();
const newHTML = `
<div class="login-container">
@@ -48,7 +44,7 @@ async function transformLoginPage() {
<h1 class="school-name">${schoolInfo.name}</h1>
<div class="school-details">
${schoolInfo.kretaId ? `<div>${schoolInfo.kretaId}</div>` : ''}
${schoolInfo.omCode ? `<div>KRÉTA azonosító: ${schoolInfo.omCode}</div>` : ''}
${schoolInfo.omCode ? `<div>${LanguageManager.t('login.kreta_id')}: ${schoolInfo.omCode}</div>` : ''}
</div>
</div>
@@ -61,27 +57,27 @@ async function transformLoginPage() {
<div class="form-group">
<input class="form-control" type="text" id="UserName" name="UserName"
placeholder="Felhasználónév" maxlength="256" autocomplete="username" required>
<div class="error-message">Kérjük, add meg a felhasználóneved.</div>
placeholder="${LanguageManager.t('login.username_placeholder')}" maxlength="256" autocomplete="username" required value="${formData.userName}">
<div class="error-message">${LanguageManager.t('login.username_required')}</div>
</div>
<div class="form-group password-group">
<input class="form-control" type="password" id="Password" name="Password"
placeholder="Jelszó" maxlength="256" autocomplete="current-password" required>
<button type="button" class="show-password" aria-label="Jelszó mutatása">
placeholder="${LanguageManager.t('login.password_placeholder')}" maxlength="256" autocomplete="current-password" required value="${formData.password}">
<button type="button" class="show-password" aria-label="${LanguageManager.t('login.show_password')}">
<img src="${chrome.runtime.getURL('icons/eye-off.svg')}" alt="Show password" class="icon-eye">
</button>
<div class="error-message">Kérjük, add meg a jelszavad.</div>
<div class="error-message">${LanguageManager.t('login.password_required')}</div>
</div>
<div class="form-actions">
<button type="submit" class="btn-login">
<span class="spinner"></span>
<span class="btn-text">Bejelentkezés</span>
<span class="btn-text">${LanguageManager.t('login.login_button')}</span>
</button>
<div class="help-links">
<a href="https://${schoolInfo.omCode ? `${schoolInfo.omCode}` : ''}.e-kreta.hu/Adminisztracio/ElfelejtettJelszo" class="help-link">Elfelejtettem a jelszavam</a>
<a href="https://tudasbazis.ekreta.hu/pages/viewpage.action?pageId=2425086" target="_blank" class="help-link">Nem tudsz bejelentkezni?</a>
<a href="https://${schoolInfo.omCode ? `${schoolInfo.omCode}` : ''}.e-kreta.hu/Adminisztracio/ElfelejtettJelszo" class="help-link">${LanguageManager.t('login.forgot_password')}</a>
<a href="https://tudasbazis.ekreta.hu/pages/viewpage.action?pageId=2425086" target="_blank" class="help-link">${LanguageManager.t('login.help_link')}</a>
</div>
</div>
</form>
@@ -89,21 +85,19 @@ async function transformLoginPage() {
${systemMessage ? `
<div class="system-message">
<h4>Rendszerértesítés</h4>
<h4>${LanguageManager.t('login.system_message')}</h4>
<p>${systemMessage}</p>
</div>
` : ''}
<footer class="login-footer">
<a href="https://tudasbazis.ekreta.hu/pages/viewpage.action?pageId=4064926"
target="_blank" class="privacy-link">Adatkezelési tájékoztató</a>
target="_blank" class="privacy-link">${LanguageManager.t('login.privacy_policy')}</a>
</footer>
</div>
`;
document.body.innerHTML = newHTML;
setupEventListeners();
@@ -117,7 +111,6 @@ function setupEventListeners() {
const passwordInput = document.getElementById('Password');
const togglePasswordBtn = document.querySelector('.show-password');
const formInputs = document.querySelectorAll('.form-control');
if (togglePasswordBtn && passwordInput) {
togglePasswordBtn.addEventListener('click', () => {
@@ -175,11 +168,6 @@ function handleSubmit(event) {
isValid = false;
}
});
if (!isValid) {
return;
}
const submitButton = form.querySelector('.btn-login');
const spinner = submitButton.querySelector('.spinner');

301
login/twofactor.css Normal file
View File

@@ -0,0 +1,301 @@
:root {
--icon-invert: 0.1;
--icon-sepia: 0.1;
--icon-saturate: 0.1;
--icon-hue-rotate: 0deg;
--icon-brightness: 0.1;
}
:root[data-theme="light-green"] {
--icon-invert: 0.1;
--icon-sepia: 0.1;
--icon-saturate: 0.1;
--icon-hue-rotate: 0deg;
--icon-brightness: 0.1;
}
:root[data-theme="dark-blue"] {
--icon-invert: 0.9;
--icon-sepia: 0.1;
--icon-saturate: 0.1;
--icon-hue-rotate: 0deg;
--icon-brightness: 1;
}
:root[data-theme="dark-green"] {
--icon-invert: 0.9;
--icon-sepia: 0.1;
--icon-saturate: 0.1;
--icon-hue-rotate: 0deg;
--icon-brightness: 1;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
margin: 0;
padding: 0;
color: var(--text-primary);
background-color: var(--background) !important;
font-family: "Montserrat", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif !important;
min-height: 100vh;
font-size: 16px;
display: flex;
align-items: center;
justify-content: center;
}
.login-container {
width: 90%;
max-width: 500px;
padding: 20px;
margin: 0 auto;
}
.login-card {
background: var(--card-card);
padding: 24px;
margin-bottom: 16px;
border-radius: 24px;
box-shadow: 0px 1px var(--shadow-blur) 0px var(--accent-shadow);
}
.card-header {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
margin: 16px 0;
background: var(--card-card) !important;
border-bottom: 1px solid rgba(0, 0, 0, 0) !important;
}
.logo-text {
color: var(--text-primary);
text-align: center;
font-family: Montserrat;
font-size: 20px;
font-style: normal;
font-weight: 700;
line-height: normal;
}
.logo {
width: 48px;
height: 48px;
border-radius: 12px;
}
.twofactor-title {
color: var(--text-primary);
text-align: center;
font-family: Montserrat;
font-size: 24px;
font-style: normal;
font-weight: 600;
line-height: 130%;
margin-bottom: 16px;
}
.twofactor-form {
width: 100%;
}
.form-group {
margin-bottom: 16px;
}
.form-control {
display: flex;
height: 48px;
padding: 0px 14px;
align-items: center;
gap: 10px;
align-self: stretch;
border-radius: 12px;
background: var(--accent-15) !important;
border: 0px solid var(--accent-15) !important;
color: var(--text-primary) !important;
}
.form-control:focus {
outline: none;
border-color: var(--accent-accent) !important;
}
.form-control::placeholder {
color: var(--text-secondary) !important;
}
.password-group {
position: relative;
}
.show-password {
position: absolute;
right: 12px;
top: 50%;
transform: translateY(-50%);
background: none;
border: none;
padding: 4px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
}
.icon-eye {
width: 20px;
height: 20px;
opacity: 0.6;
transition: opacity 0.2s ease;
filter: invert(var(--icon-invert)) sepia(var(--icon-sepia)) saturate(var(--icon-saturate)) hue-rotate(var(--icon-hue-rotate)) brightness(var(--icon-brightness));
}
.show-password:hover .icon-eye {
opacity: 1;
}
.form-check {
display: flex;
align-items: center;
margin-top: 16px;
margin-bottom: 16px;
}
.form-check-input {
width: 20px;
height: 20px;
margin-right: 8px;
border-radius: 6px;
border: 2px solid var(--accent-accent);
background-color: var(--card-card);
cursor: pointer;
}
.form-check-input:checked {
background-color: var(--accent-accent);
border-color: var(--accent-accent);
}
.form-check-label {
color: var(--text-primary);
font-family: Figtree;
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 130%;
cursor: pointer;
}
.btn-kreta {
display: flex;
height: 48px;
padding: 0px 24px;
justify-content: center;
align-items: center;
gap: 8px;
border-radius: 12px;
background: var(--accent-accent);
color: white;
font-family: Montserrat;
font-size: 16px;
font-style: normal;
font-weight: 600;
line-height: normal;
border: none;
cursor: pointer;
transition: background-color 0.2s ease;
}
.btn-kreta:hover {
background-color: var(--accent-secondary);
}
.btn-link {
background: none;
border: none;
color: var(--accent-accent);
font-family: Figtree;
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 130%;
text-decoration: underline;
cursor: pointer;
padding: 0;
}
.btn-link:hover {
color: var(--accent-secondary);
}
.subtext {
color: var(--text-primary);
font-family: Figtree;
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 130%;
text-align: center;
}
.login-footer {
margin-top: 24px;
text-align: center;
}
.privacy-link {
color: var(--text-secondary);
font-family: Figtree;
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 130%;
text-decoration: none;
}
.privacy-link:hover {
text-decoration: underline;
}
.error-message {
display: none;
color: var(--error-text);
font-family: Figtree;
font-size: 12px;
font-style: normal;
font-weight: 400;
line-height: 130%;
margin-top: 4px;
}
.error-message.show {
display: block;
}
.form-control.error {
border: 1px solid var(--error-accent) !important;
}
/* Hide original elements */
header, main > .container-fluid, footer {
display: none !important;
}
/* Responsive adjustments */
@media (max-width: 576px) {
.login-container {
width: 100%;
padding: 16px;
}
.login-card {
padding: 16px;
}
}

171
login/twofactor.js Normal file
View File

@@ -0,0 +1,171 @@
async function transformTwoFactorPage() {
try {
if (document.readyState !== 'complete') {
await new Promise(resolve => {
window.addEventListener('load', resolve);
});
}
if (typeof loadingScreen !== 'undefined') {
loadingScreen.show();
}
const existingForm = document.querySelector('form');
const formData = {
action: existingForm?.getAttribute('action') || '',
clientId: document.querySelector('#ClientId')?.value || '',
rememberLogin: document.querySelector('#RememberLogin')?.value || 'False',
returnUrl: document.querySelector('#ReturnUrl')?.value || '',
isRecoveryCode: document.querySelector('#IsRecoveryCode')?.value || 'False',
requestToken: document.querySelector('input[name="__RequestVerificationToken"]')?.value || '',
trustDeviceValue: document.querySelector('input[name="TrustDevice"][type="hidden"]')?.value || 'false'
};
const newHTML = `
<div class="login-container">
<div class="login-card">
<div class="card-header">
<p class="logo-text">
<img src=${chrome.runtime.getURL('images/firka_logo.png')} alt="Firka" class="logo">
Firka
</p>
<h1 class="twofactor-title">${LanguageManager.t('twofactor.title')}</h1>
</div>
<form class="twofactor-form" action="${formData.action}" method="post" id="twoFactorForm">
<input type="hidden" id="ClientId" name="ClientId" value="${formData.clientId}">
<input type="hidden" id="RememberLogin" name="RememberLogin" value="${formData.rememberLogin}">
<input type="hidden" id="ReturnUrl" name="ReturnUrl" value="${formData.returnUrl}">
<input type="hidden" id="IsRecoveryCode" name="IsRecoveryCode" value="${formData.isRecoveryCode}">
<input name="__RequestVerificationToken" type="hidden" value="${formData.requestToken}">
<div class="form-group password-group">
<input class="form-control" type="password" id="VerificationCode" name="VerificationCode"
placeholder="${LanguageManager.t('twofactor.code_placeholder')}" maxlength="256" autocomplete="off" required autofocus>
<button type="button" class="show-password" aria-label="${LanguageManager.t('twofactor.show_code')}">
<img src="${chrome.runtime.getURL('icons/eye-off.svg')}" alt="Show password" class="icon-eye">
</button>
<div class="error-message">${LanguageManager.t('twofactor.code_required')}</div>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="trustDevice" name="TrustDevice" value="true">
<label class="form-check-label" for="trustDevice">
${LanguageManager.t('twofactor.trust_device')}
</label>
<input name="TrustDevice" type="hidden" value="false">
</div>
<div class="d-flex justify-content-center mb-3 mt-4">
<button type="submit" class="btn-kreta">${LanguageManager.t('twofactor.verify_button')}</button>
</div>
<div class="d-flex justify-content-center mt-3">
<span class="subtext">
${LanguageManager.t('twofactor.no_access')}
<button type="submit" class="btn-link" formaction="/account/loginwithrecoverycode">
${LanguageManager.t('twofactor.recovery_code')}
</button>
</span>
</div>
</form>
</div>
<footer class="login-footer">
<a href="https://tudasbazis.ekreta.hu/pages/viewpage.action?pageId=4064926"
target="_blank" class="privacy-link">${LanguageManager.t('login.privacy_policy')}</a>
</footer>
</div>
`;
document.body.innerHTML = newHTML;
applyTheme();
setupEventListeners();
if (typeof loadingScreen !== 'undefined') {
loadingScreen.hide();
}
} catch (error) {
console.error('Error transforming two-factor page:', error);
if (typeof loadingScreen !== 'undefined') {
loadingScreen.hide();
}
}
}
function applyTheme() {
try {
if (typeof getCookie === 'function') {
const theme = getCookie('theme') || 'light-blue';
document.documentElement.setAttribute('data-theme', theme);
}
} catch (error) {
console.error('Error applying theme:', error);
}
}
function setupEventListeners() {
const twoFactorForm = document.getElementById('twoFactorForm');
const verificationInput = document.getElementById('VerificationCode');
const togglePasswordBtn = document.querySelector('.show-password');
const formInputs = document.querySelectorAll('.form-control');
if (togglePasswordBtn && verificationInput) {
togglePasswordBtn.addEventListener('click', () => {
const isPassword = verificationInput.type === 'password';
verificationInput.type = isPassword ? 'text' : 'password';
const icon = togglePasswordBtn.querySelector('.icon-eye');
icon.src = chrome.runtime.getURL(`icons/${isPassword ? 'eye-on' : 'eye-off'}.svg`);
});
}
formInputs.forEach(input => {
input.addEventListener('input', () => {
validateInput(input);
});
input.addEventListener('blur', () => {
validateInput(input, true);
});
});
if (twoFactorForm) {
twoFactorForm.addEventListener('submit', handleSubmit);
}
}
function validateInput(input, showError = false) {
const isValid = input.value.trim().length > 0;
const errorElement = input.nextElementSibling?.nextElementSibling;
if (!isValid && showError) {
input.classList.add('error');
errorElement?.classList.add('show');
} else {
input.classList.remove('error');
errorElement?.classList.remove('show');
}
return isValid;
}
function handleSubmit(event) {
event.preventDefault();
const form = event.target;
const inputs = form.querySelectorAll('.form-control[required]');
let isValid = true;
inputs.forEach(input => {
if (!validateInput(input, true)) {
isValid = false;
}
});
if (isValid) {
const submitButton = form.querySelector('.btn-kreta');
if (submitButton) {
submitButton.disabled = true;
submitButton.innerHTML = `<span class="spinner"></span><span class="btn-text">${LanguageManager.t('twofactor.verifying')}</span>`;
}
form.submit();
}
}
transformTwoFactorPage();

View File

@@ -1,21 +1,4 @@
(() => {
function getCookie(name) {
const cookieName = `${name}=`;
const decodedCookie = decodeURIComponent(document.cookie);
const cookieArray = decodedCookie.split(';');
for(let i = 0; i < cookieArray.length; i++) {
let cookie = cookieArray[i];
while (cookie.charAt(0) === ' ') {
cookie = cookie.substring(1);
}
if (cookie.indexOf(cookieName) === 0) {
return cookie.substring(cookieName.length, cookie.length);
}
}
return null;
}
function loadFonts() {
// Create a new style element
const style = document.createElement('style');
@@ -27,8 +10,8 @@
}
function transformLogoutPage() {
// Get current theme and school ID from cookies
const theme = getCookie('themePreference') || localStorage.getItem('themePreference') || 'light-blue';
const instituteCode = getCookie('schoolSubdomain');
const theme = cookieManager.get('themePreference') || localStorage.getItem('themePreference') || 'light-green';
const instituteCode = cookieManager.get('schoolSubdomain');
document.documentElement.setAttribute('data-theme', theme);
// Create new HTML structure

View File

@@ -1,7 +1,7 @@
{
"manifest_version": 3,
"name": "Firxa",
"version": "1.1.0",
"version": "1.2.2",
"description": "KRÉTA webes verziójának újraírása",
"icons": {
"128": "images/firka_logo_128.png"
@@ -15,20 +15,24 @@
"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/*"]
"matches": ["https://*.e-kreta.hu/*", "https://idp.e-kreta.hu/*"]
}],
"content_scripts": [
{
"matches": [
"https://*.e-kreta.hu/*"
],
"js": ["global/maintenance.js", "global/theme.js", "global/navigation.js"],
"css": ["global/theme.css", "global/navigation.css"],
"js": ["tools/cookieManager.js", "tools/helper.js", "tools/loadingScreen.js", "tools/createTemplate.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"
},
{
@@ -43,6 +47,11 @@
"js": ["login/login.js"],
"css": ["login/login.css"]
},
{
"matches": ["https://idp.e-kreta.hu/account/loginwithtwofactor*"],
"js": ["login/twofactor.js"],
"css": ["login/twofactor.css"]
},
{
"matches": [
"https://*.e-kreta.hu/Hianyzas/Hianyzasok*"
@@ -114,6 +123,14 @@
"js": ["homework/homework.js"],
"css": ["homework/homework.css"],
"run_at": "document_end"
},
{
"matches": [
"https://intezmenykereso.e-kreta.hu/"
],
"js": ["search/search.js"],
"css": ["search/search.css"],
"run_at": "document_end"
}
]
}

View File

@@ -59,37 +59,6 @@ body {
max-width: 300px;
}
.nav-links {
display: flex;
gap: clamp(0.5rem, 2vw, 1rem);
padding: 0.25rem;
justify-content: center;
}
.nav-links a {
color: var(--text-secondary);
text-decoration: none;
padding: clamp(0.5rem, 1.5vw, 1rem) 0.5rem;
font-weight: 500;
white-space: nowrap;
transition: all 0.2s ease;
display: flex;
align-items: center;
gap: 0.5rem;
border-radius: 8px;
}
.nav-links a:hover {
color: var(--text-primary);
background-color: var(--card-card);
}
.nav-links a.active {
color: var(--accent-accent);
}
.user-profile {
position: relative;
justify-self: flex-end;
@@ -115,19 +84,6 @@ body {
text-align: right;
}
.user-name {
display: block;
color: var(--text-primary);
font-size: 16px;
font-weight: 500;
}
.user-time {
display: block;
color: var(--text-secondary);
font-size: 14px;
}
.user-dropdown {
position: absolute;
top: 100%;
@@ -426,10 +382,10 @@ body {
@media (max-width: 768px) {
.kreta-header {
grid-template-columns: 1fr auto;
grid-template-columns: 1fr auto auto;
grid-template-areas:
"school user"
"nav nav";
"school toggle user"
"nav nav nav";
padding: 1rem;
gap: 0.5rem;
}
@@ -438,12 +394,6 @@ body {
grid-area: school;
}
.nav-links {
grid-area: nav;
margin-top: 0.5rem;
justify-content: flex-start;
}
.user-profile {
grid-area: user;
}

View File

@@ -1,34 +1,4 @@
(() => {
function getCookie(name) {
const cookieName = `${name}=`;
const decodedCookie = decodeURIComponent(document.cookie);
const cookieArray = decodedCookie.split(';');
for(let i = 0; i < cookieArray.length; i++) {
let cookie = cookieArray[i];
while (cookie.charAt(0) === ' ') {
cookie = cookie.substring(1);
}
if (cookie.indexOf(cookieName) === 0) {
return cookie.substring(cookieName.length, cookie.length);
}
}
return null;
}
function shortenSchoolName(name) {
if (!name) return '';
const maxLength = 30;
if (name.length <= maxLength) return name;
const parts = name.split(' - ');
if (parts.length === 2) {
const [code, fullName] = parts;
return `${code} - ${fullName.substring(0, maxLength - code.length - 5)}...`;
}
return name.substring(0, maxLength - 3) + '...';
}
(() => {
function createSecurityTab() {
return `
<div class="security-content">
@@ -196,25 +166,25 @@
const saveButton = form.querySelector('#saveContacts');
emailInput.value = getCookie('userEmail') || '';
phoneInput.value = getCookie('userPhone') || '';
emailInput.value = cookieManager.get('userEmail') || '';
phoneInput.value = cookieManager.get('userPhone') || '';
saveButton?.addEventListener('click', async () => {
const email = emailInput.value.trim();
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;
}
@@ -229,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'));
}
});
}
@@ -289,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'));
}
});
@@ -306,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;
}
@@ -335,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'));
}
});
@@ -371,67 +341,10 @@
}
}
function createProfileHTML(data) {
const schoolNameFull = `${data.schoolInfo.id} - ${data.schoolInfo.name}`;
const shortenedSchoolName = shortenSchoolName(schoolNameFull);
function createProfileHTML() {
return `
<div class="kreta-container">
<header class="kreta-header">
<div class="school-info">
<p class="logo-text">
<img src=${chrome.runtime.getURL('images/firka_logo.png')} alt="Firka" class="logo">
Firka
</p>
<div class="school-details" title="${schoolNameFull}">
${shortenedSchoolName}
</div>
</div>
<nav class="kreta-nav">
<div class="nav-links">
<a href="/Intezmeny/Faliujsag">
<span class="material-icons-round">calendar_today</span>
Kezdőlap
</a>
<a href="/TanuloErtekeles/Osztalyzatok">
<span class="material-icons-round">bookmark_border</span>
Jegyek
</a>
<a href="/Orarend/InformaciokOrarend">
<span class="material-icons-round">home</span>
Órarend
</a>
<a href="/Hianyzas/Hianyzasok">
<span class="material-icons-round">schedule</span>
Hiányok
</a>
</div>
</nav>
<div class="user-profile">
<button class="user-dropdown-btn">
<div class="user-info">
<span class="user-name">${data.userData.name}</span>
<span class="user-time" id="logoutTimer">${data.userData.time}</span>
</div>
</button>
<div class="user-dropdown">
<a href="/Adminisztracio/Profil" class="dropdown-item">
<span class="material-icons-round">person</span>
Profil
</a>
<a href="#" class="dropdown-item" id="settingsBtn">
<span class="material-icons-round">settings</span>
Beállítások
</a>
<a href="/Home/Logout" class="dropdown-item">
<span class="material-icons-round">logout</span>
Kijelentkezés
</a>
</div>
</div>
</header>
${createTemplate.header()}
<main class="kreta-main">
<div class="card">
@@ -469,34 +382,11 @@
async function init() {
if (window.location.pathname.includes('/Adminisztracio/Profil')) {
const links = [
{ rel: 'preconnect', href: 'https://fonts.googleapis.com' },
{ rel: 'preconnect', href: 'https://fonts.gstatic.com', crossorigin: true },
{ rel: 'stylesheet', href: 'https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&display=swap' },
{ rel: 'stylesheet', href: 'https://fonts.googleapis.com/icon?family=Material+Icons+Round' }
];
createTemplate.importFonts();
links.forEach(link => {
const linkElement = document.createElement('link');
Object.entries(link).forEach(([key, value]) => {
linkElement[key] = value;
});
document.head.appendChild(linkElement);
});
const userData = {
schoolInfo: {
name: getCookie('schoolName') || 'Iskola',
id: getCookie('schoolCode') || ''
},
userData: {
name: getCookie('userName') || 'Felhasználó',
time: document.querySelector('.usermenu_timer')?.textContent?.trim() || '45:00',
email: getCookie('userEmail') || ''
}
};
document.body.innerHTML = createProfileHTML(userData);
document.body.innerHTML = createProfileHTML();
setupUserDropdown();
setupMobileNavigation();
setupEventListeners();
setupContactForm();
}

View File

@@ -1,29 +1,3 @@
@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;
}
* {
box-sizing: border-box;
margin: 0;

View File

@@ -1,11 +1,4 @@
(() => {
const setCookie = (name, value, days = 365) => {
const date = new Date();
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
document.cookie = `${name}=${value}; expires=${date.toUTCString()}; path=/; domain=.e-kreta.hu`;
};
const startLogoutTimer = () => {
let timeLeft = 45 * 60;
const timerElement = document.getElementById('logoutTimer');
@@ -41,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'));
}
};
@@ -73,8 +66,8 @@
<img src="${chrome.runtime.getURL('icons/naplo.svg')}" alt="Napló ikon">
</div>
<div class="role-text">
Ellenőrzőkönyv
<div class="role-description">Jegyek, hiányzások, órarended és egyéb információk megtekintése.</div>
${LanguageManager.t('roleselect.student_book')}
<div class="role-description">${LanguageManager.t('roleselect.student_description')}</div>
</div>
</div>
</div>
@@ -85,8 +78,8 @@
<img src="${chrome.runtime.getURL('icons/dkt.svg')}" alt="DKT ikon">
</div>
<div class="role-text">
Digitális Kollaborációs Tér (DKT)
<div class="role-description">Osztálytermi kommunikáció és feladatok.</div>
${LanguageManager.t('roleselect.dkt_title')}
<div class="role-description">${LanguageManager.t('roleselect.dkt_description')}</div>
</div>
</div>
@@ -95,8 +88,8 @@
<img src="${chrome.runtime.getURL('icons/logout.svg')}" alt="Kijelentkezés ikon">
</div>
<div class="role-text">
Kijelentkezés
<div class="role-description">Kilépés a rendszerből</div>
${LanguageManager.t('roleselect.logout_title')}
<div class="role-description">${LanguageManager.t('roleselect.logout_description')}</div>
</div>
</div>
</div>
@@ -120,34 +113,22 @@
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) {
setCookie('schoolCode', schoolCode);
setCookie('schoolName', fullSchoolName);
setCookie('schoolSubdomain', schoolSubdomain);
cookieManager.set('schoolCode', schoolCode);
cookieManager.set('schoolName', fullSchoolName);
cookieManager.set('schoolSubdomain', schoolSubdomain);
}
if (userName) {
setCookie('userName', userName);
cookieManager.set('userName', userName);
}
document.body.innerHTML = createHTML(schoolCode, fullSchoolName, userName);
const links = [
{ rel: 'preconnect', href: 'https://fonts.googleapis.com' },
{ rel: 'preconnect', href: 'https://fonts.gstatic.com', crossorigin: true },
{ href: 'https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&family=Figtree:wght@300..900&display=swap', rel: 'stylesheet' }
];
links.forEach(link => {
const linkElement = document.createElement('link');
Object.entries(link).forEach(([key, value]) => linkElement[key] = value);
document.head.appendChild(linkElement);
});
createTemplate.importFonts();
const timerInterval = startLogoutTimer();

222
search/search.css Normal file
View File

@@ -0,0 +1,222 @@
/* Hide original elements */
header, footer, .page-title, .card-kreta {
display: none !important;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
margin: 0;
padding: 0;
color: var(--text-primary);
background-color: var(--background) !important;
font-family: "Montserrat", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif !important;
min-height: 100vh;
font-size: 16px;
display: flex;
align-items: center;
justify-content: center;
}
/* Firka search page styling */
.firka-search-wrapper {
width: 90%;
max-width: 500px;
padding: 0;
margin: 0 auto;
display: flex;
flex-direction: column;
align-items: center;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
/* Firka header styling */
.firka-header {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
margin: 0;
background: var(--card-card) !important;
padding: 24px;
border-radius: 24px 24px 0 0;
width: 100%;
box-shadow: 0px 1px var(--shadow-blur) 0px var(--accent-shadow);
}
.logo-text {
color: var(--text-primary);
text-align: center;
font-family: Montserrat;
font-size: 20px;
font-style: normal;
font-weight: 700;
line-height: normal;
display: flex;
align-items: center;
gap: 8px;
}
.logo {
width: 32px;
height: 32px;
}
.search-title {
color: var(--text-primary);
text-align: center;
font-family: Montserrat;
font-size: 24px;
font-style: normal;
font-weight: 600;
line-height: normal;
margin: 8px 0;
}
/* Form container styling */
.firka-form-container {
background: var(--card-card);
padding: 0 24px 24px 24px;
border-radius: 0 0 24px 24px;
width: 100%;
box-shadow: 0px 1px var(--shadow-blur) 0px var(--accent-shadow);
}
/* Form styling */
form {
width: 100%;
}
.form-control {
background-color: var(--button-secondaryFill) !important;
border: 1px solid var(--accent-15) !important;
border-radius: 12px !important;
color: var(--text-primary) !important;
padding: 12px 16px !important;
font-family: Montserrat !important;
font-size: 16px !important;
font-weight: 400 !important;
height: auto !important;
transition: border-color 0.2s ease-in-out, box-shadow 0.2s ease-in-out;
}
.form-control:focus {
border-color: var(--accent-accent) !important;
box-shadow: 0 0 0 2px var(--accent-15) !important;
outline: none !important;
}
.form-control::placeholder {
color: var(--text-teritary) !important;
}
/* Autocomplete dropdown styling */
.dropdown-menu {
background-color: var(--card-card) !important;
border: 1px solid var(--accent-15) !important;
border-radius: 12px !important;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1) !important;
padding: 8px !important;
max-height: 300px !important;
overflow-y: auto !important;
width: 100% !important;
}
.dropdown-item {
color: var(--text-primary) !important;
padding: 10px 16px !important;
border-radius: 8px !important;
margin-bottom: 4px !important;
font-family: Montserrat !important;
font-size: 14px !important;
transition: background-color 0.2s ease-in-out !important;
}
li.dropdown-item:hover, li.dropdown-item:focus {
background-color: var(--accent-15) !important;
color: var(--text-primary) !important;
}
a.dropdown-item:hover, a.dropdown-item:focus {
background-color: #00000000 !important;
}
.dropdown-item.active {
background-color: var(--accent-accent) !important;
color: white !important;
}
/* Button styling */
.btn-kreta {
background-color: var(--accent-accent) !important;
color: white !important;
border: none !important;
border-radius: 12px !important;
padding: 12px 24px !important;
font-family: Montserrat !important;
font-size: 16px !important;
font-weight: 600 !important;
cursor: pointer !important;
transition: background-color 0.2s ease-in-out, transform 0.1s ease-in-out !important;
display: inline-flex !important;
align-items: center !important;
justify-content: center !important;
}
.btn-kreta:hover {
background-color: var(--accent-secondary) !important;
transform: translateY(-1px) !important;
}
.btn-kreta:active {
transform: translateY(1px) !important;
}
.btn-kreta:disabled {
background-color: var(--text-teritary) !important;
cursor: not-allowed !important;
transform: none !important;
}
/* Footer styling */
.firka-footer {
margin-top: 16px;
text-align: center;
width: 100%;
}
.privacy-link {
color: var(--text-secondary);
font-family: Montserrat;
font-size: 14px;
text-decoration: none;
transition: color 0.2s ease-in-out;
}
.privacy-link:hover {
color: var(--accent-accent);
text-decoration: underline;
}
/* Responsive adjustments */
@media (max-width: 576px) {
.firka-search-wrapper {
width: 95%;
}
.search-title {
font-size: 20px;
}
.btn-kreta {
padding: 10px 20px !important;
font-size: 14px !important;
}
}

124
search/search.js Normal file
View File

@@ -0,0 +1,124 @@
function initializeTransformation() {
const form = document.querySelector('form');
const autocomplete = document.querySelector('.autocomplete');
if (form && autocomplete) {
applyFirkaStyling();
} else {
setTimeout(initializeTransformation, 500);
}
}
document.addEventListener('DOMContentLoaded', () => {
setTimeout(initializeTransformation, 1000);
});
if (document.readyState === 'complete' || document.readyState === 'interactive') {
setTimeout(initializeTransformation, 1000);
}
function applyFirkaStyling() {
try {
const theme = cookieManager.get('themePreference') || localStorage.getItem('themePreference') || 'light-green';
document.documentElement.setAttribute('data-theme', theme);
if (typeof loadingScreen !== 'undefined') {
loadingScreen.hide();
}
const originalForm = document.querySelector('form');
const instituteInput = document.querySelector('.autocomplete');
const redirectButton = document.getElementById('redirectToInstitute');
const instituteCodeInput = document.querySelector('.autocomplete-value');
const requestToken = document.querySelector('input[name="__RequestVerificationToken"]');
const searchWrapper = document.createElement('div');
searchWrapper.className = 'firka-search-wrapper';
const firkaHeader = document.createElement('div');
firkaHeader.className = 'firka-header';
firkaHeader.innerHTML = `
<p class="logo-text">
<img src="${chrome.runtime.getURL('images/firka_logo.png')}" alt="Firka" class="logo">
Firka
</p>
<h1 class="search-title">Válassz iskolát</h1>
`;
const formContainer = document.createElement('div');
formContainer.className = 'firka-form-container';
const firkaFooter = document.createElement('div');
firkaFooter.className = 'firka-footer';
firkaFooter.innerHTML = `
<a href="https://tudasbazis.ekreta.hu/pages/viewpage.action?pageId=4064926"
target="_blank" class="privacy-link">Adatkezelési tájékoztató</a>
`;
const existingWrapper = document.querySelector('.firka-search-wrapper');
if (existingWrapper) {
existingWrapper.remove();
}
searchWrapper.appendChild(firkaHeader);
if (originalForm) {
formContainer.appendChild(originalForm);
searchWrapper.appendChild(formContainer);
}
searchWrapper.appendChild(firkaFooter);
document.body.appendChild(searchWrapper);
setupAutocompleteListeners();
if (redirectButton) {
redirectButton.addEventListener('click', function(event) {
if (!instituteCodeInput.value) {
event.preventDefault();
alert(LanguageManager.t('search.select_institution'));
}
});
}
observeAutocompleteValue(instituteCodeInput, redirectButton);
} catch (error) {
console.error('Error applying Firka styling:', error);
}
}
function setupAutocompleteListeners() {
const autocompleteInput = document.querySelector('.autocomplete');
const autocompleteValue = document.querySelector('.autocomplete-value');
const redirectButton = document.getElementById('redirectToInstitute');
if (autocompleteInput && autocompleteValue) {
const observer = new MutationObserver((mutations) => {
const dropdown = document.querySelector('.autocomplete-dropdown');
if (dropdown) {
dropdown.classList.add('dropdown-menu');
const items = dropdown.querySelectorAll('li');
items.forEach(item => {
item.classList.add('dropdown-item');
item.addEventListener('click', () => {
if (redirectButton) {
redirectButton.disabled = false;
}
});
});
}
});
observer.observe(document.body, {
childList: true,
subtree: true
});
}
}
function observeAutocompleteValue(valueInput, button) {
if (!valueInput || !button) return;
const observer = new MutationObserver((mutations) => {
button.disabled = !valueInput.value;
});
observer.observe(valueInput, {
attributes: true,
attributeFilter: ['value']
});
const checkInterval = setInterval(() => {
if (valueInput.value) {
button.disabled = false;
clearInterval(checkInterval);
}
}, 500);
}

View File

@@ -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,31 @@ 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 {
color: var(--text-primary);
font-size: 12px;
font-weight: 500;
}
.theme-preview.light-blue {
background: #DAE4F7;

View File

@@ -3,7 +3,9 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Firxa Beállítások</title>
<title>Firxa - Beállítások</title>
<link rel="icon" type="image/png" href="../images/firka_logo_128.png">
<link rel="shortcut icon" type="image/png" href="../images/firka_logo_128.png">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&display=swap" rel="stylesheet">
@@ -21,12 +23,12 @@
</header>
<div class="settings-card">
<h2>Beállítások</h2>
<h2 data-i18n="settings.title">Beállítások</h2>
<div class="settings-group">
<div class="setting-section">
<div class="setting-header">
<span class="material-icons-round">palette</span>
Téma
<span data-i18n="settings.theme">Téma</span>
</div>
<div class="theme-grid">
<button class="theme-option" data-theme="default">
@@ -36,7 +38,7 @@
<div class="preview-card"></div>
</div>
</div>
<span class="theme-name">Világos Kék</span>
<span class="theme-name" data-i18n="settings.themes.light_blue">Világos Kék</span>
</button>
<button class="theme-option" data-theme="light-green">
<div class="theme-preview light-green">
@@ -45,7 +47,7 @@
<div class="preview-card"></div>
</div>
</div>
<span class="theme-name">Világos Zöld</span>
<span class="theme-name" data-i18n="settings.themes.light_green">Világos Zöld</span>
</button>
<button class="theme-option" data-theme="dark-blue">
<div class="theme-preview dark-blue">
@@ -54,7 +56,7 @@
<div class="preview-card"></div>
</div>
</div>
<span class="theme-name">Sötét Kék</span>
<span class="theme-name" data-i18n="settings.themes.dark_blue">Sötét Kék</span>
</button>
<button class="theme-option" data-theme="dark-green">
<div class="theme-preview dark-green">
@@ -63,7 +65,22 @@
<div class="preview-card"></div>
</div>
</div>
<span class="theme-name">Sötét Zöld</span>
<span class="theme-name" data-i18n="settings.themes.dark_green">Sötét Zöld</span>
</button>
</div>
</div>
<div class="setting-section">
<div class="setting-header">
<span class="material-icons-round">language</span>
<span data-i18n="settings.language">Nyelv</span>
</div>
<div class="language-grid">
<button class="language-option" data-language="hu">
<span class="language-name" data-i18n="settings.languages.hu">Magyar</span>
</button>
<button class="language-option" data-language="en">
<span class="language-name" data-i18n="settings.languages.en">English</span>
</button>
</div>
</div>
@@ -71,24 +88,24 @@
</div>
<div class="about-card">
<h2>Névjegy</h2>
<h2 data-i18n="settings.about.title">Névjegy</h2>
<div class="about-content">
<p>A Firka egy nyílt forráskódú projekt, amely a KRÉTA rendszerhez készít saját felhasználói felületet.</p>
<p data-i18n="settings.about.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.</p>
<a href="https://github.com/QwIT-Development/" target="_blank" class="github-link">
<span class="material-icons-round">code</span>
GitHub
<span data-i18n="settings.about.github">GitHub</span>
</a>
</div>
</div>
<div class="support-card">
<h2>Támogatás</h2>
<h2 data-i18n="settings.support.title">Támogatás</h2>
<div class="support-content">
<p>Ha tetszik a munkánk és szeretnéd támogatni a fejlesztést, az alábbi módon teheted meg:</p>
<p data-i18n="settings.support.description">Ha tetszik a munkánk és szeretnéd támogatni a fejlesztést, az alábbi módon teheted meg:</p>
<div class="support-buttons">
<a href="https://ko-fi.com/zan1456" target="_blank" class="support-button">
<span class="material-icons-round">coffee</span>
Ko-Fi
<span data-i18n="settings.support.kofi">Ko-Fi</span>
</a>
</div>
</div>
@@ -98,6 +115,8 @@
<div class="version-info" id="version">v1.1.0</div>
</footer>
</div>
<script src="index.js"></script>
<script src="../tools/cookieManager.js"></script>
<script src="../global/language.js"></script>
<script src="index.js"></script>
</body>
</html>

View File

@@ -1,4 +1,28 @@
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';
return (theme === 'default' || theme === 'light-blue' || theme === 'dark-blue') && !blueThemesUnlocked;
}
function updateThemeAvailability() {
const blueThemesUnlocked = localStorage.getItem('blueThemesUnlocked') === 'true';
document.querySelectorAll('.theme-option').forEach(button => {
const theme = button.dataset.theme;
if (theme === 'default' || theme === 'light-blue' || theme === 'dark-blue') {
if (blueThemesUnlocked) {
button.style.display = 'block';
button.classList.remove('disabled');
button.removeAttribute('disabled');
} else {
button.style.display = 'none';
}
}
});
}
function getCookie(name) {
const cookieName = `${name}=`;
@@ -25,43 +49,50 @@ document.addEventListener('DOMContentLoaded', async () => {
document.cookie = `${name}=${value}; ${expires}; path=/; domain=.e-kreta.hu`;
}
async function getCurrentTheme() {
try {
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
const response = await chrome.tabs.sendMessage(tab.id, { action: 'getTheme' });
return response.theme;
} catch (error) {
console.error('Error getting current theme:', error);
return 'default';
}
function getCurrentTheme() {
return localStorage.getItem('themePreference') ||
getCookie('themePreference') ||
'light-green';
}
function updateThemeButtons(currentTheme) {
document.querySelectorAll('.theme-option').forEach(button => {
const theme = button.dataset.theme;
button.classList.toggle('active', theme === currentTheme);
/*if (theme === 'light-blue' || theme === 'dark-blue' || theme === 'default') {
button.classList.add('disabled');
button.setAttribute('disabled', 'true');
}*/
});
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);
});
}
function isThemeDisabled(theme) {
return theme === 'default' || theme === 'dark-blue';
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) {
if (isThemeDisabled(theme)) {
alert('Ez a téma jelenleg nem elérhető.');
return;
}
setCookie('themePreference', theme);
localStorage.setItem('themePreference', theme);
@@ -78,8 +109,6 @@ document.addEventListener('DOMContentLoaded', async () => {
action: 'changeTheme',
theme: theme
}).catch(() => {
console.log('Tab not ready for theme change:', tab.id);
});
});
}
@@ -92,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;
}
@@ -100,11 +129,16 @@ 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 = localStorage.getItem('themePreference') ||
getCookie('themePreference') ||
await getCurrentTheme() ||
'light-green';
let initialTheme = getCurrentTheme();
if (isThemeDisabled(initialTheme)) {
@@ -112,8 +146,12 @@ document.addEventListener('DOMContentLoaded', async () => {
}
updateThemeAvailability();
await applyTheme(initialTheme);
const initialLanguage = getCurrentLanguage();
updateLanguageButtons(initialLanguage);
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.action === 'themeChanged') {
@@ -124,7 +162,56 @@ document.addEventListener('DOMContentLoaded', async () => {
const manifest = chrome.runtime.getManifest();
document.getElementById('version').textContent = `v${manifest.version}`;
const versionElement = document.getElementById('version');
versionElement.textContent = `v${manifest.version}`;
let clickCount = 0;
versionElement.addEventListener('click', () => {
clickCount++;
if (clickCount >= 5) {
localStorage.setItem('blueThemesUnlocked', 'true');
updateThemeAvailability();
const notification = document.createElement('div');
notification.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
background: var(--accent-accent);
color: white;
padding: 12px 20px;
border-radius: 8px;
font-family: 'Montserrat', sans-serif;
font-weight: 500;
z-index: 10000;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
animation: slideIn 0.3s ease-out;
`;
notification.textContent = window.LanguageManager.t('common.success') + ': ' + window.LanguageManager.t('settings.blue_themes_unlocked');
const style = document.createElement('style');
style.textContent = `
@keyframes slideIn {
from { transform: translateX(100%); opacity: 0; }
to { transform: translateX(0); opacity: 1; }
}
`;
document.head.appendChild(style);
document.body.appendChild(notification);
setTimeout(() => {
notification.remove();
style.remove();
}, 3000);
clickCount = 0;
}
});
themeButtons.forEach(button => {
@@ -138,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);
});
});

View File

@@ -1,32 +1,3 @@
@font-face {
font-family: 'Montserrat';
font-style: normal;
font-weight: 400;
src: url(https://fonts.gstatic.com/s/montserrat/v25/JTUHjIg1_i6t8kCHKm4532VJOt5-QNFgpCtr6Hw5aXp-p7K4KLg.woff2) format('woff2');
}
@font-face {
font-family: 'Montserrat';
font-style: normal;
font-weight: 500;
src: url(https://fonts.gstatic.com/s/montserrat/v25/JTUHjIg1_i6t8kCHKm4532VJOt5-QNFgpCtZ6Hw5aXp-p7K4KLg.woff2) format('woff2');
}
@font-face {
font-family: 'Montserrat';
font-style: normal;
font-weight: 600;
src: url(https://fonts.gstatic.com/s/montserrat/v25/JTUHjIg1_i6t8kCHKm4532VJOt5-QNFgpCu173w5aXp-p7K4KLg.woff2) format('woff2');
}
@font-face {
font-family: 'Figtree';
src: url('chrome-extension://__MSG_@@extension_id__/fonts/Figtree-Regular.woff2') format('woff2');
font-weight: 400;
font-style: normal;
}
* {
box-sizing: border-box;
margin: 0;
@@ -43,57 +14,6 @@ body {
font-size: 16px;
}
/* Loading Screen */
.loading-screen {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: var(--background);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
z-index: 9999;
}
.loading-logo {
width: 48px;
height: 48px;
border-radius: 16px;
}
.loading-text {
color: var(--text-primary);
text-align: center;
font-family: Montserrat;
font-size: 20px;
font-style: normal;
font-weight: 700;
line-height: normal;
}
.loading-text2 {
align-self: stretch;
color: var(--text-secondary);
text-align: center;
font-family: Figtree;
font-size: 16px;
font-style: normal;
font-weight: 500;
line-height: 130%;
}
@keyframes spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
@media (max-width: 768px) {
body {
font-size: 14px;
@@ -106,7 +26,6 @@ body {
flex-direction: column;
}
/* Update header styles to match dashboard */
.kreta-header {
padding: clamp(1rem, 3vw, 2rem);
display: grid;
@@ -123,10 +42,10 @@ body {
@media (max-width: 768px) {
.kreta-header {
grid-template-columns: 1fr auto;
grid-template-columns: 1fr auto auto;
grid-template-areas:
"school user"
"nav nav";
"school toggle user"
"nav nav nav";
padding: 1rem;
gap: 0.5rem;
}
@@ -191,82 +110,6 @@ body {
}
}
/* Updated navigation styles */
.kreta-nav {
padding: 0 clamp(0.5rem, 3vw, 1.5rem);
position: sticky;
top: 0;
z-index: 100;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
scrollbar-width: none;
-ms-overflow-style: none;
display: flex;
justify-content: center;
}
@media (max-width: 768px) {
.kreta-nav {
grid-area: nav;
padding: 0;
margin-top: 0.5rem;
}
}
.kreta-nav::-webkit-scrollbar {
display: none;
}
.nav-links {
display: flex;
gap: clamp(0.5rem, 2vw, 1rem);
padding: 0.25rem;
justify-content: center;
}
@media (max-width: 768px) {
.nav-links {
justify-content: flex-start;
width: 100%;
gap: 0.25rem;
}
}
.nav-links a {
color: var(--text-secondary);
text-decoration: none;
padding: clamp(0.5rem, 1.5vw, 1rem) 0.5rem;
font-weight: 500;
white-space: nowrap;
transition: all 0.2s ease;
display: flex;
align-items: center;
gap: 0.5rem;
border-radius: 8px;
}
@media (max-width: 768px) {
.nav-links a {
padding: 0.5rem;
font-size: 13px;
}
.nav-links a .material-icons-round {
font-size: 20px;
}
}
.nav-links a:hover {
color: var(--text-primary);
text-decoration: none;
background-color: var(--card-card);
}
.nav-links a.active {
color: var(--accent-accent);
}
/* User profile styles */
.user-profile {
position: relative;
justify-self: flex-end;
@@ -298,19 +141,6 @@ body {
text-align: right;
}
.user-name {
display: block;
color: var(--text-primary);
font-size: 16px;
font-weight: 500;
}
.user-time {
display: block;
color: var(--text-secondary);
font-size: 14px;
}
.user-dropdown {
position: absolute;
top: 100%;
@@ -343,7 +173,6 @@ body {
background: var(--button-secondaryFill);
}
/* Main content styles */
.kreta-main {
flex: 1;
padding: clamp(1rem, 3vw, 2rem);
@@ -352,7 +181,10 @@ body {
width: 100%;
}
/* Card styles */
.k-overlay, .k-widget.k-window {
display: none !important;
}
.card {
border-radius: 24px;
overflow: hidden;
@@ -360,7 +192,6 @@ body {
margin-bottom: 1rem;
}
/* Timetable specific styles */
.timetable-grid {
display: grid;
grid-template-columns: 80px repeat(5, 1fr);
@@ -419,10 +250,8 @@ body {
border-radius: 12px;
padding: 8px;
transition: transform 0.2s ease;
}
.lesson-slot:hover {
transform: scale(1.02);
display: grid;
grid-gap: 16px;
}
.lesson-card {
@@ -516,8 +345,8 @@ body {
display: flex;
align-items: center;
justify-content: center;
width: 20px;
height: 20px;
width: 28px;
height: 28px;
border-radius: 50%;
background: var(--accent-15);
color: var(--accent-accent);
@@ -534,29 +363,57 @@ body {
}
.lesson-indicator .material-icons-round {
font-size: 14px;
font-size: 18px;
}
/* Week selector styling */
.week-controls {
display: flex;
gap: 16px;
flex-direction: column;
align-items: center;
margin: 16px;
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 {
@@ -566,19 +423,338 @@ 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);
}
/* Responsive adjustments */
.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;
justify-content: space-between;
margin-bottom: 1rem;
padding: 0 1rem;
}
.day-nav-btn {
background: var(--card-card);
border: 1px solid var(--accent-15);
border-radius: 12px;
padding: 12px 16px;
color: var(--text-secondary);
cursor: pointer;
transition: all 0.2s ease;
display: flex;
align-items: center;
gap: 8px;
}
.day-nav-btn:hover {
background: var(--accent-15);
color: var(--accent-accent);
}
.current-day-info {
text-align: center;
flex: 1;
margin: 0 1rem;
}
.current-day-name {
font-size: 18px;
font-weight: 600;
color: var(--text-primary);
margin-bottom: 4px;
}
.current-day-date {
font-size: 14px;
color: var(--text-secondary);
}
@media (max-width: 1024px) {
.timetable-grid {
grid-template-columns: 60px repeat(5, minmax(200px, 1fr));
@@ -590,9 +766,33 @@ body {
}
@media (max-width: 768px) {
.day-navigation {
display: flex;
}
.timetable-container {
overflow-x: auto;
-webkit-overflow-scrolling: touch;
overflow: hidden;
}
.timetable-grid {
grid-template-columns: 60px 1fr;
overflow: visible;
}
.grid-header:not(:first-child) {
display: none;
}
.grid-header.active {
display: flex !important;
}
.lesson-slot {
display: none;
}
.lesson-slot.active {
display: block;
}
.lesson-card {
@@ -609,7 +809,6 @@ body {
}
}
/* Modal styles */
.lesson-modal {
display: none;
position: fixed;
@@ -627,7 +826,7 @@ body {
}
.lesson-modal.show {
display: flex;
display: flex !important;
opacity: 1;
}
@@ -736,7 +935,14 @@ body {
color: var(--text-primary);
}
/* Animations */
.detail-item .line-through {
text-decoration: line-through;
}
.detail-item.hidden {
display: none;
}
@keyframes fadeIn {
from {
opacity: 0;
@@ -770,7 +976,6 @@ body {
}
}
/* Responsive adjustments */
@media (max-width: 768px) {
.kreta-header {
flex-direction: column;
@@ -781,28 +986,11 @@ body {
text-align: center;
}
.nav-links {
justify-content: start;
overflow-x: auto;
padding-bottom: 0.5rem;
}
.nav-links::-webkit-scrollbar {
display: none;
}
.lesson-cell {
min-width: 200px;
}
}
/* Material Icons */
.material-icons-round {
font-size: 20px;
vertical-align: middle;
}
/* Scrollbar styling */
::-webkit-scrollbar {
width: 8px;
height: 8px;
@@ -819,4 +1007,85 @@ body {
::-webkit-scrollbar-thumb:hover {
background: var(--text-primary);
}
.grid-header.special-day {
background: linear-gradient(135deg, #F99F50, #FF8C42);
color: white;
position: relative;
}
.special-day-indicator {
display: block;
font-size: 0.7rem;
font-weight: 500;
margin-top: 2px;
opacity: 0.9;
text-align: center;
}
.special-day-card {
background: #F99F50;
color: white;
padding: 12px;
border-radius: 8px;
margin: 4px;
text-align: center;
cursor: pointer;
transition: all 0.2s ease;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.special-day-card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
}
.special-day-title {
font-weight: 600;
font-size: 1.1rem;
margin-bottom: 4px;
}
.special-day-subtitle {
font-size: 0.9rem;
opacity: 0.9;
font-weight: 400;
}
.week-select {
min-width: 200px;
max-width: 300px;
}
@media (max-width: 768px) {
.special-day-indicator {
font-size: 0.6rem;
}
.special-day-card {
padding: 8px;
margin: 2px;
}
.special-day-title {
font-size: 0.8rem;
}
.special-day-subtitle {
font-size: 0.7rem;
}
}
.more-link {
margin-top: auto;
display: inline-flex;
align-items: center;
gap: 0.5rem;
color: var(--accent-accent);
text-decoration: none;
font-weight: 500;
padding-top: 16px;
transition: gap 0.2s ease;
font-size: clamp(0.875rem, 1.5vw, 1rem);
}

File diff suppressed because it is too large Load Diff

25
tools/cookieManager.js Normal file
View File

@@ -0,0 +1,25 @@
const cookieManager = {
get(name) {
const cookieName = `${name}=`;
const decodedCookie = decodeURIComponent(document.cookie);
const cookieArray = decodedCookie.split(';');
for(let i = 0; i < cookieArray.length; i++) {
let cookie = cookieArray[i];
while (cookie.charAt(0) === ' ') {
cookie = cookie.substring(1);
}
if (cookie.indexOf(cookieName) === 0) {
return cookie.substring(cookieName.length, cookie.length);
}
}
return null;
},
set(name, value, days = 365) {
const date = new Date();
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
const expires = `expires=${date.toUTCString()}`;
document.cookie = `${name}=${value}; ${expires}; path=/; domain=.e-kreta.hu`;
}
}

136
tools/createTemplate.js Normal file
View File

@@ -0,0 +1,136 @@
const createTemplate = {
header() {
const data = {
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',
email: cookieManager.get('userEmail') || ''
}
};
const schoolNameFull = `${data.schoolInfo.id} - ${data.schoolInfo.name}`;
const shortenedSchoolName = helper.shortenSchoolName(schoolNameFull);
const element = `<header class="kreta-header">
<div class="school-info">
<p class="logo-text">
<img src="${chrome.runtime.getURL('images/firka_logo.png')}" alt="Firka" class="logo">
Firka
</p>
<div class="school-details" title="${schoolNameFull}">
${shortenedSchoolName}
</div>
</div>
<button class="nav-toggle" aria-label="${LanguageManager.t('navigation.nav_toggle')}">
<svg viewBox="0 0 24 24">
<path d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z"/>
</svg>
</button>
<nav class="kreta-nav">
<div class="nav-links">
<a href="/Intezmeny/Faliujsag" data-page="dashboard" class="nav-item ${( location.pathname == '/Intezmeny/Faliujsag' ? 'active' : '')}">
<img src="${chrome.runtime.getURL('icons/dashboard-' + ( location.pathname == '/Intezmeny/Faliujsag' ? 'active' : 'inactive') + '.svg')}" alt="${LanguageManager.t('navigation.dashboard')}">
${LanguageManager.t('navigation.dashboard')}
</a>
<a href="/TanuloErtekeles/Osztalyzatok" data-page="grades" class="nav-item ${( location.pathname == '/TanuloErtekeles/Osztalyzatok' ? 'active' : '')}">
<img src="${chrome.runtime.getURL('icons/grades-' + ( location.pathname == '/TanuloErtekeles/Osztalyzatok' ? 'active' : 'inactive') + '.svg')}" alt="${LanguageManager.t('navigation.grades')}">
${LanguageManager.t('navigation.grades')}
</a>
<a href="/Orarend/InformaciokOrarend" data-page="timetable" class="nav-item ${( location.pathname == '/Orarend/InformaciokOrarend' ? 'active' : '')}">
<img src="${chrome.runtime.getURL('icons/timetable-' + ( location.pathname == '/Orarend/InformaciokOrarend' ? 'active' : 'inactive') + '.svg')}" alt="${LanguageManager.t('navigation.timetable')}">
${LanguageManager.t('navigation.timetable')}
</a>
<a href="/Hianyzas/Hianyzasok" data-page="absences" class="nav-item ${( location.pathname == '/Hianyzas/Hianyzasok' ? 'active' : '')}">
<img src="${chrome.runtime.getURL('icons/absences-' + ( location.pathname == '/Hianyzas/Hianyzasok' ? 'active' : 'inactive') + '.svg')}" alt="${LanguageManager.t('navigation.absences')}">
${LanguageManager.t('navigation.absences')}
</a>
<a href="/Tanulo/TanuloHaziFeladat" data-page="other" class="nav-item ${( location.pathname == '/Tanulo/TanuloHaziFeladat' ? 'active' : '')}">
<img src="${chrome.runtime.getURL('icons/others.svg')}" alt="${LanguageManager.t('navigation.other')}">
${LanguageManager.t('navigation.other')}
</a>
</div>
</nav>
<div class="user-profile">
<button class="user-dropdown-btn">
<div class="user-info">
<span class="user-name">${data.userData.name}</span>
<span class="nav-logout-timer" id="logoutTimer">${data.userData.time}</span>
</div>
</button>
<div class="user-dropdown">
<a href="/Adminisztracio/Profil" data-page="profile" class="dropdown-item">
<img src="${chrome.runtime.getURL('icons/profile.svg')}" alt="${LanguageManager.t('navigation.profile')}">
${LanguageManager.t('navigation.profile')}
</a>
<a href="#" class="dropdown-item" id="settingsBtn">
<img src="${chrome.runtime.getURL('icons/settings.svg')}" alt="${LanguageManager.t('navigation.settings')}">
${LanguageManager.t('navigation.settings')}
</a>
<a href="/Home/Logout" data-page="logout" class="dropdown-item">
<img src="${chrome.runtime.getURL('icons/logout.svg')}" alt="${LanguageManager.t('navigation.logout')}">
${LanguageManager.t('navigation.logout')}
</a>
</div>
</div>
</header>`
const startTime = parseInt(data.userData.time?.match(/\d+/)?.[0] || "45");
let timeLeft = startTime * 60;
const updateTimer = () => {
const minutes = Math.floor(timeLeft / 60);
const seconds = timeLeft % 60;
const timerEl = document.getElementById('logoutTimer');
if (timerEl) {
timerEl.textContent = `${minutes}:${seconds.toString().padStart(2, '0')}`;
}
if (timeLeft <= 0) {
window.location.href = '/Home/Logout';
} else {
timeLeft--;
}
};
setInterval(updateTimer, 1000);
return element;
},
importFonts() {
const links = [
{ rel: 'preconnect', href: 'https://fonts.googleapis.com' },
{ rel: 'preconnect', href: 'https://fonts.gstatic.com', crossorigin: true },
{ rel: 'stylesheet', href: 'https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&display=swap' },
{ rel: 'stylesheet', href: 'https://fonts.googleapis.com/css2?family=Figtree:ital,wght@0,300..900;1,300..900&display=swap' },
{ rel: 'stylesheet', href: 'https://fonts.googleapis.com/icon?family=Material+Icons+Round' }
];
links.forEach(link => {
const linkElement = document.createElement('link');
Object.entries(link).forEach(([key, value]) => {
linkElement[key] = value;
});
document.head.appendChild(linkElement);
});
}
}
document.addEventListener("DOMContentLoaded", async () => {
await helper.waitForElement('#settingsBtn');
document.querySelector('#settingsBtn').addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
const url = chrome.runtime.getURL('settings/index.html');
window.open(url, '_blank', 'width=400,height=600');
});
});

40
tools/helper.js Normal file
View File

@@ -0,0 +1,40 @@
const helper = {
shortenSchoolName(name, maxLength = 50) {
if (!name) return '';
if (name.length <= maxLength) return name;
const parts = name.split(' - ');
if (parts.length === 2) {
const [code, fullName] = parts;
if (fullName.length > maxLength - code.length - 3) {
return `${code} - ${fullName.substring(0, maxLength - code.length - 6)}...`;
}
}
return name.substring(0, maxLength - 3) + '...';
},
async waitForElement(selector) {
return new Promise(resolve => {
if (document.querySelector(selector)) {
return resolve(document.querySelector(selector));
}
const observer = new MutationObserver(mutations => {
if (document.querySelector(selector)) {
observer.disconnect();
resolve(document.querySelector(selector));
}
});
observer.observe(document.body, {
childList: true,
subtree: true
});
});
},
convertTimeToMinutes(timeStr) {
const [hours, minutes] = timeStr.split(':').map(Number);
return hours * 60 + minutes;
}
}

59
tools/loadingScreen.css Normal file
View File

@@ -0,0 +1,59 @@
.modalBckgroundMain {
display: none !important;
}
body:not(.loaded) {
opacity: 0 !important;
visibility: hidden !important;
height: 100vh !important;
overflow: hidden !important;
}
.loading-screen {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: var(--background);
z-index: 9999;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
transition: opacity 0.3s ease;
}
.loading-content {
display: flex;
flex-direction: column;
align-items: center;
gap: 1rem;
}
.loading-logo {
width: 48px;
height: 48px;
border-radius: 16px;
}
.loading-text {
color: var(--text-primary);
text-align: center;
font-family: Montserrat;
font-size: 20px;
font-style: normal;
font-weight: 700;
line-height: normal;
}
.loading-text2 {
align-self: stretch;
color: var(--text-secondary);
text-align: center;
font-family: Figtree;
font-size: 16px;
font-style: normal;
font-weight: 500;
line-height: 130%;
}

53
tools/loadingScreen.js Normal file
View File

@@ -0,0 +1,53 @@
const loadingScreen = {
show() {
document.body.classList.remove('loaded');
const existingLoadingScreen = document.querySelector('.loading-screen');
if (existingLoadingScreen) return;
const loadingScreen = document.createElement('div');
loadingScreen.className = 'loading-screen';
loadingScreen.innerHTML = `
<div class="loading-content">
<img src="${chrome.runtime.getURL('images/loading.gif')}" alt="Firka" class="loading-logo">
<div class="loading-text" data-i18n="loading.text">Betöltés alatt...</div>
<div class="loading-text2" data-i18n="loading.subtext">Kis türelmet!</div>
</div>
`;
document.body.appendChild(loadingScreen);
document.body.classList.add('loaded');
},
hide() {
document.body.classList.add('loaded');
const loadingScreen = document.querySelector('.loading-screen');
if (loadingScreen) {
loadingScreen.style.opacity = '0';
const removeLoadingScreen = () => {
if (loadingScreen && loadingScreen.parentNode) {
loadingScreen.remove();
}
};
loadingScreen.addEventListener('transitionend', removeLoadingScreen, { once: true });
setTimeout(removeLoadingScreen, 500);
}
}
}
window.addEventListener('DOMContentLoaded', () => {
const manifest = chrome.runtime.getManifest();
const urls = [];
if (manifest.content_scripts) {
manifest.content_scripts.forEach(script => {
if (script.matches) {
urls.push(...script.matches);
}
});
}
if (urls.some(url => url.includes(location.pathname))) {
loadingScreen.show();
}
});