77 Commits

Author SHA1 Message Date
Zan
f00f5f4738 Action bar to messages 2025-11-29 20:46:50 +01:00
Zan
23ceb4f367 Little fixes 2025-11-29 20:46:42 +01:00
Zan
c01a69586a Theme fixes 2025-11-29 19:51:24 +01:00
Zan
a107c51fa2 Mark as seen 2025-11-29 18:59:40 +01:00
Zan
e171ce93b6 Fixes 2025-11-29 18:42:54 +01:00
Zan
de59809872 Get bff for messages page 2025-11-29 18:38:31 +01:00
Zan
004b198f52 Fix for messages page 2025-11-29 18:36:18 +01:00
Zan
d35b50d5e4 -w 2025-10-17 21:10:54 +02:00
Zan
48a5155772 w 2025-10-17 21:10:23 +02:00
Zan
3f3e16b4e5 more tests 2025-10-09 15:01:51 +02:00
Zan
12ecf72052 Temp remove custom themes 2025-10-06 18:21:59 +02:00
Zan
80feac12b2 Update README.md 2025-09-25 16:21:37 +02:00
Zan
757c6b6451 Create manifest_fox.json 2025-09-25 16:20:14 +02:00
Zan
67aa3ed836 Remove icon 2025-09-25 16:18:41 +02:00
Zan
9ac962daf6 Removed homework page from navbar and added messages page 2025-09-24 20:37:33 +02:00
Zan
a446afa153 Removed support for homework page 2025-09-24 20:37:04 +02:00
Zan
8037f2f241 Homework attachment support 2025-09-24 20:28:00 +02:00
Zan
bf16c91a0a Updated 2025-09-23 22:15:12 +02:00
Zan
ca71eba13f Update README.md 2025-09-23 22:12:58 +02:00
Zan
a88a4b2ed6 fixes 2025-09-23 22:06:58 +02:00
Zan
f70f8d682b Added messages page support 2025-09-23 22:05:39 +02:00
Zan
084308abab Shorter theme ID-s 2025-09-23 22:01:14 +02:00
Zan
5a25de4c45 navbar redirects by schoolID 2025-09-23 21:36:24 +02:00
Zan
4180fe823b removed old cookierequests 2025-09-23 20:40:37 +02:00
Zan
636a325470 Removed refilc colors, added theme customisation (WIP) 2025-09-23 19:31:22 +02:00
Zan
0f9bae45a5 Update manifest.json 2025-09-23 18:24:57 +02:00
Zan
86cab01fba Lang fixes 2025-09-15 22:47:04 +02:00
Zan
eb3924f60f Fixes for custom things and mark as done 2025-09-15 22:44:53 +02:00
Zan
8c57152fe4 Use localstorage rather than cookies 2025-09-15 22:17:50 +02:00
Zan
9d877a1d68 Fixed maintenance 2025-09-14 18:17:45 +02:00
Zan
1ca8e24805 Added manual adding for homework 2025-09-14 18:17:17 +02:00
Zan
8a8d81b67f version update 2025-09-10 15:27:49 +02:00
Zan
245142c833 Fix loading remove 2025-09-10 15:27:43 +02:00
Zan
7e1d4cd9a7 Mobile UI fix 2025-09-10 15:27:18 +02:00
Zan
f29f7a2366 Mobile UI fix 2025-09-10 15:27:01 +02:00
Zan
6e0d3b6b7f Link underline remove 2025-09-10 15:26:52 +02:00
Zan
9fd0190ab8 Elmaradt órák megjelenítése 2025-09-09 22:16:53 +02:00
Zan
94fbc472f6 Mark homework as done (timetable) 2025-09-09 22:10:50 +02:00
Zan
96be6471f0 Homework in timetable site 2025-09-09 21:52:36 +02:00
Zan
10cabc3889 Update login.js 2025-09-09 21:38:04 +02:00
Zan
6cddce142c Profile page rework 2025-09-09 20:05:05 +02:00
Zan
f81d821c56 Óra témája és számonkérés megjelenik a modalban (HW WIP) 2025-09-08 23:21:15 +02:00
Zan
0a5439acfd Mark as done, fixes 2025-09-08 23:09:49 +02:00
Zan
dac927cf7d Data collection, firefox 2025-09-08 21:59:59 +02:00
Zan
b4a270a8d2 Firefox addons fix 2025-09-08 21:51:55 +02:00
Zan
110ba0d4fb Delete KRÉTA Iskolai Alaprendszer.html 2025-09-08 19:42:55 +02:00
Zan
1ba976e06e Update manifest.json 2025-09-08 19:35:32 +02:00
Zan
df8adf65de . 2025-09-08 19:32:05 +02:00
Zan
e96c9e1c83 1.3.0 2025-09-01 19:20:21 +02:00
Zan
a3631d749f Navbar open fix and logo fix in settings 2025-09-01 15:52:08 +02:00
Zan
fa7c773393 Added new icons, fixed day notice on timetables 2025-09-01 15:21:15 +02:00
Zan
a39295ae68 test 2025-08-31 20:53:36 +02:00
Zan1456
d722740bdf New layout for absences 2025-08-06 22:56:12 +02:00
Zan1456
1218d3b905 . 2025-08-06 22:48:21 +02:00
Zan1456
bbe8a4b2b3 Minimal fixes 2025-08-06 14:41:22 +02:00
Zan
ec99a3b5c8 Merge pull request #20 from QwIT-Development/firefox
Firefox
2025-08-06 14:12:35 +02:00
Zan
02ffa23e1a Merge branch 'main' into firefox 2025-08-06 14:12:24 +02:00
Zan1456
d7ea449a1b Minimal fixes 2025-08-06 14:06:00 +02:00
Zan1456
e7ec225e18 Updated chart.js 2025-06-14 09:42:33 +02:00
Zan1456
5a9837e655 . 2025-06-14 09:05:07 +02:00
Zan1456
1a340a1950 fixed week select 2025-06-13 14:50:49 +02:00
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
6a52c51ece . 2025-06-13 13:59:17 +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
75 changed files with 14838 additions and 7360 deletions

View File

@@ -8,9 +8,9 @@
</p>
<p align="center">
<a href="https://github.com/QwIT-Development/firka-extension/releases">
<!--<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>-->
<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>
@@ -19,10 +19,19 @@
</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>
## 📱 Funkciók
- **Modern Dizájn**: Teljesen újratervezett, modern felhasználói felület
- **Személyre Szabható Témák**: Világos és sötét módok, kék és zöld színsémákkal
- **Személyre Szabható Témák**: Világos és sötét mód vagy akár egyedi témáddal
- **Továbbfejlesztett Felületek**:
- Átdolgozott bejelentkezési képernyő
- Átláthatóbb jegynapló
@@ -30,30 +39,16 @@
- Fejlett hiányzás kezelés
- Egyszerűsített szerepkör választó
- Új kezdőlap elrendezés
- Továbbfejlesztett profilkezelő
## 🚀 Telepítés
1. Töltsd le a legfrissebb verziót a [Releases](https://github.com/QwIT-Development/firka-extension/releases/) oldalról
### Chromium alapú böngészők esetén
2. Navigálj a `chrome://extensions` oldalra
3. Kapcsold be a "Fejlesztői mód"-ot a jobb felső sarokban
4. Keresd meg a letöltött zip fájlt
5. Húzd a letöltött zip fájlt a böngészőbe
### Firefox alapú böngészők esetén
2. Navigálj az `about:addons` oldalra
3. Kattints rá a fogaskerék ikonra (⚙️) a jobb felső sarokban
4. Ebben a menüben válaszd a(z) "Kiegészítő telepítése fájlból..."/"Install Add-on From File..."
5. Válaszd ki a `.zip` fájlt amit letöltöttél
### Kész!
6. 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
A bővítmény beállításait a böngésző eszköztárán található Filx ikonra kattintva érheted el. Itt módosíthatod:
A bővítmény beállításait a böngésző eszköztárán található Firka ikonra kattintva érheted el. Itt módosíthatod:
- A felület színsémáját (Világos/Sötét)
- Az fő színeket (Kék/Zöld)
- A felület témáját, legyen az világos, sötét vagy akár egyedi témáddal.
## 💡 Támogatott Oldalak
@@ -62,16 +57,17 @@ A bővítmény jelenleg az alábbi e-KRÉTA oldalakat támogatja:
- Bejelentkezés
- Kijelentkezés
- Szerepkörválasztó
- Órarend
- Órarend (Házi feladatok, Számonkérések)
- Faliújság
- Hiányzások
- Házi feladatok
- Jegyek
- Intézménykereső
- Üzenetek
- Profil (Béta)
## 👥 Csapat
- **[Zan1456](https://github.com/Zan1456)** - Vezető Fejlesztő
- **[BalazsManus](https://github.com/olajcsere)** - Fejlesztő
- **[Xou](https://yoursit.ee/xou)** - Designer
## 🤝 Közreműködés

View File

@@ -1,436 +1,485 @@
* {
box-sizing: border-box;
margin: 0;
padding: 0;
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", serif !important;
min-height: 100vh;
font-size: 16px;
margin:0;
padding:0;
color:var(--text-primary);
background-color:var(--background) !important;
font-family:"Montserrat",serif !important;
min-height:100vh;
font-size:16px;
}
@media (max-width: 768px) {
@media (max-width:768px) {
body {
font-size: 14px;
}
font-size:14px;
}
.kreta-container {
min-height: 100vh;
display: flex;
flex-direction: column;
}.kreta-container {
min-height:100vh;
display:flex;
flex-direction:column;
}
.kreta-header {
padding: clamp(1rem, 3vw, 2rem);
display: grid;
grid-template-columns: minmax(300px, 400px) 1fr minmax(200px, 300px);
align-items: center;
gap: 1rem;
padding:clamp(1rem,3vw,2rem);
display:grid;
grid-template-columns:minmax(300px,400px) 1fr minmax(200px,300px);
align-items:center;
gap:1rem;
}
@media (max-width: 1200px) {
@media (max-width:1200px) {
.kreta-header {
grid-template-columns: minmax(250px, 350px) 1fr minmax(180px, 250px);
}
grid-template-columns:minmax(250px,350px) 1fr minmax(180px,250px);
}
@media (max-width: 768px) {
}@media (max-width:768px) {
.kreta-header {
grid-template-columns: 1fr auto auto;
grid-template-areas:
"school toggle user"
grid-template-columns:1fr auto auto;
grid-template-areas:"school toggle user"
"nav nav nav";
padding: 1rem;
gap: 0.5rem;
}
padding:1rem;
gap:0.5rem;
}
.school-info {
margin: 0;
}.school-info {
margin:0;
}
@media (max-width: 768px) {
@media (max-width:768px) {
.school-info {
grid-area: school;
max-width: none;
display: flex;
align-items: center;
gap: 0.5rem;
}
grid-area:school;
max-width:none;
display:flex;
align-items:center;
gap:0.5rem;
}
.logo-text {
color: var(--text-primary);
font-size: 24px;
font-weight: 600;
margin: 0 0 0.5rem;
display: flex;
align-items: center;
}.logo-text {
color:var(--text-primary);
font-size:24px;
font-weight:600;
margin:0 0 0.5rem;
display:flex;
align-items:center;
}
@media (max-width: 768px) {
@media (max-width:768px) {
.logo-text {
margin: 0;
font-size: 20px;
}
margin:0;
font-size:20px;
}
.logo {
width: 24px;
border-radius: 8px;
margin-right: 0.5rem;
}.logo {
width:24px;
border-radius:8px;
margin-right:0.5rem;
}
.school-details {
color: var(--text-secondary);
font-size: 14px;
color:var(--text-secondary);
font-size:14px;
}
.school-details span {
display: block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 300px;
display:block;
white-space:nowrap;
overflow:hidden;
text-overflow:ellipsis;
max-width:300px;
}
@media (max-width: 768px) {
@media (max-width:768px) {
.school-details span {
max-width: 200px;
}
.school-details {
font-size: 12px;
}
max-width:200px;
}
.user-profile {
position: relative;
justify-self: flex-end;
.school-details {
font-size:12px;
}
@media (max-width: 768px) {
}.user-profile {
position:relative;
justify-self:flex-end;
}
@media (max-width:768px) {
.user-profile {
grid-area: user;
}
grid-area:user;
}
.user-dropdown-btn {
display: flex;
align-items: center;
gap: 1rem;
background: none;
border: none;
cursor: pointer;
padding: 0.5rem;
border-radius: 8px;
transition: background-color 0.2s;
}.user-dropdown-btn {
display:flex;
align-items:center;
gap:1rem;
background:none;
border:none;
cursor:pointer;
padding:0.5rem;
border-radius:8px;
transition:background-color 0.2s;
}
.user-dropdown-btn:hover {
background: var(--card-card);
background:var(--card-card);
}
.user-info {
text-align: right;
text-align:right;
}
.user-dropdown {
position: absolute;
top: 100%;
right: 0;
margin-top: 0.5rem;
background: var(--card-card);
border-radius: 12px;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
width: 200px;
display: none;
z-index: 1000;
position:absolute;
top:100%;
right:0;
margin-top:0.5rem;
background:var(--card-card);
border-radius:12px;
box-shadow:0 4px 6px -1px rgba(0,0,0,0.1);
width:200px;
display:none;
z-index:1000;
}
.user-dropdown.show {
display: block;
animation: dropdownShow 0.2s ease;
display:block;
animation:dropdownShow 0.2s ease;
}
.dropdown-item {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 0.75rem 1rem;
color: var(--text-primary);
text-decoration: none;
transition: background-color 0.2s;
display:flex;
align-items:center;
gap:0.75rem;
padding:0.75rem 1rem;
color:var(--text-primary);
text-decoration:none;
transition:background-color 0.2s;
}
.dropdown-item:hover {
background: var(--button-secondaryFill);
background:var(--button-secondaryFill);
text-decoration:none;
}
.kreta-main {
flex: 1;
padding: clamp(1rem, 3vw, 2rem);
max-width: 1200px;
margin: 0 auto;
width: 100%;
flex:1;
padding:clamp(1rem,3vw,2rem);
max-width:1200px;
margin:0 auto;
width:100%;
}
.filter-card {
background: var(--card-card);
border-radius: 24px;
padding: 20px;
margin-bottom: 24px;
box-shadow: 0px 1px var(--shadow-blur) 0px var(--accent-shadow);
background:var(--card-card);
border-radius:24px;
padding:20px;
margin-bottom:24px;
box-shadow:0px 1px var(--shadow-blur) 0px var(--accent-shadow);
}
.filter-header {
margin-bottom: 16px;
margin-bottom:16px;
}
.filter-header h2 {
font-size: 18px;
font-weight: 600;
color: var(--text-primary);
background-color: var(--card-card);
font-size:18px;
font-weight:600;
color:var(--text-primary);
background-color:var(--card-card);
}
.filter-content {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 16px;
display:grid;
grid-template-columns:repeat(auto-fit,minmax(200px,1fr));
gap:16px;
}
.filter-group {
display: flex;
flex-direction: column;
gap: 8px;
display:flex;
flex-direction:column;
gap:8px;
}
.filter-group label {
display: flex;
align-items: center;
gap: 8px;
color: var(--text-secondary);
font-size: 14px;
display:flex;
align-items:center;
gap:8px;
color:var(--text-secondary);
font-size:14px;
}
.filter-input {
padding: 10px;
border: none;
border-radius: 8px;
background: var(--button-secondaryFill);
color: var(--text-primary);
font-family: inherit;
font-size: 14px;
transition: all 0.2s ease;
padding:10px;
border:none;
border-radius:8px;
background:var(--button-secondaryFill);
color:var(--text-primary);
font-family:inherit;
font-size:14px;
transition:all 0.2s ease;
}
.filter-input:focus {
outline: none;
box-shadow: 0 0 0 2px var(--accent-accent);
outline:none;
box-shadow:0 0 0 2px var(--accent-accent);
}
.stats-overview {
display:grid;
grid-template-columns:repeat(auto-fit,minmax(200px,1fr));
gap:16px;
margin-bottom:24px;
}
.stat-card {
background:var(--card-card);
border-radius:16px;
padding:20px;
text-align:center;
box-shadow:0px 1px var(--shadow-blur) 0px var(--accent-shadow);
transition:transform 0.2s ease;
}
.stat-card:hover {
transform:translateY(-2px);
}
.stat-number {
font-size:32px;
font-weight:700;
color:var(--accent-accent);
margin-bottom:8px;
}
.stat-label {
color:var(--text-secondary);
font-size:14px;
font-weight:500;
}
.absences-container {
display: flex;
flex-direction: column;
gap: 16px;
background:var(--card-card);
border-radius:16px;
overflow:hidden;
box-shadow:0px 1px var(--shadow-blur) 0px var(--accent-shadow);
}
.absence-group {
background: var(--card-card);
border-radius: 24px;
overflow: hidden;
animation: fadeIn 0.3s ease;
box-shadow: 0px 1px var(--shadow-blur) 0px var(--accent-shadow);
.absences-table {
width:100%;
border-collapse:collapse;
}
.absence-date {
padding: 16px;
color: var(--text-primary);
font-weight: 600;
display: flex;
align-items: center;
gap: 8px;
.table-header {
background:var(--accent-15);
border-bottom:1px solid var(--accent-30);
}
.absence-count {
margin-left: auto;
background: var(--accent-accent);
color: var(--button-secondaryFill);
padding: 4px 8px;
border-radius: 12px;
font-size: 14px;
.table-header th {
padding:16px;
text-align:left;
font-weight:600;
color:var(--text-primary);
font-size:14px;
text-transform:uppercase;
letter-spacing:0.5px;
}
.absence-list {
padding: 16px;
display: flex;
flex-direction: column;
gap: 12px;
.table-row {
border-bottom:1px solid var(--accent-15);
transition:background-color 0.2s ease;
}
.absence-item {
display: grid;
grid-template-columns: auto 1fr auto;
align-items: center;
gap: 16px;
padding: 12px;
background: var(--accent-15);
border-radius: 12px;
transition: transform 0.2s ease;
.table-row:hover {
background:var(--accent-10);
}
.absence-item:hover {
transform: translateX(4px);
.table-row:last-child {
border-bottom:none;
}
.absence-time {
display: flex;
align-items: center;
gap: 4px;
color: var(--text-secondary);
font-size: 14px;
.table-cell {
padding:16px;
color:var(--text-primary);
vertical-align:middle;
}
.absence-details {
display: flex;
flex-direction: column;
gap: 4px;
.date-cell {
font-weight:600;
color:var(--accent-accent);
}
.lesson-cell {
text-align:center;
font-weight:500;
}
.subject-cell {
font-weight:600;
}
.topic-cell {
color:var(--text-secondary);
font-size:14px;
max-width:200px;
overflow:hidden;
text-overflow:ellipsis;
white-space:nowrap;
}
.status-cell {
text-align:center;
}
.status-badge {
display:inline-flex;
align-items:center;
gap:4px;
padding:6px 12px;
border-radius:20px;
font-size:12px;
font-weight:600;
text-transform:uppercase;
letter-spacing:0.5px;
}
.status-badge.justified {
background:var(--grades-4-bg);
color:var(--grades-4);
}
.status-badge.unjustified {
background:var(--grades-1-bg);
color:var(--grades-1);
}
.status-badge.pending {
background:var(--grades-3-bg);
color:var(--grades-3);
}
@media (max-width:768px) {
.absences-table {
font-size:14px;
}
.table-header th,.table-cell {
padding:12px 8px;
}
.topic-cell {
max-width:120px;
}
.stats-overview {
grid-template-columns:repeat(2,1fr);
}
}@media (max-width:480px) {
.absences-table,.table-header,.table-row {
display:block;
}
.table-header {
display:none;
}
.date-group {
margin-bottom:24px;
}
.date-group-header {
background:var(--accent-accent);
color:white;
padding:12px 16px;
border-radius:12px 12px 0 0;
font-weight:600;
font-size:16px;
margin-bottom:0;
}
.date-group-content {
background:var(--card-card);
border:1px solid var(--accent-15);
border-radius:0 0 12px 12px;
overflow:hidden;
}
.table-row {
width:100%;
margin-bottom:0;
border:none;
border-bottom:1px solid var(--accent-15);
border-radius:0;
padding:16px;
background:transparent;
}
.table-row:last-child {
border-bottom:none;
}
.table-cell {
display:flex;
justify-content:space-between;
align-items:flex-start;
padding:6px 0;
border-bottom:1px solid var(--accent-15);
}
.table-cell:last-child {
border-bottom:none;
}
.table-cell::before {
content:attr(data-label);
font-weight:600;
color:var(--text-secondary);
font-size:12px;
text-transform:uppercase;
flex-shrink:0;
}
.topic-cell {
max-width:none;
white-space:normal;
text-overflow:initial;
overflow:visible;
text-align:right;
flex:1;
}
.stats-overview {
grid-template-columns:1fr;
}
}.absence-details {
display:flex;
flex-direction:column;
gap:4px;
}
.absence-subject {
font-weight: 600;
color: var(--text-primary);
font-weight:600;
color:var(--text-primary);
}
.absence-topic {
color: var(--text-secondary);
font-size: 14px;
color:var(--text-secondary);
font-size:14px;
}
.absence-status {
display: flex;
align-items: center;
gap: 4px;
font-size: 14px;
font-weight: 500;
display:flex;
align-items:center;
gap:4px;
font-size:14px;
font-weight:500;
}
.absence-status.justified {
color: var(--grades-4);
color:var(--grades-4);
}
.absence-status.unjustified {
color: var(--grades-1);
color:var(--grades-1);
}
.absence-status.pending {
color: var(--grades-3);
color:var(--grades-3);
}
.loading-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: var(--background);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
z-index: 9999;
position:fixed;
top:0;
left:0;
width:100%;
height:100%;
background:var(--background);
display:flex;
flex-direction:column;
align-items:center;
justify-content:center;
z-index:9999;
}
.loading-container {
display: flex;
flex-direction: column;
align-items: center;
gap: 1rem;
padding: 2rem;
border-radius: 24px;
display:flex;
flex-direction:column;
align-items:center;
gap:1rem;
padding:2rem;
border-radius:24px;
}
.loading-text {
color: var(--Text-Primary);
text-align: center;
font-family: Montserrat;
font-size: 20px;
font-style: normal;
font-weight: 700;
line-height: normal;
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%;
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%;
}
.loading-logo {
width: 48px;
height: 48px;
margin-bottom: 1rem;
border-radius: 8px;
width:48px;
height:48px;
margin-bottom:1rem;
border-radius:8px;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
opacity:0;
transform:translateY(-10px);
}
@keyframes dropdownShow {
to {
opacity:1;
transform:translateY(0);
}
}@keyframes dropdownShow {
from {
opacity: 0;
transform: translateY(-10px);
}
opacity:0;
transform:translateY(-10px);
}
to {
opacity:1;
transform:translateY(0);
}
}@keyframes spin {
to {
opacity: 1;
transform: translateY(0);
}
transform:rotate(360deg);
}
@keyframes spin {
to { transform: rotate(360deg); }
}
::-webkit-scrollbar {
width: clamp(4px, 1vw, 8px);
height: clamp(4px, 1vw, 8px);
}
::-webkit-scrollbar-track {
background: var(--background);
}
::-webkit-scrollbar-thumb {
background: var(--text-secondary);
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: var(--text-primary);
}

View File

@@ -1,257 +1,638 @@
async function collectAbsencesData() {
await helper.waitForElement('#HianyzasGrid');
await new Promise(resolve => setTimeout(resolve, 1000));
const basicData = {
schoolInfo: {
name: cookieManager.get('schoolName') || 'Iskola',
id: cookieManager.get('schoolCode') || ''
name: await storageManager.get("schoolName", "OM azonosító - Iskola neve"),
id: await storageManager.get("schoolCode", ""),
},
userData: {
name: cookieManager.get('userName') || 'Felhasználó',
time: document.querySelector('.usermenu_timer')?.textContent?.trim() || '45:00'
}
name: await storageManager.get("userName", "Felhasználónév"),
time:
document.querySelector(".usermenu_timer")?.textContent?.trim() ||
"45:00",
},
};
const absences = [];
const rows = document.querySelectorAll('#HianyzasGrid .k-grid-content tr');
rows.forEach(row => {
const cells = row.querySelectorAll('td');
if (cells.length >= 9) {
absences.push({
date: cells[1]?.textContent?.trim() || '',
lesson: cells[2]?.textContent?.trim() || '',
subject: cells[3]?.textContent?.trim() || '',
topic: cells[4]?.textContent?.trim() || '',
type: cells[5]?.textContent?.trim() || '',
justified: cells[6]?.textContent?.trim() === 'Igen',
justificationStatus: cells[6]?.textContent?.trim() === 'Igen' ? 'justified' :
cells[6]?.textContent?.trim() === 'Nem' ? 'unjustified' : 'pending',
purposeful: cells[7]?.textContent?.trim() || '',
justificationType: cells[8]?.textContent?.trim() || ''
try {
const currentDomain = window.location.hostname;
const response = await fetch(
`https://${currentDomain}/api/HianyzasokApi/GetHianyzasGrid?sort=MulasztasDatum-desc&page=1&pageSize=100&group=&filter=&data=%7B%7D&_=${Date.now()}`,
{
method: "GET",
credentials: "include",
headers: {
Accept: "application/json, text/javascript, */*; q=0.01",
"X-Requested-With": "XMLHttpRequest",
},
},
);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const apiData = await response.json();
const absences = [];
if (apiData.Data && Array.isArray(apiData.Data)) {
apiData.Data.forEach((item) => {
const date = new Date(item.MulasztasDatum);
const formattedDate = `${date.getFullYear()}.${(date.getMonth() + 1).toString().padStart(2, "0")}.${date.getDate().toString().padStart(2, "0")}.`;
let justificationStatus = "pending";
if (item.Igazolt_BOOL === true) {
justificationStatus = "justified";
} else if (item.Igazolt_BOOL === false && item.IgazolasTipus !== null) {
justificationStatus = "unjustified";
}
absences.push({
date: formattedDate,
lesson: item.Oraszam?.toString() || "",
subject: item.Targy || "",
topic: item.Tema || "",
type: item.MulasztasTipus_DNAME || "",
justified: item.Igazolt_BOOL === true,
justificationStatus: justificationStatus,
purposeful: item.TanoraiCeluMulasztas_BNAME || "",
justificationType: item.IgazolasTipus_DNAME || "",
});
});
}
});
const groupedAbsences = {};
absences.forEach(absence => {
if (!groupedAbsences[absence.date]) {
groupedAbsences[absence.date] = [];
}
groupedAbsences[absence.date].push(absence);
});
const groupedAbsences = {};
absences.forEach((absence) => {
if (!groupedAbsences[absence.date]) {
groupedAbsences[absence.date] = [];
}
groupedAbsences[absence.date].push(absence);
});
return { basicData, absences, groupedAbsences };
return { basicData, absences, groupedAbsences };
} catch (error) {
console.error("Hiba az API hívás során:", error);
return { basicData, absences: [], groupedAbsences: {} };
}
}
function createFilterCard(absences) {
const filterCard = document.createElement('div');
filterCard.className = 'filter-card';
const filterHeader = document.createElement('div');
filterHeader.className = 'filter-header';
const h2 = document.createElement('h2');
h2.textContent = LanguageManager.t('absences.filter_title');
filterHeader.appendChild(h2);
const filterContent = document.createElement('div');
filterContent.className = 'filter-content';
const dateGroup = createFilterGroup(
'Calendar.svg',
'Dátum',
LanguageManager.t('absences.date'),
'input',
{ type: 'date', id: 'dateFilter', className: 'filter-input' }
);
filterContent.appendChild(dateGroup);
const subjectGroup = createSubjectFilterGroup(absences);
filterContent.appendChild(subjectGroup);
const justificationGroup = createJustificationFilterGroup();
filterContent.appendChild(justificationGroup);
filterCard.appendChild(filterHeader);
filterCard.appendChild(filterContent);
return filterCard;
}
function createFilterGroup(iconName, altText, labelText, elementType, attributes) {
const group = document.createElement('div');
group.className = 'filter-group';
const label = document.createElement('label');
const img = document.createElement('img');
img.src = chrome.runtime.getURL(`icons/${iconName}`);
img.alt = altText;
img.style.width = '24px';
img.style.height = '24px';
label.appendChild(img);
label.appendChild(document.createTextNode(' ' + labelText));
const element = document.createElement(elementType);
Object.assign(element, attributes);
group.appendChild(label);
group.appendChild(element);
return group;
}
function createSubjectFilterGroup(absences) {
const group = document.createElement('div');
group.className = 'filter-group';
const label = document.createElement('label');
const img = document.createElement('img');
img.src = chrome.runtime.getURL('icons/Subject.svg');
img.alt = 'Tantárgy';
img.style.width = '24px';
img.style.height = '24px';
label.appendChild(img);
label.appendChild(document.createTextNode(' ' + LanguageManager.t('absences.subject')));
const select = document.createElement('select');
select.id = 'subjectFilter';
select.className = 'filter-input';
const defaultOption = document.createElement('option');
defaultOption.value = '';
defaultOption.textContent = LanguageManager.t('absences.all_subjects');
select.appendChild(defaultOption);
const subjects = [...new Set(absences.map(a => a.subject))].sort();
subjects.forEach(subject => {
const option = document.createElement('option');
option.value = subject;
option.textContent = subject;
select.appendChild(option);
});
group.appendChild(label);
group.appendChild(select);
return group;
}
function createJustificationFilterGroup() {
const group = document.createElement('div');
group.className = 'filter-group';
const label = document.createElement('label');
const img = document.createElement('img');
img.src = chrome.runtime.getURL('icons/BadgeCheck.svg');
img.alt = 'Igazolás';
img.style.width = '24px';
img.style.height = '24px';
label.appendChild(img);
label.appendChild(document.createTextNode(' ' + LanguageManager.t('absences.justification')));
const select = document.createElement('select');
select.id = 'justificationFilter';
select.className = 'filter-input';
const options = [
{ value: '', text: LanguageManager.t('absences.all_types') },
{ value: 'justified', text: LanguageManager.t('absences.justified') },
{ value: 'unjustified', text: LanguageManager.t('absences.unjustified') },
{ value: 'pending', text: LanguageManager.t('absences.pending') }
];
options.forEach(optionData => {
const option = document.createElement('option');
option.value = optionData.value;
option.textContent = optionData.text;
select.appendChild(option);
});
group.appendChild(label);
group.appendChild(select);
return group;
}
function createStatsOverview(absences) {
const statsOverview = document.createElement('div');
statsOverview.className = 'stats-overview';
const stats = [
{ number: absences.length, label: LanguageManager.t('absences.total_absences') },
{ number: absences.filter(a => a.justificationStatus === 'justified').length, label: LanguageManager.t('absences.justified') },
{ number: absences.filter(a => a.justificationStatus === 'unjustified').length, label: LanguageManager.t('absences.unjustified') },
{ number: absences.filter(a => a.justificationStatus === 'pending').length, label: LanguageManager.t('absences.pending') }
];
stats.forEach(stat => {
const statCard = document.createElement('div');
statCard.className = 'stat-card';
const statNumber = document.createElement('div');
statNumber.className = 'stat-number';
statNumber.textContent = stat.number;
const statLabel = document.createElement('div');
statLabel.className = 'stat-label';
statLabel.textContent = stat.label;
statCard.appendChild(statNumber);
statCard.appendChild(statLabel);
statsOverview.appendChild(statCard);
});
return statsOverview;
}
function createAbsencesContainer(absences) {
const container = document.createElement('div');
container.className = 'absences-container';
const table = document.createElement('table');
table.className = 'absences-table';
const thead = document.createElement('thead');
thead.className = 'table-header';
const headerRow = document.createElement('tr');
const headers = [
LanguageManager.t('absences.date'),
LanguageManager.t('absences.lesson'),
LanguageManager.t('absences.subject'),
LanguageManager.t('absences.topic'),
LanguageManager.t('absences.status')
];
headers.forEach(headerText => {
const th = document.createElement('th');
th.textContent = headerText;
headerRow.appendChild(th);
});
thead.appendChild(headerRow);
const tbody = document.createElement('tbody');
generateAbsencesRows(absences, tbody);
table.appendChild(thead);
table.appendChild(tbody);
container.appendChild(table);
return container;
}
async function transformAbsencesPage() {
const { basicData, absences, groupedAbsences } = await collectAbsencesData();
document.body.textContent = '';
const container = document.createElement('div');
container.className = 'kreta-container';
const headerDiv = document.createElement('div');
const parser = new DOMParser();
const doc = parser.parseFromString(await createTemplate.header(), 'text/html');
const tempDiv = doc.body;
while (tempDiv.firstChild) {
headerDiv.appendChild(tempDiv.firstChild);
}
container.appendChild(headerDiv);
const main = document.createElement('main');
main.className = 'kreta-main';
const filterCard = createFilterCard(absences);
main.appendChild(filterCard);
const statsOverview = createStatsOverview(absences);
main.appendChild(statsOverview);
const absencesContainer = createAbsencesContainer(absences);
main.appendChild(absencesContainer);
container.appendChild(main);
document.body.appendChild(container);
document.body.innerHTML = `
<div class="kreta-container">
${createTemplate.header()}
<main class="kreta-main">
<div class="filter-card">
<div class="filter-header">
<h2>Szűrés</h2>
</div>
<div class="filter-content">
<div class="filter-group">
<label>
<span class="material-icons-round">date_range</span>
Dátum
</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
</label>
<select id="subjectFilter" class="filter-input">
<option value="">Minden tantárgy</option>
${[...new Set(absences.map(a => a.subject))].sort().map(subject =>
`<option value="${subject}">${subject}</option>`
).join('')}
</select>
</div>
<div class="filter-group">
<label>
<span class="material-icons-round">check_circle</span>
Igazolás
</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>
</select>
</div>
</div>
</div>
<div class="absences-container">
${Object.entries(groupedAbsences).map(([date, dayAbsences]) => `
<div class="absence-group" data-date="${date}">
<div class="absence-date">
<span class="material-icons-round">event</span>
${date}
<span class="absence-count">${dayAbsences.length} óra</span>
</div>
<div class="absence-list">
${dayAbsences.map(absence => `
<div class="absence-item"
data-subject="${absence.subject}"
data-justified="${absence.justified}">
<div class="absence-time">
<span class="material-icons-round">schedule</span>
${absence.lesson}. óra
</div>
<div class="absence-details">
<div class="absence-subject">${absence.subject}</div>
<div class="absence-topic">${absence.topic}</div>
</div>
<div class="absence-status ${absence.justificationStatus}">
${absence.justificationStatus === 'justified' ?
`Igazolt <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>`}
</div>
</div>
`).join('')}
</div>
</div>
`).join('')}
</div>
</main>
</div>
`;
createTemplate.importFonts();
setupUserDropdown();
setupMobileNavigation();
setupEventListeners();
setupFilters();
loadingScreen.hide();
}
function generateAbsencesRows(absences, tbody) {
const groupedByDate = absences.reduce((groups, absence) => {
const date = absence.date;
if (!groups[date]) {
groups[date] = [];
}
groups[date].push(absence);
return groups;
}, {});
const sortedDates = Object.keys(groupedByDate).sort(
(a, b) => new Date(b) - new Date(a),
);
sortedDates.forEach((date) => {
const dateAbsences = groupedByDate[date];
const divider = document.createElement('tr');
divider.className = 'date-group-divider';
divider.style.display = 'none';
tbody.appendChild(divider);
dateAbsences.forEach((absence) => {
const row = document.createElement('tr');
row.className = 'table-row';
row.dataset.subject = absence.subject;
row.dataset.justified = absence.justified;
row.dataset.date = absence.date;
row.dataset.dateGroup = date;
const dateCell = document.createElement('td');
dateCell.className = 'table-cell date-cell';
dateCell.dataset.label = LanguageManager.t('absences.date');
dateCell.textContent = absence.date;
row.appendChild(dateCell);
const lessonCell = document.createElement('td');
lessonCell.className = 'table-cell lesson-cell';
lessonCell.dataset.label = LanguageManager.t('absences.lesson');
lessonCell.textContent = absence.lesson + '.';
row.appendChild(lessonCell);
const subjectCell = document.createElement('td');
subjectCell.className = 'table-cell subject-cell';
subjectCell.dataset.label = LanguageManager.t('absences.subject');
subjectCell.textContent = absence.subject;
row.appendChild(subjectCell);
const topicCell = document.createElement('td');
topicCell.className = 'table-cell topic-cell';
topicCell.dataset.label = LanguageManager.t('absences.topic');
topicCell.title = absence.topic;
topicCell.textContent = absence.topic;
row.appendChild(topicCell);
const statusCell = document.createElement('td');
statusCell.className = 'table-cell status-cell';
statusCell.dataset.label = LanguageManager.t('absences.status');
const statusBadge = document.createElement('span');
statusBadge.className = `status-badge ${absence.justificationStatus}`;
if (absence.justificationStatus === 'justified') {
const img = document.createElement('img');
img.src = chrome.runtime.getURL('icons/BadgeCheck.svg');
img.alt = 'Igazolt';
img.style.width = '16px';
img.style.height = '16px';
statusBadge.appendChild(img);
statusBadge.appendChild(document.createTextNode(' ' + LanguageManager.t('absences.justified')));
} else if (absence.justificationStatus === 'unjustified') {
const span = document.createElement('span');
span.className = 'material-icons-round';
span.textContent = 'cancel';
statusBadge.appendChild(span);
statusBadge.appendChild(document.createTextNode(' ' + LanguageManager.t('absences.unjustified')));
} else {
const span = document.createElement('span');
span.className = 'material-icons-round';
span.textContent = 'pending';
statusBadge.appendChild(span);
statusBadge.appendChild(document.createTextNode(' ' + LanguageManager.t('absences.pending')));
}
statusCell.appendChild(statusBadge);
row.appendChild(statusCell);
tbody.appendChild(row);
});
});
}
function setupEventListeners() {
function setupMobileGrouping() {
if (window.innerWidth <= 480) {
createMobileGroups();
} else {
removeMobileGroups();
}
}
window.addEventListener("resize", setupMobileGrouping);
setupMobileGrouping();
}
function createMobileGroups() {
const tbody = document.querySelector(".absences-table tbody");
if (!tbody) return;
removeMobileGroups();
const rows = Array.from(tbody.querySelectorAll(".table-row"));
const groupedRows = {};
rows.forEach((row) => {
const date = row.dataset.date;
if (!groupedRows[date]) {
groupedRows[date] = [];
}
groupedRows[date].push(row);
});
const sortedDates = Object.keys(groupedRows).sort(
(a, b) => new Date(b) - new Date(a),
);
while (tbody.firstChild) {
tbody.removeChild(tbody.firstChild);
}
sortedDates.forEach((date) => {
const dateRows = groupedRows[date];
const dateGroup = document.createElement("div");
dateGroup.className = "date-group";
const dateHeader = document.createElement("div");
dateHeader.className = "date-group-header";
dateHeader.textContent = date;
const dateContent = document.createElement("div");
dateContent.className = "date-group-content";
dateRows.forEach((row) => {
dateContent.appendChild(row);
});
dateGroup.appendChild(dateHeader);
dateGroup.appendChild(dateContent);
tbody.appendChild(dateGroup);
});
}
function removeMobileGroups() {
const tbody = document.querySelector(".absences-table tbody");
if (!tbody) return;
const dateGroups = tbody.querySelectorAll(".date-group");
if (dateGroups.length === 0) return;
const allRows = [];
dateGroups.forEach((group) => {
const rows = group.querySelectorAll(".table-row");
rows.forEach((row) => allRows.push(row));
});
while (tbody.firstChild) {
tbody.removeChild(tbody.firstChild);
}
allRows.forEach((row) => tbody.appendChild(row));
}
function updateDateGroupsVisibility() {
if (window.innerWidth > 480) return;
const dateGroups = document.querySelectorAll(".date-group");
dateGroups.forEach((group) => {
const visibleRows = group.querySelectorAll(
'.table-row[style=""], .table-row:not([style])',
);
if (visibleRows.length > 0) {
group.style.display = "";
} else {
group.style.display = "none";
}
});
}
function setupFilters() {
try {
const filters = {
dateFilter: document.getElementById('dateFilter'),
subject: document.getElementById('subjectFilter'),
justified: document.getElementById('justificationFilter')
dateFilter: document.getElementById("dateFilter"),
subject: document.getElementById("subjectFilter"),
justified: document.getElementById("justificationFilter"),
};
if (!filters.dateFilter || !filters.subject || !filters.justified) {
console.warn('Some filter elements were not found in the DOM');
console.warn("Some filter elements were not found in the DOM");
return;
}
if (filters.dateFilter) {
filters.dateFilter.disabled = true;
}
const filterAbsences = () => {
try {
const dateFilterValue = filters.dateFilter.value;
const subject = filters.subject.value;
const justified = filters.justified.value;
const selectedDate = dateFilterValue ? new Date(dateFilterValue) : null;
document.querySelectorAll('.absence-group').forEach(group => {
const dateStr = group.dataset.date;
const dateParts = dateStr.split('.');
document.querySelectorAll(".table-row").forEach((row) => {
const dateStr = row.dataset.date;
const dateParts = dateStr.split(".");
if (dateParts.length < 3) {
console.error(`Invalid date format: ${dateStr}`);
return;
}
const parsedDay = parseInt(dateParts[0].trim(), 10);
const parsedYear = parseInt(dateParts[0].trim(), 10);
const parsedMonth = parseInt(dateParts[1].trim(), 10) - 1;
const parsedYear = parseInt(dateParts[2].trim(), 10);
const parsedDay = parseInt(dateParts[2].trim(), 10);
if (isNaN(parsedDay) || isNaN(parsedMonth) || isNaN(parsedYear)) {
console.error(`Invalid date components: ${dateStr}`);
return;
}
const groupDate = new Date(parsedYear, parsedMonth, parsedDay);
let showGroup = true;
const absenceItems = group.querySelectorAll('.absence-item');
let visibleItems = 0;
const rowDate = new Date(parsedYear, parsedMonth, parsedDay);
absenceItems.forEach(item => {
let showItem = true;
if (subject && item.dataset.subject !== subject) showItem = false;
if (justified) {
const statusElement = item.querySelector('.absence-status');
const hasStatus = statusElement.classList.contains(justified);
if (!hasStatus) showItem = false;
let showRow = true;
if (selectedDate) {
if (
rowDate.getFullYear() !== selectedDate.getFullYear() ||
rowDate.getMonth() !== selectedDate.getMonth() ||
rowDate.getDate() !== selectedDate.getDate()
) {
showRow = false;
}
}
item.style.display = showItem ? '' : 'none';
if (showItem) visibleItems++;
});
if (subject && row.dataset.subject !== subject) {
showRow = false;
}
group.style.display = (showGroup && visibleItems > 0) ? '' : 'none';
if (justified) {
const statusElement = row.querySelector(".status-badge");
const hasStatus = statusElement.classList.contains(justified);
if (!hasStatus) showRow = false;
}
row.style.display = showRow ? "" : "none";
});
updateDateGroupsVisibility();
updateStatistics();
} catch (err) {
console.error('Error during filtering absences:', err);
console.error("Error during filtering absences:", err);
}
};
Object.values(filters).forEach(filter => {
Object.values(filters).forEach((filter) => {
try {
if (filter && filter !== filters.dateFilter) {
filter.addEventListener('change', filterAbsences);
if (filter) {
filter.addEventListener("change", filterAbsences);
}
} catch (err) {
if (err.message && err.message.includes('Extension context invalidated')) {
console.warn('Extension context invalidated during event listener setup');
if (
err.message &&
err.message.includes("Extension context invalidated")
) {
console.warn(
"Extension context invalidated during event listener setup",
);
} else {
console.error('Error setting up filter event listener:', err);
console.error("Error setting up filter event listener:", err);
}
}
});
filterAbsences();
} catch (err) {
if (err.message && err.message.includes('Extension context invalidated')) {
console.warn('Extension context invalidated during filter setup');
if (err.message && err.message.includes("Extension context invalidated")) {
console.warn("Extension context invalidated during filter setup");
} else {
console.error('Error setting up filters:', err);
console.error("Error setting up filters:", err);
}
}
}
function updateStatistics() {
try {
const visibleRows = document.querySelectorAll(
'.table-row:not([style*="display: none"])',
);
const totalVisible = visibleRows.length;
const justifiedVisible = Array.from(visibleRows).filter((row) =>
row.querySelector(".status-badge.justified"),
).length;
const unjustifiedVisible = Array.from(visibleRows).filter((row) =>
row.querySelector(".status-badge.unjustified"),
).length;
const pendingVisible = Array.from(visibleRows).filter((row) =>
row.querySelector(".status-badge.pending"),
).length;
if (window.location.href.includes('/Hianyzas/Hianyzasok')) {
transformAbsencesPage().catch(error => {
console.error('Hiba történt az oldal átalakítása során:', error);
const statCards = document.querySelectorAll(".stat-card");
if (statCards[0])
statCards[0].querySelector(".stat-number").textContent = totalVisible;
if (statCards[1])
statCards[1].querySelector(".stat-number").textContent = justifiedVisible;
if (statCards[2])
statCards[2].querySelector(".stat-number").textContent =
unjustifiedVisible;
if (statCards[3])
statCards[3].querySelector(".stat-number").textContent = pendingVisible;
} catch (err) {
console.error("Error updating statistics:", err);
}
}
if (window.location.href.includes("/Hianyzas/Hianyzasok")) {
transformAbsencesPage().catch((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,239 @@ 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);
display: flex;
flex-direction: column;
margin-bottom: 12px;
padding: 12px;
padding: 8px;
position: relative;
min-height: 80px;
}
.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;
word-wrap: break-word;
overflow-wrap: break-word;
hyphens: auto;
max-width: 100%;
}
.widget-subtitle {
color: var(--text-secondary);
font-weight: 500;
font-size: 14px;
line-height: 1.2;
word-wrap: break-word;
overflow-wrap: break-word;
}
.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;
word-wrap: break-word;
overflow-wrap: break-word;
}
.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;
word-wrap: break-word;
overflow-wrap: break-word;
max-width: 100%;
}
.news-author {
display: flex;
flex-direction: column;
align-items: flex-end;
gap: 4px;
text-align: right;
word-wrap: break-word;
overflow-wrap: break-word;
hyphens: auto;
max-width: 200px;
}
/* Mobile view: author below content */
@media (max-width: 768px) {
.news-item .widget-row {
flex-direction: column;
align-items: stretch;
}
.news-item .widget-meta {
order: 2;
display: flex;
flex-direction: column;
align-items: center;
margin-top: 8px;
}
.news-item .widget-details {
order: 1;
}
.news-author {
align-items: center;
text-align: center;
max-width: 100%;
margin-top: 4px;
}
.widget-date {
order: 1;
}
.news-author {
order: 2;
}
}
/* Desktop view: limit author width and wrap text */
@media (min-width: 769px) {
.news-author {
max-width: 180px;
white-space: normal;
line-height: 1.3;
}
}
.widget-empty {
color: var(--text-secondary);
font-style: italic;
text-align: center;
padding: 20px;
}
.grade {
width: 32px;
@@ -84,16 +285,32 @@ 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;
word-wrap: break-word;
overflow-wrap: break-word;
hyphens: auto;
line-height: 1.4;
}
.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);
word-wrap: break-word;
overflow-wrap: break-word;
hyphens: auto;
}
.exam-type {
font-size: 12px;
font-weight: 500;
letter-spacing: 0.5px;
}
.more-link {
margin-top: auto;
display: inline-flex;
@@ -174,21 +391,24 @@ body {
.dropdown-item:hover {
background-color: var(--accent-15);
text-decoration: none;
}
.dropdown-item svg {
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);
}
.more-link {
margin-top: auto;
@@ -212,22 +432,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,325 +1,588 @@
const utils = {
const DashboardUtils = {
formatGradeValue(value) {
const trimmedValue = value?.trim() || '';
if (trimmedValue.toLowerCase() === 'szöveges') {
return 'Sz';
const trimmedValue = value?.trim() || "";
if (trimmedValue.toLowerCase() === "szöveges") {
return "Sz";
}
return trimmedValue;
},
parseDate(dateStr) {
return dateStr?.trim() || '';
return dateStr?.trim() || "";
},
formatHungarianDate(dateStr) {
if (!dateStr) return '';
const dateParts = dateStr.trim().split('.');
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: [],
upcomingExams: [],
news: []
news: [],
};
}
extractGrades() {
const gradeRows = document.querySelectorAll('#legutobbiErtekelesek tr:not(:first-child)');
this.data.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;
extractGradeData() {
const gradeRows = document.querySelectorAll(
"#legutobbiErtekelesek tr:not(:first-child)",
);
const [fullSubject, date] = gradeInfo.split('\n').map(str => str.trim());
const { subject, type } = this.parseSubjectInfo(fullSubject);
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, dateInSubject } =
this.parseSubjectInformation(fullSubject);
return {
value: DashboardUtils.formatGradeValue(gradeValue),
subject,
date: DashboardUtils.parseDate(date),
type: type || LanguageManager.t("dashboard.evaluation"),
dateInSubject: dateInSubject || null,
};
})
.filter(Boolean);
}
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 (dateMatch) {
const subjectPart = fullSubject.substring(0, dateMatch.index).trim();
const datePart = dateMatch[0].trim();
return {
value: utils.formatGradeValue(gradeValue),
subject,
date: utils.parseDate(date),
type: type || 'Értékelés'
subject: subjectPart,
type: "",
dateInSubject: datePart,
};
}).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);
if (!monthMatch) return { subject: fullSubject, type: '' };
const monthIndex = fullSubject.lastIndexOf(monthMatch[0]);
return {
subject: fullSubject.substring(0, monthIndex).trim(),
type: fullSubject.substring(monthIndex).trim()
};
}
extractAbsences() {
const absenceRows = document.querySelectorAll('#legutobbiMulasztasok tr:not(:first-child)');
this.data.absences = Array.from(absenceRows).map(row => {
const spans = row.querySelectorAll('span');
if (spans.length < 4) return null;
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);
if (monthMatch) {
const monthIndex = fullSubject.lastIndexOf(monthMatch[0]);
return {
date: spans[0]?.textContent?.trim() || '',
day: spans[2]?.textContent?.trim() || '',
type: spans[1]?.textContent?.trim() || '',
count: spans[3]?.textContent?.trim() || ''
subject: fullSubject.substring(0, monthIndex).trim(),
type: fullSubject.substring(monthIndex).trim(),
};
}).filter(Boolean);
}
return { subject: fullSubject, type: "" };
}
extractNotes() {
const noteRows = document.querySelectorAll('#legutobbiFeljegyzesek tr:not(:first-child)');
this.data.notes = Array.from(noteRows).map(row => {
const spans = row.querySelectorAll('span');
if (spans.length < 3) return null;
extractAbsenceData() {
const absenceRows = document.querySelectorAll(
"#legutobbiMulasztasok tr:not(:first-child)",
);
return {
title: spans[0]?.textContent?.trim() || '',
author: spans[1]?.textContent?.trim() || '',
date: spans[2]?.textContent?.trim() || ''
};
}).filter(Boolean);
this.dashboardData.absences = Array.from(absenceRows)
.map((row) => {
const spans = row.querySelectorAll("span");
if (spans.length < 4) return null;
return {
date: spans[0]?.textContent?.trim() || "",
day: spans[2]?.textContent?.trim() || "",
type: spans[1]?.textContent?.trim() || "",
count: spans[3]?.textContent?.trim() || "",
};
})
.filter(Boolean);
}
extractExams() {
const examRows = document.querySelectorAll('#legutobbiBejelentettSzamonkeres tr:not(:first-child)');
this.data.upcomingExams = Array.from(examRows).map(row => {
const spans = row.querySelectorAll('span');
if (spans.length < 4) return null;
extractNoteData() {
const noteRows = document.querySelectorAll(
"#legutobbiFeljegyzesek tr:not(:first-child)",
);
return {
date: spans[0]?.textContent?.trim() || '',
subject: spans[1]?.textContent?.trim() || '',
day: spans[2]?.textContent?.trim() || '',
type: spans[3]?.textContent?.trim() || ''
};
}).filter(Boolean);
this.dashboardData.notes = Array.from(noteRows)
.map((row) => {
const spans = row.querySelectorAll("span");
if (spans.length < 3) return null;
return {
title: spans[0]?.textContent?.trim() || "",
author: spans[1]?.textContent?.trim() || "",
date: spans[2]?.textContent?.trim() || "",
};
})
.filter(Boolean);
}
extractNews() {
const newsContainer = document.querySelector('.faliujsag-lista, #faliujsagLista');
if (!newsContainer) return;
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');
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;
}
extractExamData() {
const examRows = document.querySelectorAll(
"#legutobbiBejelentettSzamonkeres tr:not(:first-child)",
);
this.dashboardData.upcomingExams = Array.from(examRows)
.map((row) => {
const spans = row.querySelectorAll("span");
if (spans.length < 4) return null;
return {
date: spans[0]?.textContent?.trim() || "",
subject: spans[1]?.textContent?.trim() || "",
day: spans[2]?.textContent?.trim() || "",
type: spans[3]?.textContent?.trim() || "",
};
})
.filter(Boolean);
}
async extractNewsData() {
try {
const timestamp = Date.now();
const apiUrl = `https://${window.location.hostname}/Intezmeny/Faliujsag/GetMoreEntries?startindex=0&range=10&_=${timestamp}`;
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 authorElement = item.querySelector('.auth-name span, .news-author');
return {
title: titleElement?.textContent?.trim() || '',
date: dateStr.trim(),
content: contentElement?.textContent?.trim() || '',
author: authorElement?.textContent?.trim() || ''
};
}).filter(news => news.title || news.content);
const data = await response.json();
if (!data.FaliujsagElemek || !Array.isArray(data.FaliujsagElemek)) {
return;
}
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.baseData = data;
}
async init() {
this.data = {
...data,
...this.baseData,
schoolInfo: {
name: cookieManager.get(COOKIE_KEYS.SCHOOL_NAME) || DEFAULT_VALUES.SCHOOL,
id: cookieManager.get(COOKIE_KEYS.SCHOOL_CODE) || ''
name:
await storageManager.get("schoolName", "OM azonosító - Iskola neve"),
id: await storageManager.get("schoolCode", ""),
},
userData: {
name: cookieManager.get(COOKIE_KEYS.USER_NAME) || DEFAULT_VALUES.USER,
time: document.querySelector('.usermenu_timer')?.textContent?.trim() || DEFAULT_VALUES.TIMER
}
name: await storageManager.get("userName", "Felhasználónév"),
time:
document.querySelector(".usermenu_timer")?.textContent?.trim() ||
"45:00",
},
};
this.schoolNameFull = `${this.data.schoolInfo.id} - ${this.data.schoolInfo.name}`;
this.shortenedSchoolName = helper.shortenSchoolName(this.schoolNameFull);
}
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() {
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>
createNewsCard() {
const newsItems = this.data.news
.map(
(news) => `
<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('');
`,
)
.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() {
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>
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('');
`,
)
.join("");
return this.generateCard('Mulasztások', absenceItems, '/Hianyzas/Hianyzasok', 'Összes mulasztás');
return this.createCard(
LanguageManager.t("dashboard.grades"),
gradeItems,
"/TanuloErtekeles/Osztalyzatok",
LanguageManager.t("dashboard.all_grades"),
);
}
generateNoteCard() {
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>
createAbsenceCard() {
const absenceItems = this.data.absences
.map(
(absence) => `
<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('');
`,
)
.join("");
return this.generateCard('Feljegyzések', noteItems, '/TanuloErtekeles/InformaciokFeljegyzesek', 'Összes üzeneted');
return this.createCard(
LanguageManager.t("dashboard.absences"),
absenceItems,
"/Hianyzas/Hianyzasok",
LanguageManager.t("dashboard.all_absences"),
);
}
generateExamCard() {
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>
createNoteCard() {
const noteItems = this.data.notes
.map(
(note) => `
<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('');
`,
)
.join("");
return this.generateCard('Bejelentett dolgozatok', examItems, '/Tanulo/TanuloBejelentettSzamonkeresek', 'Összes dolgozat');
return this.createCard(
LanguageManager.t("dashboard.notes"),
noteItems,
"/TanuloErtekeles/InformaciokFeljegyzesek",
LanguageManager.t("dashboard.all_messages"),
);
}
generateCard(title, content, linkHref, linkText) {
createExamCard() {
const examItems = this.data.upcomingExams
.map(
(exam) => `
<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.createCard(
LanguageManager.t("dashboard.exams"),
examItems,
"/Tanulo/TanuloBejelentettSzamonkeresek",
LanguageManager.t("dashboard.all_exams"),
);
}
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">
${createTemplate.header()}
${this.generateMainContentHTML()}
</div>
`;
async render() {
await this.init();
document.body.innerHTML = '';
const kretaContainer = document.createElement('div');
kretaContainer.className = 'kreta-container';
const headerDiv = document.createElement('div');
const parser = new DOMParser();
const headerDoc = parser.parseFromString(await createTemplate.header(), 'text/html');
const headerContent = headerDoc.body;
while (headerContent.firstChild) {
headerDiv.appendChild(headerContent.firstChild);
}
kretaContainer.appendChild(headerDiv);
const mainContentDiv = document.createElement('div');
const parser2 = new DOMParser();
const mainDoc = parser2.parseFromString(this.generateMainContent(), 'text/html');
const mainContent = mainDoc.body;
while (mainContent.firstChild) {
mainContentDiv.appendChild(mainContent.firstChild);
}
kretaContainer.appendChild(mainContentDiv);
document.body.appendChild(kretaContainer);
setupUserDropdown();
setupMobileNavigation();
}
}
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();
createTemplate.importFonts();
const ui = new DashboardUI(dashboardData);
ui.render();
const dataManager = new DashboardDataManager();
const dashboardData = await dataManager.extractAllData();
const renderer = new DashboardRenderer(dashboardData);
await renderer.render();
} catch (error) {
console.error('Error initializing dashboard:', error);
console.error("Error initializing dashboard:", error);
}
}
}
new DashboardApp();
new DashboardApplication();

BIN
fonts/Icons.woff2 Normal file

Binary file not shown.

View File

@@ -1,202 +1,57 @@
: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;
}
.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 {
padding: 16px;
}
.forgot-card {
padding: 20px;
}
.form-actions {
flex-direction: column;
gap: 16px;
align-items: stretch;
}
.btn-submit {
width: 100%;
}
}
body.maintenance-mode {
margin:0;
padding:0;
height:100vh;
display:flex;
align-items:center;
justify-content:center;
background-color:var(--background);
color:var(--text-primary);
font-family:'Figtree',sans-serif;
}
body {
background-color:var(--background) !important;
}
.maintenance-container {
text-align:center;
padding:2rem;
border-radius:8px;
background-color:var(--card-card);
box-shadow:0 var(--shadow-blur) 6px var(--accent-shadow);
max-width:600px;
width:90%;
}
.maintenance-logo {
width:128px;
height:128px;
margin:0 auto 2rem;
}
.maintenance-title {
font-size:1.5rem;
font-weight:600;
margin-bottom:1rem;
color:var(--accent-accent);
font-family:'Montserrat',sans-serif;
}
.maintenance-message {
font-size:1rem;
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

@@ -1,147 +1,290 @@
(() => {
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') {
document.documentElement.setAttribute('data-theme', message.darkMode ? 'dark' : 'light');
localStorage.setItem('darkMode', message.darkMode);
}
});
document.body.innerHTML = `
<div class="forgot-container">
<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">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons+Round" rel="stylesheet">
// reCAPTCHA functionality removed for security compliance
const loadDependencies = async () => {
// reCAPTCHA functionality removed for security compliance
// Extension now works without external script dependencies
};
const createPageStructure = () => {
// Biztonságos DOM létrehozás innerHTML helyett
document.body.innerHTML = '';
// Biztonságos HTML parsing DOMParser használatával
const parser = new DOMParser();
const doc = parser.parseFromString(`
<div class="forgot-container">
<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">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons+Round" rel="stylesheet">
<style>
.g-recaptcha {
margin-top: 5px;
display: flex;
justify-content: center;
}
</style>
<div class="forgot-card">
<header class="forgot-header">
<p class="logo-text">
<img src="${chrome.runtime.getURL('images/firka_logo.png')}" alt="Firka" class="logo">
<img src="${chrome.runtime.getURL("images/firka_logo.png")}" alt="Firka" class="logo">
Firka
</p>
</header>
<div class="forgot-card">
<h1 class="forgot-title">Elfelejtett jelszó</h1>
<form id="forgotForm" novalidate>
<div class="form-group">
<label class="form-label" for="username">OM azonosítód</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>
</div>
<div class="form-group">
<label class="form-label" for="email">E-mail cím</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>
</div>
<div class="g-recaptcha" data-sitekey="6LcmPB8dAAAAACJPQBj7WfpBoBsEfyibZeIG5Vbl"></div>
<div class="form-actions">
<a href="/Adminisztracio/Login" class="help-link">
Vissza a bejelentkezéshez
</a>
<button type="submit" class="btn-submit">
Jelszó visszaállítása
</button>
</div>
</form>
</div>
<h1 class="forgot-title" data-i18n="forgotpassword.title">Elfelejtett jelszó</h1>
<form id="forgotForm" novalidate>
<div class="form-group">
<label class="form-label" for="BejelentkezesiNev" data-i18n="forgotpassword.om_id_label">OM azonosító</label>
<input type="text" id="BejelentkezesiNev" name="BejelentkezesiNev" class="form-control"
data-i18n-attr="placeholder" data-i18n="forgotpassword.om_id_placeholder"
placeholder="Adja meg az OM azonosítóját" required>
<div class="error-message" data-i18n="forgotpassword.om_id_required">Az OM azonosító megadása kötelező</div>
</div>
<div class="form-group">
<label class="form-label" for="EmailCim" data-i18n="forgotpassword.email_label">E-mail cím</label>
<input type="email" id="EmailCim" name="EmailCim" class="form-control"
data-i18n-attr="placeholder" data-i18n="forgotpassword.email_placeholder"
placeholder="Adja meg az e-mail címét" required>
<div class="error-message" data-i18n="forgotpassword.email_required">Az e-mail cím megadása kötelező</div>
</div>
<!-- reCAPTCHA container removed for security compliance -->
<div class="form-actions">
<a href="/Adminisztracio/Login" class="help-link" data-i18n="forgotpassword.back_to_login">
Vissza a bejelentkezéshez
</a>
<button type="submit" class="btn-submit" data-i18n="forgotpassword.reset_button">
Jelszó visszaállítása
</button>
</div>
</form>
</div>
`;
setupFormValidation();
};
const setupFormValidation = () => {
const form = document.getElementById('forgotForm');
const inputs = form.querySelectorAll('.form-control');
inputs.forEach(input => {
input.addEventListener('input', () => {
validateInput(input);
});
input.addEventListener('blur', () => {
validateInput(input, true);
});
});
form.addEventListener('submit', handleSubmit);
};
const validateInput = (input, showError = false) => {
const isValid = input.value.trim().length > 0;
const errorElement = input.nextElementSibling;
if (!isValid && showError) {
input.classList.add('error');
errorElement?.classList.add('show');
} else {
input.classList.remove('error');
errorElement?.classList.remove('show');
</div>
`, 'text/html');
const tempDiv = doc.body;
// Biztonságos DOM hozzáadás
while (tempDiv.firstChild) {
document.body.appendChild(tempDiv.firstChild);
}
};
const transformForgotPasswordPage = async () => {
await loadDependencies();
const isDarkMode = localStorage.getItem("darkMode") === "true";
document.documentElement.setAttribute(
"data-theme",
isDarkMode ? "dark" : "light",
);
chrome.runtime.onMessage.addListener((message) => {
if (message.action === "toggleTheme") {
document.documentElement.setAttribute(
"data-theme",
message.darkMode ? "dark" : "light",
);
localStorage.setItem("darkMode", message.darkMode);
}
return isValid;
});
createPageStructure();
let attempts = 0;
const maxAttempts = 50;
const waitForLanguageManager = () => {
return new Promise((resolve) => {
const checkLanguageManager = () => {
attempts++;
if (typeof LanguageManager !== "undefined" && LanguageManager.t) {
setTimeout(resolve, 200);
} else if (attempts < maxAttempts) {
setTimeout(checkLanguageManager, 100);
} else {
console.warn("LanguageManager not available, using fallback texts");
resolve();
}
};
checkLanguageManager();
});
};
const handleSubmit = async (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;
await waitForLanguageManager();
if (typeof LanguageManager !== "undefined" && LanguageManager.t) {
const elements = document.querySelectorAll("[data-i18n]");
elements.forEach((element) => {
const key = element.getAttribute("data-i18n");
const translation = LanguageManager.t(key);
if (translation && translation !== key) {
const attr = element.getAttribute("data-i18n-attr");
if (attr) {
element.setAttribute(attr, translation);
} else {
element.textContent = translation;
}
}
});
if (!isValid) {
return;
}
// reCAPTCHA rendering removed for security compliance
setupFormValidation();
};
const setupFormValidation = () => {
const form = document.getElementById("forgotForm");
const inputs = form.querySelectorAll(".form-control");
inputs.forEach((input) => {
input.addEventListener("input", () => {
validateInput(input);
});
input.addEventListener("blur", () => {
validateInput(input, true);
});
});
form.addEventListener("submit", handleSubmit);
};
const validateInput = (input, showError = false) => {
const isValid = input.value.trim().length > 0;
const errorElement = input.nextElementSibling;
if (!isValid && showError) {
input.classList.add("error");
errorElement?.classList.add("show");
} else {
input.classList.remove("error");
errorElement?.classList.remove("show");
}
return isValid;
};
const showMessage = (message, isError = false) => {
const existingMessage = document.querySelector(".message");
if (existingMessage) {
existingMessage.remove();
}
const messageDiv = document.createElement("div");
messageDiv.className = `message ${isError ? "error" : "success"}`;
messageDiv.textContent = message;
const form = document.getElementById("forgotForm");
form.insertBefore(messageDiv, form.firstChild);
setTimeout(() => {
if (messageDiv.parentNode) {
messageDiv.remove();
}
const submitButton = form.querySelector('.btn-submit');
submitButton.disabled = true;
try {
const formData = new FormData(form);
const response = await fetch('/Adminisztracio/ElfelejtettJelszo/LinkKuldes', {
method: 'POST',
}, 5000);
};
const validateEmail = (email) => {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
};
const handleSubmit = async (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;
}
});
const emailInput = form.querySelector("#EmailCim");
if (emailInput.value && !validateEmail(emailInput.value)) {
emailInput.classList.add("error");
const errorElement = emailInput.nextElementSibling;
if (errorElement) {
errorElement.textContent = LanguageManager.t(
"forgotpassword.invalid_email",
);
errorElement.classList.add("show");
}
isValid = false;
}
// reCAPTCHA validation removed for security compliance
if (!isValid) {
return;
}
const submitButton = form.querySelector(".btn-submit");
const originalText = submitButton.textContent;
submitButton.disabled = true;
submitButton.textContent = LanguageManager.t("loading.text") || "Küldés...";
try {
const formData = new FormData(form);
// reCAPTCHA data removed for security compliance
const response = await fetch(
"/Adminisztracio/ElfelejtettJelszo/LinkKuldes",
{
method: "POST",
body: formData,
headers: {
'X-Requested-With': 'XMLHttpRequest'
}
});
const result = await response.json();
if (result.Success) {
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)');
grecaptcha.reset();
}
} catch (error) {
//alert('Hiba történt a jelszó visszaállítása során.');
grecaptcha.reset();
} finally {
submitButton.disabled = false;
"X-Requested-With": "XMLHttpRequest",
},
},
);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
};
if (window.location.href.includes('/Adminisztracio/ElfelejtettJelszo')) {
transformForgotPasswordPage();
const result = await response.json();
if (result.Success) {
showMessage(LanguageManager.t("forgotpassword.success_message"));
form.reset();
// reCAPTCHA reset removed
setTimeout(() => {
window.location.href = "/Adminisztracio/Login";
}, 3000);
} else {
showMessage(
result.Message || LanguageManager.t("forgotpassword.error_message"),
true,
);
// reCAPTCHA reset removed
}
} catch (error) {
console.error("Password reset error:", error);
showMessage(LanguageManager.t("forgotpassword.error_message"), true);
// reCAPTCHA reset removed
} finally {
submitButton.disabled = false;
submitButton.textContent = originalText;
}
})();
};
if (window.location.href.includes("/Adminisztracio/ElfelejtettJelszo")) {
transformForgotPasswordPage().catch(console.error);
}
})();

189
global/language.js Normal file
View File

@@ -0,0 +1,189 @@
(function () {
let currentLanguage = "hu";
let translations = {};
async function setLanguage(language) {
try {
currentLanguage = language;
await storageManager.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() {
try {
const storageLanguage = await storageManager.get("languagePreference");
const localStorageLanguage = localStorage.getItem("languagePreference");
const language = storageLanguage || localStorageLanguage || "hu";
await setLanguage(language);
loadTranslationsForPage();
if (storageLanguage !== localStorageLanguage) {
if (storageLanguage) {
localStorage.setItem("languagePreference", storageLanguage);
} else if (localStorageLanguage) {
await storageManager.set("languagePreference", localStorageLanguage);
}
}
} catch (error) {
console.error("Error initializing language:", error);
await setLanguage("hu");
loadTranslationsForPage();
}
}
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;
});
function loadTranslationsForPage() {
try {
applyTranslations();
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.type === 'childList') {
mutation.addedNodes.forEach((node) => {
if (node.nodeType === Node.ELEMENT_NODE) {
const elementsWithI18n = node.querySelectorAll ? node.querySelectorAll('[data-i18n]') : [];
elementsWithI18n.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;
}
}
});
if (node.hasAttribute && node.hasAttribute('data-i18n')) {
const key = node.getAttribute('data-i18n');
const translation = getTranslation(key);
if (translation && translation !== key) {
const attr = node.getAttribute('data-i18n-attr');
if (attr) {
node.setAttribute(attr, translation);
} else {
node.textContent = translation;
}
}
}
}
});
}
});
});
observer.observe(document.body, {
childList: true,
subtree: true
});
} catch (error) {
console.error('Error loading translations for page:', error);
}
}
window.LanguageManager = {
getCurrentLanguage: () => currentLanguage,
changeLanguage: setLanguage,
t: getTranslation,
loadTranslationsForPage: loadTranslationsForPage,
getAvailableLanguages: () => [
{ code: "hu", name: "Magyar" },
{ code: "en", name: "English" },
],
};
})();

View File

@@ -1,51 +1,57 @@
body.maintenance-mode {
margin: 0;
padding: 0;
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
background-color: var(--background);
color: var(--text-primary);
font-family: 'Inter', sans-serif;
margin:0;
padding:0;
height:100vh;
display:flex;
align-items:center;
justify-content:center;
background-color:var(--background);
color:var(--text-primary);
font-family:'Figtree',sans-serif;
}
body {
background-color: var(--background) !important;
background-color:var(--background) !important;
}
.maintenance-container {
text-align: center;
padding: 2rem;
border-radius: 8px;
background-color: var(--card-card);
box-shadow: 0 var(--shadow-blur) 6px var(--accent-shadow);
max-width: 600px;
width: 90%;
text-align:center;
padding:2rem;
border-radius:8px;
background-color:var(--card-card);
box-shadow:0 var(--shadow-blur) 6px var(--accent-shadow);
max-width:600px;
width:90%;
}
.maintenance-logo {
width: 128px;
height: 128px;
margin: 0 auto 2rem;
width:128px;
height:128px;
margin:0 auto 2rem;
}
.maintenance-title {
font-size: 1.5rem;
font-weight: 600;
margin-bottom: 1rem;
color: var(--accent-accent);
font-size:1.5rem;
font-weight:600;
margin-bottom:1rem;
color:var(--accent-accent);
font-family:'Montserrat',sans-serif;
}
.maintenance-message {
font-size: 1rem;
line-height: 1.5;
margin-bottom: 1.5rem;
color: var(--text-primary);
font-size:1rem;
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-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

@@ -1,65 +1,106 @@
function loadMaintenanceCSS() {
const maintenanceCSS = document.createElement('link');
maintenanceCSS.rel = 'stylesheet';
maintenanceCSS.href = chrome.runtime.getURL('global/maintenance.css');
document.head.appendChild(maintenanceCSS);
const maintenanceCSS = document.createElement("link");
maintenanceCSS.rel = "stylesheet";
maintenanceCSS.href = chrome.runtime.getURL("global/maintenance.css");
document.head.appendChild(maintenanceCSS);
}
function checkMaintenancePage() {
const maintenanceContent = document.querySelector('.login_content');
if (maintenanceContent && maintenanceContent.textContent.includes('frissítés alatt')) {
const body = document.body;
const mainLogo = chrome.runtime.getURL('images/firka_logo_128.png');
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');
loadMaintenanceCSS();
const container = document.createElement('div');
container.className = 'maintenance-container';
const logo = document.createElement('img');
logo.src = mainLogo;
logo.alt = 'Firka Logo';
logo.className = 'maintenance-logo';
const title = document.createElement('h1');
title.className = 'maintenance-title';
title.textContent = '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.';
const paragraph2 = document.createElement('p');
paragraph2.textContent = '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';
messageDiv.appendChild(paragraph1);
messageDiv.appendChild(paragraph2);
container.appendChild(logo);
container.appendChild(title);
container.appendChild(messageDiv);
container.appendChild(footer);
body.appendChild(container);
}
const maintenanceContent = document.querySelector(".login_content");
const bodyText = document.body ? document.body.textContent : "";
const hasSpecificMessage = bodyText.includes("A KRÉTA rendszer jelenleg frissítés alatt van");
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();
const container = document.createElement("div");
container.className = "maintenance-container";
const logo = document.createElement("img");
logo.src = mainLogo;
logo.alt = "Firka Logo";
logo.className = "maintenance-logo";
const title = document.createElement("h1");
title.className = "maintenance-title";
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 = 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 = 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 = 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);
messageDiv.appendChild(paragraph2);
container.appendChild(logo);
container.appendChild(title);
container.appendChild(messageDiv);
container.appendChild(footer);
body.appendChild(container);
body.appendChild(cactus);
}
}
document.addEventListener('DOMContentLoaded', checkMaintenancePage);
document.addEventListener("DOMContentLoaded", checkMaintenancePage);

View File

@@ -1,401 +1,334 @@
.kreta-header {
padding: clamp(1rem, 3vw, 2rem);
display: grid;
grid-template-columns: minmax(300px, 400px) 1fr minmax(200px, 300px);
align-items: center;
gap: 1rem;
background-color: var(--background);
padding:clamp(1rem,3vw,2rem);
display:grid;
grid-template-columns:minmax(300px,400px) 1fr minmax(200px,300px);
align-items:center;
gap:1rem;
background-color:var(--background);
}
.school-info {
margin: 0;
display: flex;
flex-direction: column;
margin:0;
display:flex;
flex-direction:column;
}
.logo-text {
color: var(--text-primary);
font-size: 24px;
font-weight: 600;
margin: 0 0 0.5rem;
display: flex;
align-items: center;
color:var(--text-primary);
font-size:24px;
font-weight:600;
margin:0 0 0.5rem;
display:flex;
align-items:center;
}
.logo {
width: 24px;
height: 24px;
border-radius: 8px;
margin-right: 0.5rem;
width:24px;
height:24px;
border-radius:8px;
margin-right:0.5rem;
}
.school-details {
color: var(--text-secondary);
font-size: 14px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
color:var(--text-secondary);
font-size:14px;
white-space:nowrap;
overflow:hidden;
text-overflow:ellipsis;
}
.kreta-nav {
padding: 0 clamp(0.5rem, 3vw, 1.5rem);
position: sticky;
top: 0;
z-index: 100;
display: flex;
justify-content: center;
padding:0 clamp(0.5rem,3vw,1.5rem);
position:sticky;
top:0;
z-index:100;
display:flex;
justify-content:center;
}
.nav-links {
display: flex;
gap: clamp(0.5rem, 2vw, 1rem);
padding: 0.25rem;
align-items: center;
display:flex;
gap:clamp(0.5rem,2vw,1rem);
padding:0.25rem;
align-items:center;
}
.nav-item {
display: flex;
align-items: center;
padding: clamp(0.5rem, 1.5vw, 1rem) 0.5rem;
color: var(--text-secondary);
text-decoration: none;
font-weight: 500;
white-space: nowrap;
border-radius: 8px;
transition: all 0.2s ease;
gap: 0.5rem;
text-decoration: none;
display:flex;
align-items:center;
padding:8px 14px 8px 12px;
color:var(--text-secondary);
text-decoration:none;
font-weight:500;
white-space:nowrap;
border-radius:20px;
transition:all 0.2s ease;
gap:0.5rem;
text-decoration:none;
background:var(--button-secondaryFill);
box-shadow:0px 1px var(--shadow-blur,2px) 0px var(--accent-shadow);
}
.nav-item.active {
display: flex;
padding: 8px 14px 8px 12px;
align-items: center;
gap: 8px;
border-radius: 20px;
background: var(--button-secondaryFill);
box-shadow: 0px 1px var(--shadow-blur, 2px) 0px var(--accent-shadow);
display:flex;
padding:8px 14px 8px 12px;
align-items:center;
gap:8px;
border-radius:20px;
background:var(--button-secondaryFill);
box-shadow:0px 1px var(--shadow-blur,2px) 0px var(--accent-shadow);
}
.nav-item:hover {
color: var(--text-primary);
background-color: var(--hover);
border-radius: 8px;
text-decoration: none;
color:var(--text-primary);
background-color:var(--accent-15);
border-radius:8px;
text-decoration:none;
}
.nav-item.active:hover {
color: var(--accent-accent);
background-color: var(--accent-hover);
text-decoration: none;
color:var(--accent-accent);
background-color:var(--accent-15);
text-decoration:none;
}
.nav-item img,
.nav-item svg {
width: 24px;
height: 24px;
.nav-item img,.nav-item svg {
width:24px;
height:24px;
}
.nav-item.active svg path {
fill: var(--accent-accent);
fill:var(--accent-accent);
}
.user-profile {
position: relative;
justify-self: flex-end;
position:relative;
justify-self:flex-end;
}
.user-dropdown-btn {
display: flex;
align-items: center;
gap: 1rem;
background: none;
border: none;
cursor: pointer;
padding: 0.5rem;
border-radius: 8px;
transition: background-color 0.2s;
display:flex;
align-items:center;
gap:1rem;
background:none;
border:none;
cursor:pointer;
padding:0.5rem;
border-radius:8px;
transition:background-color 0.2s;
}
.user-dropdown-btn:hover {
background: var(--hover);
background:var(--hover);
}
.user-info {
text-align: right;
text-align:right;
}
.user-name {
display: block;
color: var(--text-primary);
font-size: 16px;
display:block;
color:var(--text-primary);
font-size:16px;
}
.nav-logout-timer {
display: block;
color: var(--text-secondary);
font-size: 14px;
display:block;
color:var(--text-secondary);
font-size:14px;
}
.user-dropdown {
position: absolute;
top: 100%;
right: 0;
margin-top: 0.5rem;
background: var(--card-card);
border-radius: 12px;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
width: 200px;
display: none;
z-index: 1000;
position:absolute;
top:100%;
right:0;
margin-top:0.5rem;
background:var(--card-card);
border-radius:12px;
box-shadow:0 4px 6px -1px rgba(0,0,0,0.1);
width:200px;
display:none;
z-index:1000;
}
.user-dropdown.show {
display: block;
animation: dropdownShow 0.2s ease;
display:block;
animation:dropdownShow 0.2s ease;
}
.dropdown-item {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 0.75rem 1rem;
color: var(--text-primary);
text-decoration: none;
transition: background-color 0.2s;
display:flex;
align-items:center;
gap:0.75rem;
padding:0.75rem 1rem;
color:var(--text-primary);
text-decoration:none;
transition:background-color 0.2s;
}
.dropdown-item:hover {
background: var(--hover);
color: var(--accent-accent);
border-radius: 8px;
background:var(--hover);
color:var(--accent-accent);
border-radius:8px;
text-decoration:none;
}
@keyframes dropdownShow {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
opacity:0;
transform:translateY(-10px);
}
@media (max-width: 1200px) {
to {
opacity:1;
transform:translateY(0);
}
}@media (max-width:1200px) {
.kreta-header {
grid-template-columns: minmax(250px, 350px) 1fr minmax(180px, 250px);
}
grid-template-columns:minmax(250px,350px) 1fr minmax(180px,250px);
}
/* Hamburger menu styles */
}/* Hamburger menu styles */
.nav-toggle {
display: none;
background: none;
border: none;
cursor: pointer;
padding: 0.5rem;
border-radius: 8px;
transition: background-color 0.2s;
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);
background:var(--hover);
}
.nav-toggle svg {
width: 24px;
height: 24px;
fill: var(--text-primary);
width:24px;
height:24px;
fill:var(--text-primary);
}
@media (max-width: 768px) {
@media (max-width:768px) {
.kreta-header {
grid-template-columns: 1fr auto auto;
grid-template-areas:
"school toggle user"
grid-template-columns:1fr auto auto;
grid-template-areas:"school toggle user"
"nav nav nav";
padding: 1rem;
gap: 0.5rem;
}
.nav-toggle {
display: block;
grid-area: toggle;
}
.school-info {
grid-area: school;
max-width: none;
display: flex;
align-items: center;
gap: 0.5rem;
}
.logo-text {
margin: 0;
font-size: 18px;
}
.school-details {
font-size: 11px;
max-width: 200px;
}
.kreta-nav {
grid-area: nav;
padding: 0;
margin-top: 0.5rem;
display: none;
}
.kreta-nav.show {
display: flex;
animation: slideDown 0.3s ease;
}
.kreta-nav::-webkit-scrollbar {
display: none;
}
.nav-links {
flex-direction: column;
width: 100%;
gap: 0.5rem;
background: var(--card-card);
border-radius: 12px;
padding: 1rem;
box-shadow: 0px 1px var(--shadow-blur) 0px var(--accent-shadow);
}
.nav-item {
width: 100%;
justify-content: flex-start;
padding: 0.75rem;
font-size: 14px;
}
.user-profile {
grid-area: user;
}
.user-info {
text-align: right;
max-width: 120px;
}
.user-name {
font-size: 13px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.nav-logout-timer {
font-size: 11px;
}
padding:1rem;
gap:0.5rem;
}
@media (max-width: 480px) {
.nav-toggle {
display:block;
grid-area:toggle;
}
.school-info {
grid-area:school;
max-width:none;
display:flex;
align-items:center;
gap:0.5rem;
}
.logo-text {
margin:0;
font-size:18px;
}
.school-details {
font-size:11px;
max-width:200px;
}
.kreta-nav {
grid-area:nav;
padding:0;
margin-top:0.5rem;
display:none;
}
.kreta-nav.show {
display:flex;
animation:slideDown 0.3s ease;
}
.kreta-nav::-webkit-scrollbar {
display:none;
}
.nav-links {
flex-direction:column;
width:100%;
gap:0.5rem;
background:var(--card-card);
border-radius:12px;
padding:1rem;
box-shadow:0px 1px var(--shadow-blur) 0px var(--accent-shadow);
}
.nav-item {
width:100%;
justify-content:flex-start;
padding:0.75rem;
font-size:14px;
}
.user-profile {
grid-area:user;
}
.user-info {
text-align:right;
max-width:120px;
}
.user-name {
font-size:13px;
white-space:nowrap;
overflow:hidden;
text-overflow:ellipsis;
}
.nav-logout-timer {
font-size:11px;
}
}@media (max-width:480px) {
.kreta-header {
grid-template-columns: 1fr auto auto;
grid-template-areas:
"school toggle user"
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;
}
padding:0.75rem;
gap:0.25rem;
}
@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;
}
.school-info {
min-width:0;
flex:1;
}
@keyframes slideDown {
.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);
}
opacity:0;
transform:translateY(-10px);
}
to {
opacity:1;
transform:translateY(0);
}
}

View File

@@ -1,122 +1,177 @@
const COOKIE_KEYS = {
SCHOOL_NAME: 'schoolName',
SCHOOL_CODE: 'schoolCode',
USER_NAME: 'userName',
SCHOOL_SUBDOMAIN: 'schoolSubdomain'
SCHOOL_NAME: "schoolName",
SCHOOL_CODE: "schoolCode",
USER_NAME: "userName",
SCHOOL_SUBDOMAIN: "schoolSubdomain",
};
const DEFAULT_VALUES = {
SCHOOL: 'Iskola',
USER: 'Felhasználó',
TIMER: '45:00'
SCHOOL: LanguageManager.t("navigation.school_default"),
USER: LanguageManager.t("navigation.user_default"),
TIMER: "45:00",
};
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: 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
};
if (schoolName) {
schoolName.textContent = `${userData.schoolId} - ${userData.schoolName}`;
}
if (userName) {
userName.textContent = userData.name;
}
if (logoutTimer) {
startLogoutTimer(userData.time);
}
async 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:
await storageManager.get("schoolName", DEFAULT_VALUES.SCHOOL),
schoolId: await storageManager.get("schoolCode", ""),
name: await storageManager.get("userName", DEFAULT_VALUES.USER),
time:
document.querySelector(".usermenu_timer")?.textContent?.trim() ||
DEFAULT_VALUES.TIMER,
};
if (schoolName) {
schoolName.textContent = `${userData.schoolId} - ${userData.schoolName}`;
}
if (userName) {
userName.textContent = userData.name;
}
if (logoutTimer) {
startLogoutTimer(userData.time);
}
}
function startLogoutTimer(timeString) {
const startTime = parseInt(timeString?.match(/\d+/)?.[0] || "45");
let timeLeft = startTime * 60;
const timerElement = document.querySelector('.nav-logout-timer');
const updateTimer = () => {
const minutes = Math.floor(timeLeft / 60);
const seconds = timeLeft % 60;
timerElement.textContent = `${minutes}:${seconds.toString().padStart(2, '0')}`;
if (timeLeft <= 0) {
window.location.href = '/Home/Logout';
}
timeLeft--;
};
const startTime = parseInt(timeString?.match(/\d+/)?.[0] || "45");
let timeLeft = startTime * 60;
const timerElement = document.querySelector(".nav-logout-timer");
updateTimer();
setInterval(updateTimer, 1000);
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);
}
document.addEventListener('DOMContentLoaded', () => {
updateHeaderInfo();
document.addEventListener("DOMContentLoaded", async () => {
await updateHeaderInfo();
});
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');
});
const userBtn = document.querySelector(".user-dropdown-btn");
const userDropdown = document.querySelector(".user-dropdown");
document.addEventListener('click', () => {
userDropdown?.classList.remove('show');
});
userBtn?.addEventListener("click", (e) => {
e.stopPropagation();
userDropdown?.classList.toggle("show");
});
document.addEventListener("click", () => {
userDropdown?.classList.remove("show");
});
}
function setupSettingsButton() {
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');
});
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 setupMobileNavigation() {
setTimeout(() => {
const navToggle = document.querySelector('.nav-toggle');
const nav = document.querySelector('.kreta-nav');
setTimeout(() => {
const navToggle = document.querySelector(".nav-toggle");
const nav = document.querySelector(".kreta-nav");
if (!navToggle || !nav) {
return;
}
const isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
if (isFirefox) {
let isNavOpen = false;
navToggle.addEventListener("click", (e) => {
e.preventDefault();
e.stopPropagation();
if (!navToggle || !nav) {
return;
if (isNavOpen) {
nav.style.display = "none";
nav.classList.remove("show");
isNavOpen = false;
} else {
nav.style.display = "flex";
nav.classList.add("show");
isNavOpen = true;
}
});
navToggle.addEventListener("touchend", (e) => {
e.preventDefault();
e.stopPropagation();
navToggle.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
nav.classList.toggle('show');
if (isNavOpen) {
nav.style.display = "none";
nav.classList.remove("show");
isNavOpen = false;
} else {
nav.style.display = "flex";
nav.classList.add("show");
isNavOpen = true;
}
});
document.addEventListener("click", (e) => {
if (!nav.contains(e.target) && !navToggle.contains(e.target) && isNavOpen) {
nav.style.display = "none";
nav.classList.remove("show");
isNavOpen = false;
}
});
const navItems = document.querySelectorAll(".nav-item");
navItems.forEach((item) => {
item.addEventListener("click", () => {
nav.style.display = "none";
nav.classList.remove("show");
isNavOpen = false;
});
});
} else {
navToggle.addEventListener("click", (e) => {
e.preventDefault();
e.stopPropagation();
nav.classList.toggle("show");
});
document.addEventListener('click', (e) => {
if (!nav.contains(e.target) && !navToggle.contains(e.target)) {
nav.classList.remove('show');
}
});
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');
});
const navItems = document.querySelectorAll(".nav-item");
navItems.forEach((item) => {
item.addEventListener("click", () => {
nav.classList.remove("show");
});
}, 100);
});
}
}, 100);
}
document.addEventListener('DOMContentLoaded', () => {
updateHeaderInfo();
setupUserDropdown();
setupSettingsButton();
setupMobileNavigation();
document.addEventListener("DOMContentLoaded", async () => {
await updateHeaderInfo();
setupUserDropdown();
setupSettingsButton();
setupMobileNavigation();
});

View File

@@ -1,160 +1,129 @@
:root {
--background: #DAE4F7;
--background-0: #dae4f700;
--success: var(--grades-4);
--shadow-blur: 2px;
--text-primary: #050B15;
--text-secondary: #050b15cc;
--text-teritary: #050b1580;
--card-card: #EDF3FF;
--card-translucent: #edf3ff80;
--button-secondaryFill: #FBFCFF;
--accent-accent: #3673EE;
--accent-secondary: #1C469A;
--accent-shadow: #1c469a26;
--accent-15: #3673ee26;
--warning-accent: var(--grades-2);
--warning-text: #8F531B;
--warning-15: #ffa04626;
--warning-card: #FAEBDC;
--error-accent: var(--grades-1);
--error-text: #8F1B4F;
--error-15: #FF54A126;
--error-card: #FADCE9;
--grades-1: #FF54A1;
--grades-2: #FFA046;
--grades-3: #F9CF00;
--grades-4: #92EA3B;
--grades-5: #22CCAD;
--grades-background-1: #FF54A126;
--grades-background-2: #FFA04626;
--grades-background-3: #F9CF0026;
--grades-background-4: #92EA3B26;
--grades-background-5: #22CCAD26;
--background:#FAFFF0;
--background-0:#fafff000;
--success:var(--grades-4);
--shadow-blur:2px;
--text-primary:#394C0A;
--text-secondary:#394c0acc;
--text-teritary:#394c0a80;
--card-card:#F3FBDE;
--card-translucent:#f3fbde80;
--button-secondaryFill:#FEFFFD;
--accent-accent:#A7DC22;
--accent-secondary:#6E8F1B;
--accent-shadow:#647e2226;
--accent-15:#a7dc2226;
--warning-accent:var(--grades-2);
--warning-text:#8F531B;
--warning-15:#ffa04626;
--warning-card:#FAEBDC;
--error-accent:var(--grades-1);
--error-text:#8F1B4F;
--error-15:#FF54A126;
--error-card:#FADCE9;
--grades-1:#FF54A1;
--grades-2:#FFA046;
--grades-3:#F9CF00;
--grades-4:#92EA3B;
--grades-5:#22CCAD;
--grades-background-1:#FF54A126;
--grades-background-2:#FFA04626;
--grades-background-3:#F9CF0026;
--grades-background-4:#92EA3B26;
--grades-background-5:#22CCAD26;
}
:root[data-theme="light-green"] {
--background: #FAFFF0;
--background-0: #fafff000;
--success: var(--grades-4);
--shadow-blur: 2px;
--text-primary: #394C0A;
--text-secondary: #394c0acc;
--text-teritary: #394c0a80;
--card-card: #F3FBDE;
--card-translucent: #f3fbde80;
--button-secondaryFill: #FEFFFD;
--accent-accent: #A7DC22;
--accent-secondary: #6E8F1B;
--accent-shadow: #647e2226;
--accent-15: #a7dc2226;
--warning-accent: var(--grades-2);
--warning-text: #8F531B;
--warning-15: #ffa04626;
--warning-card: #FAEBDC;
--error-accent: var(--grades-1);
--error-text: #8F1B4F;
--error-15: #FF54A126;
--error-card: #FADCE9;
--grades-1: #FF54A1;
--grades-2: #FFA046;
--grades-3: #F9CF00;
--grades-4: #92EA3B;
--grades-5: #22CCAD;
--grades-background-1: #FF54A126;
--grades-background-2: #FFA04626;
--grades-background-3: #F9CF0026;
--grades-background-4: #92EA3B26;
--grades-background-5: #22CCAD26;
--background:#FAFFF0;
--background-0:#fafff000;
--success:var(--grades-4);
--shadow-blur:2px;
--text-primary:#394C0A;
--text-secondary:#394c0acc;
--text-teritary:#394c0a80;
--card-card:#F3FBDE;
--card-translucent:#f3fbde80;
--button-secondaryFill:#FEFFFD;
--accent-accent:#A7DC22;
--accent-secondary:#6E8F1B;
--accent-shadow:#647e2226;
--accent-15:#a7dc2226;
--warning-accent:var(--grades-2);
--warning-text:#8F531B;
--warning-15:#ffa04626;
--warning-card:#FAEBDC;
--error-accent:var(--grades-1);
--error-text:#8F1B4F;
--error-15:#FF54A126;
--error-card:#FADCE9;
--grades-1:#FF54A1;
--grades-2:#FFA046;
--grades-3:#F9CF00;
--grades-4:#92EA3B;
--grades-5:#22CCAD;
--grades-background-1:#FF54A126;
--grades-background-2:#FFA04626;
--grades-background-3:#F9CF0026;
--grades-background-4:#92EA3B26;
--grades-background-5:#22CCAD26;
}
:root[data-theme="dark-blue"] {
--background: #070A0E;
--background-0: #070a0e00;
--success: var(--grades-4);
--shadow-blur: 0;
--text-primary: #EBF1FD;
--text-secondary: #ebf1fdcc;
--text-teritary: #ebf1fd80;
--card-card: #0F131B;
--card-translucent: #0f131b80;
--button-secondaryFill: #131822;
--accent-accent: #3673EE;
--accent-secondary: #AEC8FC;
--accent-shadow: #0000;
--accent-15: #3673ee26;
--warning-accent: var(--grades-2);
--warning-text: #f0b37a;
--warning-15: #ffa04626;
--warning-card: #201203;
--error-accent: var(--grades-1);
--error-text: #f59ec5;
--error-15: #ff54a126;
--error-card: #1e030f;
--grades-1: #FF54A1;
--grades-2: #FFA046;
--grades-3: #F9CF00;
--grades-4: #92EA3B;
--grades-5: #22CCAD;
--grades-background-1: #FF54A126;
--grades-background-2: #FFA04626;
--grades-background-3: #F9CF0026;
--grades-background-4: #92EA3B26;
--grades-background-5: #22CCAD26;
}
:root[data-theme="dark-green"] {
--background: #0D1202;
--background-0: #0E130200;
--success: var(--grades-4);
--shadow-blur: 0;
--text-primary: #EAF7CC;
--text-secondary: #EAF7CCCC;
--text-teritary: #EAF7CC80;
--card-card: #141905;
--card-translucent: #14190580;
--button-secondaryFill: #20290b;
--accent-accent: #A7DC22;
--accent-secondary: #CBEE71;
--accent-shadow: #0000;
--accent-15: #a7dc2226;
--warning-accent: var(--grades-2);
--warning-text: #f0b37a;
--warning-15: #ffa04626;
--warning-card: #201203;
--error-accent: var(--grades-1);
--error-text: #f59ec5;
--error-15: #ff54a126;
--error-card: #1e030f;
--grades-1: #FF54A1;
--grades-2: #FFA046;
--grades-3: #F9CF00;
--grades-4: #92EA3B;
--grades-5: #22CCAD;
--grades-background-1: #FF54A126;
--grades-background-2: #FFA04626;
--grades-background-3: #F9CF0026;
--grades-background-4: #92EA3B26;
--grades-background-5: #22CCAD26;
--background:#0D1202;
--background-0:#0E130200;
--success:var(--grades-4);
--shadow-blur:0;
--text-primary:#EAF7CC;
--text-secondary:#EAF7CCCC;
--text-teritary:#EAF7CC80;
--card-card:#141905;
--card-translucent:#14190580;
--button-secondaryFill:#20290b;
--accent-accent:#A7DC22;
--accent-secondary:#CBEE71;
--accent-shadow:#0000;
--accent-15:#a7dc2226;
--warning-accent:var(--grades-2);
--warning-text:#f0b37a;
--warning-15:#ffa04626;
--warning-card:#201203;
--error-accent:var(--grades-1);
--error-text:#f59ec5;
--error-15:#ff54a126;
--error-card:#1e030f;
--grades-1:#FF54A1;
--grades-2:#FFA046;
--grades-3:#F9CF00;
--grades-4:#92EA3B;
--grades-5:#22CCAD;
--grades-background-1:#FF54A126;
--grades-background-2:#FFA04626;
--grades-background-3:#F9CF0026;
--grades-background-4:#92EA3B26;
--grades-background-5:#22CCAD26;
}
html {
background-color: var(--background) !important;
color: var(--text-primary) !important;
background-color:var(--background) !important;
color:var(--text-primary) !important;
}
body {
background-color: var(--background) !important;
color: var(--text-primary) !important;
transition: background-color 0.2s ease, color 0.2s ease;
min-height: 100vh;
background-color:var(--background) !important;
color:var(--text-primary) !important;
transition:background-color 0.2s ease,color 0.2s ease;
min-height:100vh;
}
* {
transition: background-color 0.2s ease, color 0.2s ease, border-color 0.2s ease;
}
transition:background-color 0.2s ease,color 0.2s ease,border-color 0.2s ease;
}
::-webkit-scrollbar {
width:clamp(4px,1vw,8px);
height:clamp(4px,1vw,8px);
}
::-webkit-scrollbar-track {
background:var(--background);
}
::-webkit-scrollbar-thumb {
background:var(--text-secondary);
border-radius:4px;
}
::-webkit-scrollbar-thumb:hover {
background:var(--text-primary);
}

View File

@@ -1,132 +1,197 @@
(() => {
function setTheme(theme) {
try {
const actualTheme = theme === 'default' ? 'light-blue' : theme;
document.documentElement.setAttribute('data-theme', actualTheme);
cookieManager.set('themePreference', actualTheme);
localStorage.setItem('themePreference', actualTheme);
chrome.runtime.sendMessage({
action: 'themeChanged',
theme: actualTheme
}).catch(() => {
});
} catch (error) {
console.error('Error setting theme:', error);
}
async function setTheme(theme) {
try {
document.documentElement.setAttribute("data-theme", theme);
await storageManager.set("themePreference", theme);
chrome.runtime
.sendMessage({
action: "themeChanged",
theme: theme,
})
.catch(() => {});
} 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 importFonts() {
try {
const fontFaces = `
@font-face {
font-family: 'Montserrat';
font-style: normal;
font-weight: 400;
src: url('${chrome.runtime.getURL('fonts/Montserrat-Regular.woff2')}') format('woff2');
font-display: swap;
}
}
function initializeTheme() {
const cookieTheme = cookieManager.get('themePreference');
const localStorageTheme = localStorage.getItem('themePreference');
const theme = cookieTheme || localStorageTheme || 'light-green';
setTheme(theme);
setPageTitleAndFavicon();
if (cookieTheme !== localStorageTheme) {
if (cookieTheme) {
localStorage.setItem('themePreference', cookieTheme);
} else if (localStorageTheme) {
cookieManager.set('themePreference', localStorageTheme);
}
@font-face {
font-family: 'Montserrat';
font-style: normal;
font-weight: 500;
src: url('${chrome.runtime.getURL('fonts/Montserrat-Medium.woff2')}') format('woff2');
font-display: swap;
}
@font-face {
font-family: 'Montserrat';
font-style: normal;
font-weight: 600;
src: url('${chrome.runtime.getURL('fonts/Montserrat-SemiBold.woff2')}') format('woff2');
font-display: swap;
}
@font-face {
font-family: 'Figtree';
font-style: normal;
font-weight: 400;
src: url('${chrome.runtime.getURL('fonts/Figtree-Regular.woff2')}') format('woff2');
font-display: swap;
}
`;
const styleElement = document.createElement('style');
styleElement.textContent = fontFaces;
document.head.appendChild(styleElement);
const iconFontFace = `
@font-face {
font-family: 'Material Icons Round';
font-style: normal;
font-weight: 400;
src: url('${chrome.runtime.getURL('fonts/Icons.woff2')}') format('woff2');
font-display: swap;
}
`;
const iconStyleElement = document.createElement('style');
iconStyleElement.textContent = iconFontFace;
document.head.appendChild(iconStyleElement);
} catch (error) {
console.error("Error importing fonts:", error);
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initializeTheme);
} else {
initializeTheme();
}
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);
}
}
async function initializeTheme() {
try {
const theme = await storageManager.get("themePreference", "light-green");
await setTheme(theme);
setPageTitleAndFavicon();
importFonts();
} catch (error) {
console.error("Error initializing theme:", error);
await setTheme("light-green");
setPageTitleAndFavicon();
importFonts();
}
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", initializeTheme);
} else {
initializeTheme();
}
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.action === "changeTheme") {
setTheme(message.theme);
sendResponse({ success: true });
}
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.action === 'changeTheme') {
setTheme(message.theme);
sendResponse({ success: true });
}
if (message.action === 'getTheme') {
const currentTheme = document.documentElement.getAttribute('data-theme') || 'light-green';
sendResponse({ theme: currentTheme });
}
if (message.action === "getTheme") {
const currentTheme =
document.documentElement.getAttribute("data-theme") || "light-green";
sendResponse({ theme: currentTheme });
}
return true;
return true;
});
let titleCheckTimeout;
const observer = new MutationObserver(async (mutations) => {
const currentTheme = document.documentElement.getAttribute("data-theme");
try {
const savedTheme = await storageManager.get("themePreference");
if (
(!currentTheme && savedTheme) ||
(currentTheme !== savedTheme && savedTheme)
) {
await setTheme(savedTheme);
}
} catch (error) {
console.error("Error checking theme in observer:", error);
}
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,
});
});
let titleCheckTimeout;
const observer = new MutationObserver((mutations) => {
const currentTheme = document.documentElement.getAttribute('data-theme');
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);
}
} else {
observer.observe(document.documentElement, {
attributes: true,
attributeFilter: ["data-theme"],
});
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
});
}
})();
observer.observe(document.head, {
childList: true,
subtree: true,
});
}
})();

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,458 +0,0 @@
@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;
padding: 0;
}
body {
margin: 0;
padding: 0;
color: var(--text-primary);
background-color: var(--background) !important;
font-family: "Montserrat", serif !important;
min-height: 100vh;
font-size: 16px;
}
@media (max-width: 768px) {
body {
font-size: 14px;
}
}
.kreta-container {
min-height: 100vh;
display: flex;
flex-direction: column;
}
.kreta-header {
padding: clamp(1rem, 3vw, 2rem);
display: grid;
grid-template-columns: minmax(300px, 400px) 1fr minmax(200px, 300px);
align-items: center;
gap: 1rem;
}
@media (max-width: 1200px) {
.kreta-header {
grid-template-columns: minmax(250px, 350px) 1fr minmax(180px, 250px);
}
}
@media (max-width: 768px) {
.kreta-header {
grid-template-columns: 1fr auto auto;
grid-template-areas:
"school toggle user"
"nav nav nav";
padding: 1rem;
gap: 0.5rem;
}
}
.school-info {
margin: 0;
}
@media (max-width: 768px) {
.school-info {
grid-area: school;
max-width: none;
display: flex;
align-items: center;
gap: 0.5rem;
}
}
.logo-text {
color: var(--text-primary);
font-size: 24px;
font-weight: 600;
margin: 0 0 0.5rem;
display: flex;
align-items: center;
}
@media (max-width: 768px) {
.logo-text {
margin: 0;
font-size: 20px;
}
}
.logo {
width: 24px;
border-radius: 8px;
margin-right: 0.5rem;
}
.school-details {
color: var(--text-secondary);
font-size: 14px;
}
.school-details span {
display: block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 300px;
}
@media (max-width: 768px) {
.school-details span {
max-width: 200px;
}
.school-details {
font-size: 12px;
}
}
.user-profile {
position: relative;
justify-self: flex-end;
}
@media (max-width: 768px) {
.user-profile {
grid-area: user;
}
}
.user-dropdown-btn {
display: flex;
align-items: center;
gap: 1rem;
background: none;
border: none;
cursor: pointer;
padding: 0.5rem;
color: var(--text-primary);
}
.user-info {
display: flex;
flex-direction: column;
align-items: flex-end;
gap: 0.25rem;
}
.user-dropdown {
position: absolute;
top: 100%;
right: 0;
margin-top: 0.5rem;
background: var(--card-card);
border-radius: 12px;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
width: 200px;
display: none;
z-index: 1000;
}
.user-dropdown.show {
display: block;
animation: dropdownShow 0.2s ease;
}
.dropdown-item {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.75rem 1rem;
color: var(--text-primary);
text-decoration: none;
transition: background-color 0.2s ease;
}
.dropdown-item:hover {
background-color: var(--accent-15);
}
.kreta-main {
flex: 1;
padding: clamp(1rem, 3vw, 2rem);
max-width: 1400px;
margin: 0 auto;
width: 100%;
}
.filter-card {
background: var(--card-card);
border-radius: 24px;
padding: 1.5rem;
margin-bottom: 1.5rem;
box-shadow: 0px 1px 3px 0px var(--accent-shadow);
}
.filter-header {
margin-bottom: 1rem;
}
.filter-header h2 {
font-size: 18px;
font-weight: 600;
color: var(--text-primary);
background-color: var(--card-card);
}
.filter-content {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 1rem;
width: 100%;
}
.filter-group {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.filter-group label {
display: flex;
align-items: center;
gap: 0.5rem;
font-size: 14px;
color: var(--text-secondary);
}
.filter-group select,
.filter-group input {
padding: 0.75rem;
border-radius: 12px;
border: 1px solid var(--accent-15);
background-color: var(--background);
color: var(--text-primary);
font-family: inherit;
font-size: 14px;
width: 100%;
transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
.filter-group select:focus,
.filter-group input:focus {
outline: none;
border-color: var(--accent-accent);
box-shadow: 0 0 0 2px var(--accent-15);
}
.filter-actions {
display: flex;
justify-content: flex-end;
gap: 1rem;
margin-top: 1rem;
}
.filter-button {
padding: 0.5rem 1rem;
border-radius: 8px;
border: none;
font-family: inherit;
font-weight: 500;
cursor: pointer;
transition: all 0.2s ease;
}
.filter-button.primary {
background-color: var(--accent-accent);
color: white;
}
.filter-button.secondary {
background-color: var(--accent-15);
color: var(--text-primary);
}
.filter-button:hover {
opacity: 0.9;
}
.homework-list {
display: grid;
grid-template-columns: 1fr;
gap: 1rem;
}
.homework-date-group {
margin-bottom: 2rem;
}
.date-header {
display: flex;
align-items: center;
gap: 0.5rem;
margin-bottom: 0.5rem;
padding-bottom: 0.5rem;
border-bottom: 1px solid var(--accent-15);
}
.date-header h3 {
font-size: 16px;
font-weight: 600;
color: var(--text-primary);
}
.homework-item {
background: var(--card-card);
border-radius: 12px;
padding: 1rem;
margin-bottom: 0.5rem;
box-shadow: 0px 1px 2px 0px var(--accent-shadow);
transition: transform 0.2s ease;
}
.homework-item:hover {
transform: translateY(-2px);
}
.homework-item.due-tomorrow {
border-left: 4px solid var(--accent-accent);
background-color: var(--accent-5);
}
.homework-header {
display: flex;
justify-content: space-between;
margin-bottom: 0.5rem;
}
.homework-subject {
font-weight: 600;
font-size: 16px;
color: var(--text-primary);
}
.homework-deadline {
font-size: 14px;
color: var(--text-secondary);
}
.homework-deadline.urgent {
color: var(--accent-accent);
font-weight: 500;
}
.homework-content {
margin-bottom: 0.5rem;
color: var(--text-primary);
font-size: 14px;
}
.homework-footer {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 12px;
color: var(--text-secondary);
}
.homework-teacher {
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;
color: var(--text-secondary);
display: none;
}
.empty-state p {
margin-bottom: 1rem;
font-size: 16px;
}
@media (max-width: 768px) {
.filter-content {
grid-template-columns: 1fr;
}
.homework-header {
flex-direction: column;
gap: 0.25rem;
}
}
@keyframes dropdownShow {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}

View File

@@ -1,391 +0,0 @@
async function collectHomeworkData() {
await helper.waitForElement('#TanulotHaziFeladatkGrid');
await new Promise(resolve => setTimeout(resolve, 1000));
const basicData = {
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'
}
};
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() || ''
});
}
});
const groupedHomework = {};
homeworkItems.forEach(homework => {
const deadlineDate = homework.deadline.split(' ').slice(0, 3).join(' ');
if (!groupedHomework[deadlineDate]) {
groupedHomework[deadlineDate] = [];
}
groupedHomework[deadlineDate].push(homework);
});
return { basicData, homeworkItems, groupedHomework };
}
function isTomorrow(dateStr) {
if (!dateStr) return false;
const parts = dateStr.split('.');
if (parts.length < 3) return false;
const year = parseInt(parts[0].trim());
const month = parseInt(parts[1].trim()) - 1;
const day = parseInt(parts[2].trim());
const homeworkDate = new Date(year, month, day);
const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
tomorrow.setHours(0, 0, 0, 0);
const dayAfterTomorrow = new Date(tomorrow);
dayAfterTomorrow.setDate(dayAfterTomorrow.getDate() + 1);
return homeworkDate >= tomorrow && homeworkDate < dayAfterTomorrow;
}
async function transformHomeworkPage() {
const { basicData, homeworkItems, groupedHomework } = await collectHomeworkData();
document.body.innerHTML = `
<div class="kreta-container">
${createTemplate.header()}
<main class="kreta-main">
<div class="filter-card">
<div class="filter-header">
<h2>Szűrés</h2>
</div>
<div class="filter-content">
<div class="filter-group">
<label>
<!--<span class="material-icons-round">subject</span>-->
Tantárgy
</label>
<select id="subjectFilter">
<option value="">Összes tantárgy</option>
${[...new Set(homeworkItems.map(item => item.subject))]
.sort()
.map(subject => `<option value="${subject}">${subject}</option>`)
.join('')}
</select>
</div>
<div class="filter-group">
<label>
<!--<span class="material-icons-round">person</span>-->
Tanár
</label>
<select id="teacherFilter">
<option value="">Összes tanár</option>
${[...new Set(homeworkItems.map(item => item.teacher))]
.sort()
.map(teacher => `<option value="${teacher}">${teacher}</option>`)
.join('')}
</select>
</div>
<div class="filter-group">
<label>
<!--<span class="material-icons-round">date_range</span>-->
Határidő
</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>
</select>
</div>
</div>
</div>
<div class="homework-list" id="homeworkList">
${renderHomeworkList(groupedHomework)}
</div>
</main>
</div>
`;
setupFilters(homeworkItems, groupedHomework);
setupUserDropdown();
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;
});
if (sortedDates.length === 0) {
return `
<div class="empty-state">
<p>Nincs megjeleníthető házi feladat.</p>
</div>
`;
}
return sortedDates.map(date => {
const homeworkItems = groupedHomework[date];
return `
<div class="homework-date-group" data-date="${date}">
<div class="date-header">
<h3>${formatDateHeader(date)}</h3>
</div>
${homeworkItems.map(homework => {
const isTomorrowClass = isTomorrow(homework.deadline) ? 'due-tomorrow' : '';
const urgentClass = isTomorrow(homework.deadline) ? 'urgent' : '';
return `
<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>
<div class="homework-content">${formatHomeworkDescription(homework.description)}</div>
<div class="homework-footer">
<div class="homework-teacher">${homework.teacher}</div>
</div>
</div>
`;
}).join('')}
</div>
`;
}).join('');
}
function formatDateHeader(dateStr) {
if (!dateStr) return '';
const today = new Date();
today.setHours(0, 0, 0, 0);
const tomorrow = new Date(today);
tomorrow.setDate(tomorrow.getDate() + 1);
const parts = dateStr.split('.');
if (parts.length < 3) 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);
if (date.toDateString() === today.toDateString()) {
return 'Ma - ' + dateStr;
} else if (date.toDateString() === tomorrow.toDateString()) {
return 'Holnap - ' + dateStr;
}
const weekdays = ['Vasárnap', 'Hétfő', 'Kedd', 'Szerda', 'Csütörtök', 'Péntek', 'Szombat'];
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;
}
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;
}
function setupFilters(homeworkItems, groupedHomework) {
const subjectFilter = document.getElementById('subjectFilter');
const teacherFilter = document.getElementById('teacherFilter');
const deadlineFilter = document.getElementById('deadlineFilter');
const applyFilterBtn = document.getElementById('applyFilterBtn');
const resetFilterBtn = document.getElementById('resetFilterBtn');
const applyFilters = () => {
const selectedSubject = subjectFilter.value;
const selectedTeacher = teacherFilter.value;
const selectedDeadline = deadlineFilter.value;
const homeworkElements = document.querySelectorAll('.homework-item');
const dateGroups = document.querySelectorAll('.homework-date-group');
dateGroups.forEach(group => {
group.style.display = 'none';
});
homeworkElements.forEach(item => {
const subject = item.getAttribute('data-subject');
const teacher = item.getAttribute('data-teacher');
const dateGroup = item.closest('.homework-date-group');
const dateStr = dateGroup.getAttribute('data-date');
let showItem = true;
if (selectedSubject && subject !== selectedSubject) {
showItem = false;
}
if (selectedTeacher && teacher !== selectedTeacher) {
showItem = false;
}
if (selectedDeadline) {
const parts = dateStr.split('.');
if (parts.length >= 3) {
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 today = new Date();
today.setHours(0, 0, 0, 0);
const tomorrow = new Date(today);
tomorrow.setDate(tomorrow.getDate() + 1);
const startOfWeek = new Date(today);
const dayOfWeek = today.getDay() || 7;
startOfWeek.setDate(today.getDate() - dayOfWeek + 1);
const endOfWeek = new Date(startOfWeek);
endOfWeek.setDate(startOfWeek.getDate() + 6);
const startOfNextWeek = new Date(endOfWeek);
startOfNextWeek.setDate(endOfWeek.getDate() + 1);
const endOfNextWeek = new Date(startOfNextWeek);
endOfNextWeek.setDate(startOfNextWeek.getDate() + 6);
if (selectedDeadline === 'tomorrow' && date.toDateString() !== tomorrow.toDateString()) {
showItem = false;
} else if (selectedDeadline === 'thisWeek' && (date < startOfWeek || date > endOfWeek)) {
showItem = false;
} else if (selectedDeadline === 'nextWeek' && (date < startOfNextWeek || date > endOfNextWeek)) {
showItem = false;
}
}
}
item.style.display = showItem ? 'block' : 'none';
if (showItem) {
dateGroup.style.display = 'block';
}
});
const visibleItems = document.querySelectorAll('.homework-item[style="display: block"]');
const homeworkList = document.getElementById('homeworkList');
let emptyState = homeworkList.querySelector('.empty-state');
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>';
homeworkList.appendChild(emptyState);
}
if (visibleItems.length === 0) {
emptyState.style.display = 'block';
} else {
emptyState.style.display = 'none';
}
};
const resetFilters = () => {
subjectFilter.value = '';
teacherFilter.value = '';
deadlineFilter.value = '';
document.querySelectorAll('.homework-item').forEach(item => {
item.style.display = 'block';
});
document.querySelectorAll('.homework-date-group').forEach(group => {
group.style.display = 'block';
});
const homeworkList = document.getElementById('homeworkList');
const existingEmptyState = homeworkList.querySelector('.empty-state');
if (existingEmptyState) {
homeworkList.removeChild(existingEmptyState);
}
};
subjectFilter.addEventListener('change', applyFilters);
teacherFilter.addEventListener('change', applyFilters);
deadlineFilter.addEventListener('change', applyFilters);
}
if (window.location.href.includes('/Tanulo/TanuloHaziFeladat')) {
transformHomeworkPage().catch(error => {
console.error('Error transforming homework page:', error);
});
}

463
i18n/en.json Normal file
View File

@@ -0,0 +1,463 @@
{
"loading": {
"text": "Loading...",
"subtext": "Please wait!"
},
"settings": {
"title": "Settings",
"theme": "Theme",
"language": "Language",
"themes": {
"light_green": "Light Green",
"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"
},
"custom_theme": {
"title": "Custom Theme",
"create": "Create New Theme",
"import": "Import Theme",
"editor_title": "Theme Editor",
"import_title": "Import Theme",
"name": "Theme name",
"background": "Background",
"text": "Text",
"accent": "Accent colors",
"main_background": "Main background",
"card_background": "Card background",
"primary_text": "Primary text",
"secondary_text": "Secondary text",
"primary_accent": "Primary accent",
"secondary_accent": "Secondary accent",
"preview": "Preview",
"save": "Save",
"cancel": "Cancel",
"import_string": "Theme ID",
"apply": "Apply",
"edit": "Edit",
"export": "Export",
"delete": "Delete",
"no_themes": "No custom themes yet",
"delete_confirm": "Are you sure you want to delete the \"{name}\" theme?"
}
},
"navigation": {
"dashboard": "Home",
"timetable": "Timetable",
"grades": "Grades",
"homework": "Homework",
"absences": "Absences",
"messages": "Messages",
"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_evaluation": "Semester evaluation",
"semester_evaluations": "Semester evaluations",
"year_end_evaluations": "End of year tickets",
"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",
"open_homework": "Open homework",
"all_day": "All day",
"special_day": "Special day",
"unknown_subject": "Unknown subject",
"lesson_topic": "Lesson topic",
"homework_completed": "Completed homework",
"homework_mark_completed": "Mark as completed",
"homework_mark_uncompleted": "Completed - click to undo",
"custom_homework": "Custom homework",
"custom_test": "Custom test",
"add_homework_test": "Add homework or test",
"close": "Close",
"add": "Add",
"homework_details_loading": "Loading homework details...",
"homework_details_error": "Error occurred while loading homework details.",
"test_details_loading": "Loading details...",
"test_details_error": "Failed to load test details.",
"test_details_error_general": "Error occurred while loading test details.",
"custom_homework_title": "Custom homework:",
"custom_tests_title": "Custom tests:",
"delete_homework_confirm": "Are you sure you want to delete this homework?",
"delete_test_confirm": "Are you sure you want to delete this test?",
"task_label": "Task:",
"deadline_label": "Deadline:",
"name_label": "Name:",
"type_label": "Type:",
"announce_date_label": "Announcement date:",
"no_name": "No name",
"no_type": "No type specified",
"no_date": "No date"
},
"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.",
"items": "items",
"status": "Status",
"total_homework": "Total homework",
"urgent_homework": "Urgent homework",
"completed_homework": "Completed homework",
"pending_homework": "Pending homework",
"completed": "Completed",
"urgent": "Urgent",
"pending": "Pending"
},
"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",
"time_period": "Time period",
"all_periods": "All periods",
"current_month": "Current month",
"last_month": "Last month",
"current_semester": "Current semester",
"last_30_days": "Last 30 days",
"total_absences": "Total absences",
"topic": "Topic",
"status": "Status"
},
"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"
},
"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.3.0"
},
"app": {
"title": "Firka - KRÉTA",
"settings_title": "Firka - Settings"
},
"forgotpassword": {
"title": "Forgot Password",
"om_id_label": "OM ID",
"om_id_placeholder": "Enter your OM ID",
"om_id_required": "OM ID is required",
"email_label": "Email address",
"email_placeholder": "Enter your email address",
"email_required": "Email address is required",
"back_to_login": "Back to login",
"reset_button": "Reset password",
"error_message": "An error occurred during password reset",
"success_message": "Password reset link sent to your email address",
"invalid_data": "Invalid data",
"invalid_email": "Invalid email format",
"recaptcha_required": "Please complete the reCAPTCHA"
},
"modal": {
"add_item_title": "Add new item",
"type_label": "Type:",
"homework_option": "Homework",
"test_option": "Test",
"description_label": "Description:",
"cancel": "Cancel",
"save": "Save"
},
"search": {
"title": "Choose school",
"select_institution": "Please select an institution to continue!",
"choose_school": "Choose school",
"privacy_policy": "Privacy policy"
},
"icons": {
"cancel": "cancel",
"pending": "pending"
},
"messages": {
"title": "Messages",
"back": "Back",
"surveys": "Surveys",
"loading": "Loading messages...",
"error": {
"title": "Error occurred",
"description": "Failed to load messages.",
"retry": "Retry"
},
"empty": {
"title": "No messages",
"description": "There are currently no received messages."
},
"sender": "Sender",
"subject": "Subject",
"date": "Date",
"unread": "Unread",
"read": "Read",
"message_detail": {
"title": "Message Details",
"loading": "Loading message...",
"error": "Error loading message.",
"from": "From",
"to": "To",
"subject": "Subject",
"date": "Date",
"content": "Content",
"attachments": "Attachments",
"no_attachments": "No attachments",
"reply": "Reply",
"forward": "Forward",
"delete": "Delete",
"mark_read": "Mark as read",
"mark_unread": "Mark as unread",
"back_to_messages": "Back to messages"
}
}
}

465
i18n/hu.json Normal file
View File

@@ -0,0 +1,465 @@
{
"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"
},
"custom_theme": {
"title": "Egyéni Téma",
"create": "Új Téma Létrehozása",
"import": "Téma Importálása",
"editor_title": "Téma Szerkesztő",
"import_title": "Téma Importálása",
"name": "Téma neve",
"background": "Háttér",
"text": "Szöveg",
"accent": "Kiemelő színek",
"main_background": "Fő háttér",
"card_background": "Kártya háttér",
"primary_text": "Elsődleges szöveg",
"secondary_text": "Másodlagos szöveg",
"primary_accent": "Elsődleges kiemelő",
"secondary_accent": "Másodlagos kiemelő",
"preview": "Előnézet",
"save": "Mentés",
"cancel": "Mégse",
"import_string": "Téma azonosító",
"apply": "Alkalmaz",
"edit": "Szerkeszt",
"export": "Export",
"delete": "Töröl",
"no_themes": "Még nincsenek egyéni témák",
"delete_confirm": "Biztosan törölni szeretnéd a \"{name}\" témát?"
}
},
"navigation": {
"dashboard": "Kezdőlap",
"timetable": "Órarend",
"grades": "Jegyek",
"homework": "Házi feladatok",
"absences": "Mulasztások",
"messages": "Üzenetek",
"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_evaluation": "Félévi értékelés",
"semester_evaluations": "Félévi értékelések",
"year_end_evaluations": "Év végi é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",
"open_homework": "Ugrás a házi feladatokhoz",
"all_day": "Egész nap",
"special_day": "Különleges nap",
"unknown_subject": "Ismeretlen tantárgy",
"lesson_topic": "Óra témája",
"homework_completed": "Megoldott házi feladat",
"homework_mark_completed": "Megoldottként jelöl",
"homework_mark_uncompleted": "Megoldva - kattints a visszavonáshoz",
"custom_homework": "Saját házi feladat",
"custom_test": "Saját számonkérés",
"add_homework_test": "Házi feladat vagy számonkérés hozzáadása",
"close": "Bezárás",
"add": "Hozzáadás",
"homework_details_loading": "Házi feladat részletek betöltése...",
"homework_details_error": "Hiba történt a házi feladat részletek betöltésekor.",
"test_details_loading": "Részletek betöltése...",
"test_details_error": "Nem sikerült betölteni a számonkérés részleteit.",
"test_details_error_general": "Hiba történt a számonkérés részletek betöltése során.",
"custom_homework_title": "Saját házi feladatok:",
"custom_tests_title": "Saját számonkérések:",
"delete_homework_confirm": "Biztosan törölni szeretnéd ezt a házi feladatot?",
"delete_test_confirm": "Biztosan törölni szeretnéd ezt a számonkérést?",
"task_label": "Feladat:",
"deadline_label": "Határidő:",
"name_label": "Megnevezés:",
"type_label": "Típus:",
"announce_date_label": "Bejelentés dátuma:",
"no_name": "Nincs megnevezés",
"no_type": "Nincs típus megadva",
"no_date": "Nincs dátum"
},
"homework": {
"title": "Házi feladatok",
"due_date": "Határidő",
"subject": "Tantárgy",
"description": "Leírás",
"filter_title": "Szűrő",
"all_subjects": "Minden tantárgy",
"all_teachers": "Minden tanár",
"all_deadlines": "Minden határidő",
"tomorrow_deadline": "Holnapi határidő",
"this_week": "Ezen a héten",
"next_week": "Következő 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.",
"items": "elem",
"status": "Állapot",
"total_homework": "Összes feladat",
"urgent_homework": "Sürgős feladat",
"completed_homework": "Befejezett feladat",
"pending_homework": "Függőben lévő feladat",
"completed": "Befejezve",
"urgent": "Sürgős",
"pending": "Függőben"
},
"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",
"time_period": "Időszak",
"all_periods": "Összes időszak",
"current_month": "Aktuális hónap",
"last_month": "Előző hónap",
"current_semester": "Aktuális félév",
"last_30_days": "Utolsó 30 nap",
"total_absences": "Összes hiányzás",
"topic": "Téma",
"status": "Állapot"
},
"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"
},
"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.3.0"
},
"app": {
"title": "Firka - KRÉTA",
"settings_title": "Firxa - Beállítások"
},
"forgotpassword": {
"title": "Elfelejtett jelszó",
"om_id_label": "OM azonosító",
"om_id_placeholder": "Adja meg az OM azonosítóját",
"om_id_required": "Az OM azonosító megadása kötelező",
"email_label": "E-mail cím",
"email_placeholder": "Adja meg az e-mail címét",
"email_required": "Az e-mail cím megadása kötelező",
"back_to_login": "Vissza a bejelentkezéshez",
"reset_button": "Jelszó visszaállítása",
"error_message": "Hiba történt a jelszó visszaállítása során",
"success_message": "A jelszó visszaállítási link elküldve az e-mail címére",
"invalid_data": "Hibás adatok",
"invalid_email": "Érvénytelen e-mail cím formátum",
"recaptcha_required": "Kérjük, töltse ki a reCAPTCHA-t"
},
"modal": {
"add_item_title": "Új elem hozzáadása",
"type_label": "Típus:",
"homework_option": "Házi feladat",
"test_option": "Számonkérés",
"description_label": "Leírás:",
"cancel": "Mégse",
"save": "Mentés"
},
"search": {
"choose_school": "Válassz iskolát",
"privacy_policy": "Adatkezelési tájékoztató",
"title": "Válassz iskolát",
"select_institution": "Kérjük, válasszon egy intézményt a folytatáshoz!"
},
"icons": {
"cancel": "cancel",
"pending": "pending"
},
"messages": {
"title": "Üzenetek",
"back": "Vissza",
"surveys": "Felmérések",
"loading": "Üzenetek betöltése...",
"error": {
"title": "Hiba történt",
"description": "Nem sikerült betölteni az üzeneteket.",
"retry": "Újrapróbálás"
},
"empty": {
"title": "Nincsenek üzenetek",
"description": "Jelenleg nincsenek beérkezett üzenetek."
},
"sender": "Feladó",
"subject": "Tárgy",
"date": "Dátum",
"unread": "Olvasatlan",
"read": "Olvasott",
"message_detail": {
"title": "Üzenet részletei",
"loading": "Üzenet betöltése...",
"error": "Hiba történt az üzenet betöltése során.",
"from": "Feladó",
"to": "Címzett",
"subject": "Tárgy",
"date": "Dátum",
"content": "Tartalom",
"attachments": "Mellékletek",
"no_attachments": "Nincsenek mellékletek",
"reply": "Válasz",
"forward": "Továbbítás",
"delete": "Törlés",
"mark_read": "Olvasottként jelöl",
"mark_unread": "Olvasatlanként jelöl",
"back_to_messages": "Vissza az üzenetekhez"
}
}
}

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 24 24"><path fill="none" stroke="#A7DC22" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19h4m0 0v-4m0 4l-4-4M9 5H5m0 0v4m0-4l4 4m6-4h4m0 0v4m0-4l-4 4M9 19H5m0 0v-4m0 4l4-4"/></svg>

After

Width:  |  Height:  |  Size: 283 B

1
icons/BadgeCheck.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 24 24"><g fill="none"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.054 2.344a3 3 0 0 1 3.892 0l1.271 1.084a1 1 0 0 0 .57.236l1.665.133a3 3 0 0 1 2.751 2.751l.133 1.666a1 1 0 0 0 .236.569l1.084 1.271a3 3 0 0 1 0 3.892l-1.084 1.271a1 1 0 0 0-.236.57l-.133 1.665a3 3 0 0 1-2.751 2.751l-1.666.133a1 1 0 0 0-.569.236l-1.271 1.084a3 3 0 0 1-3.892 0l-1.271-1.084a1 1 0 0 0-.57-.236l-1.665-.133a3 3 0 0 1-2.751-2.751l-.133-1.666a1 1 0 0 0-.236-.569l-1.084-1.271a3 3 0 0 1 0-3.892l1.084-1.271a1 1 0 0 0 .236-.57l.133-1.665a3 3 0 0 1 2.751-2.751l1.666-.133a1 1 0 0 0 .569-.236l1.271-1.084zm5.653 8.363a1 1 0 0 0-1.414-1.414L11 12.586l-1.293-1.293a1 1 0 0 0-1.414 1.414l2 2a1 1 0 0 0 1.414 0l4-4z" fill="#A7DC22"/></g></svg>

After

Width:  |  Height:  |  Size: 799 B

1
icons/Calendar.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 24 24"><g fill="none"><path fill="#A7DC22" d="M4 7v2h16V7a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2"/><path stroke="#A7DC22" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 5h2a2 2 0 0 1 2 2v2H4V7a2 2 0 0 1 2-2h2m8 0V3m0 2H8m0-2v2M4 9.5V19a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9.5"/></g></svg>

After

Width:  |  Height:  |  Size: 376 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 24 24"><path fill="#A7DC22" fill-rule="evenodd" d="M12 21a9 9 0 1 0 0-18a9 9 0 0 0 0 18m1.707-11.293a1 1 0 0 0-1.414-1.414l-3 3a1 1 0 0 0 0 1.414l3 3a1 1 0 0 0 1.414-1.414L11.414 12z" clip-rule="evenodd"/></svg>

After

Width:  |  Height:  |  Size: 289 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 24 24"><path fill="#A7DC22" fill-rule="evenodd" d="M12 21a9 9 0 1 0 0-18a9 9 0 0 0 0 18M10.293 9.707a1 1 0 1 1 1.414-1.414l3 3a1 1 0 0 1 0 1.414l-3 3a1 1 0 0 1-1.414-1.414L12.586 12z" clip-rule="evenodd"/></svg>

After

Width:  |  Height:  |  Size: 289 B

1
icons/CloseCircle.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 24 24"><g fill="none"><path fill-rule="evenodd" clip-rule="evenodd" d="M2 12C2 6.477 6.477 2 12 2s10 4.477 10 10s-4.477 10-10 10S2 17.523 2 12zm8.207-3.207a1 1 0 0 0-1.414 1.414L10.586 12l-1.793 1.793a1 1 0 1 0 1.414 1.414L12 13.414l1.793 1.793a1 1 0 0 0 1.414-1.414L13.414 12l1.793-1.793a1 1 0 0 0-1.414-1.414L12 10.586l-1.793-1.793z" fill="#A7DC22"/></g></svg>

After

Width:  |  Height:  |  Size: 440 B

1
icons/Subject.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 24 24"><g fill="none"><path fill-rule="evenodd" clip-rule="evenodd" d="M11.514 3.126a1 1 0 0 1 .972 0l9 5A1 1 0 0 1 22 9v7a1 1 0 1 1-2 0v-5.3l-1 .555v.004l-6.067 3.016a2 2 0 0 1-1.848-.035L2.357 9.479a1 1 0 0 0-.284-.103a1 1 0 0 1 .441-1.25l9-5zM5 13.199V17a1 1 0 0 0 .553.894l6 3a1 1 0 0 0 .894 0l6-3A1 1 0 0 0 19 17v-3.256l-6.083 2.844a2 2 0 0 1-1.805-.056L5 13.2z" fill="#A7DC22"/></g></svg>

After

Width:  |  Height:  |  Size: 472 B

1
icons/assigment.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 24 24"><g fill="none"><path stroke="#FFA046" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14 8H9m2 4H9"/><path fill="#FFA046" d="M3 16h8c0 1.333.8 4 4 4a3 3 0 0 0 3-3V4h1a2 2 0 0 1 2 2v2a2 2 0 0 1-2 2h-1v7a3 3 0 0 1-3 3H5a2 2 0 0 1-2-2z"/><path stroke="#FFA046" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 20c-3.2 0-4-2.667-4-4H3v2a2 2 0 0 0 2 2zm0 0a3 3 0 0 0 3-3v-7m0-6H7a2 2 0 0 0-2 2v9.5M18 4h1a2 2 0 0 1 2 2v2a2 2 0 0 1-2 2h-1m0-6v6"/></g></svg>

After

Width:  |  Height:  |  Size: 578 B

1
icons/delete.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 24 24"><g fill="none"><path fill="#A7DC22" d="M9 7h9v11a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2V7z"/><path stroke="#A7DC22" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20 7h-2M4 7h2m0 0h12M6 7v11a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V7m-9-.5A2.5 2.5 0 0 1 11.5 4h1A2.5 2.5 0 0 1 15 6.5v0"/></g></svg>

After

Width:  |  Height:  |  Size: 384 B

View File

@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 24 24"><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 6.362A9.7 9.7 0 0 1 12 5c6.307 0 9.367 5.683 9.91 6.808c.06.123.06.261 0 .385c-.352.728-1.756 3.362-4.41 5.131M14 18.8a10 10 0 0 1-2 .2c-6.307 0-9.367-5.683-9.91-6.808a.44.44 0 0 1 0-.386c.219-.452.84-1.632 1.91-2.885m6 .843A3 3 0 0 1 14.236 14M3 3l18 18"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 24 24"><g fill="none"><path stroke="#A7DC22" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m3 3l18 18"/><path fill="#A7DC22" fill-rule="evenodd" d="M5.4 6.23c-.44.33-.843.678-1.21 1.032a15.1 15.1 0 0 0-3.001 4.11a1.44 1.44 0 0 0 0 1.255a15.1 15.1 0 0 0 3 4.111C5.94 18.423 8.518 20 12 20c2.236 0 4.1-.65 5.61-1.562l-3.944-3.943a3 3 0 0 1-4.161-4.161L5.401 6.229zm15.266 9.608a15 15 0 0 0 2.145-3.21a1.44 1.44 0 0 0 0-1.255a15.1 15.1 0 0 0-3-4.111C18.06 5.577 15.483 4 12 4a10.8 10.8 0 0 0-2.808.363z" clip-rule="evenodd"/></g></svg>

Before

Width:  |  Height:  |  Size: 458 B

After

Width:  |  Height:  |  Size: 631 B

View File

@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 24 24"><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><path d="M12 5c-6.307 0-9.367 5.683-9.91 6.808a.44.44 0 0 0 0 .384C2.632 13.317 5.692 19 12 19s9.367-5.683 9.91-6.808a.44.44 0 0 0 0-.384C21.368 10.683 18.308 5 12 5"/><circle cx="12" cy="12" r="3"/></g></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 24 24"><path fill="#A7DC22" fill-rule="evenodd" d="M4.19 7.262C5.94 5.577 8.517 4 12 4s6.06 1.577 7.81 3.262a15.1 15.1 0 0 1 3.001 4.11c.193.399.193.857 0 1.255a15.1 15.1 0 0 1-3 4.111C18.06 18.423 15.483 20 12 20s-6.06-1.577-7.81-3.262a15.1 15.1 0 0 1-3.001-4.11a1.44 1.44 0 0 1 0-1.255a15.1 15.1 0 0 1 3-4.111zM12 15a3 3 0 1 0 0-6a3 3 0 0 0 0 6" clip-rule="evenodd"/></svg>

Before

Width:  |  Height:  |  Size: 395 B

After

Width:  |  Height:  |  Size: 453 B

1
icons/homework.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 24 24"><g fill="none"><path fill-rule="evenodd" clip-rule="evenodd" d="M15.586 3a2 2 0 0 1 2.828 0L21 5.586a2 2 0 0 1 0 2.828L19.414 10L14 4.586L15.586 3zm-3 3l-9 9A2 2 0 0 0 3 16.414V19a2 2 0 0 0 2 2h2.586A2 2 0 0 0 9 20.414l9-9L12.586 6z" fill="#A7DC22"/></g></svg>

After

Width:  |  Height:  |  Size: 345 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 24 24"><g fill="none"><path fill-rule="evenodd" clip-rule="evenodd" d="M6 21a3 3 0 0 1-3-3v-3h5a2 2 0 0 0 2 2h4a2 2 0 0 0 2-2h5v3a3 3 0 0 1-3 3H6zm15-8h-5a2 2 0 0 0-2 2h-4a2 2 0 0 0-2-2H3V6a3 3 0 0 1 3-3h12a3 3 0 0 1 3 3v7z" fill="#A7DC22"/></g></svg>

After

Width:  |  Height:  |  Size: 329 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 24 24"><g fill="#6E8F1B"><path d="M5 13h3a2 2 0 0 1 2 2h4a2 2 0 0 1 2-2h3V6a1 1 0 0 0-1-1H6a1 1 0 0 0-1 1v7zm14 2h-3a2 2 0 0 1-2 2h-4a2 2 0 0 1-2-2H5v3a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-3zM3 6a3 3 0 0 1 3-3h12a3 3 0 0 1 3 3v12a3 3 0 0 1-3 3H6a3 3 0 0 1-3-3V6z"/></g></svg>

After

Width:  |  Height:  |  Size: 348 B

1
icons/pipa.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 24 24"><g fill="#A7DC22"><path d="M19.707 6.293a1 1 0 0 1 0 1.414l-10 10a1 1 0 0 1-1.414 0l-4-4a1 1 0 1 1 1.414-1.414L9 15.586l9.293-9.293a1 1 0 0 1 1.414 0z"/></g></svg>

After

Width:  |  Height:  |  Size: 248 B

1
icons/plus.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 24 24"><path fill="none" stroke="#A7DC22" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 12h7m7 0h-7m0 0V5m0 7v7"/></svg>

After

Width:  |  Height:  |  Size: 222 B

1
icons/select-all.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 24 24"><path fill="#A7DC22" fill-rule="evenodd" d="M10 2a3 3 0 0 0-2.83 2H6a3 3 0 0 0-3 3v12a3 3 0 0 0 3 3h12a3 3 0 0 0 3-3V7a3 3 0 0 0-3-3h-1.17A3 3 0 0 0 14 2zM9 5a1 1 0 0 1 1-1h4a1 1 0 1 1 0 2h-4a1 1 0 0 1-1-1m6.78 6.625a1 1 0 1 0-1.56-1.25l-3.303 4.128l-1.21-1.21a1 1 0 0 0-1.414 1.414l2 2a1 1 0 0 0 1.488-.082l4-5z" clip-rule="evenodd"/></svg>

After

Width:  |  Height:  |  Size: 426 B

1
icons/select-none.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 24 24"><path fill="#A7DC22" fill-rule="evenodd" d="M10 2a3 3 0 0 0-2.83 2H6a3 3 0 0 0-3 3v12a3 3 0 0 0 3 3h12a3 3 0 0 0 3-3V7a3 3 0 0 0-3-3h-1.17A3 3 0 0 0 14 2zM9 5a1 1 0 0 1 1-1h4a1 1 0 1 1 0 2h-4a1 1 0 0 1-1-1m6 8a1 1 0 1 1 0 2H9a1 1 0 1 1 0-2z" clip-rule="evenodd"/></svg>

After

Width:  |  Height:  |  Size: 354 B

1
icons/select.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 24 24"><g fill="none"><path fill-rule="evenodd" clip-rule="evenodd" d="M8.949 2.684a1 1 0 0 0-1.898.632l1 3a1 1 0 1 0 1.898-.632l-1-3zm6.758 3.023a1 1 0 0 0-1.414-1.414l-2 2a1 1 0 0 0 1.414 1.414l2-2zM3.317 7.051a1 1 0 0 0-.633 1.898l3 1a1 1 0 1 0 .632-1.898l-3-1zm7.025 2.01a1 1 0 0 0-1.282 1.28l4 11a1 1 0 0 0 1.868.03l1.437-3.591l3.928 3.927a1 1 0 1 0 1.414-1.414l-3.928-3.928l3.592-1.436a1 1 0 0 0-.03-1.869l-11-4zm-2.635 4.646a1 1 0 1 0-1.414-1.414l-2 2a1 1 0 1 0 1.414 1.414l2-2z" fill="#A7DC22"/></g></svg>

After

Width:  |  Height:  |  Size: 591 B

1
icons/trash.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 24 24"><g fill="none"><path fill-rule="evenodd" clip-rule="evenodd" d="M11 5a1 1 0 0 0-1 1h4a1 1 0 0 0-1-1h-2zm0-2a3 3 0 0 0-3 3H4a1 1 0 0 0 0 2h1v10a3 3 0 0 0 3 3h8a3 3 0 0 0 3-3V8h1a1 1 0 1 0 0-2h-4a3 3 0 0 0-3-3h-2zm0 8a1 1 0 1 0-2 0v5a1 1 0 1 0 2 0v-5zm4 0a1 1 0 1 0-2 0v5a1 1 0 1 0 2 0v-5z" fill="#A7DC22"/></g></svg>

After

Width:  |  Height:  |  Size: 400 B

1
icons/undo.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 24 24"><path fill="none" stroke="#A7DC22" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 9v5h5m11 2c-.497-4.5-3.367-8-8-8c-2.73 0-5.929 2.268-7.294 5.5"/></svg>

After

Width:  |  Height:  |  Size: 261 B

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: 53 KiB

BIN
images/firefoxact.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

View File

@@ -1,385 +1,306 @@
@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;
--icon-saturate: 0.1;
--icon-hue-rotate: 0deg;
--icon-brightness: 0.1;
--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;
--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;
--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;
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;
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;
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);
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;
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;
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;
width:48px;
height:48px;
border-radius:12px;
}
.school-name {
color: var(--text-secondary);
text-align: center;
font-family: Figtree;
font-size: 16px;
font-style: normal;
font-weight: 500;
line-height: 130%;
color:var(--text-secondary);
text-align:center;
font-family:Figtree;
font-size:16px;
font-style:normal;
font-weight:500;
line-height:130%;
}
.school-details {
color: var(--text-secondary);
text-align: center;
font-family: Figtree;
font-size: 16px;
font-style: normal;
font-weight: 500;
line-height: 130%;
color:var(--text-secondary);
text-align:center;
font-family:Figtree;
font-size:16px;
font-style:normal;
font-weight:500;
line-height:130%;
}
.login-form {
width: 100%;
width:100%;
}
.form-group {
margin-bottom: 16px;
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;
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;
outline:none;
border-color:var(--accent-accent) !important;
}
.form-control::placeholder {
color: var(--text-secondary) !important;
color:var(--text-secondary) !important;
}
.password-group {
position: relative;
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;
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));
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;
opacity:1;
}
.form-actions {
margin-top: 24px;
margin-top:24px;
}
.help-links {
margin-top: 16px;
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
margin-top:16px;
display:flex;
flex-direction:column;
align-items:center;
gap:8px;
}
.help-link {
color: var(--text-primary);
font-size: 14px;
font-weight: 500;
text-decoration: none;
transition: color 0.2s ease;
color:var(--text-primary);
font-size:14px;
font-weight:500;
text-decoration:none;
transition:color 0.2s ease;
}
.help-link:hover {
color: var(--text-teritary);
color:var(--text-teritary);
}
.btn-login {
width: 100%;
padding: 12px;
background: var(--accent-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;
position: relative;
width:100%;
padding:12px;
background:var(--accent-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;
position:relative;
}
.btn-login:hover {
background: var(--text-teritary);
background:var(--text-teritary);
}
.btn-login:disabled {
opacity: 0.7;
cursor: not-allowed;
opacity:0.7;
cursor:not-allowed;
}
.btn-text {
color: var(--text-primary);
text-align: center;
font-family: Montserrat;
font-size: 18px;
font-style: normal;
font-weight: 700;
line-height: normal;
letter-spacing: -0.3px;
color:var(--text-primary);
text-align:center;
font-family:Montserrat;
font-size:18px;
font-style:normal;
font-weight:700;
line-height:normal;
letter-spacing:-0.3px;
}
.system-message {
display: flex;
padding: 20px;
flex-direction: column;
align-items: center;
gap: 12px;
align-self: stretch;
border-radius: 24px;
background: var(--card-card);
box-shadow: 0px 1px var(--shadow-blur) 0px var(--accent-shadow);
display:flex;
padding:20px;
flex-direction:column;
align-items:center;
gap:12px;
align-self:stretch;
border-radius:24px;
background:var(--card-card);
box-shadow:0px 1px var(--shadow-blur) 0px var(--accent-shadow);
}
.system-message h4 {
color: var(--text-primary);
text-align: center;
font-family: Montserrat;
font-size: 20px;
font-style: normal;
font-weight: 700;
line-height: normal;
color:var(--text-primary);
text-align:center;
font-family:Montserrat;
font-size:20px;
font-style:normal;
font-weight:700;
line-height:normal;
}
.system-message p {
color: var(--text-secondary);
text-align: center;
font-family: Figtree;
font-size: 16px;
font-style: normal;
font-weight: 500;
line-height: 130%;
color:var(--text-secondary);
text-align:center;
font-family:Figtree;
font-size:16px;
font-style:normal;
font-weight:500;
line-height:130%;
}
.login-footer {
align-self: stretch;
color: var(--text-secondary);
text-align: center;
font-family: Figtree;
font-size: 14px;
font-style: normal;
font-weight: 500;
line-height: 130%;
align-self:stretch;
color:var(--text-secondary);
text-align:center;
font-family:Figtree;
font-size:14px;
font-style:normal;
font-weight:500;
line-height:130%;
}
.privacy-link {
align-self: stretch;
color: var(--text-secondary);
text-align: center;
font-family: Figtree;
font-size: 14px;
font-style: normal;
font-weight: 500;
line-height: 130%;
text-decoration: none;
align-self:stretch;
color:var(--text-secondary);
text-align:center;
font-family:Figtree;
font-size:14px;
font-style:normal;
font-weight:500;
line-height:130%;
text-decoration:none;
}
.privacy-link:hover {
color: var(--text-teritary);
color:var(--text-teritary);
}
.form-control.error {
border-color: var(--error-accent) !important;
background: var(--error-card) !important;
border-color:var(--error-accent) !important;
background:var(--error-card) !important;
}
.error-message {
color: var(--error-text);
font-size: 14px;
margin-top: 4px;
display: none;
color:var(--error-text);
font-size:14px;
margin-top:4px;
display:none;
}
.error-message.show {
display: block;
animation: fadeIn 0.2s ease;
display:block;
animation:fadeIn 0.2s ease;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(-4px); }
to { opacity: 1; transform: translateY(0); }
from {
opacity:0;
transform:translateY(-4px);
}
@keyframes spin {
to { transform: translate(-50%, -50%) rotate(360deg); }
to {
opacity:1;
transform:translateY(0);
}
@media (max-width: 480px) {
}@keyframes spin {
to {
transform:translate(-50%,-50%) rotate(360deg);
}
}@media (max-width:480px) {
.login-container {
padding: 16px;
}
.login-card {
padding: 20px;
}
.card-header {
margin-bottom: 20px;
padding-bottom: 16px;
}
.logo-text {
font-size: 20px;
}
.school-name {
font-size: 16px;
}
.school-details {
font-size: 12px;
}
.form-control {
height: 44px;
font-size: 14px;
}
.btn-login {
padding: 10px;
font-size: 14px;
}
.help-link {
font-size: 12px;
}
padding:16px;
}
.login-card {
padding:20px;
}
.card-header {
margin-bottom:20px;
padding-bottom:16px;
}
.logo-text {
font-size:20px;
}
.school-name {
font-size:16px;
}
.school-details {
font-size:12px;
}
.form-control {
height:44px;
font-size:14px;
}
.btn-login {
padding:10px;
font-size:14px;
}
.help-link {
font-size:12px;
}
}

View File

@@ -1,50 +1,59 @@
async function transformLoginPage() {
try {
if (document.readyState !== 'complete') {
await new Promise(resolve => {
window.addEventListener('load', resolve);
if (document.readyState !== "complete") {
await new Promise((resolve) => {
window.addEventListener("load", resolve);
});
}
const existingForm = document.querySelector('form');
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 || '',
userName: document.querySelector('#UserName')?.value || '',
password: document.querySelector('#Password')?.value || ''
action: existingForm?.getAttribute("action") || "",
returnUrl: document.querySelector("#ReturnUrl")?.value || "",
instituteCode: document.querySelector("#instituteCode")?.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 titleElement = document.querySelector(".page-title");
const schoolInfo = {
name: titleElement?.querySelector('b')?.textContent?.trim() || '',
kretaId: '',
omCode: ''
name: titleElement?.querySelector("b")?.textContent?.trim() || "",
kretaId: "",
omCode: "",
};
const spanElement = titleElement?.querySelector('span');
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ó: ', '');
const lines =
spanElement.textContent?.split("\n").map((line) => line.trim()) || [];
schoolInfo.kretaId = lines[0] || "";
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 rawSystemMessage =
document.querySelector(".alert-primary")?.textContent?.trim() || "";
const systemMessage = rawSystemMessage
.replace("Rendszerértesítés", "")
.trim();
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">
<img src=${chrome.runtime.getURL("images/firka_logo.png")} alt="Firka" class="logo">
Firka
</p>
<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.kretaId ? `<div>${schoolInfo.kretaId}</div>` : ""}
${schoolInfo.omCode ? `<div>${LanguageManager.t("login.kreta_id")}: ${schoolInfo.omCode}</div>` : ""}
</div>
</div>
@@ -57,133 +66,136 @@ 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 value="${formData.userName}">
<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 value="${formData.password}">
<button type="button" class="show-password" aria-label="Jelszó mutatása">
<img src="${chrome.runtime.getURL('icons/eye-off.svg')}" alt="Show password" class="icon-eye">
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>
</div>
${systemMessage ? `
${
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();
document.body.innerHTML = '';
const parser = new DOMParser();
const doc = parser.parseFromString(newHTML, 'text/html');
const tempDiv = doc.body;
while (tempDiv.firstChild) {
document.body.appendChild(tempDiv.firstChild);
}
setupEventListeners();
} catch (error) {
console.error('Error transforming page:', error);
console.error("Error transforming page:", error);
}
}
function setupEventListeners() {
const loginForm = document.getElementById('loginForm');
const passwordInput = document.getElementById('Password');
const togglePasswordBtn = document.querySelector('.show-password');
const formInputs = document.querySelectorAll('.form-control');
if (togglePasswordBtn && passwordInput) {
togglePasswordBtn.addEventListener('click', () => {
const isPassword = passwordInput.type === 'password';
passwordInput.type = isPassword ? 'text' : 'password';
const icon = togglePasswordBtn.querySelector('.icon-eye');
icon.src = chrome.runtime.getURL(`icons/${isPassword ? 'eye-on' : 'eye-off'}.svg`);
});
}
const loginForm = document.getElementById("loginForm");
const passwordInput = document.getElementById("Password");
const togglePasswordBtn = document.querySelector(".show-password");
const formInputs = document.querySelectorAll(".form-control");
formInputs.forEach(input => {
input.addEventListener('input', () => {
if (togglePasswordBtn && passwordInput) {
togglePasswordBtn.addEventListener("click", () => {
const isPassword = passwordInput.type === "password";
passwordInput.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', () => {
input.addEventListener("blur", () => {
validateInput(input, true);
});
});
if (loginForm) {
loginForm.addEventListener('submit', handleSubmit);
loginForm.addEventListener("submit", handleSubmit);
}
}
function validateInput(input, showError = false) {
const isValid = input.value.trim().length > 0;
const errorElement = input.nextElementSibling;
if (!isValid && showError) {
input.classList.add('error');
errorElement?.classList.add('show');
input.classList.add("error");
errorElement?.classList.add("show");
} else {
input.classList.remove('error');
errorElement?.classList.remove('show');
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]');
const inputs = form.querySelectorAll(".form-control[required]");
let isValid = true;
inputs.forEach(input => {
inputs.forEach((input) => {
if (!validateInput(input, true)) {
isValid = false;
}
});
const submitButton = form.querySelector('.btn-login');
const spinner = submitButton.querySelector('.spinner');
const buttonText = submitButton.querySelector('.btn-text');
const submitButton = form.querySelector(".btn-login");
const spinner = submitButton.querySelector(".spinner");
const buttonText = submitButton.querySelector(".btn-text");
submitButton.disabled = true;
spinner.style.display = 'inline-block';
buttonText.style.opacity = '0';
spinner.style.display = "inline-block";
buttonText.style.opacity = "0";
form.submit();
}
if (window.location.href.includes('idp.e-kreta.hu/Account/Login')) {
transformLoginPage().catch(error => {
console.error('Error:', error);
if (window.location.href.includes("idp.e-kreta.hu/Account/Login")) {
transformLoginPage().catch((error) => {
});
}
}

View File

@@ -1,326 +1,254 @@
@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;
--icon-saturate: 0.1;
--icon-hue-rotate: 0deg;
--icon-brightness: 0.1;
--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;
--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;
--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;
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;
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;
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);
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;
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;
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;
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;
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%;
width:100%;
}
.form-group {
margin-bottom: 16px;
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;
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;
outline:none;
border-color:var(--accent-accent) !important;
}
.form-control::placeholder {
color: var(--text-secondary) !important;
color:var(--text-secondary) !important;
}
.password-group {
position: relative;
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;
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));
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;
opacity:1;
}
.form-check {
display: flex;
align-items: center;
margin-top: 16px;
margin-bottom: 16px;
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;
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);
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;
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;
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);
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;
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);
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;
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;
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;
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;
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;
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;
display:block;
}
.form-control.error {
border: 1px solid var(--error-accent) !important;
border:1px solid var(--error-accent) !important;
}
/* Hide original elements */
header, main > .container-fluid, footer {
display: none !important;
header,main > .container-fluid,footer {
display:none !important;
}
/* Responsive adjustments */
@media (max-width: 576px) {
@media (max-width:576px) {
.login-container {
width: 100%;
padding: 16px;
}
.login-card {
padding: 16px;
}
width:100%;
padding:16px;
}
.login-card {
padding:16px;
}
}

View File

@@ -1,35 +1,40 @@
async function transformTwoFactorPage() {
try {
if (document.readyState !== 'complete') {
await new Promise(resolve => {
window.addEventListener('load', resolve);
if (document.readyState !== "complete") {
await new Promise((resolve) => {
window.addEventListener("load", resolve);
});
}
if (typeof loadingScreen !== 'undefined') {
if (typeof loadingScreen !== "undefined") {
loadingScreen.show();
}
const existingForm = document.querySelector('form');
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'
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">
<img src=${chrome.runtime.getURL("images/firka_logo.png")} alt="Firka" class="logo">
Firka
</p>
<h1 class="twofactor-title">Kétfaktoros azonosítás</h1>
<h1 class="twofactor-title">${LanguageManager.t("twofactor.title")}</h1>
</div>
<form class="twofactor-form" action="${formData.action}" method="post" id="twoFactorForm">
@@ -41,30 +46,30 @@ async function transformTwoFactorPage() {
<div class="form-group password-group">
<input class="form-control" type="password" id="VerificationCode" name="VerificationCode"
placeholder="Egyszeri jelszó" maxlength="256" autocomplete="off" required autofocus>
<button type="button" class="show-password" aria-label="Jelszó mutatása">
<img src="${chrome.runtime.getURL('icons/eye-off.svg')}" alt="Show password" class="icon-eye">
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">Kérjük, add meg az egyszeri jelszót.</div>
<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">
Eszköz megjelölése biztonságosként
${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">Kód ellenőrzése</button>
<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">
Nem fér hozzá eszközéhez? Lépjen be
${LanguageManager.t("twofactor.no_access")}
<button type="submit" class="btn-link" formaction="/account/loginwithrecoverycode">
helyreállító kóddal.
${LanguageManager.t("twofactor.recovery_code")}
</button>
</span>
</div>
@@ -73,21 +78,25 @@ async function transformTwoFactorPage() {
<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;
document.body.innerHTML = '';
const parser = new DOMParser();
const doc = parser.parseFromString(newHTML, 'text/html');
const tempDiv = doc.body;
while (tempDiv.firstChild) {
document.body.appendChild(tempDiv.firstChild);
}
applyTheme();
setupEventListeners();
if (typeof loadingScreen !== 'undefined') {
if (typeof loadingScreen !== "undefined") {
loadingScreen.hide();
}
} catch (error) {
console.error('Error transforming two-factor page:', error);
if (typeof loadingScreen !== 'undefined') {
if (typeof loadingScreen !== "undefined") {
loadingScreen.hide();
}
}
@@ -95,77 +104,83 @@ async function transformTwoFactorPage() {
function applyTheme() {
try {
if (typeof getCookie === 'function') {
const theme = getCookie('theme') || 'light-blue';
document.documentElement.setAttribute('data-theme', theme);
}
const theme = localStorage.getItem("themePreference") || "light-green";
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');
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`);
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', () => {
formInputs.forEach((input) => {
input.addEventListener("input", () => {
validateInput(input);
});
input.addEventListener('blur', () => {
input.addEventListener("blur", () => {
validateInput(input, true);
});
});
if (twoFactorForm) {
twoFactorForm.addEventListener('submit', handleSubmit);
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');
input.classList.add("error");
errorElement?.classList.add("show");
} else {
input.classList.remove('error');
errorElement?.classList.remove('show');
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]');
const inputs = form.querySelectorAll(".form-control[required]");
let isValid = true;
inputs.forEach(input => {
inputs.forEach((input) => {
if (!validateInput(input, true)) {
isValid = false;
}
});
if (isValid) {
const submitButton = form.querySelector('.btn-kreta');
const submitButton = form.querySelector(".btn-kreta");
if (submitButton) {
submitButton.disabled = true;
submitButton.innerHTML = '<span class="spinner"></span><span class="btn-text">Ellenőrzés...</span>';
submitButton.innerHTML = '';
const spinnerSpan = document.createElement('span');
spinnerSpan.className = 'spinner';
const textSpan = document.createElement('span');
textSpan.className = 'btn-text';
textSpan.textContent = LanguageManager.t('twofactor.verifying');
submitButton.appendChild(spinnerSpan);
submitButton.appendChild(textSpan);
}
form.submit();
}
}
transformTwoFactorPage();
transformTwoFactorPage();

View File

@@ -1,138 +1,117 @@
* {
box-sizing: border-box;
margin: 0;
padding: 0;
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", serif !important;
min-height: 100vh;
font-size: 16px;
display: flex;
align-items: center;
justify-content: center;
margin:0;
padding:0;
color:var(--text-primary);
background-color:var(--background) !important;
font-family:"Montserrat",serif !important;
min-height:100vh;
font-size:16px;
display:flex;
align-items:center;
justify-content:center;
}
.logout-container {
width: 100%;
max-width: 500px;
padding: 20px;
margin: 0 auto;
width:100%;
max-width:500px;
padding:20px;
margin:0 auto;
}
.logout-header {
text-align: center;
margin-bottom: 24px;
text-align:center;
margin-bottom:24px;
}
.logo-text {
color: var(--text-primary);
font-size: 24px;
font-weight: 600;
margin: 16px 0;
display: flex;
align-items: center;
justify-content: center;
color:var(--text-primary);
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;
width:24px;
border-radius:8px;
margin-right:8px;
}
.logout-card {
background: var(--card-card);
padding: 24px;
border-radius: 24px;
text-align: center;
margin-bottom: 16px;
box-shadow: 0px 1px var(--shadow-blur) 0px var(--accent-shadow);
background:var(--card-card);
padding:24px;
border-radius:24px;
text-align:center;
margin-bottom:16px;
box-shadow:0px 1px var(--shadow-blur) 0px var(--accent-shadow);
}
.logout-message {
margin-bottom: 24px;
margin-bottom:24px;
}
.logout-message strong {
font-size: 18px;
color: var(--text-primary);
font-weight: 600;
font-size:18px;
color:var(--text-primary);
font-weight:600;
}
.redirect-timer {
font-size: 48px;
font-weight: 700;
color: var(--accent-accent);
margin-bottom: 24px;
font-size:48px;
font-weight:700;
color:var(--accent-accent);
margin-bottom:24px;
}
.btn-continue {
display: inline-block;
padding: 12px 24px;
background: var(--accent-accent);
color: var(--button-secondaryFill);
text-decoration: none;
border-radius: 12px;
font-weight: 600;
transition: background 0.2s ease, color 0.2s ease;
display:inline-block;
padding:12px 24px;
background:var(--accent-accent);
color:var(--button-secondaryFill);
text-decoration:none;
border-radius:12px;
font-weight:600;
transition:background 0.2s ease,color 0.2s ease;
}
.btn-continue:hover {
background: var(--accent-secondary) !important;
color: var(--button-secondaryFill) !important;
background:var(--accent-secondary) !important;
color:var(--button-secondaryFill) !important;
}
.logout-footer {
text-align: center;
text-align:center;
}
.privacy-link {
color: var(--text-secondary);
text-decoration: none;
font-size: 14px;
display: inline-block;
margin-bottom: 8px;
transition: color 0.2s ease;
color:var(--text-secondary);
text-decoration:none;
font-size:14px;
display:inline-block;
margin-bottom:8px;
transition:color 0.2s ease;
}
.privacy-link:hover {
color: var(--text-primary);
color:var(--text-primary);
}
@media (max-width: 600px) {
@media (max-width:600px) {
.logout-container {
padding: 16px;
}
.logout-card {
padding: 20px;
}
.redirect-timer {
font-size: 36px;
}
.logo-text {
font-size: 20px;
}
.logo {
width: 20px;
}
padding:16px;
}
@keyframes fadeIn {
.logout-card {
padding:20px;
}
.redirect-timer {
font-size:36px;
}
.logo-text {
font-size:20px;
}
.logo {
width:20px;
}
}@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
opacity:0;
transform:translateY(-10px);
}
to {
opacity:1;
transform:translateY(0);
}
}

View File

@@ -1,25 +1,17 @@
(() => {
function loadFonts() {
// Create a new style element
const style = document.createElement('style');
style.textContent = `
@import url('https://fonts.googleapis.com/css2?family=Montserrat:wght@400;500;600;700&display=swap');
@import url('https://fonts.googleapis.com/icon?family=Material+Icons+Round');
`;
document.head.appendChild(style);
}
function transformLogoutPage() {
// Get current theme and school ID from cookies
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
async function transformLogoutPage() {
const theme =
await storageManager.get("themePreference", null) ||
localStorage.getItem("themePreference") ||
"light-green";
const instituteCode = await storageManager.get("schoolSubdomain", null);
document.documentElement.setAttribute("data-theme", theme);
const newHTML = `
<div class="logout-container">
<header class="logout-header">
<p class="logo-text">
<img src=${chrome.runtime.getURL('images/firka_logo.png')} alt="Firka" class="logo">
<img src=${chrome.runtime.getURL("images/firka_logo.png")} alt="Firka" class="logo">
Firka
</p>
</header>
@@ -42,39 +34,49 @@
</footer>
</div>
`;
// Replace body content
document.body.innerHTML = newHTML;
// Start countdown timer
const timerElement = document.getElementById('automaticRedirectTimer');
document.body.innerHTML = '';
const parser = new DOMParser();
const doc = parser.parseFromString(newHTML, 'text/html');
const tempDiv = doc.body;
while (tempDiv.firstChild) {
document.body.appendChild(tempDiv.firstChild);
}
const timerElement = document.getElementById("automaticRedirectTimer");
let remainingTime = 5;
const countdownInterval = setInterval(() => {
remainingTime--;
if (timerElement) {
timerElement.textContent = remainingTime;
}
if (remainingTime <= 0) {
clearInterval(countdownInterval);
window.location.href = `https://${instituteCode}.e-kreta.hu`;
}
}, 1000);
// Handle manual redirect click
document.querySelector('.btn-continue')?.addEventListener('click', (e) => {
document.querySelector(".btn-continue")?.addEventListener("click", (e) => {
e.preventDefault();
clearInterval(countdownInterval);
window.location.href = `https://${instituteCode}.e-kreta.hu`;
});
}
// Load fonts immediately
loadFonts();
// Run the transformation
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', transformLogoutPage);
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", () => {
setTimeout(() => {
if (typeof loadingScreen !== 'undefined') {
loadingScreen.hide();
}
}, 100);
transformLogoutPage();
});
} else {
if (typeof loadingScreen !== 'undefined') {
loadingScreen.hide();
}
transformLogoutPage();
}
})();

View File

@@ -1,7 +1,7 @@
{
"manifest_version": 3,
"name": "Firxa",
"version": "1.1.0",
"version": "1.3.5",
"description": "KRÉTA webes verziójának újraírása",
"icons": {
"128": "images/firka_logo_128.png"
@@ -12,122 +12,219 @@
"128": "images/firka_logo_128.png"
}
},
"web_accessible_resources": [{
"resources": [
"settings/*",
"images/*",
"fonts/*.woff2",
"icons/*.svg",
"grades/chart.js"
],
"matches": ["https://*.e-kreta.hu/*", "https://idp.e-kreta.hu/*"]
}],
"permissions": [
"storage"
],
"background": {
"service_worker": "tools/background.js"
},
"browser_specific_settings": {
"gecko": {
"id": "firxa@zan1456.hu",
"strict_min_version": "109.0",
"data_collection_permissions": {
"required": ["none"]
}
}
},
"web_accessible_resources": [
{
"resources": [
"settings/*",
"global/language.js",
"images/*",
"fonts/*.woff2",
"icons/*.svg",
"grades/chart.js",
"i18n/*.json",
"tools/storageManager.js",
"tools/storageTest.js"
],
"matches": [
"<all_urls>"
]
}
],
"content_scripts": [
{
"matches": [
"https://*.e-kreta.hu/*"
"https://idp.e-kreta.hu/Account/Login*",
"https://idp.e-kreta.hu/Account/Logout*",
"https://idp.e-kreta.hu/Account/LoginWithTwoFactor*",
"https://*.e-kreta.hu/Hianyzas/Hianyzasok*",
"https://*.e-kreta.hu/Adminisztracio/BelepesKezelo*",
"https://*.e-kreta.hu/Intezmeny/Faliujsag*",
"https://*.e-kreta.hu/TanuloErtekeles/Osztalyzatok*",
"https://*.e-kreta.hu/Orarend/InformaciokOrarend*",
"https://*.e-kreta.hu/Adminisztracio/Profil*",
"https://*.e-kreta.hu/Tanulo/TanuloHaziFeladat*",
"https://*.e-kreta.hu/Adminisztracio/ElfelejtettJelszo*",
"https://intezmenykereso.e-kreta.hu/*",
"https://eugyintezes.e-kreta.hu/*"
],
"js": [
"global/language.js",
"global/theme.js",
"tools/loadingScreen.js",
"tools/storageManager.js",
"tools/helper.js",
"tools/createTemplate.js",
"global/maintenance.js",
"global/navigation.js"
],
"css": [
"tools/loadingScreen.css",
"global/theme.css",
"global/navigation.css",
"global/maintenance.css"
],
"js": ["tools/cookieManager.js", "tools/helper.js", "tools/loadingScreen.js", "tools/createTemplate.js",
"global/maintenance.js", "global/theme.js", "global/navigation.js"],
"css": ["tools/loadingScreen.css", "global/theme.css", "global/navigation.css"],
"run_at": "document_start"
},
{
"matches": [
"https://*.e-kreta.hu/*"
"https://idp.e-kreta.hu/Account/Login*"
],
"css": ["global/maintenance.css"],
"run_at": "document_end"
"js": [
"login/login.js"
],
"css": [
"login/login.css"
],
"run_at": "document_start"
},
{
"matches": ["https://idp.e-kreta.hu/Account/Login*"],
"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://idp.e-kreta.hu/account/loginwithtwofactor*"
],
"js": [
"login/twofactor.js"
],
"css": [
"login/twofactor.css"
],
"run_at": "document_start"
},
{
"matches": [
"https://*.e-kreta.hu/Hianyzas/Hianyzasok*"
],
"js": ["absences/absences.js"],
"css": ["absences/absences.css"],
"js": [
"absences/absences.js"
],
"css": [
"absences/absences.css"
],
"run_at": "document_end"
},
{
"matches": [
"https://idp.e-kreta.hu/Account/Logout*"
],
"js": ["logout/logout.js"],
"css": ["logout/logout.css"],
"run_at": "document_end"
"js": [
"logout/logout.js"
],
"css": [
"logout/logout.css"
],
"run_at": "document_start"
},
{
"matches": [
"https://*.e-kreta.hu/Adminisztracio/BelepesKezelo*"
],
"js": ["roleselect/roleselect.js"],
"css": ["roleselect/roleselect.css"],
"run_at": "document_end"
},
{
"matches": [
"https://*.e-kreta.hu/Adminisztracio/ElfelejtettJelszo*"
"js": [
"roleselect/roleselect.js"
],
"js": ["forgotpassword/forgotpassword.js"],
"css": ["forgotpassword/forgotpassword.css"],
"run_at": "document_end"
"css": [
"roleselect/roleselect.css"
],
"run_at": "document_start"
},
{
"matches": [
"https://*.e-kreta.hu/Intezmeny/Faliujsag*"
],
"js": ["dashboard/dashboard.js"],
"css": ["dashboard/dashboard.css"],
"js": [
"dashboard/dashboard.js"
],
"css": [
"dashboard/dashboard.css"
],
"run_at": "document_end"
},
{
"matches": [
"https://*.e-kreta.hu/TanuloErtekeles/Osztalyzatok*"
],
"js": ["grades/grades.js", "grades/chart.js"],
"css": ["grades/grades.css"],
"js": [
"grades/grades.js",
"grades/chart.js"
],
"css": [
"grades/grades.css"
],
"run_at": "document_end"
},
{
"matches": [
"https://*.e-kreta.hu/Orarend/InformaciokOrarend*"
],
"js": ["timetable/timetable.js"],
"css": ["timetable/timetable.css"],
"js": [
"timetable/timetable.js"
],
"css": [
"timetable/timetable.css"
],
"run_at": "document_end"
},
{
"matches": [
"https://*.e-kreta.hu/Adminisztracio/Profil*"
],
"js": ["profile/profile.js"],
"css": ["profile/profile.css"],
"js": [
"profile/profile.js"
],
"css": [
"profile/profile.css"
],
"run_at": "document_end"
},
{
"matches": [
"https://*.e-kreta.hu/Tanulo/TanuloHaziFeladat*"
"https://*.e-kreta.hu/Adminisztracio/ElfelejtettJelszo*"
],
"js": [
"forgotpassword/forgotpassword.js"
],
"css": [
"forgotpassword/forgotpassword.css"
],
"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"],
"js": [
"search/search.js"
],
"css": [
"search/search.css"
],
"run_at": "document_end"
},
{
"matches": [
"https://eugyintezes.e-kreta.hu/*",
"https://eugyintezes.e-kreta.hu/"
],
"js": [
"messages/messages.js"
],
"css": [
"messages/messages.css"
],
"run_at": "document_start"
}
]
}

232
manifest_fox.json Normal file
View File

@@ -0,0 +1,232 @@
{
"manifest_version": 3,
"name": "Firxa",
"version": "1.3.4",
"description": "KRÉTA webes verziójának újraírása",
"icons": {
"128": "images/firka_logo_128.png"
},
"action": {
"default_popup": "settings/index.html",
"default_icon": {
"128": "images/firka_logo_128.png"
}
},
"permissions": [
"storage"
],
"background": {
"service_worker": "tools/background.js",
"scripts": ["tools/background.js"],
"persistent": false
},
"browser_specific_settings": {
"gecko": {
"id": "firxa@zan1456.hu",
"strict_min_version": "109.0",
"data_collection_permissions": {
"required": ["none"]
}
}
},
"web_accessible_resources": [
{
"resources": [
"settings/*",
"global/language.js",
"images/*",
"fonts/*.woff2",
"icons/*.svg",
"grades/chart.js",
"i18n/*.json",
"tools/storageManager.js",
"tools/storageTest.js"
],
"matches": [
"<all_urls>"
]
}
],
"content_scripts": [
{
"matches": [
"https://idp.e-kreta.hu/Account/Login*",
"https://idp.e-kreta.hu/Account/Logout*",
"https://idp.e-kreta.hu/Account/LoginWithTwoFactor*",
"https://*.e-kreta.hu/Hianyzas/Hianyzasok*",
"https://*.e-kreta.hu/Adminisztracio/BelepesKezelo*",
"https://*.e-kreta.hu/Intezmeny/Faliujsag*",
"https://*.e-kreta.hu/TanuloErtekeles/Osztalyzatok*",
"https://*.e-kreta.hu/Orarend/InformaciokOrarend*",
"https://*.e-kreta.hu/Adminisztracio/Profil*",
"https://*.e-kreta.hu/Tanulo/TanuloHaziFeladat*",
"https://*.e-kreta.hu/Adminisztracio/ElfelejtettJelszo*",
"https://intezmenykereso.e-kreta.hu/*",
"https://eugyintezes.e-kreta.hu/*"
],
"js": [
"global/language.js",
"global/theme.js",
"tools/loadingScreen.js",
"tools/storageManager.js",
"tools/helper.js",
"tools/createTemplate.js",
"global/maintenance.js",
"global/navigation.js"
],
"css": [
"tools/loadingScreen.css",
"global/theme.css",
"global/navigation.css",
"global/maintenance.css"
],
"run_at": "document_start"
},
{
"matches": [
"https://idp.e-kreta.hu/Account/Login*"
],
"js": [
"login/login.js"
],
"css": [
"login/login.css"
],
"run_at": "document_start"
},
{
"matches": [
"https://idp.e-kreta.hu/account/loginwithtwofactor*"
],
"js": [
"login/twofactor.js"
],
"css": [
"login/twofactor.css"
],
"run_at": "document_start"
},
{
"matches": [
"https://*.e-kreta.hu/Hianyzas/Hianyzasok*"
],
"js": [
"absences/absences.js"
],
"css": [
"absences/absences.css"
],
"run_at": "document_end"
},
{
"matches": [
"https://idp.e-kreta.hu/Account/Logout*"
],
"js": [
"logout/logout.js"
],
"css": [
"logout/logout.css"
],
"run_at": "document_start"
},
{
"matches": [
"https://*.e-kreta.hu/Adminisztracio/BelepesKezelo*"
],
"js": [
"roleselect/roleselect.js"
],
"css": [
"roleselect/roleselect.css"
],
"run_at": "document_start"
},
{
"matches": [
"https://*.e-kreta.hu/Intezmeny/Faliujsag*"
],
"js": [
"dashboard/dashboard.js"
],
"css": [
"dashboard/dashboard.css"
],
"run_at": "document_end"
},
{
"matches": [
"https://*.e-kreta.hu/TanuloErtekeles/Osztalyzatok*"
],
"js": [
"grades/grades.js",
"grades/chart.js"
],
"css": [
"grades/grades.css"
],
"run_at": "document_end"
},
{
"matches": [
"https://*.e-kreta.hu/Orarend/InformaciokOrarend*"
],
"js": [
"timetable/timetable.js"
],
"css": [
"timetable/timetable.css"
],
"run_at": "document_end"
},
{
"matches": [
"https://*.e-kreta.hu/Adminisztracio/Profil*"
],
"js": [
"profile/profile.js"
],
"css": [
"profile/profile.css"
],
"run_at": "document_end"
},
{
"matches": [
"https://*.e-kreta.hu/Adminisztracio/ElfelejtettJelszo*"
],
"js": [
"forgotpassword/forgotpassword.js"
],
"css": [
"forgotpassword/forgotpassword.css"
],
"run_at": "document_end"
},
{
"matches": [
"https://intezmenykereso.e-kreta.hu/"
],
"js": [
"search/search.js"
],
"css": [
"search/search.css"
],
"run_at": "document_end"
},
{
"matches": [
"https://eugyintezes.e-kreta.hu/uzenetek/",
"https://eugyintezes.e-kreta.hu/uzenetek"
],
"js": [
"messages/messages.js"
],
"css": [
"messages/messages.css"
],
"run_at": "document_end"
}
]
}

669
messages/messages.css Normal file
View File

@@ -0,0 +1,669 @@
* {
box-sizing: border-box;
margin: 0;
padding: 0;
font-family: 'Montserrat', sans-serif;
}
body {
margin: 0;
padding: 0;
color: var(--text-primary);
background-color: var(--background) !important;
min-height: 100vh;
font-size: 16px;
}
@media (max-width: 768px) {
body {
font-size: 14px;
}
}
.kreta-container {
min-height: 100vh;
display: flex;
flex-direction: column;
}
.kreta-main {
flex: 1;
padding: clamp(1rem, 3vw, 2rem);
max-width: 1400px;
margin: 0 auto;
width: 100%;
}
.messages-container {
background-color: var(--background);
border-radius: 12px;
overflow: hidden;
}
.bulk-actions-card {
margin: 0 auto 1rem;
max-width: 900px;
background: var(--card-card);
border-radius: 12px;
box-shadow: 0px 1px var(--shadow-blur) 0px var(--accent-shadow);
padding: 12px 16px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
}
.bulk-actions-left,
.bulk-actions-right {
display: flex;
align-items: center;
gap: 8px;
}
.bulk-btn {
background: var(--button-secondaryFill);
color: var(--text-primary);
border: none;
border-radius: 8px;
width: 36px;
height: 36px;
display: inline-flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.2s ease;
}
.bulk-btn:hover:not(:disabled) {
background: var(--accent-15);
color: var(--accent-accent);
}
.bulk-btn.active {
background: var(--accent-15);
color: var(--accent-accent);
}
.bulk-btn:disabled {
opacity: 0.6;
cursor: not-allowed;
}
.view-toggle {
background: var(--button-secondaryFill);
border-radius: 8px;
overflow: hidden;
display: inline-flex;
}
.view-toggle button {
background: transparent;
border: none;
width: 36px;
height: 36px;
display: inline-flex;
align-items: center;
justify-content: center;
color: var(--text-secondary);
cursor: pointer;
}
.view-toggle button.active {
color: var(--accent-accent);
background: var(--accent-15);
}
.bulk-btn img,
.view-toggle button img {
width: 20px;
height: 20px;
}
@media (max-width: 768px) {
.bulk-actions-card {
flex-direction: column;
align-items: stretch;
justify-content: center;
gap: 8px;
padding: 10px 12px;
max-width: 100%;
}
.bulk-actions-left,
.bulk-actions-right {
justify-content: center;
flex-wrap: wrap;
gap: 6px;
}
.bulk-btn,
.view-toggle button {
width: 32px;
height: 32px;
}
.bulk-btn img,
.view-toggle button img {
width: 18px;
height: 18px;
}
}
@media (max-width: 480px) {
.bulk-btn,
.view-toggle button {
width: 28px;
height: 28px;
}
.bulk-btn img,
.view-toggle button img {
width: 16px;
height: 16px;
}
}
.messages-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
gap: 1.5rem;
padding: 1.5rem;
}
@media (max-width: 768px) {
.messages-grid {
grid-template-columns: 1fr;
gap: 1rem;
padding: 1rem;
}
}
.message-card {
background-color: var(--card-card);
border: 1px solid var(--border-color);
border-radius: 12px;
padding: 1.5rem;
cursor: pointer;
transition: all 0.2s ease-in-out;
position: relative;
overflow: hidden;
}
.message-card.selected {
background-color: var(--card-translucent);
border-color: var(--accent-15);
}
.message-card:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
border-color: var(--primary-color);
}
.message-card.unread {
border-left: 4px solid var(--accent-accent);
}
.message-card.unread::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 3px;
background: linear-gradient(90deg, var(--primary-color), var(--secondary-color));
}
.message-card-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 1rem;
gap: 1rem;
}
.sender-info {
display: flex;
align-items: center;
gap: 0.5rem;
flex: 1;
min-width: 0;
}
.sender-name {
font-weight: 600;
color: var(--text-primary);
font-size: 0.95rem;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.unread-indicator {
width: 8px;
height: 8px;
background-color: var(--accent-accent);
border-radius: 50%;
flex-shrink: 0;
animation: pulse 2s infinite;
}
@keyframes pulse {
0%, 100% {
opacity: 1;
transform: scale(1);
}
50% {
opacity: 0.7;
transform: scale(1.1);
}
}
.message-date {
font-size: 0.8rem;
color: var(--text-secondary);
white-space: nowrap;
flex-shrink: 0;
}
.message-subject {
font-size: 1rem;
font-weight: 500;
color: var(--text-primary);
line-height: 1.4;
margin-bottom: 1rem;
display: -webkit-box;
line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
}
.attachment-indicator {
position: absolute;
top: 1rem;
right: 1rem;
font-size: 1.2rem;
color: var(--text-secondary);
opacity: 0.7;
}
.loading-state {
display: flex;
justify-content: center;
align-items: center;
min-height: 400px;
padding: 2rem;
}
.loading-content {
display: flex;
flex-direction: column;
align-items: center;
gap: 1rem;
text-align: center;
}
.loading-spinner {
width: 40px;
height: 40px;
border: 3px solid var(--border-color);
border-top: 3px solid var(--primary-color);
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.loading-content p {
color: var(--text-secondary);
font-size: 0.9rem;
font-weight: 500;
}
.empty-state {
display: flex;
justify-content: center;
align-items: center;
min-height: 400px;
padding: 2rem;
}
.empty-content {
text-align: center;
max-width: 400px;
}
.empty-content h3 {
font-size: 1.5rem;
font-weight: 600;
color: var(--text-primary);
margin-bottom: 0.5rem;
}
.empty-content p {
color: var(--text-secondary);
font-size: 1rem;
line-height: 1.5;
}
.error-state {
display: flex;
justify-content: center;
align-items: center;
min-height: 400px;
padding: 2rem;
}
.error-content {
text-align: center;
max-width: 500px;
}
.error-content h3 {
font-size: 1.5rem;
font-weight: 600;
color: var(--error-color);
margin-bottom: 0.5rem;
}
.error-content p {
color: var(--text-secondary);
font-size: 1rem;
line-height: 1.5;
margin-bottom: 1.5rem;
}
.retry-btn {
padding: 0.75rem 1.5rem;
background-color: var(--primary-color);
color: white;
border: none;
border-radius: 8px;
font-weight: 600;
font-size: 0.9rem;
cursor: pointer;
transition: all 0.2s ease-in-out;
}
.retry-btn:hover {
background-color: var(--primary-hover);
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.modal-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.8);
backdrop-filter: blur(4px);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
}
body.modal-open {
overflow: hidden;
}
.modal-content {
background: var(--card-card);
border-radius: 12px;
max-width: 1200px;
width: 95%;
max-height: 95%;
overflow-y: auto;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
border: 1px solid var(--background-0);
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20px;
border-bottom: 1px solid var(--background-0);
background: var(--background);
border-radius: 12px 12px 0 0;
}
.modal-header h2 {
margin: 0;
color: var(--text-primary);
font-size: 1.5em;
font-weight: 600;
}
.modal-close {
background: none;
border: none;
font-size: 24px;
cursor: pointer;
color: var(--text-secondary);
padding: 0;
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
transition: all 0.2s ease;
}
.modal-close:hover {
background: var(--background-0);
color: var(--text-primary);
transform: scale(1.1);
}
.modal-body {
padding: 20px;
background: var(--card-card);
}
.modal-body iframe {
min-height: 600px;
border-radius: 12px;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
border: 1px solid var(--background-0);
}
.loading-content {
text-align: center;
padding: 40px 20px;
}
.loading-spinner {
width: 40px;
height: 40px;
border: 4px solid #f3f3f3;
border-top: 4px solid #1976d2;
border-radius: 50%;
animation: spin 1s linear infinite;
margin: 0 auto 20px;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.error-content {
text-align: center;
padding: 40px 20px;
}
.error-content h3 {
color: #d32f2f;
margin-bottom: 10px;
}
.error-content p {
color: #666;
margin-bottom: 20px;
}
.message-details {
max-width: 100%;
}
.message-info {
background-color: var(--card-card);
padding: 15px;
border-radius: 6px;
margin-bottom: 20px;
color: var(--text-secondary);
}
.info-row {
display: flex;
margin-bottom: 10px;
align-items: flex-start;
}
.info-row:last-child {
margin-bottom: 0;
}
.info-label {
font-weight: bold;
min-width: 80px;
margin-right: 10px;
}
.info-value {
flex: 1;
word-break: break-word;
}
.message-content {
margin-bottom: 20px;
}
.message-content h4 {
margin: 0 0 15px 0;
color: var(--text-secondary);
font-size: 1.1em;
}
.message-text {
background-color: var(--card-card);
border-radius: 6px;
padding: 15px;
line-height: 1.6;
color: var(--text-secondary);
max-height: 300px;
overflow-y: auto;
}
.message-text p {
margin: 0 0 10px 0;
}
.message-text p:last-child {
margin-bottom: 0;
}
.message-text a {
color: #1976d2;
text-decoration: none;
}
.message-text a:hover {
text-decoration: underline;
}
.message-attachments {
background: #f9f9f9;
padding: 15px;
border-radius: 6px;
}
.message-attachments h4 {
margin: 0 0 10px 0;
color: #333;
font-size: 1.1em;
}
.message-attachments ul {
list-style: none;
padding: 0;
margin: 0;
}
.message-attachments li {
padding: 5px 0;
}
.message-attachments a {
color: #1976d2;
text-decoration: none;
display: inline-flex;
align-items: center;
}
.message-attachments a:hover {
text-decoration: underline;
}
@media (max-width: 1200px) {
.messages-grid {
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
}
}
@media (max-width: 768px) {
.kreta-main {
padding: 1rem;
}
.message-card {
padding: 1rem;
}
.message-card-header {
flex-direction: column;
align-items: flex-start;
gap: 0.5rem;
}
.message-date {
align-self: flex-end;
margin-top: -0.5rem;
}
.sender-name {
font-size: 0.9rem;
}
.message-subject {
font-size: 0.95rem;
}
}
@media (max-width: 480px) {
.messages-grid {
grid-template-columns: 1fr;
gap: 0.75rem;
padding: 0.75rem;
}
.message-card {
padding: 0.75rem;
}
.sender-name {
font-size: 0.85rem;
}
.message-subject {
font-size: 0.9rem;
}
.attachment-indicator {
font-size: 1rem;
}
}

670
messages/messages.js Normal file
View File

@@ -0,0 +1,670 @@
(() => {
async function waitForTranslations() {
let attempts = 0;
const maxAttempts = 200;
while (
(typeof window.LanguageManager === 'undefined') ||
(window.LanguageManager && window.LanguageManager.t('navigation.dashboard') === 'navigation.dashboard')
) {
if (attempts++ > maxAttempts) break;
await new Promise(resolve => setTimeout(resolve, 50));
}
}
function formatDate(dateString) {
if (!dateString) {
return 'Ismeretlen dátum';
}
const date = new Date(dateString);
if (isNaN(date.getTime())) {
return 'Érvénytelen dátum';
}
const now = new Date();
const diffTime = Math.abs(now - date);
const diffDays = Math.floor(diffTime / (1000 * 60 * 60 * 24));
if (diffDays === 0) {
return 'Ma';
} else if (diffDays === 1) {
return 'Tegnap';
} else if (diffDays <= 7) {
return `${diffDays} napja`;
} else {
return date.toLocaleDateString('hu-HU', {
year: 'numeric',
month: 'short',
day: 'numeric'
});
}
}
function sanitizeHTML(html) {
const div = document.createElement('div');
div.textContent = html;
return div.innerHTML;
}
class APIManager {
static async fetchMessages() {
try {
const response = await fetch('https://eugyintezes.e-kreta.hu/api/v1/kommunikacio/postaladaelemek/beerkezett', {
method: 'GET',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Content-Length': '8023',
'x-csrf': '1',
'x-uzenet-json-formatum': 'CamelCase'
}
});
if (!response.ok) {
if (response.status === 401 && window.location.href.startsWith('https://eugyintezes.e-kreta.hu/uzenetek')) {
window.location.reload();
throw new Error('401');
}
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
console.error('Error fetching messages:', error);
throw error;
}
}
static async fetchDeletedMessages() {
const response = await fetch('https://eugyintezes.e-kreta.hu/api/v1/kommunikacio/postaladaelemek/torolt', {
method: 'GET',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'x-csrf': '1',
'x-uzenet-json-formatum': 'CamelCase'
}
});
if (!response.ok) {
if (response.status === 401 && window.location.href.startsWith('https://eugyintezes.e-kreta.hu/uzenetek')) {
window.location.reload();
throw new Error('401');
}
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
}
static async markMessagesRead(isRead, ids) {
const payload = {
isOlvasott: !!isRead,
postaladaElemAzonositoLista: ids.map(Number)
};
const response = await fetch('https://eugyintezes.e-kreta.hu/api/v1/kommunikacio/postaladaelemek/olvasott', {
method: 'POST',
credentials: 'include',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'x-csrf': '1',
'x-uzenet-json-formatum': 'CamelCase'
},
body: JSON.stringify(payload)
});
if (!response.ok) {
if (response.status === 401 && window.location.href.startsWith('https://eugyintezes.e-kreta.hu/uzenetek')) {
window.location.reload();
throw new Error('401');
}
throw new Error(`HTTP error! status: ${response.status}`);
}
}
static async moveToTrash(ids, toTrash = true) {
const payload = {
isKuka: !!toTrash,
postaladaElemAzonositoLista: ids.map(Number)
};
const response = await fetch('https://eugyintezes.e-kreta.hu/api/v1/kommunikacio/postaladaelemek/kuka', {
method: 'POST',
credentials: 'include',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'x-csrf': '1',
'x-uzenet-json-formatum': 'CamelCase'
},
body: JSON.stringify(payload)
});
if (!response.ok) {
if (response.status === 401 && window.location.href.startsWith('https://eugyintezes.e-kreta.hu/uzenetek')) {
window.location.reload();
throw new Error('401');
}
throw new Error(`HTTP error! status: ${response.status}`);
}
}
}
async function openMessageModal(messageId, isRead = true) {
document.body.classList.add('modal-open');
try {
const modalOverlay = document.createElement('div');
modalOverlay.className = 'modal-overlay';
modalOverlay.onclick = (e) => {
if (e.target === modalOverlay) {
closeMessageModal();
}
};
const modalContent = document.createElement('div');
modalContent.className = 'modal-content';
modalContent.innerHTML = `
<div class="modal-header">
<h2>Üzenet részletei</h2>
<button class="modal-close" onclick="closeMessageModal()">×</button>
</div>
<div class="modal-body">
<div class="loading-content">
<div class="loading-spinner"></div>
<p>Üzenet betöltése...</p>
</div>
</div>
`;
modalOverlay.appendChild(modalContent);
document.body.appendChild(modalOverlay);
if (!isRead) {
try {
await markMessageAsRead(messageId);
const cardEl = document.querySelector(`.message-card[data-id="${messageId}"]`);
if (cardEl) {
cardEl.classList.remove('unread');
const indicator = cardEl.querySelector('.unread-indicator');
if (indicator) indicator.remove();
}
} catch (e) {
}
}
const response = await fetch(`https://eugyintezes.e-kreta.hu/api/v1/kommunikacio/postaladaelemek/${messageId}`, {
method: 'GET',
credentials: 'include',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Content-Length': '8023',
'x-csrf': '1',
'x-uzenet-json-formatum': 'CamelCase'
}
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const messageData = await response.json();
displayMessageDetails(modalContent, messageData);
} catch (error) {
console.error('Error loading message details:', error);
const modalContent = document.querySelector('.modal-content');
if (modalContent) {
modalContent.querySelector('.modal-body').innerHTML = `
<div class="error-content">
<h3>Hiba történt</h3>
<p>Az üzenet betöltése sikertelen.</p>
<button class="retry-btn" onclick="openMessageModal(${messageId})">Újrapróbálás</button>
</div>
`;
}
}
}
function displayMessageDetails(modalContent, messageData) {
const message = messageData.uzenet;
const sender = message.feladoNev + (message.feladoTitulus ? ` (${message.feladoTitulus})` : '');
const date = formatDate(message.kuldesDatum);
const subject = message.targy || 'Nincs tárgy';
const content = message.szoveg || 'Nincs tartalom';
modalContent.querySelector('.modal-body').innerHTML = `
<div class="message-details">
<div class="message-info">
<div class="info-row">
<span class="info-label">Feladó:</span>
<span class="info-value">${sanitizeHTML(sender)}</span>
</div>
<div class="info-row">
<span class="info-label">Dátum:</span>
<span class="info-value">${date}</span>
</div>
<div class="info-row">
<span class="info-label">Tárgy:</span>
<span class="info-value">${sanitizeHTML(subject)}</span>
</div>
</div>
<div class="message-content">
<h4>Üzenet tartalma:</h4>
<div class="message-text">${content}</div>
</div>
${message.csatolmanyok && message.csatolmanyok.length > 0 ? `
<div class="message-attachments">
<h4>Mellékletek:</h4>
<ul>
${message.csatolmanyok.map(attachment => `
<li><a href="#" onclick="downloadAttachment('${attachment.azonosito}')">${sanitizeHTML(attachment.nev)}</a></li>
`).join('')}
</ul>
</div>
` : ''}
</div>
`;
}
function closeMessageModal() {
const modalOverlay = document.querySelector('.modal-overlay');
if (modalOverlay) {
modalOverlay.remove();
}
document.body.classList.remove('modal-open');
}
window.openMessageModal = openMessageModal;
window.closeMessageModal = closeMessageModal;
function createLoadingState() {
const loadingDiv = document.createElement('div');
loadingDiv.className = 'loading-state';
loadingDiv.innerHTML = `
<div class="loading-content">
<div class="loading-spinner"></div>
<p>${LanguageManager.t('messages.loading', 'Üzenetek betöltése...')}</p>
</div>
`;
return loadingDiv;
}
function createErrorState(onRetry) {
const errorDiv = document.createElement('div');
errorDiv.className = 'error-state';
errorDiv.innerHTML = `
<div class="error-content">
<h3>${LanguageManager.t('messages.error.title', 'Hiba történt')}</h3>
<p>${LanguageManager.t('messages.error.description', 'Az üzenetek betöltése sikertelen volt.')}</p>
<button class="retry-btn">${LanguageManager.t('messages.error.retry', 'Újrapróbálás')}</button>
</div>
`;
const retryBtn = errorDiv.querySelector('.retry-btn');
retryBtn.addEventListener('click', onRetry);
return errorDiv;
}
function createEmptyState() {
const emptyDiv = document.createElement('div');
emptyDiv.className = 'empty-state';
emptyDiv.innerHTML = `
<div class="empty-content">
<h3>${LanguageManager.t('messages.empty.title', 'Nincsenek üzenetek')}</h3>
<p>${LanguageManager.t('messages.empty.description', 'Jelenleg nincsenek elérhető üzenetek.')}</p>
</div>
`;
return emptyDiv;
}
function createMessageCard(message) {
const card = document.createElement('div');
card.className = `message-card ${message.isElolvasva ? '' : 'unread'}`;
card.dataset.id = String(message.azonosito);
card.onclick = () => {
if (selectionMode) {
toggleSelect(message.azonosito, card);
return;
}
openMessageModal(message.azonosito, !!message.isElolvasva);
};
const senderName = message.uzenetFeladoNev || 'Ismeretlen feladó';
const subject = message.uzenetTargy || 'Nincs tárgy';
const date = formatDate(message.uzenetKuldesDatum);
const hasAttachment = message.hasCsatolmany;
card.innerHTML = `
<div class="message-card-header">
<div class="sender-info">
<span class="sender-name">${sanitizeHTML(senderName)}</span>
${!message.isElolvasva ? '<span class="unread-indicator"></span>' : ''}
</div>
<div class="message-date">${date}</div>
</div>
<div class="message-subject">${sanitizeHTML(subject)}</div>
${hasAttachment ? '<div class="attachment-indicator">📎</div>' : ''}
`;
return card;
}
async function markMessageAsRead(messageId) {
const payload = {
isOlvasott: true,
postaladaElemAzonositoLista: [Number(messageId)]
};
const response = await fetch('https://eugyintezes.e-kreta.hu/api/v1/kommunikacio/postaladaelemek/olvasott', {
method: 'POST',
credentials: 'include',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'x-csrf': '1',
'x-uzenet-json-formatum': 'CamelCase'
},
body: JSON.stringify(payload)
});
if (!response.ok) {
if (response.status === 401 && window.location.href.startsWith('https://eugyintezes.e-kreta.hu/uzenetek')) {
window.location.reload();
throw new Error('401');
}
throw new Error(`HTTP error! status: ${response.status}`);
}
}
function createMessagesGrid(messages) {
const gridContainer = document.createElement('div');
gridContainer.className = 'messages-grid';
if (messages.length === 0) {
return createEmptyState();
}
messages.forEach(message => {
const messageCard = createMessageCard(message);
gridContainer.appendChild(messageCard);
});
return gridContainer;
}
let currentView = 'inbox';
let selectionMode = false;
const selection = new Set();
function renderBulkActions(container) {
const bulk = document.createElement('div');
bulk.className = 'bulk-actions-card';
bulk.innerHTML = `
<div class="bulk-actions-left">
<div class="view-toggle">
<button id="viewInboxBtn" class="${currentView==='inbox'?'active':''}" title="Beérkezett">
<img src="${chrome.runtime.getURL('icons/messages-active.svg')}" alt="Beérkezett">
</button>
<button id="viewTrashBtn" class="${currentView==='trash'?'active':''}" title="Törölt">
<img src="${chrome.runtime.getURL('icons/delete.svg')}" alt="Törölt">
</button>
</div>
<button id="toggleSelectionModeBtn" class="bulk-btn" title="Kijelölés mód">
<img src="${chrome.runtime.getURL('icons/select.svg')}" alt="Kijelölés mód">
</button>
<button id="selectAllBtn" class="bulk-btn" title="Mind kijelöl">
<img src="${chrome.runtime.getURL('icons/select-all.svg')}" alt="Mind kijelöl">
</button>
<button id="clearSelectionBtn" class="bulk-btn" title="Kijelölés törlése">
<img src="${chrome.runtime.getURL('icons/select-none.svg')}" alt="Kijelölés törlése">
</button>
</div>
<div class="bulk-actions-right">
<button id="markReadBtn" class="bulk-btn" title="Olvasott">
<img src="${chrome.runtime.getURL('icons/eye-on.svg')}" alt="Olvasott">
</button>
<button id="markUnreadBtn" class="bulk-btn" title="Olvasatlan">
<img src="${chrome.runtime.getURL('icons/eye-off.svg')}" alt="Olvasatlan">
</button>
<button id="deleteBtn" class="bulk-btn" title="Törlés">
<img src="${chrome.runtime.getURL('icons/trash.svg')}" alt="Törlés">
</button>
<button id="restoreBtn" class="bulk-btn" title="Visszaállítás">
<img src="${chrome.runtime.getURL('icons/undo.svg')}" alt="Visszaállítás">
</button>
</div>
`;
container.appendChild(bulk);
bulk.querySelector('#viewInboxBtn').addEventListener('click', () => switchView('inbox'));
bulk.querySelector('#viewTrashBtn').addEventListener('click', () => switchView('trash'));
bulk.querySelector('#toggleSelectionModeBtn').addEventListener('click', toggleSelectionMode);
bulk.querySelector('#selectAllBtn').addEventListener('click', selectAllVisible);
bulk.querySelector('#clearSelectionBtn').addEventListener('click', clearSelection);
bulk.querySelector('#markReadBtn').addEventListener('click', () => bulkMark(true));
bulk.querySelector('#markUnreadBtn').addEventListener('click', () => bulkMark(false));
bulk.querySelector('#deleteBtn').addEventListener('click', bulkDelete);
bulk.querySelector('#restoreBtn').addEventListener('click', bulkRestore);
updateBulkActionsState();
}
function updateBulkActionsState() {
const ids = Array.from(selection);
const bulk = document.querySelector('.bulk-actions-card');
if (!bulk) return;
const disableAll = ids.length === 0;
bulk.querySelector('#markReadBtn').disabled = disableAll || currentView !== 'inbox';
bulk.querySelector('#markUnreadBtn').disabled = disableAll || currentView !== 'inbox';
bulk.querySelector('#deleteBtn').disabled = disableAll || currentView !== 'inbox';
bulk.querySelector('#restoreBtn').disabled = disableAll || currentView !== 'trash';
}
function selectAllVisible() {
const cards = document.querySelectorAll('.messages-grid .message-card');
cards.forEach(card => {
const id = parseInt(card.dataset.id);
selection.add(id);
card.classList.add('selected');
});
updateBulkActionsState();
}
function clearSelection() {
selection.clear();
document.querySelectorAll('.messages-grid .message-card.selected').forEach(card => card.classList.remove('selected'));
updateBulkActionsState();
}
function toggleSelect(id, card) {
if (selection.has(id)) {
selection.delete(id);
card.classList.remove('selected');
} else {
selection.add(id);
card.classList.add('selected');
}
updateBulkActionsState();
}
function toggleSelectionMode() {
selectionMode = !selectionMode;
const btn = document.getElementById('toggleSelectionModeBtn');
if (btn) {
btn.classList.toggle('active', selectionMode);
btn.title = selectionMode ? 'Kilépés kijelölésből' : 'Kijelölés mód';
}
if (!selectionMode) {
clearSelection();
}
}
async function bulkMark(isRead) {
const ids = Array.from(selection);
if (ids.length === 0) return;
await APIManager.markMessagesRead(isRead, ids);
ids.forEach(id => {
const card = document.querySelector(`.message-card[data-id="${id}"]`);
if (!card) return;
if (isRead) {
card.classList.remove('unread');
const ind = card.querySelector('.unread-indicator');
if (ind) ind.remove();
} else {
card.classList.add('unread');
if (!card.querySelector('.unread-indicator')) {
const senderInfo = card.querySelector('.sender-info');
const span = document.createElement('span');
span.className = 'unread-indicator';
senderInfo.appendChild(span);
}
}
});
clearSelection();
}
async function bulkDelete() {
const ids = Array.from(selection);
if (ids.length === 0) return;
await APIManager.moveToTrash(ids, true);
ids.forEach(id => {
const card = document.querySelector(`.message-card[data-id="${id}"]`);
if (card) card.remove();
});
clearSelection();
}
async function bulkRestore() {
const ids = Array.from(selection);
if (ids.length === 0) return;
await APIManager.moveToTrash(ids, false);
ids.forEach(id => {
const card = document.querySelector(`.message-card[data-id="${id}"]`);
if (card) card.remove();
});
clearSelection();
}
async function switchView(view) {
if (currentView === view) return;
currentView = view;
if (selectionMode) toggleSelectionMode();
clearSelection();
const container = document.querySelector('.messages-container');
const grid = container.querySelector('.messages-grid') || container.querySelector('.empty-state') || container.querySelector('.error-state');
if (grid) grid.remove();
const toggleInbox = document.getElementById('viewInboxBtn');
const toggleTrash = document.getElementById('viewTrashBtn');
if (toggleInbox && toggleTrash) {
toggleInbox.classList.toggle('active', currentView==='inbox');
toggleTrash.classList.toggle('active', currentView==='trash');
}
const loadingState = createLoadingState();
container.appendChild(loadingState);
if (view === 'inbox') {
await loadMessages(container);
} else {
await loadDeleted(container);
}
}
async function transformMessagesPage() {
try {
await waitForTranslations();
document.body.innerHTML = '';
const kretaContainer = document.createElement('div');
kretaContainer.className = 'kreta-container';
const headerDiv = document.createElement('div');
const parser = new DOMParser();
const headerDoc = parser.parseFromString(await createTemplate.header(), 'text/html');
const headerContent = headerDoc.body;
while (headerContent.firstChild) {
headerDiv.appendChild(headerContent.firstChild);
}
kretaContainer.appendChild(headerDiv);
const main = document.createElement('main');
main.className = 'kreta-main';
const messagesContainer = document.createElement('div');
messagesContainer.className = 'messages-container';
renderBulkActions(main);
const loadingState = createLoadingState();
messagesContainer.appendChild(loadingState);
main.appendChild(messagesContainer);
kretaContainer.appendChild(main);
document.body.appendChild(kretaContainer);
if (typeof setupUserDropdown === 'function') {
setupUserDropdown();
}
if (typeof setupMobileNavigation === 'function') {
setupMobileNavigation();
}
await loadMessages(messagesContainer);
loadingScreen.hide();
} catch (error) {
console.error('Error transforming messages page:', error);
loadingScreen.hide();
}
}
async function loadMessages(container) {
try {
const messages = await APIManager.fetchMessages();
messages.sort((a, b) => {
const dateA = new Date(a.uzenetKuldesDatum);
const dateB = new Date(b.uzenetKuldesDatum);
return dateB - dateA;
});
const loadingState = container.querySelector('.loading-state');
if (loadingState) {
loadingState.remove();
}
const messagesGrid = createMessagesGrid(messages);
container.appendChild(messagesGrid);
} catch (error) {
console.error('Error loading messages:', error);
const loadingState = container.querySelector('.loading-state');
if (loadingState) {
loadingState.remove();
}
const errorState = createErrorState(() => loadMessages(container));
container.appendChild(errorState);
}
}
async function loadDeleted(container) {
try {
const messages = await APIManager.fetchDeletedMessages();
messages.sort((a, b) => {
const dateA = new Date(a.uzenetKuldesDatum);
const dateB = new Date(b.uzenetKuldesDatum);
return dateB - dateA;
});
const loadingState = container.querySelector('.loading-state');
if (loadingState) {
loadingState.remove();
}
const messagesGrid = createMessagesGrid(messages);
container.appendChild(messagesGrid);
} catch (error) {
const loadingState = container.querySelector('.loading-state');
if (loadingState) {
loadingState.remove();
}
const errorState = createErrorState(() => loadDeleted(container));
container.appendChild(errorState);
}
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', transformMessagesPage);
} else {
transformMessagesPage();
}
})();

View File

@@ -1,366 +1,325 @@
* {
box-sizing: border-box;
margin: 0;
padding: 0;
@import url('../global/theme.css');
.main-header,
.main-menu,
.main-sidebar,
.content-header,
.favoriteIconContainer,
#frissitesDatumDiv,
#layout_navigationBar,
.navbar,
.sidebar-container,
#sidepanel_tabs,
.sidepanel-wrapper {
display: none !important;
}
body {
margin: 0;
padding: 0;
color: var(--text-primary);
color: var(--text-primary) !important;
background-color: var(--background) !important;
font-family: "Montserrat", serif !important;
font-family: 'Montserrat', sans-serif !important;
min-height: 100vh;
font-size: 16px;
}
.kreta-container {
.page-wrapper {
background-color: var(--background) !important;
min-height: 100vh;
}
.main-wrapper {
display: flex;
flex-direction: column;
min-height: 100vh;
background-color: var(--background) !important;
}
.main-content {
flex: 1;
padding: 0;
background-color: var(--background) !important;
}
.content-content {
background-color: var(--background) !important;
min-height: auto !important;
padding: 0;
}
.content-container {
max-width: 1200px;
margin: 0 auto;
padding: clamp(1rem, 3vw, 2rem);
background-color: var(--background) !important;
}
.kreta-header {
.firka-header {
padding: clamp(1rem, 3vw, 2rem);
display: grid;
grid-template-columns: minmax(300px, 400px) 1fr minmax(200px, 300px);
display: flex;
align-items: center;
gap: 1rem;
background-color: var(--background);
border-bottom: 1px solid var(--accent-15);
}
.school-info {
.back-button {
display: flex;
flex-direction: column;
align-items: center;
gap: 0.5rem;
background: var(--card-card);
border: none;
border-radius: 12px;
padding: 12px 16px;
color: var(--text-primary);
font-family: 'Montserrat', sans-serif;
font-weight: 500;
font-size: 14px;
cursor: pointer;
transition: all 0.2s ease;
text-decoration: none;
box-shadow: 0px 1px var(--shadow-blur, 2px) 0px var(--accent-shadow);
}
.logo-text {
.back-button:hover {
background: var(--button-secondaryFill);
transform: translateY(-1px);
box-shadow: 0px 2px var(--shadow-blur, 4px) 0px var(--accent-shadow);
}
.back-button svg {
width: 16px;
height: 16px;
fill: var(--text-primary);
}
.page-title {
color: var(--text-primary);
font-size: 24px;
font-weight: 600;
display: flex;
align-items: center;
margin: 0;
}
.logo {
width: 24px;
border-radius: 8px;
margin-right: 8px;
.k-content {
background-color: var(--background) !important;
}
.school-details {
color: var(--text-secondary);
font-size: 14px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 300px;
}
.user-profile {
position: relative;
justify-self: flex-end;
}
.user-dropdown-btn {
display: flex;
align-items: center;
gap: 1rem;
background: none;
border: none;
cursor: pointer;
padding: 0.5rem;
border-radius: 8px;
transition: background-color 0.2s;
}
.user-dropdown-btn:hover {
background: var(--card-card);
}
.user-info {
text-align: right;
}
.user-dropdown {
position: absolute;
top: 100%;
right: 0;
margin-top: 0.5rem;
background: var(--card-card);
border-radius: 12px;
box-shadow: 0px 1px var(--shadow-blur) 0px var(--accent-shadow);
width: 200px;
display: none;
z-index: 1000;
}
.user-dropdown.show {
display: block;
animation: dropdownShow 0.2s ease;
}
.dropdown-item {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 0.75rem 1rem;
color: var(--text-primary);
text-decoration: none;
transition: background-color 0.2s;
}
.dropdown-item:hover {
background: var(--button-secondaryFill);
}
.kreta-main {
flex: 1;
padding: clamp(1rem, 3vw, 2rem);
max-width: 800px;
margin: 0 auto;
width: 100%;
}
.card {
background: var(--card-card);
border-radius: 24px;
overflow: hidden;
box-shadow: 0px 1px var(--shadow-blur) 0px var(--accent-shadow);
}
.card h2 {
font-size: 18px;
font-weight: 600;
color: var(--text-primary);
padding: 1.5rem;
padding-bottom: 0.5rem;
background-color: var(--card-card);
}
.profile-tabs {
padding: 1.5rem;
}
.tab-headers {
display: flex;
gap: 0.5rem;
margin-bottom: 1.5rem;
border-bottom: 1px solid var(--card-translucent);
overflow-x: auto;
scrollbar-width: none;
}
.tab-headers::-webkit-scrollbar {
display: none;
}
.tab-header {
padding: 0.75rem 1rem;
background: none;
border: none;
color: var(--text-secondary);
font-weight: 500;
cursor: pointer;
white-space: nowrap;
border-bottom: 2px solid transparent;
font-size: 14px;
}
.tab-header.active {
color: var(--accent-accent);
border-bottom-color: var(--accent-accent);
}
.tab-content {
display: none;
background: var(--button-secondaryFill);
border-radius: 8px;
padding: 1.5rem;
}
.tab-content.active {
display: block;
}
.form-group {
margin-bottom: 1rem;
}
.form-label {
display: block;
color: var(--text-secondary);
font-size: 14px;
margin-bottom: 0.5rem;
}
.form-control {
width: 100%;
padding: 0.75rem;
border: 1px solid var(--accent-15);
border-radius: 8px;
background: var(--accent-15);
color: var(--text-primary);
font-size: 14px;
}
.form-control:focus {
outline: none;
border-color: var(--accent-accent);
}
.form-text {
font-size: 12px;
color: var(--text-secondary);
margin-top: 0.25rem;
}
.security-content {
display: flex;
flex-direction: column;
gap: 1.5rem;
}
.step-card {
background: var(--button-secondaryFill);
border-radius: 12px;
padding: 1.5rem;
animation: fadeIn 0.3s ease;
}
.step-card h3 {
color: var(--text-primary);
font-size: 16px;
font-weight: 600;
margin-bottom: 1rem;
}
.step-card p {
color: var(--text-secondary);
font-size: 14px;
margin-bottom: 1rem;
line-height: 1.5;
}
.app-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1.5rem;
margin-top: 1rem;
}
.app-section h4 {
color: var(--text-primary);
font-size: 14px;
font-weight: 600;
margin-bottom: 0.75rem;
}
.app-links {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.app-link {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.75rem;
background: var(--card-card);
border-radius: 8px;
color: var(--text-primary);
text-decoration: none;
font-size: 14px;
transition: background-color 0.2s;
}
.app-link:hover {
background: var(--button-secondaryFill);
}
.qr-container {
display: flex;
flex-direction: column;
align-items: center;
gap: 1rem;
margin: 1.5rem 0;
}
.qr-container img {
background: white;
padding: 1rem;
border-radius: 8px;
max-width: 200px;
}
.setup-key {
display: flex;
flex-direction: column;
gap: 0.5rem;
width: 100%;
}
.key-display {
display: flex;
align-items: center;
gap: 0.5rem;
background: var(--card-card);
padding: 0.75rem;
border-radius: 8px;
}
.key-display code {
font-family: monospace;
color: var(--text-primary);
font-size: 14px;
flex-grow: 1;
.k-content h4 {
color: var(--text-primary) !important;
font-family: 'Montserrat', sans-serif !important;
font-size: 28px !important;
font-weight: 600 !important;
margin: 0 0 2rem 0 !important;
text-align: center;
}
.btn-copy {
background: none;
border: none;
color: var(--text-secondary);
cursor: pointer;
padding: 0.25rem;
border-radius: 4px;
transition: all 0.2s;
.k-tabstrip-wrapper {
background: var(--card-card) !important;
border-radius: 24px !important;
overflow: hidden;
box-shadow: 0px 1px var(--shadow-blur, 2px) 0px var(--accent-shadow);
border: none !important;
}
.btn-copy:hover {
color: var(--text-primary);
background: var(--button-secondaryFill);
.k-tabstrip {
background: var(--card-card) !important;
border: none !important;
}
.k-tabstrip-items {
background: var(--card-card) !important;
border: none !important;
border-radius: 24px 24px 0 0 !important;
padding: 0 20px !important;
}
.k-tabstrip-items .k-item {
background: transparent !important;
border: none !important;
margin: 0 !important;
border-radius: 0 !important;
}
.k-tabstrip-items .k-item .k-link {
color: var(--text-secondary) !important;
font-family: 'Montserrat', sans-serif !important;
font-weight: 500 !important;
font-size: 14px !important;
padding: 16px 20px !important;
border: none !important;
background: transparent !important;
border-radius: 0 !important;
transition: all 0.2s ease !important;
}
.k-tabstrip-items .k-item.k-state-active .k-link {
color: var(--accent-accent) !important;
font-weight: 600 !important;
border-bottom: 2px solid var(--accent-accent) !important;
}
.k-tabstrip-items .k-item:hover .k-link {
color: var(--text-primary) !important;
}
.btn-save {
padding: 0.75rem 1.5rem;
background: var(--accent-accent);
color: var(--button-secondaryFill);
border: none;
border-radius: 8px;
font-weight: 500;
cursor: pointer;
font-size: 14px;
transition: background-color 0.2s;
.k-tabstrip .k-content {
background: var(--card-card) !important;
border: none !important;
padding: 20px !important;
border-radius: 0 0 24px 24px !important;
}
.btn-save:hover {
background: var(--accent-secondary);
form {
background: transparent !important;
}
.container-fluid.details {
background: transparent !important;
margin-bottom: 2rem;
}
.row {
margin-bottom: 1rem;
align-items: center;
}
.windowInputLabel {
color: var(--text-primary) !important;
font-family: 'Montserrat', sans-serif !important;
font-weight: 500 !important;
font-size: 14px !important;
margin: 0 !important;
}
input[type="text"],
input[type="password"],
input[type="email"],
select,
textarea {
background: var(--button-secondaryFill) !important;
border: 1px solid var(--accent-15) !important;
border-radius: 12px !important;
padding: 12px 16px !important;
color: var(--text-primary) !important;
font-family: 'Montserrat', sans-serif !important;
font-size: 14px !important;
transition: all 0.2s ease !important;
}
input[type="text"]:focus,
input[type="password"]:focus,
input[type="email"]:focus,
select:focus,
textarea:focus {
outline: none !important;
border-color: var(--accent-accent) !important;
box-shadow: 0 0 0 3px var(--accent-15) !important;
}
.k-checkbox {
appearance: none;
width: 20px !important;
height: 20px !important;
border: 2px solid var(--accent-15) !important;
border-radius: 4px !important;
background: var(--button-secondaryFill) !important;
cursor: pointer !important;
position: relative !important;
transition: all 0.2s ease !important;
}
.k-checkbox:checked {
background: var(--accent-accent) !important;
border-color: var(--accent-accent) !important;
}
.k-checkbox:checked::after {
content: '✓';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: white;
font-size: 12px;
font-weight: bold;
}
.k-checkbox-label {
margin-left: 8px !important;
color: var(--text-primary) !important;
font-family: 'Montserrat', sans-serif !important;
cursor: pointer !important;
}
.k-button,
button {
background: var(--accent-accent) !important;
border: none !important;
border-radius: 12px !important;
padding: 12px 24px !important;
color: white !important;
font-family: 'Montserrat', sans-serif !important;
font-weight: 600 !important;
font-size: 14px !important;
cursor: pointer !important;
transition: all 0.2s ease !important;
box-shadow: 0px 1px var(--shadow-blur, 2px) 0px var(--accent-shadow) !important;
}
.k-button:hover,
button:hover {
background: var(--accent-secondary) !important;
transform: translateY(-1px) !important;
box-shadow: 0px 2px var(--shadow-blur, 4px) 0px var(--accent-shadow) !important;
}
.k-button:active,
button:active {
transform: translateY(0) !important;
}
@media (max-width: 768px) {
.content-container {
padding: 1rem;
}
.firka-header {
padding: 1rem;
}
.page-title {
font-size: 20px;
}
.k-content h4 {
font-size: 24px !important;
}
.k-tabstrip-items {
padding: 0 10px !important;
}
.k-tabstrip-items .k-item .k-link {
padding: 12px 16px !important;
font-size: 13px !important;
}
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(-10px);
transform: translateY(20px);
}
to {
opacity: 1;
@@ -368,70 +327,159 @@ body {
}
}
@keyframes dropdownShow {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
.k-tabstrip-wrapper {
animation: fadeIn 0.5s ease forwards;
}
@media (max-width: 768px) {
.kreta-header {
grid-template-columns: 1fr auto auto;
grid-template-areas:
"school toggle user"
"nav nav nav";
padding: 1rem;
gap: 0.5rem;
}
.k-widget,
.k-header {
background: var(--card-card) !important;
color: var(--text-primary) !important;
border: none !important;
}
.school-info {
grid-area: school;
}
.k-state-default {
background: transparent !important;
border: none !important;
}
.user-profile {
grid-area: user;
}
.app-grid {
grid-template-columns: 1fr;
}
.profile-tabs {
padding: 1rem;
}
.tab-content {
padding: 1rem;
}
.k-state-active {
background: transparent !important;
}
.material-icons-round {
font-size: 20px;
vertical-align: middle;
.k-overlay,
.k-window,
.k-notification {
display: none !important;
}
::-webkit-scrollbar {
width: 8px;
height: 8px;
.main-content .content-content {
display: block !important;
}
::-webkit-scrollbar-track {
background: var(--background);
div[style*="display:flex;justify-content:space-between"] {
display: flex !important;
justify-content: space-between !important;
align-items: center !important;
margin-top: 2rem !important;
padding-top: 2rem !important;
border-top: 1px solid var(--accent-15) !important;
}
::-webkit-scrollbar-thumb {
background: var(--text-secondary);
border-radius: 4px;
div[style*="display:flex;justify-content:space-between"] label {
color: var(--text-secondary) !important;
font-size: 12px !important;
font-style: italic !important;
margin: 0 !important;
}
::-webkit-scrollbar-thumb:hover {
background: var(--text-primary);
.hidden-contact-info {
display: none !important;
visibility: hidden !important;
}
.hidden-tab {
display: none !important;
}
#ProfilTab-3 {
padding: 20px;
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
min-height: 500px;
}
#ProfilTab-3 .container-fluid {
background: white;
border-radius: 12px;
padding: 30px;
margin-top: 20px;
}
#ProfilTab-3 h4 {
color: #2c3e50;
font-weight: 600;
margin-bottom: 25px;
padding-bottom: 10px;
border-bottom: 3px solid #3498db;
display: inline-block;
}
#ProfilTab-3 .row {
margin-bottom: 20px;
padding: 15px;
background: #f8f9fa;
border-radius: 8px;
border-left: 4px solid #3498db;
transition: all 0.3s ease;
}
#ProfilTab-3 .row:hover {
background: #e3f2fd;
transform: translateX(5px);
box-shadow: 0 2px 10px rgba(52, 152, 219, 0.2);
}
#ProfilTab-3 .windowInputLabel {
color: #34495e;
font-weight: 500;
font-size: 14px;
}
#ProfilTab-3 .k-button {
background: linear-gradient(135deg, #3498db, #2980b9);
border: none;
border-radius: 6px;
padding: 10px 20px;
color: white;
font-weight: 500;
transition: all 0.3s ease;
}
#ProfilTab-3 .k-button:hover {
background: linear-gradient(135deg, #2980b9, #1f5f8b);
transform: translateY(-2px);
box-shadow: 0 4px 15px rgba(52, 152, 219, 0.4);
}
#ProfilTab-3 .k-input {
border: 2px solid #e0e6ed;
border-radius: 6px;
padding: 10px;
transition: border-color 0.3s ease;
}
#ProfilTab-3 .k-input:focus {
border-color: #3498db;
box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.1);
}
#ProfilTab-3 .alert {
border-radius: 8px;
border: none;
padding: 15px;
margin: 15px 0;
}
#ProfilTab-3 .alert-info {
background: linear-gradient(135deg, #e3f2fd, #bbdefb);
color: #1565c0;
}
#ProfilTab-3 .alert-success {
background: linear-gradient(135deg, #e8f5e8, #c8e6c9);
color: #2e7d32;
}
#ProfilTab-3 .alert-warning {
background: linear-gradient(135deg, #fff3e0, #ffcc02);
color: #ef6c00;
}
.details .row {
border-bottom: none !important;
}

View File

@@ -1,396 +1,228 @@
(() => {
function createSecurityTab() {
return `
<div class="security-content">
<div class="setup-steps">
<div class="step-card">
<h3>1. lépés: Hitelesítési alkalmazás telepítése</h3>
<p>A kétfaktoros hitelesítés használatához telepítsen egy időalapú, egyszer használatos jelszó (TOTP) alkalmazást:</p>
<div class="app-grid">
<div class="app-section">
<h4>Android</h4>
<div class="app-links">
<a href="https://play.google.com/store/apps/details?id=hu.innobile.niszauth" target="_blank" class="app-link">
<span class="material-icons-round">download</span>
NISZ Hitelesítő
</a>
<a href="https://play.google.com/store/apps/details?id=com.azure.authenticator" target="_blank" class="app-link">
<span class="material-icons-round">download</span>
Microsoft Authenticator
</a>
<a href="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2" target="_blank" class="app-link">
<span class="material-icons-round">download</span>
Google Authenticator
</a>
</div>
</div>
<div class="app-section">
<h4>iPhone</h4>
<div class="app-links">
<a href="https://apps.apple.com/hu/app/nisz-hiteles%C3%ADt%C5%91/id1603444961" target="_blank" class="app-link">
<span class="material-icons-round">download</span>
NISZ Hitelesítő
</a>
<a href="https://apps.apple.com/hu/app/microsoft-authenticator/id983156458" target="_blank" class="app-link">
<span class="material-icons-round">download</span>
Microsoft Authenticator
</a>
<a href="https://apps.apple.com/hu/app/google-authenticator/id388497605" target="_blank" class="app-link">
<span class="material-icons-round">download</span>
Google Authenticator
</a>
</div>
</div>
</div>
</div>
<div class="step-card">
<h3>2. lépés: Kétfaktoros azonosítás beállítása</h3>
<div class="setup-form">
<div class="form-group">
<button type="button" class="btn-save" id="enable2FA">Kétfaktoros azonosítás bekapcsolása</button>
</div>
<div id="qrSetup" style="display: none;">
<div class="qr-container">
<img id="qrCode" alt="QR kód" style="display: none;">
<div class="setup-key">
<label class="form-label">Biztonsági kulcs:</label>
<div class="key-display">
<code id="secretKey"></code>
<button type="button" class="btn-copy" id="copyKey">
<span class="material-icons-round">content_copy</span>
</button>
</div>
</div>
</div>
<div class="form-group">
<label class="form-label" for="verificationCode">Ellenőrző kód</label>
<input type="text" class="form-control" id="verificationCode" maxlength="6" placeholder="123456">
<small class="form-text">Adja meg a hitelesítő alkalmazásban megjelenő 6 számjegyű kódot.</small>
</div>
<div class="form-group">
<button type="button" class="btn-save" id="verify2FA">Ellenőrzés és aktiválás</button>
</div>
</div>
</div>
</div>
<div class="step-card" id="backupCodes" style="display: none;">
<h3>3. lépés: Biztonsági kódok mentése</h3>
<p>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ó.</p>
<div class="backup-codes">
<pre id="backupCodesList"></pre>
<button type="button" class="btn-save" id="downloadCodes">
<span class="material-icons-round">download</span>
Kódok letöltése
</button>
</div>
</div>
</div>
</div>
`;
}
function createContactTab() {
return `
<div class="contact-form">
<div class="form-group">
<label class="form-label" for="email">E-mail cím</label>
<input type="email" class="form-control" id="email" required>
<small class="form-text">Az e-mail cím megadása a jelszó emlékeztető miatt szükséges.</small>
</div>
<div class="form-group">
<label class="form-label" for="phone">Telefonszám</label>
<input type="tel" class="form-control" id="phone" placeholder="+36 xx xxx xxxx">
<small class="form-text">A telefonszám megadása nem kötelező.</small>
</div>
<div class="form-group">
<button type="button" class="btn-save" id="saveContacts">Mentés</button>
</div>
</div>
`;
}
function createPasswordTab() {
return `
<div class="password-form">
<div class="form-group">
<label class="form-label" for="currentPassword">Jelenlegi jelszó</label>
<input type="password" class="form-control" id="currentPassword" required>
</div>
<div class="form-group">
<label class="form-label" for="newPassword">Új jelszó</label>
<input type="password" class="form-control" id="newPassword" required minlength="8">
<small class="form-text">A jelszónak legalább 8 karakter hosszúnak kell lennie.</small>
</div>
<div class="form-group">
<label class="form-label" for="confirmPassword">Új jelszó megerősítése</label>
<input type="password" class="form-control" id="confirmPassword" required>
</div>
<div class="form-group">
<button type="button" class="btn-save" id="savePassword">Jelszó módosítása</button>
</div>
</div>
`;
}
function createSettingsTab() {
return `
<div class="settings-form">
<div class="form-group">
<label class="form-label">
<input type="checkbox" id="hideTips">
Tippek elrejtése
</label>
<small class="form-text">A tippek megjelenítésének ki/be kapcsolása.</small>
</div>
<div class="form-group">
<button type="button" class="btn-save" id="saveSettings">Mentés</button>
</div>
</div>
`;
}
function setupContactForm() {
const form = document.querySelector('.contact-form');
if (!form) return;
const emailInput = form.querySelector('#email');
const phoneInput = form.querySelector('#phone');
const saveButton = form.querySelector('#saveContacts');
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ő!');
return;
(function() {
'use strict';
function hideLoadingScreen() {
const loadingElement = document.getElementById('KretaProgressBar');
if (loadingElement) {
loadingElement.style.display = 'none !important';
loadingElement.style.visibility = 'hidden';
loadingElement.style.opacity = '0';
loadingElement.remove();
}
if (email && !isValidEmail(email)) {
alert('Kérjük, adjon meg egy érvényes e-mail címet!');
return;
}
if (phone && !isValidPhone(phone)) {
alert('Kérjük, adjon meg egy érvényes telefonszámot!');
return;
}
try {
const response = await fetch('/Adminisztracio/Profil/SaveElerhetosegek', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'RequestVerificationToken': document.querySelector('input[name="__RequestVerificationToken"]').value
},
body: JSON.stringify({ email, phone })
});
if (response.ok) {
alert('Elérhetőségek sikeresen mentve!');
} else {
throw new Error('Hiba történt a mentés során.');
}
} 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.');
}
});
}
function isValidEmail(email) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
function isValidPhone(phone) {
return /^\+?[0-9\s-]{9,}$/.test(phone);
}
function setupEventListeners() {
document.querySelectorAll('.tab-header').forEach(header => {
header.addEventListener('click', () => {
document.querySelectorAll('.tab-header').forEach(h => h.classList.remove('active'));
document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active'));
header.classList.add('active');
const targetId = header.dataset.tab;
document.getElementById(`${targetId}-content`).classList.add('active');
const loadingElements = document.querySelectorAll('[class*="loading"], [id*="loading"], [class*="Loading"], [id*="Loading"]');
loadingElements.forEach(el => {
el.style.display = 'none !important';
el.style.visibility = 'hidden';
el.style.opacity = '0';
});
});
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('saveSettings')?.addEventListener('click', async () => {
const hideTips = document.getElementById('hideTips').checked;
try {
const response = await fetch('/Adminisztracio/Profil/SaveTippekBeallitasa', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'RequestVerificationToken': document.querySelector('input[name="__RequestVerificationToken"]').value
},
body: JSON.stringify({ hideTips })
});
if (response.ok) {
alert('Beállítások sikeresen mentve! A változtatások érvényesítéséhez jelentkezzen be újra.');
} else {
throw new Error('Hiba történt a mentés során.');
}
} 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.');
}
});
document.getElementById('savePassword')?.addEventListener('click', async () => {
const currentPassword = document.getElementById('currentPassword').value;
const newPassword = document.getElementById('newPassword').value;
const confirmPassword = document.getElementById('confirmPassword').value;
if (!currentPassword || !newPassword || !confirmPassword) {
alert('Kérjük, töltse ki az összes mezőt!');
return;
}
if (newPassword !== confirmPassword) {
alert('Az új jelszavak nem egyeznek!');
return;
}
if (newPassword.length < 8) {
alert('Az új jelszónak legalább 8 karakter hosszúnak kell lennie!');
return;
}
try {
const response = await fetch('/Adminisztracio/Profil/SaveJelszoModositas', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'RequestVerificationToken': document.querySelector('input[name="__RequestVerificationToken"]').value
},
body: JSON.stringify({
currentPassword,
newPassword,
confirmPassword
})
});
if (response.ok) {
alert('Jelszó sikeresen módosítva!');
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.');
}
} 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.');
}
});
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);
}
}
function createProfileHTML() {
return `
<div class="kreta-container">
${createTemplate.header()}
<main class="kreta-main">
<div class="card">
<h2>Profil beállítások</h2>
<div class="profile-tabs">
<div class="tab-headers">
<button class="tab-header active" data-tab="settings">Beállítások</button>
<button class="tab-header" data-tab="password">Jelszó módosítása</button>
<button class="tab-header" data-tab="security">Biztonsági beállítások</button>
<button class="tab-header" data-tab="contacts">Elérhetőségek</button>
</div>
<div id="settings-content" class="tab-content active">
${createSettingsTab()}
</div>
<div id="password-content" class="tab-content">
${createPasswordTab()}
</div>
<div id="security-content" class="tab-content">
${createSecurityTab()}
</div>
<div id="contacts-content" class="tab-content">
${createContactTab()}
</div>
</div>
</div>
</main>
</div>
`;
}
async function init() {
if (window.location.pathname.includes('/Adminisztracio/Profil')) {
function addBackButton() {
if (document.getElementById('firka-back-button')) {
return;
}
createTemplate.importFonts();
document.body.innerHTML = createProfileHTML();
setupUserDropdown();
setupMobileNavigation();
setupEventListeners();
setupContactForm();
}
const backButton = document.createElement('button');
backButton.id = 'firka-back-button';
backButton.innerHTML = '← Vissza';
backButton.style.cssText = `
position: static;
margin: 20px;
z-index: 100;
background-color: var(--card-background, #ffffff);
color: var(--text-primary, #333333);
border: 1px solid var(--border-color, #e0e0e0);
border-radius: 8px;
padding: 10px 16px;
font-family: 'Montserrat', sans-serif;
font-size: 14px;
font-weight: 500;
cursor: pointer;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
transition: all 0.2s ease;
display: inline-flex;
align-items: center;
gap: 8px;
width: auto;
`;
backButton.addEventListener('mouseenter', function() {
this.style.backgroundColor = 'var(--card-hover, #f5f5f5)';
this.style.transform = 'translateY(-1px)';
this.style.boxShadow = '0 4px 12px rgba(0, 0, 0, 0.15)';
});
backButton.addEventListener('mouseleave', function() {
this.style.backgroundColor = 'var(--card-background, #ffffff)';
this.style.transform = 'translateY(0)';
this.style.boxShadow = '0 2px 8px rgba(0, 0, 0, 0.1)';
});
backButton.addEventListener('click', function() {
window.history.back();
});
document.body.insertBefore(backButton, document.body.firstChild);
}
init();
function hideMainFooter2() {
const footer2 = document.querySelector('.main-footer2');
if (footer2) {
footer2.style.display = 'none';
}
}
function hideLakatImg() {
const lakatImg = document.querySelector('.lakatimg');
if (lakatImg) {
lakatImg.style.display = 'none';
}
}
function hideCustomUserSettingsTab() {
const firstTab = document.querySelector('#ProfilTab .k-tabstrip-items li[aria-controls="ProfilTab-1"]');
if (firstTab) {
firstTab.classList.add('hidden-tab');
}
const tabLinks = document.querySelectorAll('#ProfilTab .k-tabstrip-items .k-link');
tabLinks.forEach(link => {
if (link.textContent && link.textContent.includes('Egyedi felhasználó beállítások')) {
const parentTab = link.closest('li');
if (parentTab) {
parentTab.classList.add('hidden-tab');
}
}
});
const contentPanel = document.querySelector('#ProfilTab-1');
if (contentPanel) {
contentPanel.classList.add('hidden-tab');
}
}
function hideAdditionalContactInfo() {
function hideElementsWithText(text) {
const elements = document.querySelectorAll('h4');
elements.forEach(h4 => {
if (h4.textContent && h4.textContent.includes(text)) {
// Hide the parent row
let parent = h4.closest('.row');
if (parent) {
parent.classList.add('hidden-contact-info');
}
}
});
}
hideElementsWithText('TOVÁBBI E-MAIL ELÉRHETŐSÉGEK');
hideElementsWithText('TOVÁBBI TELEFONSZÁMOK');
const rows = document.querySelectorAll('.row');
rows.forEach(row => {
const h4Elements = row.querySelectorAll('h4');
h4Elements.forEach(h4 => {
if (h4.textContent &&
(h4.textContent.includes('TOVÁBBI E-MAIL ELÉRHETŐSÉGEK') ||
h4.textContent.includes('TOVÁBBI TELEFONSZÁMOK'))) {
row.classList.add('hidden-contact-info');
}
});
});
}
function init() {
hideLoadingScreen();
addBackButton();
hideAdditionalContactInfo();
hideCustomUserSettingsTab();
hideMainFooter2();
hideLakatImg();
const observer = new MutationObserver(() => {
hideAdditionalContactInfo();
hideCustomUserSettingsTab();
hideMainFooter2();
hideLakatImg();
});
observer.observe(document.body, {
childList: true,
subtree: true
});
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
window.addEventListener('load', init);
setTimeout(hideLoadingScreen, 1000);
let attempts = 0;
const maxAttempts = 20;
const aggressiveHide = setInterval(() => {
attempts++;
hideLoadingScreen();
if (attempts >= maxAttempts) {
clearInterval(aggressiveHide);
}
}, 500);
if (typeof MutationObserver !== 'undefined') {
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
mutation.addedNodes.forEach((node) => {
if (node.nodeType === 1) {
if (node.id === 'KretaProgressBar' ||
node.className && (node.className.includes('loading') || node.className.includes('Loading'))) {
node.style.display = 'none !important';
node.style.visibility = 'hidden';
node.style.opacity = '0';
if (node.parentNode) {
node.parentNode.removeChild(node);
}
}
}
});
});
});
observer.observe(document.body, {
childList: true,
subtree: true
});
}
})();

View File

@@ -1,380 +1,323 @@
@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;
padding: 0;
box-sizing:border-box;
margin:0;
padding:0;
}
body {
color: var(--text-primary);
background-color: var(--background) !important;
font-family: "Montserrat", serif !important;
min-height: 100vh;
font-size: 16px;
color:var(--text-primary);
background-color:var(--background) !important;
font-family:"Montserrat",serif !important;
min-height:100vh;
font-size:16px;
}
.kreta-container {
width: 100%;
min-height: 100vh;
display: flex;
flex-direction: column;
padding: clamp(1rem, 3vw, 2rem);
max-width: 1200px;
margin: 0 auto;
width:100%;
min-height:100vh;
display:flex;
flex-direction:column;
padding:clamp(1rem,3vw,2rem);
max-width:1200px;
margin:0 auto;
}
.kreta-header {
display: grid;
grid-template-columns: auto 1fr auto;
align-items: center;
gap: 2rem;
margin-bottom: clamp(2rem, 5vw, 3rem);
display:grid;
grid-template-columns:auto 1fr auto;
align-items:center;
gap:2rem;
margin-bottom:clamp(2rem,5vw,3rem);
}
.school-info {
display: flex;
flex-direction: column;
gap: 0.5rem;
display:flex;
flex-direction:column;
gap:0.5rem;
}
.logo-text {
display: flex;
align-items: center;
font-size: 1.5rem;
font-weight: 600;
color: var(--text-primary);
margin: 0;
display:flex;
align-items:center;
font-size:1.5rem;
font-weight:600;
color:var(--text-primary);
margin:0;
}
.logo {
width: 32px;
height: 32px;
border-radius: 8px;
margin-right: 0.75rem;
width:32px;
height:32px;
border-radius:8px;
margin-right:0.75rem;
}
.school-details {
color: var(--text-secondary);
font-size: 1.1rem;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 300px;
padding-left: 2px;
color:var(--text-secondary);
font-size:1.1rem;
white-space:nowrap;
overflow:hidden;
text-overflow:ellipsis;
max-width:300px;
padding-left:2px;
}
.user-profile {
justify-self: flex-end;
text-align: right;
justify-self:flex-end;
text-align:right;
}
.user-info {
display: flex;
flex-direction: column;
align-items: flex-end;
gap: 4px;
display:flex;
flex-direction:column;
align-items:flex-end;
gap:4px;
}
.user-name, .role-text {
color: var(--text-primary);
font-family: Montserrat;
font-weight: 600;
.user-name,.role-text {
color:var(--text-primary);
font-family:Montserrat;
font-weight:600;
}
.user-name {
font-size: 16px;
font-size:16px;
}
.role-text {
font-size: 20px;
text-align: center;
font-size:20px;
text-align:center;
}
.logout-timer, .role-description {
color: var(--text-secondary);
font-family: Figtree;
font-weight: 500;
line-height: 130%;
.logout-timer,.role-description {
color:var(--text-secondary);
font-family:Figtree;
font-weight:500;
line-height:130%;
}
.logout-timer {
font-size: 14px;
text-align: right;
font-size:14px;
text-align:right;
}
.role-description {
font-size: 16px;
text-align: center;
font-size:16px;
text-align:center;
}
.role-container {
flex: 1;
display: flex;
align-items: center;
flex:1;
display:flex;
align-items:center;
}
.role-grid {
display: grid;
grid-template-columns: 1fr minmax(300px, 1fr);
gap: 1.5rem;
width: 100%;
display:grid;
grid-template-columns:1fr minmax(300px,1fr);
gap:1.5rem;
width:100%;
}
.side-roles {
display: grid;
grid-template-rows: 1fr auto;
gap: 1.5rem;
display:grid;
grid-template-rows:1fr auto;
gap:1.5rem;
}
.role-card {
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 1.5rem;
padding: 2rem;
background: var(--card-card);
border-radius: 1rem;
cursor: pointer;
text-align: center;
border: none;
box-shadow: 0px 1px var(--shadow-blur) 0px var(--accent-shadow);
transition: all 0.2s ease;
width:100%;
display:flex;
flex-direction:column;
align-items:center;
justify-content:center;
gap:1.5rem;
padding:2rem;
background:var(--card-card);
border-radius:1rem;
cursor:pointer;
text-align:center;
border:none;
box-shadow:0px 1px var(--shadow-blur) 0px var(--accent-shadow);
transition:all 0.2s ease;
}
.role-card[data-role="Ellenorzo"] {
height: 480px;
background: var(--card-card);
height:480px;
background:var(--card-card);
}
.role-card[data-role="DKT"] {
height: 280px;
background: var(--card-card);
box-shadow: 0px 1px var(--shadow-blur) 0px var(--accent-shadow);
height:280px;
background:var(--card-card);
box-shadow:0px 1px var(--shadow-blur) 0px var(--accent-shadow);
}
.logout-card {
height: 180px;
background: var(--error-card);
box-shadow: 0px 1px var(--shadow-blur) 0px var(--accent-shadow);
height:180px;
background:var(--error-card);
box-shadow:0px 1px var(--shadow-blur) 0px var(--accent-shadow);
}
.role-icon {
width: 48px;
height: 48px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 12px;
transition: transform 0.2s ease;
background: var(--accent-15);
width:48px;
height:48px;
display:flex;
align-items:center;
justify-content:center;
border-radius:12px;
transition:transform 0.2s ease;
background:var(--accent-15);
}
.role-icon img {
width: 32px;
height: 32px;
transition: transform 0.2s ease;
width:32px;
height:32px;
transition:transform 0.2s ease;
}
.role-card[data-role="DKT"] .role-icon {
background: var(--warning-15);
background:var(--warning-15);
}
.logout-card .role-icon {
background: var(--error-15);
background:var(--error-15);
}
.role-icon img {
width: 32px;
height: 32px;
transition: transform 0.2s ease;
width:32px;
height:32px;
transition:transform 0.2s ease;
}
.role-card:hover {
transform: translateY(-2px);
transform:translateY(-2px);
}
.role-card:hover .role-icon img {
transform: scale(1.2);
transform:scale(1.2);
}
.role-card:active {
transform: translateY(1px);
box-shadow: 0px 0px var(--shadow-blur) 0px var(--accent-shadow);
transform:translateY(1px);
box-shadow:0px 0px var(--shadow-blur) 0px var(--accent-shadow);
}
.role-card[data-role="Ellenorzo"]:hover {
background-color: var(--accent-15);
background-color:var(--accent-15);
}
.role-card[data-role="DKT"]:hover {
background-color: var(--accent-15);
background-color:var(--accent-15);
}
.role-card[data-role="DKT"]:active {
box-shadow: 0px 0px var(--shadow-blur) 0px var(--accent-shadow);
box-shadow:0px 0px var(--shadow-blur) 0px var(--accent-shadow);
}
.logout-card:hover {
background-color: var(--error-15);
background-color:var(--error-15);
}
.logout-card:active {
box-shadow: 0px 0px var(--shadow-blur) 0px var(--error-shadow);
box-shadow:0px 0px var(--shadow-blur) 0px var(--error-shadow);
}
:root[data-theme="light-blue"] .role-card:hover,
:root[data-theme="light-green"] .role-card:hover {
box-shadow: 0px 1px var(--shadow-blur) 0px var(--accent-shadow);
box-shadow:0px 1px var(--shadow-blur) 0px var(--accent-shadow);
}
::-webkit-scrollbar {
width: 8px;
height: 8px;
width:8px;
height:8px;
}
::-webkit-scrollbar-track {
background: var(--background);
background:var(--background);
}
::-webkit-scrollbar-thumb {
background: var(--text-secondary);
border-radius: 4px;
background:var(--text-secondary);
border-radius:4px;
}
::-webkit-scrollbar-thumb:hover {
background: var(--text-primary);
background:var(--text-primary);
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
opacity:0;
transform:translateY(-10px);
}
@media (max-width: 1200px) {
to {
opacity:1;
transform:translateY(0);
}
}@media (max-width:1200px) {
.role-grid {
grid-template-columns: 1fr 280px;
}
grid-template-columns:1fr 280px;
}
@media (max-width: 900px) {
}@media (max-width:900px) {
.role-grid {
grid-template-columns: 1fr;
}
.side-roles {
grid-template-columns: 1fr 1fr;
grid-template-rows: none;
}
.role-card[data-role="Ellenorzo"],
.role-card[data-role="DKT"],
.logout-card {
height: 300px;
}
grid-template-columns:1fr;
}
@media (max-width: 768px) {
.side-roles {
grid-template-columns:1fr 1fr;
grid-template-rows:none;
}
.role-card[data-role="Ellenorzo"],.role-card[data-role="DKT"],.logout-card {
height:300px;
}
}@media (max-width:768px) {
.kreta-header {
grid-template-columns: 1fr auto;
gap: 1rem;
}
.school-info {
max-width: 60%;
}
.school-details {
max-width: 200px;
font-size: 1rem;
}
.logo-text {
font-size: 1.2rem;
}
.logo {
width: 24px;
height: 24px;
}
grid-template-columns:1fr auto;
gap:1rem;
}
@media (max-width: 640px) {
.school-info {
max-width:60%;
}
.school-details {
max-width:200px;
font-size:1rem;
}
.logo-text {
font-size:1.2rem;
}
.logo {
width:24px;
height:24px;
}
}@media (max-width:640px) {
.kreta-container {
padding: 1rem;
}
.side-roles {
grid-template-columns: 1fr;
gap: 1rem;
}
.role-card {
padding: 1.5rem;
}
.role-icon {
width: 40px;
height: 40px;
}
.role-icon img {
width: 24px;
height: 24px;
}
.role-text {
font-size: 1.2rem;
}
.role-description {
font-size: 1rem;
}
padding:1rem;
}
.side-roles {
grid-template-columns:1fr;
gap:0.75rem;
}
.role-card {
padding:1rem;
gap:1rem;
}
.role-card[data-role="Ellenorzo"],.role-card[data-role="DKT"],.logout-card {
height:200px;
}
.role-icon {
width:40px;
height:40px;
}
.role-icon img {
width:24px;
height:24px;
}
.role-text {
font-size:1.5rem;
}
.role-description {
font-size:1.2rem;
}
.user-name {
font-size:18px;
}
.logout-timer {
font-size:16px;
}
}
@media (max-width:480px) {
.kreta-container {
padding:0.75rem;
}
.kreta-header {
margin-bottom:1.5rem;
}
.side-roles {
gap:0.5rem;
}
.role-card {
padding:0.75rem;
gap:0.75rem;
}
.role-card[data-role="Ellenorzo"],.role-card[data-role="DKT"],.logout-card {
height:160px;
}
.role-text {
font-size:1.6rem;
line-height:1.2;
}
.role-description {
font-size:1.3rem;
line-height:1.3;
}
.user-name {
font-size:20px;
}
.logout-timer {
font-size:18px;
}
.school-details {
font-size:0.9rem;
}
.logo-text {
font-size:1.1rem;
}
}

View File

@@ -1,17 +1,17 @@
(() => {
const startLogoutTimer = () => {
let timeLeft = 45 * 60;
const timerElement = document.getElementById('logoutTimer');
const timerElement = document.getElementById("logoutTimer");
const updateTimer = () => {
const minutes = Math.floor(timeLeft / 60);
const seconds = timeLeft % 60;
if (timerElement) {
timerElement.textContent = `${minutes}:${seconds.toString().padStart(2, '0')}`;
timerElement.textContent = `${minutes}:${seconds.toString().padStart(2, "0")}`;
}
timeLeft <= 0 ? window.location.href = '/Home/Logout' : timeLeft--;
timeLeft <= 0 ? (window.location.href = "/Home/Logout") : timeLeft--;
};
updateTimer();
@@ -20,21 +20,24 @@
const handleRoleChange = async (role) => {
try {
const response = await fetch('/Adminisztracio/SzerepkorValaszto/ChangeRole', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ Role: role })
});
const response = await fetch(
"/Adminisztracio/SzerepkorValaszto/ChangeRole",
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ Role: role }),
},
);
const data = await response.json();
if (data.Success) {
window.location.assign(data.Url);
} else {
throw new Error('Role change failed');
throw new Error("Role change failed");
}
} catch (error) {
console.error('Error changing role:', error);
alert('Hiba történt a szerepkör váltása közben.');
console.error("Error changing role:", error);
alert(LanguageManager.t("roleselect.role_change_error"));
}
};
@@ -43,11 +46,11 @@
<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">
<img src=${chrome.runtime.getURL("images/firka_logo.png")} alt="Firka" class="logo">
Firka
</p>
<div class="school-details">
<span>${schoolCode || ''} - ${fullSchoolName || 'Iskola'}</span>
<span>${schoolCode || ""} - ${fullSchoolName || "Iskola"}</span>
</div>
</div>
<div class="user-profile">
@@ -63,11 +66,11 @@
<div class="main-role">
<div class="role-card" data-role="Ellenorzo">
<div class="role-icon">
<img src="${chrome.runtime.getURL('icons/naplo.svg')}" alt="Napló ikon">
<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>
@@ -75,21 +78,21 @@
<div class="side-roles">
<div class="role-card" data-role="DKT">
<div class="role-icon">
<img src="${chrome.runtime.getURL('icons/dkt.svg')}" alt="DKT ikon">
<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>
<div class="role-card logout-card">
<div class="role-icon">
<img src="${chrome.runtime.getURL('icons/logout.svg')}" alt="Kijelentkezés ikon">
<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>
@@ -100,65 +103,77 @@
const transformRoleSelectPage = async () => {
try {
if (document.readyState !== 'complete') {
await new Promise(resolve => window.addEventListener('load', resolve));
if (document.readyState !== "complete") {
await new Promise((resolve) =>
window.addEventListener("load", resolve),
);
}
const schoolNameEl = document.querySelector('.IntezmenyNev');
const schoolName = schoolNameEl?.textContent.trim() || 'Iskola neve';
const [schoolCode, fullSchoolName] = schoolName.split(' - ');
const schoolNameEl = document.querySelector(".IntezmenyNev");
const schoolName = schoolNameEl?.textContent.trim() || "Iskola neve";
const [schoolCode, fullSchoolName] = schoolName.split(" - ");
const schoolSubdomain = window.location.hostname.split('.')[0];
const schoolSubdomain = window.location.hostname.split(".")[0];
const userNameEl = document.querySelector('.UserName');
const userName = userNameEl?.textContent.trim() || 'Felhasználónév';
const userNameEl = document.querySelector(".UserName");
const userName =
userNameEl?.textContent.trim() || LanguageManager.t("common.username");
if (schoolCode && fullSchoolName) {
cookieManager.set('schoolCode', schoolCode);
cookieManager.set('schoolName', fullSchoolName);
cookieManager.set('schoolSubdomain', schoolSubdomain);
await storageManager.set("schoolCode", schoolCode);
await storageManager.set("schoolName", fullSchoolName);
await storageManager.set("schoolSubdomain", schoolSubdomain);
}
if (userName) {
cookieManager.set('userName', userName);
await storageManager.set("userName", userName);
}
document.body.innerHTML = '';
const parser = new DOMParser();
const doc = parser.parseFromString(createHTML(
schoolCode,
fullSchoolName,
userName,
), 'text/html');
const tempDiv = doc.body;
while (tempDiv.firstChild) {
document.body.appendChild(tempDiv.firstChild);
}
document.body.innerHTML = createHTML(schoolCode, fullSchoolName, userName);
createTemplate.importFonts();
const timerInterval = startLogoutTimer();
document.querySelector('.role-card[data-role="Ellenorzo"]')?.addEventListener('click', () => {
handleRoleChange('Ellenorzo');
});
document
.querySelector('.role-card[data-role="Ellenorzo"]')
?.addEventListener("click", () => {
handleRoleChange("Ellenorzo");
});
document.querySelector('.role-card[data-role="DKT"]')?.addEventListener('click', () => {
window.location.href = '/Adminisztracio/BelepesKezelo/DKTTanuloOrGondviselo';
});
document.querySelector('.logout-card')?.addEventListener('click', async () => {
try {
clearInterval(timerInterval);
await fetch('/Home/LogOut', { method: 'POST' });
} catch {
window.location.replace("https://idp.e-kreta.hu/Account/Logout");
} finally {
window.location.replace("https://idp.e-kreta.hu/Account/Logout");
}
});
document
.querySelector('.role-card[data-role="DKT"]')
?.addEventListener("click", () => {
window.location.href =
"/Adminisztracio/BelepesKezelo/DKTTanuloOrGondviselo";
});
document
.querySelector(".logout-card")
?.addEventListener("click", async () => {
try {
clearInterval(timerInterval);
await fetch("/Home/LogOut", { method: "POST" });
} catch {
window.location.replace("https://idp.e-kreta.hu/Account/Logout");
} finally {
window.location.replace("https://idp.e-kreta.hu/Account/Logout");
}
});
} catch (error) {
console.error('Error transforming page:', error);
console.error("Error transforming page:", error);
}
};
if (window.location.href.includes('/Adminisztracio/BelepesKezelo')) {
if (window.location.href.includes("/Adminisztracio/BelepesKezelo")) {
transformRoleSelectPage();
}
})();
})();

View File

@@ -1,247 +1,196 @@
@font-face {
font-family: 'Montserrat';
src: url('chrome-extension://__MSG_@@extension_id__/fonts/Montserrat-Regular.woff2') format('woff2');
font-weight: 400;
font-style: normal;
header,footer,.page-title,.card-kreta {
display:none !important;
}
@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;
}
/* Hide original elements */
header, footer, .page-title, .card-kreta {
display: none !important;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
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;
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%);
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);
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;
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;
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;
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);
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%;
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;
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;
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;
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;
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;
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;
li.dropdown-item:hover,li.dropdown-item:focus {
background-color:var(--accent-15) !important;
color:var(--text-primary) !important;
text-decoration:none !important;
}
a.dropdown-item:hover, a.dropdown-item:focus {
background-color: #00000000 !important;
a.dropdown-item:hover,a.dropdown-item:focus {
background-color:#00000000 !important;
text-decoration:none !important;
}
.dropdown-item.active {
background-color: var(--accent-accent) !important;
color: white !important;
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;
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;
background-color:var(--accent-secondary) !important;
transform:translateY(-1px) !important;
}
.btn-kreta:active {
transform: translateY(1px) !important;
transform:translateY(1px) !important;
}
.btn-kreta:disabled {
background-color: var(--text-teritary) !important;
cursor: not-allowed !important;
transform: none !important;
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%;
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;
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;
color:var(--accent-accent);
text-decoration:underline;
}
/* Responsive adjustments */
@media (max-width: 576px) {
@media (max-width:576px) {
.firka-search-wrapper {
width: 95%;
}
.search-title {
font-size: 20px;
}
.btn-kreta {
padding: 10px 20px !important;
font-size: 14px !important;
}
width:95%;
}
.search-title {
font-size:20px;
}
.btn-kreta {
padding:10px 20px !important;
font-size:14px !important;
}
}

View File

@@ -1,7 +1,7 @@
function initializeTransformation() {
const form = document.querySelector('form');
const autocomplete = document.querySelector('.autocomplete');
const form = document.querySelector("form");
const autocomplete = document.querySelector(".autocomplete");
if (form && autocomplete) {
applyFirkaStyling();
} else {
@@ -9,46 +9,67 @@ function initializeTransformation() {
}
}
document.addEventListener('DOMContentLoaded', () => {
document.addEventListener("DOMContentLoaded", () => {
setTimeout(initializeTransformation, 1000);
});
if (document.readyState === 'complete' || document.readyState === 'interactive') {
if (
document.readyState === "complete" ||
document.readyState === "interactive"
) {
setTimeout(initializeTransformation, 1000);
}
function applyFirkaStyling() {
async function applyFirkaStyling() {
try {
const theme = cookieManager.get('themePreference') || localStorage.getItem('themePreference') || 'light-green';
document.documentElement.setAttribute('data-theme', theme);
if (typeof loadingScreen !== 'undefined') {
const theme =
await storageManager.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');
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";
const logoText = document.createElement('p');
logoText.className = 'logo-text';
const logoImg = document.createElement('img');
logoImg.src = chrome.runtime.getURL('images/firka_logo.png');
logoImg.alt = 'Firka';
logoImg.className = 'logo';
logoText.appendChild(logoImg);
logoText.appendChild(document.createTextNode('Firka'));
const searchTitle = document.createElement('h1');
searchTitle.className = 'search-title';
searchTitle.textContent = 'Válassz iskolát';
firkaHeader.appendChild(logoText);
firkaHeader.appendChild(searchTitle);
const formContainer = document.createElement("div");
formContainer.className = "firka-form-container";
const firkaFooter = document.createElement("div");
firkaFooter.className = "firka-footer";
const privacyLink = document.createElement('a');
privacyLink.href = 'https://tudasbazis.ekreta.hu/pages/viewpage.action?pageId=4064926';
privacyLink.target = '_blank';
privacyLink.className = 'privacy-link';
privacyLink.textContent = 'Adatkezelési tájékoztató';
firkaFooter.appendChild(privacyLink);
const existingWrapper = document.querySelector(".firka-search-wrapper");
if (existingWrapper) {
existingWrapper.remove();
}
@@ -61,33 +82,32 @@ function applyFirkaStyling() {
document.body.appendChild(searchWrapper);
setupAutocompleteListeners();
if (redirectButton) {
redirectButton.addEventListener('click', function(event) {
redirectButton.addEventListener("click", function (event) {
if (!instituteCodeInput.value) {
event.preventDefault();
alert('Kérjük, válasszon egy intézményt a folytatáshoz!');
alert(LanguageManager.t("search.select_institution"));
}
});
}
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');
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');
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', () => {
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;
}
@@ -95,10 +115,10 @@ function setupAutocompleteListeners() {
});
}
});
observer.observe(document.body, {
childList: true,
subtree: true
subtree: true,
});
}
}
@@ -112,7 +132,7 @@ function observeAutocompleteValue(valueInput, button) {
observer.observe(valueInput, {
attributes: true,
attributeFilter: ['value']
attributeFilter: ["value"],
});
const checkInterval = setInterval(() => {
@@ -121,4 +141,4 @@ function observeAutocompleteValue(valueInput, button) {
clearInterval(checkInterval);
}
}, 500);
}
}

View File

@@ -1,276 +1,606 @@
* {
box-sizing: border-box;
margin: 0;
padding: 0;
box-sizing:border-box;
margin:0;
padding:0;
}
body {
width: 400px;
margin: 0;
padding: 0;
color: var(--text-primary);
background-color: var(--background) !important;
font-family: "Montserrat", serif;
font-size: 14px;
width:400px;
margin:0;
padding:0;
color:var(--text-primary);
background-color:var(--background) !important;
font-family:"Montserrat",serif;
font-size:14px;
}
* {
scrollbar-width: none;
-ms-overflow-style: none;
scrollbar-width:none;
-ms-overflow-style:none;
}
*::-webkit-scrollbar {
display: none;
display:none;
}
.popup-container {
padding: 16px;
padding:16px;
}
.popup-header {
text-align: center;
margin-bottom: 16px;
text-align:center;
margin-bottom:16px;
}
.logo-text {
color:var(--text-primary);
font-size:20px;
font-weight:600;
margin:8px 0;
display:flex;
align-items:center;
justify-content:center;
}
.logo {
width:20px;
border-radius:6px;
margin-right:8px;
}
.settings-card,.about-card,.support-card {
background:var(--card-card);
border-radius:16px;
padding:16px;
margin-bottom:16px;
box-shadow:0px 1px var(--shadow-blur) 0px var(--accent-shadow);
}
h2 {
font-size:16px;
font-weight:600;
color:var(--text-primary);
margin-bottom:12px;
background-color:var(--card-card);
}
.settings-group {
display:flex;
flex-direction:column;
gap:16px;
}
.setting-section {
display:flex;
flex-direction:column;
gap:12px;
}
.setting-header {
display:flex;
align-items:center;
gap:8px;
font-weight:600;
font-size:16px;
color:var(--text-primary);
}
.logo-text {
color: var(--text-primary);
font-size: 20px;
font-weight: 600;
margin: 8px 0;
.custom-theme-editor .setting-header {
justify-content:space-between;
}
.setting-header > div:first-child {
display:flex;
align-items:center;
gap:8px;
}
.theme-grid,.language-grid {
display:grid;
grid-template-columns:repeat(2,1fr);
gap:12px;
}
.theme-option,.language-option {
background:none;
border:none;
padding:0;
cursor:pointer;
display:flex;
flex-direction:column;
align-items:center;
gap:8px;
transition:transform 0.2s ease;
}
.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;
border-radius:8px;
overflow:hidden;
position:relative;
}
.theme-option.disabled {
opacity:0.5;
cursor:not-allowed;
position:relative;
}
.theme-option.disabled::after {
content:"";
position:absolute;
top:0;
left:0;
width:100%;
height:100%;
background-color:rgba(0,0,0,0.1);
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;
font-family: "Montserrat", serif;
}
.theme-preview.light-green {
background:#FAFFF0;
}
.theme-preview.light-green .preview-header {
background:#F3FBDE;
}
.theme-preview.light-green .preview-card {
background:#FEFFFD;
}
.theme-preview.dark-green {
background:#0D1202;
}
.theme-preview.dark-green .preview-header {
background:#141905;
}
.theme-preview.dark-green .preview-card {
background:#20290b;
}
.preview-header {
height:30%;
width:100%;
}
.preview-content {
padding:8px;
}
.preview-card {
height:30px;
border-radius:4px;
}
.theme-name {
color:var(--text-primary);
font-size:12px;
font-weight:500;
}
.about-content,.support-content {
color:var(--text-secondary);
font-size:14px;
line-height:1.5;
}
.about-content p,.support-content p {
margin-bottom:12px;
}
.about-links {
display: flex;
flex-direction: column;
gap: 8px;
margin-top: 8px;
}
.about-link {
display:inline-flex;
align-items:center;
gap:6px;
color:var(--accent-accent);
text-decoration:none;
font-weight:500;
transition:color 0.2s;
padding: 4px 0;
}
.about-link:hover {
color:var(--accent-secondary);
}
.support-buttons {
display:flex;
gap:8px;
margin-top:12px;
}
.support-button {
flex:1;
display:flex;
align-items:center;
justify-content:center;
gap:6px;
padding:8px;
background:var(--accent-15);
color:var(--text-primary);
text-decoration:none;
border-radius:8px;
font-weight:500;
transition:all 0.2s;
}
.support-button:hover {
background:var(--accent-accent);
color:var(--button-secondaryFill);
}
.popup-footer {
text-align:center;
margin-top:16px;
}
.version-info {
color:var(--text-secondary);
font-size:12px;
text-align:center;
}
.theme-editor-controls {
display: flex;
gap: 8px;
}
.icon-btn {
background: var(--button-secondaryFill);
border: none !important;
border-radius: 8px;
width: 36px;
height: 36px;
display: flex;
align-items: center;
justify-content: center;
}
.logo {
width: 20px;
border-radius: 6px;
margin-right: 8px;
}
.settings-card,
.about-card,
.support-card {
background: var(--card-card);
border-radius: 16px;
padding: 16px;
margin-bottom: 16px;
box-shadow: 0px 1px var(--shadow-blur) 0px var(--accent-shadow);
}
h2 {
font-size: 16px;
font-weight: 600;
color: var(--text-primary);
margin-bottom: 12px;
background-color: var(--card-card);
}
.settings-group {
display: flex;
flex-direction: column;
gap: 16px;
}
.setting-section {
display: flex;
flex-direction: column;
gap: 12px;
}
.setting-header {
display: flex;
align-items: center;
gap: 8px;
color: var(--text-primary);
font-weight: 500;
}
.theme-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 12px;
}
.theme-option {
background: none;
border: none;
padding: 0;
cursor: pointer;
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
transition: transform 0.2s ease;
}
.theme-option:hover {
transform: translateY(-2px);
}
.theme-option.active .theme-preview {
outline: 2px solid var(--accent-accent);
outline-offset: 2px;
}
.theme-preview {
width: 100%;
height: 100px;
border-radius: 8px;
overflow: hidden;
position: relative;
}
.theme-option.disabled {
opacity: 0.5;
cursor: not-allowed;
position: relative;
transition: all 0.2s ease;
color: var(--text-primary);
}
.theme-option.disabled::after {
content: "";
position: absolute;
.icon-btn:hover {
background: var(--accent-accent);
color: white;
transform: translateY(-1px);
}
.icon-btn .material-icons-round {
font-size: 18px;
}
.btn-primary, .btn-secondary {
padding: 8px 16px;
border-radius: 8px;
border: none;
font-family: inherit;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s ease;
}
.btn-primary {
background: var(--accent-accent);
color: white;
}
.btn-primary:hover {
background: var(--accent-secondary);
}
.btn-secondary {
background: var(--button-secondaryFill);
color: var(--text-primary);
border: 1px solid var(--accent-15);
}
.btn-secondary:hover {
background: var(--accent-15);
}
.custom-themes-list {
display: flex;
flex-direction: column;
gap: 8px;
}
.custom-theme-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px;
background: var(--button-secondaryFill);
border-radius: 8px;
border: 1px solid var(--accent-15);
}
.custom-theme-info {
display: flex;
align-items: center;
gap: 12px;
}
.custom-theme-preview-mini {
width: 40px;
height: 24px;
border-radius: 4px;
display: flex;
overflow: hidden;
}
.custom-theme-preview-mini .color-strip {
flex: 1;
height: 100%;
}
.custom-theme-name {
font-weight: 500;
color: var(--text-primary);
}
.custom-theme-actions {
display: flex;
gap: 4px;
}
.action-btn {
padding: 6px;
border: none;
border-radius: 4px;
background: var(--accent-15);
color: var(--text-primary);
cursor: pointer;
font-size: 16px;
transition: background 0.2s ease;
display: flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
}
.action-btn .material-icons-round {
font-size: 16px;
}
.action-btn:hover {
background: var(--accent-accent);
color: white;
}
.action-btn.delete:hover {
background: var(--error-accent);
}
.theme-editor-modal, .import-modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.1);
border-radius: 8px;
}
.theme-preview.light-blue {
background: #DAE4F7;
}
.theme-preview.light-blue .preview-header {
background: #EDF3FF;
}
.theme-preview.light-blue .preview-card {
background: #FBFCFF;
}
.theme-preview.light-green {
background: #FAFFF0;
}
.theme-preview.light-green .preview-header {
background: #F3FBDE;
}
.theme-preview.light-green .preview-card {
background: #FEFFFD;
}
.theme-preview.dark-blue {
background: #070A0E;
}
.theme-preview.dark-blue .preview-header {
background: #0F131B;
}
.theme-preview.dark-blue .preview-card {
background: #131822;
}
.theme-preview.dark-green {
background: #0D1202;
}
.theme-preview.dark-green .preview-header {
background: #141905;
}
.theme-preview.dark-green .preview-card {
background: #20290b;
}
.preview-header {
height: 30%;
width: 100%;
}
.preview-content {
padding: 8px;
}
.preview-card {
height: 30px;
border-radius: 4px;
}
.theme-name {
color: var(--text-primary);
font-size: 12px;
font-weight: 500;
}
.about-content,
.support-content {
color: var(--text-secondary);
font-size: 14px;
line-height: 1.5;
}
.about-content p,
.support-content p {
margin-bottom: 12px;
}
.github-link {
display: inline-flex;
align-items: center;
gap: 6px;
color: var(--accent-accent);
text-decoration: none;
font-weight: 500;
transition: color 0.2s;
}
.github-link:hover {
color: var(--accent-secondary);
}
.support-buttons {
display: flex;
gap: 8px;
margin-top: 12px;
}
.support-button {
flex: 1;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
gap: 6px;
padding: 8px;
background: var(--accent-15);
z-index: 1000;
}
.modal-content {
background: var(--card-card);
border-radius: 16px;
width: 90%;
max-width: 500px;
max-height: 90vh;
overflow-y: auto;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
}
.modal-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 16px;
border-bottom: 1px solid var(--accent-15);
}
.modal-header h3 {
margin: 0;
color: var(--text-primary);
text-decoration: none;
border-radius: 8px;
font-weight: 500;
transition: all 0.2s;
font-size: 18px;
}
.support-button:hover {
background: var(--accent-accent);
color: var(--button-secondaryFill);
}
.popup-footer {
text-align: center;
margin-top: 16px;
}
.version-info {
.close-modal {
background: none;
border: none;
color: var(--text-secondary);
cursor: pointer;
padding: 4px;
border-radius: 4px;
transition: background 0.2s ease;
}
.close-modal:hover {
background: var(--accent-15);
}
.modal-body {
padding: 16px;
}
.modal-footer {
display: flex;
gap: 8px;
justify-content: flex-end;
padding: 16px;
border-top: 1px solid var(--accent-15);
}
.theme-name-input {
margin-bottom: 20px;
}
.theme-name-input label {
display: block;
margin-bottom: 4px;
color: var(--text-primary);
font-weight: 500;
}
.theme-name-input input {
width: 100%;
padding: 8px 12px;
border: 1px solid var(--accent-15);
border-radius: 8px;
background: var(--button-secondaryFill);
color: var(--text-primary);
font-family: inherit;
}
.color-groups {
display: flex;
flex-direction: column;
gap: 20px;
margin-bottom: 20px;
}
.color-group h4 {
margin: 0 0 12px 0;
color: var(--text-primary);
font-size: 16px;
}
.color-inputs {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
}
.color-input-group {
display: flex;
flex-direction: column;
gap: 4px;
}
.color-input-group label {
color: var(--text-secondary);
font-size: 12px;
font-weight: 500;
}
.color-input-group input[type="color"] {
width: 100%;
height: 40px;
border: 1px solid var(--accent-15);
border-radius: 8px;
background: var(--card-card);
cursor: pointer;
padding: 4px;
}
.color-input-group input[type="color"]::-webkit-color-swatch-wrapper {
padding: 0;
border-radius: 6px;
overflow: hidden;
}
.color-input-group input[type="color"]::-webkit-color-swatch {
border: none;
border-radius: 6px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.theme-preview-container h4 {
margin: 0 0 12px 0;
color: var(--text-primary);
font-size: 16px;
}
.custom-theme-preview {
border: 1px solid var(--accent-15);
border-radius: 8px;
overflow: hidden;
height: 120px;
position: relative;
}
.custom-theme-preview .preview-header {
height: 30px;
background: var(--preview-background, var(--background));
border-bottom: 1px solid var(--accent-15);
}
.custom-theme-preview .preview-content {
padding: 12px;
background: var(--preview-background, var(--background));
}
.custom-theme-preview .preview-card {
background: var(--preview-card, var(--card-card));
border-radius: 8px;
padding: 12px;
height: 100%;
display: flex;
flex-direction: column;
gap: 8px;
}
.preview-text-primary {
color: var(--preview-text-primary, var(--text-primary));
font-weight: 600;
font-size: 14px;
}
.preview-text-secondary {
color: var(--preview-text-secondary, var(--text-secondary));
font-size: 12px;
}
.preview-accent {
background: var(--preview-accent, var(--accent-accent));
color: white;
padding: 6px 12px;
border-radius: 6px;
font-size: 12px;
font-weight: 500;
display: inline-block;
margin-top: 4px;
}
.import-input {
display: flex;
flex-direction: column;
gap: 8px;
}
.import-input label {
color: var(--text-primary);
font-weight: 500;
}
.import-input textarea {
width: 100%;
padding: 12px;
border: 1px solid var(--accent-15);
border-radius: 8px;
background: var(--button-secondaryFill);
color: var(--text-primary);
font-family: inherit;
resize: vertical;
min-height: 80px;
}
.material-icons-round {
font-size: 18px;
vertical-align: middle;
}
font-size:18px;
vertical-align:middle;
}

View File

@@ -17,29 +17,20 @@
<div class="popup-container">
<header class="popup-header">
<p class="logo-text">
<img src="https://i.imgur.com/WugwlzI.png" alt="Firka" class="logo">
<img src="../images/firka_logo.png" alt="Firka" class="logo">
Firxa
</p>
</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">
<div class="theme-preview light-blue">
<div class="preview-header"></div>
<div class="preview-content">
<div class="preview-card"></div>
</div>
</div>
<span class="theme-name">Világos Kék</span>
</button>
<button class="theme-option" data-theme="light-green">
<div class="theme-preview light-green">
<div class="preview-header"></div>
@@ -47,17 +38,9 @@
<div class="preview-card"></div>
</div>
</div>
<span class="theme-name">Világos Zöld</span>
</button>
<button class="theme-option" data-theme="dark-blue">
<div class="theme-preview dark-blue">
<div class="preview-header"></div>
<div class="preview-content">
<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.light_green">Világos Zöld</span>
</button>
<button class="theme-option" data-theme="dark-green">
<div class="theme-preview dark-green">
<div class="preview-header"></div>
@@ -65,7 +48,143 @@
<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">
<div>
<span class="material-icons-round">brush</span>
<span data-i18n="settings.custom_theme.title">Egyéni Téma</span>
</div>
<div class="theme-editor-controls">
<button class="icon-btn" id="createCustomTheme" title="Új Téma Létrehozása">
<span class="material-icons-round">add</span>
</button>
<button class="icon-btn" id="importTheme" title="Téma Importálása">
<span class="material-icons-round">file_download</span>
</button>
</div>
</div>
<div class="custom-theme-editor">
<div class="custom-themes-list" id="customThemesList">
</div>
<div class="theme-editor-modal" id="themeEditorModal" style="display: none;">
<div class="modal-content">
<div class="modal-header">
<h3 data-i18n="settings.custom_theme.editor_title">Téma Szerkesztő</h3>
<button class="close-modal" id="closeThemeEditor">
<span class="material-icons-round">close</span>
</button>
</div>
<div class="modal-body">
<div class="theme-name-input">
<label for="themeName" data-i18n="settings.custom_theme.name">Téma neve:</label>
<input type="text" id="themeName" placeholder="Saját téma" maxlength="30">
</div>
<div class="color-groups">
<div class="color-group">
<h4 data-i18n="settings.custom_theme.background">Háttér</h4>
<div class="color-inputs">
<div class="color-input-group">
<label for="background" data-i18n="settings.custom_theme.main_background">Fő háttér:</label>
<input type="color" id="background" value="#DAE4F7">
</div>
<div class="color-input-group">
<label for="cardCard" data-i18n="settings.custom_theme.card_background">Kártya háttér:</label>
<input type="color" id="cardCard" value="#EDF3FF">
</div>
</div>
</div>
<div class="color-group">
<h4 data-i18n="settings.custom_theme.text">Szöveg</h4>
<div class="color-inputs">
<div class="color-input-group">
<label for="textPrimary" data-i18n="settings.custom_theme.primary_text">Elsődleges szöveg:</label>
<input type="color" id="textPrimary" value="#050B15">
</div>
<div class="color-input-group">
<label for="textSecondary" data-i18n="settings.custom_theme.secondary_text">Másodlagos szöveg:</label>
<input type="color" id="textSecondary" value="#050B15">
</div>
</div>
</div>
<div class="color-group">
<h4 data-i18n="settings.custom_theme.accent">Kiemelő színek</h4>
<div class="color-inputs">
<div class="color-input-group">
<label for="accentAccent" data-i18n="settings.custom_theme.primary_accent">Elsődleges kiemelő:</label>
<input type="color" id="accentAccent" value="#3673EE">
</div>
<div class="color-input-group">
<label for="accentSecondary" data-i18n="settings.custom_theme.secondary_accent">Másodlagos kiemelő:</label>
<input type="color" id="accentSecondary" value="#1C469A">
</div>
</div>
</div>
</div>
<div class="theme-preview-container">
<h4 data-i18n="settings.custom_theme.preview">Előnézet</h4>
<div class="custom-theme-preview" id="customThemePreview">
<div class="preview-content">
<div class="preview-card">
<div class="preview-text-primary">Elsődleges szöveg</div>
<div class="preview-text-secondary">Másodlagos szöveg</div>
<div class="preview-accent">Kiemelő szín</div>
</div>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn-secondary" id="cancelThemeEdit" data-i18n="settings.custom_theme.cancel">Mégse</button>
<button class="btn-primary" id="saveTheme" data-i18n="settings.custom_theme.save">Mentés</button>
</div>
</div>
</div>
<div class="import-modal" id="importModal" style="display: none;">
<div class="modal-content">
<div class="modal-header">
<h3 data-i18n="settings.custom_theme.import_title">Téma Importálása</h3>
<button class="close-modal" id="closeImportModal">
<span class="material-icons-round">close</span>
</button>
</div>
<div class="modal-body">
<div class="import-input">
<label for="themeImportString" data-i18n="settings.custom_theme.import_string">Téma azonosítója:</label>
<textarea id="themeImportString" placeholder="Illeszd be ide a téma azonosítóját..." rows="4"></textarea>
</div>
</div>
<div class="modal-footer">
<button class="btn-secondary" id="cancelImport" data-i18n="settings.custom_theme.cancel">Mégse</button>
<button class="btn-primary" id="confirmImport" data-i18n="settings.custom_theme.import">Importálás</button>
</div>
</div>
</div>
</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>
@@ -73,33 +192,45 @@
</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>
<a href="https://github.com/QwIT-Development/" target="_blank" class="github-link">
<span class="material-icons-round">code</span>
GitHub
</a>
<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>
<div class="about-links">
<a href="https://firka.app" target="_blank" class="about-link">
<span class="material-icons-round">language</span>
<span>Weboldal</span>
</a>
<a href="https://github.com/QwIT-Development/" target="_blank" class="about-link">
<span class="material-icons-round">code</span>
<span data-i18n="settings.about.github">GitHub</span>
</a>
<a href="https://discord.gg/firka-1111649116020285532" target="_blank" class="about-link">
<span class="material-icons-round">forum</span>
<span>Discord</span>
</a>
</div>
</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>
</div>
<footer class="popup-footer">
<div class="version-info" id="version">v1.1.0</div>
<div class="version-info" id="version">v1.3.0</div>
</footer>
</div>
<script src="index.js"></script>
<script src="../tools/storageManager.js"></script>
<script src="../global/language.js"></script>
<script src="index.js"></script>
</body>
</html>

View File

@@ -1,187 +1,570 @@
document.addEventListener('DOMContentLoaded', async () => {
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}=`;
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;
document.addEventListener("DOMContentLoaded", async () => {
while (typeof window.LanguageManager === "undefined") {
await new Promise((resolve) => setTimeout(resolve, 10));
}
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 getCurrentTheme() {
return localStorage.getItem('themePreference') ||
getCookie('themePreference') ||
'light-green';
return (
localStorage.getItem("themePreference") ||
"light-green"
);
}
function updateThemeButtons(currentTheme) {
document.querySelectorAll('.theme-option').forEach(button => {
const theme = button.dataset.theme;
button.classList.toggle('active', theme === currentTheme);
});
updateThemeAvailability();
document.querySelectorAll(".theme-option").forEach((button) => {
const theme = button.dataset.theme;
button.classList.toggle("active", theme === currentTheme);
});
}
function getCurrentLanguage() {
return (
localStorage.getItem("languagePreference") ||
"hu"
);
}
function updateLanguageButtons(currentLanguage) {
document.querySelectorAll(".language-option").forEach((button) => {
const language = button.dataset.language;
button.classList.toggle("active", language === currentLanguage);
});
}
async function applyLanguage(language) {
localStorage.setItem("languagePreference", language);
updateLanguageButtons(language);
const tabs = await chrome.tabs.query({});
tabs.forEach((tab) => {
chrome.tabs
.sendMessage(tab.id, {
action: "changeLanguage",
language: language,
})
.catch(() => {});
});
}
async function applyTheme(theme) {
setCookie('themePreference', theme);
localStorage.setItem('themePreference', theme);
document.documentElement.setAttribute('data-theme', theme);
updateThemeButtons(theme);
const tabs = await chrome.tabs.query({});
tabs.forEach(tab => {
chrome.tabs.sendMessage(tab.id, {
action: 'changeTheme',
theme: theme
}).catch(() => {
console.log('Tab not ready for theme change:', tab.id);
});
});
localStorage.setItem("themePreference", theme);
document.documentElement.setAttribute("data-theme", theme);
updateThemeButtons(theme);
const tabs = await chrome.tabs.query({});
tabs.forEach((tab) => {
chrome.tabs
.sendMessage(tab.id, {
action: "changeTheme",
theme: theme,
})
.catch(() => {});
});
}
const themeButtons = document.querySelectorAll('.theme-option');
themeButtons.forEach(button => {
button.addEventListener('click', () => {
const theme = button.dataset.theme;
if (button.hasAttribute('disabled')) {
alert('Ez a téma jelenleg nem elérhető.');
return;
}
applyTheme(theme);
});
const themeButtons = document.querySelectorAll(".theme-option");
themeButtons.forEach((button) => {
button.addEventListener("click", () => {
const theme = button.dataset.theme;
if (button.hasAttribute("disabled")) {
alert(
window.LanguageManager.t("common.warning") +
": " +
window.LanguageManager.t("settings.theme_not_available"),
);
return;
}
applyTheme(theme);
});
});
const languageButtons = document.querySelectorAll(".language-option");
languageButtons.forEach((button) => {
button.addEventListener("click", () => {
const language = button.dataset.language;
applyLanguage(language);
});
});
let initialTheme = getCurrentTheme();
if (isThemeDisabled(initialTheme)) {
initialTheme = 'light-green';
}
updateThemeAvailability();
await applyTheme(initialTheme);
const initialLanguage = getCurrentLanguage();
updateLanguageButtons(initialLanguage);
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.action === 'themeChanged') {
updateThemeButtons(message.theme);
document.documentElement.setAttribute('data-theme', message.theme);
}
if (message.action === "themeChanged") {
updateThemeButtons(message.theme);
document.documentElement.setAttribute("data-theme", message.theme);
}
});
const manifest = chrome.runtime.getManifest();
const versionElement = document.getElementById('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 = 'Kék témák feloldva! 🎉';
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) => {
button.addEventListener("mouseover", () => {
if (!button.hasAttribute("disabled")) {
button.style.transform = "translateY(-2px)";
}
});
button.addEventListener("mouseout", () => {
button.style.transform = "translateY(0)";
});
});
themeButtons.forEach(button => {
button.addEventListener('mouseover', () => {
if (!button.hasAttribute('disabled')) {
button.style.transform = 'translateY(-2px)';
}
});
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);
});
class CustomThemeManager {
constructor() {
this.customThemes = this.loadCustomThemes();
this.currentEditingTheme = null;
this.initializeEventListeners();
this.renderCustomThemes();
}
loadCustomThemes() {
const stored = localStorage.getItem('customThemes');
return stored ? JSON.parse(stored) : [];
}
saveCustomThemes() {
localStorage.setItem('customThemes', JSON.stringify(this.customThemes));
}
generateThemeId() {
const existingIds = this.customThemes.map(theme => theme.id);
let counter = 1;
let id;
button.addEventListener('mouseout', () => {
button.style.transform = 'translateY(0)';
do {
id = 'custom-' + counter;
counter++;
} while (existingIds.includes(id));
return id;
}
createTheme(name, colors) {
const theme = {
id: this.generateThemeId(),
name: name,
colors: colors,
created: new Date().toISOString()
};
this.customThemes.push(theme);
this.saveCustomThemes();
return theme;
}
updateTheme(id, name, colors) {
const index = this.customThemes.findIndex(t => t.id === id);
if (index !== -1) {
this.customThemes[index] = {
...this.customThemes[index],
name: name,
colors: colors,
updated: new Date().toISOString()
};
this.saveCustomThemes();
return this.customThemes[index];
}
return null;
}
deleteTheme(id) {
this.customThemes = this.customThemes.filter(t => t.id !== id);
this.saveCustomThemes();
const currentTheme = getCurrentTheme();
if (currentTheme === id) {
this.applyTheme('light-green');
}
}
applyTheme(themeId) {
if (themeId.startsWith('custom-')) {
const theme = this.customThemes.find(t => t.id === themeId);
if (theme) {
this.applyCustomThemeColors(theme.colors);
localStorage.setItem("themePreference", themeId);
}
} else {
document.documentElement.setAttribute('data-theme', themeId);
localStorage.setItem("themePreference", themeId);
}
updateThemeButtons(themeId);
}
applyCustomThemeColors(colors) {
const root = document.documentElement;
root.removeAttribute('data-theme');
Object.entries(colors).forEach(([key, value]) => {
const cssVar = this.convertToCSSVariable(key);
root.style.setProperty(cssVar, value);
});
});
});
}
convertToCSSVariable(key) {
return '--' + key.replace(/([A-Z])/g, '-$1').toLowerCase();
}
exportTheme(themeId) {
const theme = this.customThemes.find(t => t.id === themeId);
if (!theme) return null;
const colors = theme.colors;
const colorString = [
colors.background.substring(1),
colors.cardCard.substring(1),
colors.textPrimary.substring(1),
colors.textSecondary.substring(1),
colors.accentAccent.substring(1),
colors.accentSecondary.substring(1)
].join('');
return theme.name + '|' + colorString;
}
importTheme(themeString) {
try {
const parts = themeString.split('|');
if (parts.length !== 2) {
throw new Error('Invalid theme format - expected name|colors');
}
const [themeName, colorString] = parts;
if (colorString.length !== 36) {
throw new Error('Invalid theme format - expected 36 hex characters');
}
const hexPattern = /^[0-9A-Fa-f]{36}$/;
if (!hexPattern.test(colorString)) {
throw new Error('Invalid hex color format');
}
const colors = [];
for (let i = 0; i < 36; i += 6) {
colors.push(colorString.substring(i, i + 6));
}
const themeColors = {
background: '#' + colors[0],
cardCard: '#' + colors[1],
textPrimary: '#' + colors[2],
textSecondary: '#' + colors[3],
accentAccent: '#' + colors[4],
accentSecondary: '#' + colors[5]
};
const importedTheme = this.createTheme(
themeName + ' (Importált)',
themeColors
);
this.renderCustomThemes();
return importedTheme;
} catch (error) {
console.error('Theme import failed:', error);
return null;
}
}
renderCustomThemes() {
/*const container = document.getElementById('customThemesList');
container.innerHTML = '';
if (this.customThemes.length === 0) {
const emptyMessage = window.translations?.settings?.custom_theme?.no_themes || 'Még nincsenek egyéni témák';
container.innerHTML = `<p style="color: var(--text-secondary); font-size: 12px; text-align: center; padding: 20px;">${emptyMessage}</p>`;
return;
}
this.customThemes.forEach(theme => {
const themeElement = this.createThemeElement(theme);
container.appendChild(themeElement);
});*/
}
createThemeElement(theme) {
const element = document.createElement('div');
element.className = 'custom-theme-item';
const colors = theme.colors;
const previewColors = [
colors.background || '#DAE4F7',
colors.cardCard || '#EDF3FF',
colors.accentAccent || '#3673EE',
colors.textPrimary || '#050B15'
];
const translations = window.translations?.settings?.custom_theme || {};
element.innerHTML = `
<div class="custom-theme-info">
<div class="custom-theme-preview-mini">
${previewColors.map(color => `<div class="color-strip" style="background: ${color}"></div>`).join('')}
</div>
<span class="custom-theme-name">${theme.name}</span>
</div>
<div class="custom-theme-actions">
<button class="action-btn apply" data-theme-id="${theme.id}" title="${translations.apply || 'Alkalmaz'}">
<span class="material-icons-round">check</span>
</button>
<button class="action-btn edit" data-theme-id="${theme.id}" title="${translations.edit || 'Szerkeszt'}">
<span class="material-icons-round">edit</span>
</button>
<button class="action-btn export" data-theme-id="${theme.id}" title="${translations.export || 'Export'}">
<span class="material-icons-round">file_upload</span>
</button>
<button class="action-btn delete" data-theme-id="${theme.id}" title="${translations.delete || 'Töröl'}">
<span class="material-icons-round">delete</span>
</button>
</div>
`;
element.querySelector('.apply').addEventListener('click', () => {
this.applyTheme(theme.id);
});
element.querySelector('.edit').addEventListener('click', () => {
this.editTheme(theme.id);
});
element.querySelector('.export').addEventListener('click', () => {
this.showExportModal(theme.id);
});
element.querySelector('.delete').addEventListener('click', () => {
const confirmMessage = window.translations?.settings?.custom_theme?.delete_confirm || `Biztosan törölni szeretnéd a "${theme.name}" témát?`;
if (confirm(confirmMessage.replace('{name}', theme.name))) {
this.deleteTheme(theme.id);
this.renderCustomThemes();
}
});
return element;
}
editTheme(themeId) {
const theme = this.customThemes.find(t => t.id === themeId);
if (!theme) return;
this.currentEditingTheme = theme;
this.openThemeEditor(theme);
}
openThemeEditor(theme = null) {
const modal = document.getElementById('themeEditorModal');
const nameInput = document.getElementById('themeName');
if (theme) {
nameInput.value = theme.name;
this.loadThemeColorsToEditor(theme.colors);
} else {
nameInput.value = '';
this.loadDefaultColorsToEditor();
}
this.updatePreview();
modal.style.display = 'flex';
}
loadThemeColorsToEditor(colors) {
Object.entries(colors).forEach(([key, value]) => {
const input = document.getElementById(key);
if (input) {
input.value = value;
}
});
}
loadDefaultColorsToEditor() {
const defaults = {
background: '#DAE4F7',
cardCard: '#EDF3FF',
textPrimary: '#050B15',
textSecondary: '#050B15',
accentAccent: '#3673EE',
accentSecondary: '#1C469A'
};
this.loadThemeColorsToEditor(defaults);
}
updatePreview() {
const preview = document.getElementById('customThemePreview');
const colors = this.getColorsFromEditor();
preview.style.setProperty('--preview-background', colors.background);
preview.style.setProperty('--preview-card', colors.cardCard);
preview.style.setProperty('--preview-text-primary', colors.textPrimary);
preview.style.setProperty('--preview-text-secondary', colors.textSecondary);
preview.style.setProperty('--preview-accent', colors.accentAccent);
}
getColorsFromEditor() {
return {
background: document.getElementById('background').value,
cardCard: document.getElementById('cardCard').value,
textPrimary: document.getElementById('textPrimary').value,
textSecondary: document.getElementById('textSecondary').value,
accentAccent: document.getElementById('accentAccent').value,
accentSecondary: document.getElementById('accentSecondary').value
};
}
saveThemeFromEditor() {
const name = document.getElementById('themeName').value.trim();
if (!name) {
alert('Add meg a téma nevét!');
return;
}
const colors = this.getColorsFromEditor();
if (this.currentEditingTheme) {
this.updateTheme(this.currentEditingTheme.id, name, colors);
} else {
this.createTheme(name, colors);
}
this.closeThemeEditor();
this.renderCustomThemes();
}
closeThemeEditor() {
document.getElementById('themeEditorModal').style.display = 'none';
this.currentEditingTheme = null;
}
showExportModal(themeId) {
const exportString = this.exportTheme(themeId);
if (exportString) {
const theme = this.customThemes.find(t => t.id === themeId);
prompt(`${theme.name} téma export azonosítója (másold ki):`, exportString);
}
}
showImportModal() {
document.getElementById('importModal').style.display = 'flex';
}
closeImportModal() {
document.getElementById('importModal').style.display = 'none';
document.getElementById('themeImportString').value = '';
}
importThemeFromModal() {
const importString = document.getElementById('themeImportString').value.trim();
if (!importString) {
alert('Add meg az azonosítót!');
return;
}
const importedTheme = this.importTheme(importString);
if (importedTheme) {
alert(`"${importedTheme.name}" téma sikeresen importálva!`);
this.closeImportModal();
} else {
alert('Hiba történt az importálás során. Ellenőrizd az azonosítót!');
}
}
initializeEventListeners() {
/*document.getElementById('createCustomTheme').addEventListener('click', () => {
this.openThemeEditor();
});
document.getElementById('importTheme').addEventListener('click', () => {
this.showImportModal();
});
document.getElementById('closeThemeEditor').addEventListener('click', () => {
this.closeThemeEditor();
});
document.getElementById('cancelThemeEdit').addEventListener('click', () => {
this.closeThemeEditor();
});
document.getElementById('saveTheme').addEventListener('click', () => {
this.saveThemeFromEditor();
});
document.getElementById('closeImportModal').addEventListener('click', () => {
this.closeImportModal();
});
document.getElementById('cancelImport').addEventListener('click', () => {
this.closeImportModal();
});
document.getElementById('confirmImport').addEventListener('click', () => {
this.importThemeFromModal();
});
const colorInputs = ['background', 'cardCard', 'textPrimary', 'textSecondary', 'accentAccent', 'accentSecondary'];
colorInputs.forEach(inputId => {
document.getElementById(inputId).addEventListener('input', () => {
this.updatePreview();
});
});
document.getElementById('themeEditorModal').addEventListener('click', (e) => {
if (e.target.id === 'themeEditorModal') {
this.closeThemeEditor();
}
});
document.getElementById('importModal').addEventListener('click', (e) => {
if (e.target.id === 'importModal') {
this.closeImportModal();
}
});*/
}
}
const customThemeManager = new CustomThemeManager();
const originalGetCurrentTheme = getCurrentTheme;
getCurrentTheme = function() {
const theme = originalGetCurrentTheme();
if (theme && theme.startsWith('custom-')) {
return theme;
}
return theme;
};
const originalUpdateThemeButtons = updateThemeButtons;
updateThemeButtons = function(currentTheme) {
originalUpdateThemeButtons(currentTheme);
document.querySelectorAll('.custom-theme-item').forEach(item => {
const applyBtn = item.querySelector('.apply');
const themeId = applyBtn.dataset.themeId;
item.classList.toggle('active', themeId === currentTheme);
});
};
const currentTheme = getCurrentTheme();
if (currentTheme && currentTheme.startsWith('custom-')) {
customThemeManager.applyTheme(currentTheme);
}
});

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

106
tools/background.js Normal file
View File

@@ -0,0 +1,106 @@
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
(async () => {
try {
switch (request.action) {
case 'storage_set':
await handleStorageSet(request.key, request.value);
sendResponse({ success: true });
break;
case 'storage_get':
const value = await handleStorageGet(request.key, request.defaultValue);
sendResponse({ success: true, value: value });
break;
case 'storage_remove':
await handleStorageRemove(request.key);
sendResponse({ success: true });
break;
case 'storage_clear':
await handleStorageClear();
sendResponse({ success: true });
break;
default:
console.warn('[Background] Unknown action:', request.action);
sendResponse({ success: false, error: 'Unknown action' });
}
} catch (error) {
console.error('[Background] Error handling message:', error);
sendResponse({ success: false, error: error.message });
}
})();
return true;
});
async function handleStorageSet(key, value) {
try {
await chrome.storage.sync.set({ [key]: value });
} catch (error) {
console.error(`[Background] Failed to save ${key}:`, error);
throw error;
}
}
async function handleStorageGet(key, defaultValue = null) {
try {
const result = await chrome.storage.sync.get(key);
const value = result[key];
return value !== undefined ? value : defaultValue;
} catch (error) {
console.error(`[Background] Failed to get ${key}:`, error);
throw error;
}
}
async function handleStorageRemove(key) {
try {
await chrome.storage.sync.remove(key);
} catch (error) {
console.error(`[Background] Failed to remove ${key}:`, error);
throw error;
}
}
async function handleStorageClear() {
try {
const allData = await chrome.storage.sync.get(null);
const firkaKeys = Object.keys(allData).filter(key => key.startsWith('firka_'));
if (firkaKeys.length > 0) {
await chrome.storage.sync.remove(firkaKeys);
}
} catch (error) {
console.error('[Background] Failed to clear storage:', error);
throw error;
}
}
chrome.storage.onChanged.addListener((changes, namespace) => {
if (namespace === 'sync') {
const firkaChanges = Object.keys(changes).filter(key => key.startsWith('firka_'));
if (firkaChanges.length > 0) {
notifyContentScriptsOfChanges(changes);
}
}
});
async function notifyContentScriptsOfChanges(changes) {
try {
const tabs = await chrome.tabs.query({ url: 'https://*.e-kreta.hu/*' });
for (const tab of tabs) {
try {
await chrome.tabs.sendMessage(tab.id, {
action: 'storage_changed',
changes: changes
});
} catch (error) {
console.debug(`[Background] Could not notify tab ${tab.id}:`, error.message);
}
}
} catch (error) {
console.error('[Background] Failed to notify content scripts:', error);
}
}

View File

@@ -1,25 +0,0 @@
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`;
}
}

View File

@@ -1,26 +1,27 @@
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') || ''
}
};
async header() {
const data = {
schoolInfo: {
name: await storageManager.get("schoolName", "OM azonosító - Iskola neve"),
id: await storageManager.get("schoolCode", ""),
},
userData: {
name: await storageManager.get("userName", "Felhasználónév"),
time:
document.querySelector(".usermenu_timer")?.textContent?.trim() ||
"45:00",
},
};
const schoolNameFull = `${data.schoolInfo.id} - ${data.schoolInfo.name}`;
const shortenedSchoolName = helper.shortenSchoolName(schoolNameFull);
const schoolSubdomain = await storageManager.get("schoolSubdomain", "");
const baseUrl = schoolSubdomain ? `https://${schoolSubdomain}.e-kreta.hu` : "";
const schoolNameFull = `${data.schoolInfo.id} - ${data.schoolInfo.name}`;
const shortenedSchoolName = helper.shortenSchoolName(schoolNameFull);
const element = `<header class="kreta-header">
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">
<img src="${chrome.runtime.getURL("images/firka_logo.png")}" alt="Firka" class="logo">
Firka
</p>
<div class="school-details" title="${schoolNameFull}">
@@ -28,7 +29,7 @@ const createTemplate = {
</div>
</div>
<button class="nav-toggle" aria-label="Navigáció megnyitása">
<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>
@@ -36,25 +37,25 @@ const createTemplate = {
<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="Kezdőlap">
Kezdőlap
<a href="${baseUrl}/Intezmeny/Faliujsag" data-page="dashboard" class="nav-item ${location.pathname == "/Intezmeny/Faliujsag" ? "active" : ""}">
<img src="${chrome.runtime.getURL("icons/dashboard-" + (location.pathname == "/Intezmeny/Faliujsag" ? "active" : "inactive") + ".svg")}" alt="${LanguageManager.t("navigation.dashboard")}">
${LanguageManager.t("navigation.dashboard")}
</a>
<a href="/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="Jegyek">
Jegyek
<a href="${baseUrl}/TanuloErtekeles/Osztalyzatok" data-page="grades" class="nav-item ${location.pathname == "/TanuloErtekeles/Osztalyzatok" ? "active" : ""}">
<img src="${chrome.runtime.getURL("icons/grades-" + (location.pathname == "/TanuloErtekeles/Osztalyzatok" ? "active" : "inactive") + ".svg")}" alt="${LanguageManager.t("navigation.grades")}">
${LanguageManager.t("navigation.grades")}
</a>
<a href="/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="Órarend">
Órarend
<a href="${baseUrl}/Orarend/InformaciokOrarend" data-page="timetable" class="nav-item ${location.pathname == "/Orarend/InformaciokOrarend" ? "active" : ""}">
<img src="${chrome.runtime.getURL("icons/timetable-" + (location.pathname == "/Orarend/InformaciokOrarend" ? "active" : "inactive") + ".svg")}" alt="${LanguageManager.t("navigation.timetable")}">
${LanguageManager.t("navigation.timetable")}
</a>
<a href="/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="Mulasztások">
Mulasztások
<a href="${baseUrl}/Hianyzas/Hianyzasok" data-page="absences" class="nav-item ${location.pathname == "/Hianyzas/Hianyzasok" ? "active" : ""}">
<img src="${chrome.runtime.getURL("icons/absences-" + (location.pathname == "/Hianyzas/Hianyzasok" ? "active" : "inactive") + ".svg")}" alt="${LanguageManager.t("navigation.absences")}">
${LanguageManager.t("navigation.absences")}
</a>
<a href="/Tanulo/TanuloHaziFeladat" data-page="other" class="nav-item ${( location.pathname == '/Tanulo/TanuloHaziFeladat' ? 'active' : '')}">
<img src="${chrome.runtime.getURL('icons/others.svg')}" alt="Egyéb">
Egyéb
<a href="https://eugyintezes.e-kreta.hu/api/bff/login" data-page="messages" class="nav-item ${location.pathname == "/" ? "active" : ""}">
<img src="${chrome.runtime.getURL("icons/messages-" + (location.pathname == "/uzenetek" ? "active" : "inactive") + ".svg")}" alt="${LanguageManager.t("navigation.messages")}">
${LanguageManager.t("navigation.messages")}
</a>
</div>
</nav>
@@ -67,69 +68,52 @@ const createTemplate = {
</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 href="${baseUrl}/Adminisztracio/Profil" data-page="profile" class="dropdown-item">
<img src="${chrome.runtime.getURL("icons/profile.svg")}" alt="${LanguageManager.t("navigation.profile")}">
${LanguageManager.t("navigation.profile")}
</a>
<a href="#" class="dropdown-item" id="settingsBtn">
<img src="${chrome.runtime.getURL('icons/settings.svg')}" alt="Beállítások">
Beállítások
<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="Kijelentkezés">
Kijelentkezés
<a href="${baseUrl}/Home/Logout" data-page="logout" class="dropdown-item">
<img src="${chrome.runtime.getURL("icons/logout.svg")}" alt="${LanguageManager.t("navigation.logout")}">
${LanguageManager.t("navigation.logout")}
</a>
</div>
</div>
</header>`
</header>`;
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--;
}
};
const startTime = parseInt(data.userData.time?.match(/\d+/)?.[0] || "45");
let timeLeft = startTime * 60;
setInterval(updateTimer, 1000);
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")}`;
}
return element;
},
if (timeLeft <= 0) {
window.location.href = "/Home/Logout";
} else {
timeLeft--;
}
};
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/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);
});
}
}
setInterval(updateTimer, 1000);
return element;
},
};
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');
});
});
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");
});
});

View File

@@ -1,40 +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));
}
shortenSchoolName(name, maxLength = 50) {
if (!name) return "";
if (name.length <= maxLength) return name;
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;
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;
},
};

View File

@@ -1,59 +1,53 @@
.modalBckgroundMain {
display: none !important;
display:none !important;
}
body:not(.loaded) {
opacity: 0 !important;
visibility: hidden !important;
height: 100vh !important;
overflow: hidden !important;
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;
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;
display:flex;
flex-direction:column;
align-items:center;
gap:1rem;
}
.loading-logo {
width: 48px;
height: 48px;
border-radius: 16px;
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;
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%;
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%;
}

View File

@@ -1,47 +1,77 @@
const loadingScreen = {
show() {
document.body.classList.remove('loaded');
const existingLoadingScreen = document.querySelector('.loading-screen');
if (existingLoadingScreen) return;
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">Betöltés alatt...</div>
<div class="loading-text2">Kis türelmet!</div>
</div>
`;
document.body.appendChild(loadingScreen);
document.body.classList.add('loaded');
},
const loadingScreen = document.createElement("div");
loadingScreen.className = "loading-screen";
const loadingContent = document.createElement('div');
loadingContent.className = 'loading-content';
const loadingLogo = document.createElement('img');
loadingLogo.src = chrome.runtime.getURL("images/loading.gif");
loadingLogo.alt = 'Firka';
loadingLogo.className = 'loading-logo';
const loadingText = document.createElement('div');
loadingText.className = 'loading-text';
loadingText.setAttribute('data-i18n', 'loading.text');
loadingText.textContent = 'Betöltés alatt...';
const loadingText2 = document.createElement('div');
loadingText2.className = 'loading-text2';
loadingText2.setAttribute('data-i18n', 'loading.subtext');
loadingText2.textContent = 'Kis türelmet!';
loadingContent.appendChild(loadingLogo);
loadingContent.appendChild(loadingText);
loadingContent.appendChild(loadingText2);
loadingScreen.appendChild(loadingContent);
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';
loadingScreen.addEventListener('transitionend', () => {
loadingScreen.remove();
});
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();
}
}
}
};
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);
}
});
loadingScreen.addEventListener("transitionend", removeLoadingScreen, {
once: true,
});
setTimeout(removeLoadingScreen, 500);
}
},
};
if (urls.some(url => url.includes(location.pathname))) {
loadingScreen.show();
}
});
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);
}
});
}
const currentUrl = location.href;
const shouldShowLoading = urls.some((urlPattern) => {
const regex = new RegExp(urlPattern.replace(/\*/g, '.*'));
return regex.test(currentUrl);
});
if (shouldShowLoading) {
loadingScreen.show();
}
});

144
tools/storageManager.js Normal file
View File

@@ -0,0 +1,144 @@
const storageManager = {
isExtensionContext() {
return typeof chrome !== 'undefined' && chrome.runtime && chrome.runtime.id;
},
isContentScript() {
return typeof chrome !== 'undefined' && chrome.runtime && chrome.runtime.sendMessage;
},
async set(key, value) {
const prefixedKey = `firka_${key}`;
try {
if (this.isExtensionContext()) {
await chrome.storage.sync.set({ [prefixedKey]: value });
} else if (this.isContentScript()) {
return new Promise((resolve, reject) => {
chrome.runtime.sendMessage({
action: 'storage_set',
key: prefixedKey,
value: value
}, (response) => {
if (chrome.runtime.lastError) {
reject(chrome.runtime.lastError);
} else if (response && response.success) {
resolve();
} else {
reject(new Error('Failed to save via message passing'));
}
});
});
} else {
localStorage.setItem(prefixedKey, JSON.stringify(value));
}
} catch (error) {
console.warn(`[StorageManager] Storage failed for ${key}:`, error);
throw error;
}
},
async get(key, defaultValue = null) {
const prefixedKey = `firka_${key}`;
try {
if (this.isExtensionContext()) {
const result = await chrome.storage.sync.get(prefixedKey);
const value = result[prefixedKey];
return value !== undefined ? value : defaultValue;
} else if (this.isContentScript()) {
return new Promise((resolve, reject) => {
chrome.runtime.sendMessage({
action: 'storage_get',
key: prefixedKey,
defaultValue: defaultValue
}, (response) => {
if (chrome.runtime.lastError) {
reject(chrome.runtime.lastError);
} else if (response && response.hasOwnProperty('value')) {
resolve(response.value);
} else {
reject(new Error('Failed to get via message passing'));
}
});
});
} else {
const value = localStorage.getItem(prefixedKey);
if (value !== null) {
return JSON.parse(value);
}
return defaultValue;
}
} catch (error) {
console.warn(`[StorageManager] Storage failed for ${key}:`, error);
return defaultValue;
}
},
async remove(key) {
const prefixedKey = `firka_${key}`;
try {
if (this.isExtensionContext()) {
await chrome.storage.sync.remove(prefixedKey);
} else if (this.isContentScript()) {
return new Promise((resolve, reject) => {
chrome.runtime.sendMessage({
action: 'storage_remove',
key: prefixedKey
}, (response) => {
if (chrome.runtime.lastError) {
reject(chrome.runtime.lastError);
} else if (response && response.success) {
resolve();
} else {
reject(new Error('Failed to remove via message passing'));
}
});
});
} else {
localStorage.removeItem(prefixedKey);
}
} catch (error) {
console.warn(`[StorageManager] Failed to remove ${key}:`, error);
}
},
async clear() {
try {
if (this.isExtensionContext()) {
const allData = await chrome.storage.sync.get(null);
const firkaKeys = Object.keys(allData).filter(key => key.startsWith('firka_'));
if (firkaKeys.length > 0) {
await chrome.storage.sync.remove(firkaKeys);
}
} else if (this.isContentScript()) {
return new Promise((resolve, reject) => {
chrome.runtime.sendMessage({
action: 'storage_clear'
}, (response) => {
if (chrome.runtime.lastError) {
reject(chrome.runtime.lastError);
} else if (response && response.success) {
resolve();
} else {
reject(new Error('Failed to clear via message passing'));
}
});
});
} else {
const keysToRemove = [];
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
if (key && key.startsWith('firka_')) {
keysToRemove.push(key);
}
}
keysToRemove.forEach(key => localStorage.removeItem(key));
}
} catch (error) {
console.warn('[StorageManager] Failed to clear storage:', error);
}
},
};