80 Commits

Author SHA1 Message Date
Zan
0dbb3402a8 Page settings 2025-12-08 14:19:36 +01:00
Zan
64c54171cb setup page 2025-12-08 13:13:50 +01:00
Zan
3d399ee8be logo fix 2025-12-08 13:13:42 +01:00
Zan
431103d2ff login text fix 2025-12-08 13:07:09 +01:00
Zan
8c45ee58ea Update Firefox image in README.md 2025-12-05 16:09:04 +01:00
Zan
0b9897e0eb Update README.md 2025-12-05 16:08:46 +01:00
Zan
d72a8b519d Update logo image in README.md 2025-12-05 16:04:28 +01:00
Zan
53af1d4c1e Update logo dimensions in README.md 2025-12-05 16:03:37 +01:00
Zan
2329ba2cab Update logo image in README.md 2025-12-05 16:03:25 +01:00
Zan
578da8fb38 Christmas logo 2025-12-05 15:55:11 +01:00
Zan
3275b79adf error fix 2025-12-03 16:04:29 +01:00
Zan
b36adf8ebf gap fix 2025-12-02 17:03:25 +01:00
Zan
6d9a45941b remove dup teacher 2025-12-02 16:35:51 +01:00
Zan
581c8ba1d5 Day view refresh 2025-12-02 16:26:21 +01:00
Zan
56182279f3 chart recolor 2025-12-02 16:19:56 +01:00
Zan
33da0de509 import fix 2025-12-02 15:58:44 +01:00
Zan
486f33efcf uptd 2025-12-02 15:58:04 +01:00
Zan
b35154f906 Navbar 2025-12-02 15:38:23 +01:00
Zan
f984f9e7a2 fix 2025-12-02 14:22:40 +01:00
Zan
92e0397c61 Close msg button fix 2025-12-02 14:20:49 +01:00
Zan
a26163f41d Week select fix 2025-12-02 14:14:00 +01:00
Zan
bf1ad731fb Absences page desktop view 2025-12-01 22:50:50 +01:00
Zan
4ded1cce20 Fixes 2025-12-01 21:47:30 +01:00
Zan
5fbe47f5ea Custom themes 2025-12-01 21:29:33 +01:00
Zan
08203f135b Remove custom themes 2025-12-01 20:34:56 +01:00
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
96 changed files with 15250 additions and 8759 deletions

View File

@@ -1,16 +1,17 @@
<p align="center">
<img src="https://i.imgur.com/WugwlzI.png" width="150">
<img width="150" height="150" alt="firka_logo_128" src="https://github.com/user-attachments/assets/089b37c8-bdb1-48af-93e5-9fc656fe8c15" />
<h1 align="center">Firka extension</h1>
</p>
<p align="center">
Modern, testre szabható felhasználói felület az e-KRÉTA rendszerhez
</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>
@@ -23,15 +24,15 @@
<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 href="https://github.com/QwIT-Development/firka-extension/releases">
<img src="https://github.com/QwIT-Development/firka-extension/blob/main/images/firefoxact.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ó
@@ -39,7 +40,6 @@
- Fejlett hiányzás kezelés
- Egyszerűsített szerepkör választó
- Új kezdőlap elrendezés
- Továbbfejlesztett profilkezelő
## 🚀 Telepítés
@@ -47,10 +47,9 @@
## ⚙️ 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
@@ -59,17 +58,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

@@ -9,203 +9,71 @@ body {
padding: 0;
color: var(--text-primary);
background-color: var(--background) !important;
font-family: "Montserrat", serif !important;
font-family: "Montserrat", sans-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;
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: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
width: 200px;
display: none;
z-index: 1000;
}
.user-dropdown.show {
display: block;
animation: dropdownShow 0.2s ease;
}
.dropdown-item {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 0.75rem 1rem;
color: var(--text-primary);
text-decoration: none;
transition: background-color 0.2s;
}
.dropdown-item:hover {
background: var(--button-secondaryFill);
}
.kreta-main {
flex: 1;
padding: clamp(1rem, 3vw, 2rem);
max-width: 1200px;
max-width: 1400px;
margin: 0 auto;
width: 100%;
}
.absences-page {
display: grid;
grid-template-columns: 300px 1fr;
grid-template-rows: 1fr;
grid-template-areas:
"sidebar content";
gap: 24px;
min-height: calc(100vh - 200px);
}
.absences-sidebar {
grid-area: sidebar;
display: flex;
flex-direction: column;
gap: 16px;
position: sticky;
top: 24px;
height: fit-content;
}
.filter-card {
background: var(--card-card);
border-radius: 24px;
padding: 20px;
margin-bottom: 24px;
border-radius: 20px;
padding: 24px;
box-shadow: 0px 1px var(--shadow-blur) 0px var(--accent-shadow);
}
.filter-header {
margin-bottom: 16px;
margin-bottom: 20px;
display: flex;
align-items: center;
gap: 12px;
}
.filter-header h2 {
font-size: 18px;
font-weight: 600;
color: var(--text-primary);
background-color: var(--card-card);
margin: 0;
background-color: #00000000 !important;
}
.filter-content {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 16px;
display: flex;
flex-direction: column;
gap: 20px;
}
.filter-group {
@@ -217,126 +85,525 @@ body {
.filter-group label {
display: flex;
align-items: center;
gap: 8px;
gap: 10px;
color: var(--text-secondary);
font-size: 14px;
font-weight: 500;
}
.filter-group label img {
width: 20px;
height: 20px;
opacity: 0.8;
}
.filter-input {
padding: 10px;
border: none;
border-radius: 8px;
padding: 12px 16px;
border: 2px solid transparent;
border-radius: 12px;
background: var(--button-secondaryFill);
color: var(--text-primary);
font-family: inherit;
font-size: 14px;
transition: all 0.2s ease;
cursor: pointer;
}
.filter-input:hover {
background: var(--accent-15);
}
.filter-input:focus {
outline: none;
box-shadow: 0 0 0 2px var(--accent-accent);
border-color: var(--accent-accent);
background: var(--button-secondaryFill);
}
.absences-container {
display: flex;
flex-direction: column;
gap: 16px;
.stats-section {
grid-area: stats;
}
.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);
}
.absence-date {
padding: 16px;
color: var(--text-primary);
font-weight: 600;
display: flex;
align-items: center;
gap: 8px;
}
.absence-count {
margin-left: auto;
background: var(--accent-accent);
color: var(--button-secondaryFill);
padding: 4px 8px;
border-radius: 12px;
font-size: 14px;
}
.absence-list {
padding: 16px;
display: flex;
flex-direction: column;
.stats-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 12px;
}
.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;
}
.absence-item:hover {
transform: translateX(4px);
}
.absence-time {
display: flex;
align-items: center;
gap: 4px;
color: var(--text-secondary);
font-size: 14px;
}
.absence-details {
.stat-card {
background: var(--card-card);
border-radius: 16px;
padding: 20px;
display: flex;
flex-direction: column;
gap: 4px;
}
.absence-subject {
font-weight: 600;
color: var(--text-primary);
}
.absence-topic {
color: var(--text-secondary);
font-size: 14px;
}
.absence-status {
display: flex;
align-items: center;
gap: 4px;
font-size: 14px;
font-weight: 500;
justify-content: center;
text-align: center;
box-shadow: 0px 1px var(--shadow-blur) 0px var(--accent-shadow);
transition: transform 0.2s ease, box-shadow 0.2s ease;
min-height: 100px;
}
.absence-status.justified {
.stat-card:hover {
transform: translateY(-2px);
}
.stat-icon {
width: 24px;
height: 24px;
margin-bottom: 8px;
opacity: 0.8;
}
.stat-number {
font-size: 32px;
font-weight: 700;
color: var(--text-primary);
line-height: 1;
margin-bottom: 6px;
}
.stat-card.total .stat-number {
color: var(--accent-accent);
}
.stat-card.justified .stat-number {
color: var(--grades-4);
}
.absence-status.unjustified {
.stat-card.unjustified .stat-number {
color: var(--grades-1);
}
.absence-status.pending {
.stat-card.pending .stat-number {
color: var(--grades-3);
}
.stat-label {
color: var(--text-secondary);
font-size: 12px;
font-weight: 500;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.absences-content {
grid-area: content;
display: flex;
flex-direction: column;
gap: 24px;
}
.day-group {
background: var(--card-card);
border-radius: 20px;
overflow: hidden;
box-shadow: 0px 1px var(--shadow-blur) 0px var(--accent-shadow);
}
.day-header {
background: var(--accent-accent);
color: white;
padding: 16px 24px;
display: flex;
align-items: center;
justify-content: space-between;
font-weight: 600;
}
.day-date {
display: flex;
align-items: center;
gap: 12px;
font-size: 16px;
}
.day-date img {
width: 20px;
height: 20px;
filter: brightness(0) invert(1);
}
.day-count {
background: rgba(255, 255, 255, 0.2);
padding: 4px 12px;
border-radius: 20px;
font-size: 13px;
}
.day-absences {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
gap: 16px;
padding: 20px;
}
.absence-card {
background: var(--button-secondaryFill);
border-radius: 16px;
padding: 20px;
display: grid;
grid-template-columns: auto 1fr minmax(auto, max-content);
grid-template-rows: auto auto;
grid-template-areas:
"lesson subject status"
"lesson topic status";
gap: 8px 12px;
transition: transform 0.2s ease, box-shadow 0.2s ease;
border: 1px solid var(--accent-15);
}
.absence-card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px var(--accent-shadow);
}
.absence-lesson {
grid-area: lesson;
display: flex;
align-items: center;
justify-content: center;
width: 48px;
height: 48px;
background: var(--accent-15);
border-radius: 12px;
font-size: 20px;
font-weight: 700;
color: var(--accent-accent);
}
.absence-subject {
grid-area: subject;
font-size: 16px;
font-weight: 600;
color: var(--text-primary);
align-self: end;
}
.absence-topic {
grid-area: topic;
font-size: 13px;
color: var(--text-secondary);
align-self: start;
display: -webkit-box;
-webkit-line-clamp: 2;
line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
}
.absence-status {
grid-area: status;
display: flex;
align-items: center;
justify-content: center;
}
.status-badge {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 8px 14px;
border-radius: 24px;
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.2px;
white-space: nowrap;
flex-shrink: 0;
}
.status-badge img {
width: 16px;
height: 16px;
}
.status-badge.justified {
background: var(--grades-background-4);
color: var(--grades-4);
}
.status-badge.justified img {
filter: brightness(0) saturate(100%) invert(76%) sepia(43%) saturate(609%) hue-rotate(53deg) brightness(101%) contrast(92%);
}
.status-badge.unjustified {
background: var(--grades-background-1);
color: var(--grades-1);
}
.status-badge.unjustified .material-icons-round {
font-size: 16px;
}
.status-badge.pending {
background: var(--grades-background-3);
color: var(--grades-3);
}
.status-badge.pending .material-icons-round {
font-size: 16px;
}
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 60px 20px;
text-align: center;
background: var(--card-card);
border-radius: 20px;
}
.empty-state img {
width: 80px;
height: 80px;
opacity: 0.5;
margin-bottom: 20px;
}
.empty-state h3 {
font-size: 18px;
font-weight: 600;
color: var(--text-primary);
margin-bottom: 8px;
}
.empty-state p {
font-size: 14px;
color: var(--text-secondary);
}
@media (max-width: 1024px) {
.absences-page {
grid-template-columns: 260px 1fr;
gap: 20px;
}
.day-absences {
grid-template-columns: 1fr;
}
}
@media (max-width: 768px) {
body {
font-size: 14px;
}
.kreta-main {
padding: 16px;
}
.absences-page {
grid-template-columns: 1fr;
grid-template-rows: auto 1fr;
grid-template-areas:
"sidebar"
"content";
gap: 16px;
}
.absences-sidebar {
position: static;
}
.filter-card {
padding: 20px;
border-radius: 16px;
}
.filter-content {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 12px;
}
.filter-group label {
font-size: 12px;
}
.filter-input {
padding: 10px 12px;
font-size: 13px;
}
.stats-grid {
grid-template-columns: repeat(4, 1fr);
gap: 10px;
}
.stat-card {
padding: 14px 10px;
min-height: 80px;
border-radius: 12px;
}
.stat-number {
font-size: 24px;
}
.stat-label {
font-size: 10px;
}
.day-group {
border-radius: 16px;
}
.day-header {
padding: 14px 18px;
font-size: 14px;
}
.day-absences {
padding: 14px;
gap: 12px;
}
.absence-card {
padding: 16px;
border-radius: 12px;
}
.absence-lesson {
width: 44px;
height: 44px;
font-size: 18px;
}
.absence-subject {
font-size: 15px;
}
}
@media (max-width: 480px) {
.kreta-main {
padding: 12px;
}
.absences-page {
gap: 12px;
}
.filter-card {
padding: 16px;
}
.filter-header {
margin-bottom: 16px;
}
.filter-header h2 {
font-size: 16px;
}
.filter-content {
grid-template-columns: 1fr;
gap: 14px;
}
.stats-grid {
grid-template-columns: repeat(2, 1fr);
gap: 8px;
}
.stat-card {
padding: 12px 8px;
min-height: 70px;
}
.stat-number {
font-size: 22px;
margin-bottom: 4px;
}
.stat-label {
font-size: 9px;
letter-spacing: 0.3px;
}
.day-header {
padding: 12px 16px;
flex-wrap: wrap;
gap: 8px;
}
.day-date {
font-size: 14px;
}
.day-count {
font-size: 12px;
padding: 3px 10px;
}
.day-absences {
padding: 12px;
gap: 10px;
}
.absence-card {
padding: 14px;
grid-template-columns: auto 1fr;
grid-template-rows: auto auto auto;
grid-template-areas:
"lesson subject"
"lesson topic"
"status status";
gap: 6px 12px;
}
.absence-lesson {
width: 40px;
height: 40px;
font-size: 16px;
grid-row: span 2;
}
.absence-status {
justify-content: flex-start;
margin-top: 8px;
padding-top: 10px;
border-top: 1px solid var(--accent-15);
}
.status-badge {
padding: 6px 12px;
font-size: 11px;
}
.absence-subject {
font-size: 14px;
}
.absence-topic {
font-size: 12px;
}
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.day-group {
animation: fadeIn 0.3s ease forwards;
}
.day-group:nth-child(1) { animation-delay: 0.05s; }
.day-group:nth-child(2) { animation-delay: 0.1s; }
.day-group:nth-child(3) { animation-delay: 0.15s; }
.day-group:nth-child(4) { animation-delay: 0.2s; }
.day-group:nth-child(5) { animation-delay: 0.25s; }
.loading-overlay {
position: fixed;
@@ -362,22 +629,18 @@ body {
}
.loading-text {
color: var(--Text-Primary);
color: var(--text-primary);
text-align: center;
font-family: Montserrat;
font-family: Montserrat, sans-serif;
font-size: 20px;
font-style: normal;
font-weight: 700;
line-height: normal;
}
.loading-text2 {
align-self: stretch;
color: var(--Text-Secondary);
color: var(--text-secondary);
text-align: center;
font-family: Figtree;
font-family: Figtree, sans-serif;
font-size: 16px;
font-style: normal;
font-weight: 500;
line-height: 130%;
}
@@ -387,50 +650,4 @@ body {
height: 48px;
margin-bottom: 1rem;
border-radius: 8px;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes dropdownShow {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@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,25 +1,30 @@
async function collectAbsencesData() {
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",
},
};
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'
}
});
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}`);
@@ -29,250 +34,543 @@ async function collectAbsencesData() {
const absences = [];
if (apiData.Data && Array.isArray(apiData.Data)) {
apiData.Data.forEach(item => {
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')}.`;
const formattedDate = `${date.getFullYear()}.${(date.getMonth() + 1).toString().padStart(2, "0")}.${date.getDate().toString().padStart(2, "0")}.`;
let justificationStatus = 'pending';
let justificationStatus = "pending";
if (item.Igazolt_BOOL === true) {
justificationStatus = 'justified';
justificationStatus = "justified";
} else if (item.Igazolt_BOOL === false && item.IgazolasTipus !== null) {
justificationStatus = 'unjustified';
justificationStatus = "unjustified";
}
absences.push({
date: formattedDate,
lesson: item.Oraszam?.toString() || '',
subject: item.Targy || '',
topic: item.Tema || '',
type: item.MulasztasTipus_DNAME || '',
rawDate: date,
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 || ''
purposeful: item.TanoraiCeluMulasztas_BNAME || "",
justificationType: item.IgazolasTipus_DNAME || "",
});
});
}
const groupedAbsences = {};
absences.forEach(absence => {
if (!groupedAbsences[absence.date]) {
groupedAbsences[absence.date] = [];
const groupedAbsences = absences.reduce((groups, absence) => {
const date = absence.date;
if (!groups[date]) {
groups[date] = [];
}
groupedAbsences[absence.date].push(absence);
groups[date].push(absence);
return groups;
}, {});
Object.keys(groupedAbsences).forEach(date => {
groupedAbsences[date].sort((a, b) => parseInt(a.lesson) - parseInt(b.lesson));
});
return { basicData, absences, groupedAbsences };
} catch (error) {
console.error('Hiba az API hívás során:', 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 = document.createElement('div');
dateGroup.className = 'filter-group';
const dateLabel = document.createElement('label');
const dateImg = document.createElement('img');
dateImg.src = chrome.runtime.getURL('icons/Calendar.svg');
dateImg.alt = 'Dátum';
dateLabel.appendChild(dateImg);
dateLabel.appendChild(document.createTextNode(' ' + LanguageManager.t('absences.date')));
const dateInput = document.createElement('input');
dateInput.type = 'date';
dateInput.id = 'dateFilter';
dateInput.className = 'filter-input';
dateGroup.appendChild(dateLabel);
dateGroup.appendChild(dateInput);
filterContent.appendChild(dateGroup);
const subjectGroup = document.createElement('div');
subjectGroup.className = 'filter-group';
const subjectLabel = document.createElement('label');
const subjectImg = document.createElement('img');
subjectImg.src = chrome.runtime.getURL('icons/Subject.svg');
subjectImg.alt = 'Tantárgy';
subjectLabel.appendChild(subjectImg);
subjectLabel.appendChild(document.createTextNode(' ' + LanguageManager.t('absences.subject')));
const subjectSelect = document.createElement('select');
subjectSelect.id = 'subjectFilter';
subjectSelect.className = 'filter-input';
const defaultSubjectOption = document.createElement('option');
defaultSubjectOption.value = '';
defaultSubjectOption.textContent = LanguageManager.t('absences.all_subjects');
subjectSelect.appendChild(defaultSubjectOption);
const subjects = [...new Set(absences.map(a => a.subject))].sort();
subjects.forEach(subject => {
const option = document.createElement('option');
option.value = subject;
option.textContent = subject;
subjectSelect.appendChild(option);
});
subjectGroup.appendChild(subjectLabel);
subjectGroup.appendChild(subjectSelect);
filterContent.appendChild(subjectGroup);
const justificationGroup = document.createElement('div');
justificationGroup.className = 'filter-group';
const justificationLabel = document.createElement('label');
const justificationImg = document.createElement('img');
justificationImg.src = chrome.runtime.getURL('icons/BadgeCheck.svg');
justificationImg.alt = 'Igazolás';
justificationLabel.appendChild(justificationImg);
justificationLabel.appendChild(document.createTextNode(' ' + LanguageManager.t('absences.justification')));
const justificationSelect = document.createElement('select');
justificationSelect.id = 'justificationFilter';
justificationSelect.className = 'filter-input';
const justificationOptions = [
{ value: '', text: LanguageManager.t('absences.all_types') },
{ value: 'justified', text: LanguageManager.t('absences.justified') },
{ value: 'unjustified', text: LanguageManager.t('absences.unjustified') },
{ value: 'pending', text: LanguageManager.t('absences.pending') }
];
justificationOptions.forEach(optionData => {
const option = document.createElement('option');
option.value = optionData.value;
option.textContent = optionData.text;
justificationSelect.appendChild(option);
});
justificationGroup.appendChild(justificationLabel);
justificationGroup.appendChild(justificationSelect);
filterContent.appendChild(justificationGroup);
filterCard.appendChild(filterHeader);
filterCard.appendChild(filterContent);
return filterCard;
}
function createStatsSection(absences) {
const statsSection = document.createElement('div');
statsSection.className = 'stats-section';
const statsGrid = document.createElement('div');
statsGrid.className = 'stats-grid';
const stats = [
{
type: 'total',
number: absences.length,
label: LanguageManager.t('absences.total_absences')
},
{
type: 'justified',
number: absences.filter(a => a.justificationStatus === 'justified').length,
label: LanguageManager.t('absences.justified')
},
{
type: 'unjustified',
number: absences.filter(a => a.justificationStatus === 'unjustified').length,
label: LanguageManager.t('absences.unjustified')
},
{
type: 'pending',
number: absences.filter(a => a.justificationStatus === 'pending').length,
label: LanguageManager.t('absences.pending')
}
];
stats.forEach(stat => {
const statCard = document.createElement('div');
statCard.className = `stat-card ${stat.type}`;
statCard.dataset.type = stat.type;
const statNumber = document.createElement('div');
statNumber.className = 'stat-number';
statNumber.textContent = stat.number;
const statLabel = document.createElement('div');
statLabel.className = 'stat-label';
statLabel.textContent = stat.label;
statCard.appendChild(statNumber);
statCard.appendChild(statLabel);
statsGrid.appendChild(statCard);
});
statsSection.appendChild(statsGrid);
return statsSection;
}
function createDayGroup(date, dayAbsences) {
const dayGroup = document.createElement('div');
dayGroup.className = 'day-group';
dayGroup.dataset.date = date;
const dayHeader = document.createElement('div');
dayHeader.className = 'day-header';
const dayDate = document.createElement('div');
dayDate.className = 'day-date';
const calendarIcon = document.createElement('img');
calendarIcon.src = chrome.runtime.getURL('icons/Calendar.svg');
calendarIcon.alt = 'Dátum';
const dateText = document.createElement('span');
dateText.textContent = formatDateWithDay(date);
dayDate.appendChild(calendarIcon);
dayDate.appendChild(dateText);
const dayCount = document.createElement('div');
dayCount.className = 'day-count';
dayCount.textContent = `${dayAbsences.length} ${LanguageManager.t('absences.hours')}`;
dayHeader.appendChild(dayDate);
dayHeader.appendChild(dayCount);
const dayAbsencesContainer = document.createElement('div');
dayAbsencesContainer.className = 'day-absences';
dayAbsences.forEach(absence => {
const absenceCard = createAbsenceCard(absence);
dayAbsencesContainer.appendChild(absenceCard);
});
dayGroup.appendChild(dayHeader);
dayGroup.appendChild(dayAbsencesContainer);
return dayGroup;
}
function formatDateWithDay(dateStr) {
const parts = dateStr.split('.');
const year = parseInt(parts[0]);
const month = parseInt(parts[1]) - 1;
const day = parseInt(parts[2]);
const date = new Date(year, month, day);
const days = [
LanguageManager.t('common.sunday'),
LanguageManager.t('common.monday'),
LanguageManager.t('common.tuesday'),
LanguageManager.t('common.wednesday'),
LanguageManager.t('common.thursday'),
LanguageManager.t('common.friday'),
LanguageManager.t('common.saturday')
];
const dayName = days[date.getDay()];
return `${dateStr} - ${dayName.charAt(0).toUpperCase() + dayName.slice(1)}`;
}
function createAbsenceCard(absence) {
const card = document.createElement('div');
card.className = 'absence-card';
card.dataset.subject = absence.subject;
card.dataset.status = absence.justificationStatus;
card.dataset.date = absence.date;
const lessonDiv = document.createElement('div');
lessonDiv.className = 'absence-lesson';
lessonDiv.textContent = absence.lesson + '.';
const subjectDiv = document.createElement('div');
subjectDiv.className = 'absence-subject';
subjectDiv.textContent = absence.subject;
const topicDiv = document.createElement('div');
topicDiv.className = 'absence-topic';
topicDiv.textContent = absence.topic || '-';
topicDiv.title = absence.topic;
const statusDiv = document.createElement('div');
statusDiv.className = 'absence-status';
const statusBadge = document.createElement('span');
statusBadge.className = `status-badge ${absence.justificationStatus}`;
if (absence.justificationStatus === 'justified') {
const img = document.createElement('img');
img.src = chrome.runtime.getURL('icons/BadgeCheck.svg');
img.alt = 'Igazolt';
statusBadge.appendChild(img);
statusBadge.appendChild(document.createTextNode(' ' + LanguageManager.t('absences.justified')));
} else if (absence.justificationStatus === 'unjustified') {
const span = document.createElement('span');
span.className = 'material-icons-round';
span.textContent = 'cancel';
statusBadge.appendChild(span);
statusBadge.appendChild(document.createTextNode(' ' + LanguageManager.t('absences.unjustified')));
} else {
const img = document.createElement('img');
img.src = chrome.runtime.getURL('icons/pending.svg');
img.alt = 'Függőben';
statusBadge.appendChild(img);
statusBadge.appendChild(document.createTextNode(' ' + LanguageManager.t('absences.pending')));
}
statusDiv.appendChild(statusBadge);
card.appendChild(lessonDiv);
card.appendChild(subjectDiv);
card.appendChild(topicDiv);
card.appendChild(statusDiv);
return card;
}
function createAbsencesContent(groupedAbsences) {
const content = document.createElement('div');
content.className = 'absences-content';
const sortedDates = Object.keys(groupedAbsences).sort((a, b) => {
const dateA = new Date(a.replace(/\./g, '-').slice(0, -1));
const dateB = new Date(b.replace(/\./g, '-').slice(0, -1));
return dateB - dateA;
});
if (sortedDates.length === 0) {
const emptyState = document.createElement('div');
emptyState.className = 'empty-state';
const emptyIcon = document.createElement('img');
emptyIcon.src = chrome.runtime.getURL('icons/BadgeCheck.svg');
emptyIcon.alt = 'Nincs hiányzás';
const emptyTitle = document.createElement('h3');
emptyTitle.textContent = LanguageManager.t('absences.title');
const emptyText = document.createElement('p');
emptyText.textContent = LanguageManager.t('dashboard.not_supported');
emptyState.appendChild(emptyIcon);
emptyState.appendChild(emptyTitle);
emptyState.appendChild(emptyText);
content.appendChild(emptyState);
} else {
sortedDates.forEach(date => {
const dayGroup = createDayGroup(date, groupedAbsences[date]);
content.appendChild(dayGroup);
});
}
return content;
}
async function transformAbsencesPage() {
const { basicData, absences, groupedAbsences } = await collectAbsencesData();
document.body.innerHTML = `
<div class="kreta-container">
${createTemplate.header()}
<main class="kreta-main">
<div class="filter-card">
<div class="filter-header">
<h2>${LanguageManager.t('absences.filter_title')}</h2>
</div>
<div class="filter-content">
<div class="filter-group">
<label>
<span class="material-icons-round">date_range</span>
${LanguageManager.t('absences.date')}
</label>
<input type="date" id="dateFilter" class="filter-input">
</div>
<div class="filter-group">
<label>
<span class="material-icons-round">school</span>
${LanguageManager.t('absences.subject')}
</label>
<select id="subjectFilter" class="filter-input">
<option value="">${LanguageManager.t('absences.all_subjects')}</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>
${LanguageManager.t('absences.justification')}
</label>
<select id="justificationFilter" class="filter-input">
<option value="">${LanguageManager.t('absences.all_types')}</option>
<option value="justified">${LanguageManager.t('absences.justified')}</option>
<option value="unjustified">${LanguageManager.t('absences.unjustified')}</option>
<option value="pending">${LanguageManager.t('absences.pending')}</option>
</select>
</div>
</div>
</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} ${LanguageManager.t('absences.hours')}</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}. ${LanguageManager.t('absences.lesson').toLowerCase()}
</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' ?
`${LanguageManager.t('absences.justified')} <span class="material-icons-round">check_circle</span>` :
absence.justificationStatus === 'unjustified' ?
`${LanguageManager.t('absences.unjustified')} <span class="material-icons-round">cancel</span>` :
`${LanguageManager.t('absences.pending')} <span class="material-icons-round">pending</span>`}
</div>
</div>
`).join('')}
</div>
</div>
`).join('')}
</div>
</main>
</div>
`;
createTemplate.importFonts();
setupUserDropdown();
setupMobileNavigation();
setupEventListeners();
setupFilters();
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 pageGrid = document.createElement('div');
pageGrid.className = 'absences-page';
const sidebar = document.createElement('div');
sidebar.className = 'absences-sidebar';
const filterCard = createFilterCard(absences);
sidebar.appendChild(filterCard);
const statsSection = createStatsSection(absences);
sidebar.appendChild(statsSection);
const absencesContent = createAbsencesContent(groupedAbsences);
pageGrid.appendChild(sidebar);
pageGrid.appendChild(absencesContent);
main.appendChild(pageGrid);
container.appendChild(main);
document.body.appendChild(container);
setupUserDropdown();
setupFilters(groupedAbsences);
loadingScreen.hide();
}
function setupEventListeners() {
}
function setupFilters() {
function setupFilters(originalGroupedAbsences) {
try {
const filters = {
dateFilter: document.getElementById('dateFilter'),
subject: document.getElementById('subjectFilter'),
justified: document.getElementById('justificationFilter')
};
const dateFilter = document.getElementById("dateFilter");
const subjectFilter = document.getElementById("subjectFilter");
const justificationFilter = document.getElementById("justificationFilter");
if (!filters.dateFilter || !filters.subject || !filters.justified) {
console.warn('Some filter elements were not found in the DOM');
if (!dateFilter || !subjectFilter || !justificationFilter) {
console.warn("Some filter elements were not found in the DOM");
return;
}
const filterAbsences = () => {
try {
const dateFilterValue = filters.dateFilter.value;
const subject = filters.subject.value;
const justified = filters.justified.value;
const dateFilterValue = dateFilter.value;
const subject = subjectFilter.value;
const justified = justificationFilter.value;
const selectedDate = dateFilterValue ? new Date(dateFilterValue) : null;
document.querySelectorAll('.absence-group').forEach(group => {
const dateStr = group.dataset.date;
const dateParts = dateStr.split('.');
document.querySelectorAll(".absence-card").forEach((card) => {
const dateStr = card.dataset.date;
const dateParts = dateStr.split(".");
if (dateParts.length < 3) {
console.error(`Invalid date format: ${dateStr}`);
return;
}
const parsedYear = parseInt(dateParts[0].trim(), 10);
const parsedMonth = parseInt(dateParts[1].trim(), 10) - 1;
const parsedDay = parseInt(dateParts[2].trim(), 10);
if (isNaN(parsedDay) || isNaN(parsedMonth) || isNaN(parsedYear)) {
console.error(`Invalid date components: ${dateStr}`);
return;
}
const groupDate = new Date(parsedYear, parsedMonth, parsedDay);
let showGroup = true;
const cardDate = new Date(parsedYear, parsedMonth, parsedDay);
let showCard = true;
if (selectedDate) {
if (groupDate.getFullYear() !== selectedDate.getFullYear() ||
groupDate.getMonth() !== selectedDate.getMonth() ||
groupDate.getDate() !== selectedDate.getDate()) {
showGroup = false;
if (
cardDate.getFullYear() !== selectedDate.getFullYear() ||
cardDate.getMonth() !== selectedDate.getMonth() ||
cardDate.getDate() !== selectedDate.getDate()
) {
showCard = false;
}
}
const absenceItems = group.querySelectorAll('.absence-item');
let visibleItems = 0;
if (subject && card.dataset.subject !== subject) {
showCard = false;
}
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;
}
if (justified && card.dataset.status !== justified) {
showCard = false;
}
item.style.display = showItem ? '' : 'none';
if (showItem) visibleItems++;
});
group.style.display = (showGroup && visibleItems > 0) ? '' : 'none';
card.style.display = showCard ? "" : "none";
});
updateDayGroupsVisibility();
updateStatistics();
} catch (err) {
console.error('Error during filtering absences:', err);
console.error("Error during filtering absences:", err);
}
};
Object.values(filters).forEach(filter => {
try {
if (filter) {
filter.addEventListener('change', filterAbsences);
}
} catch (err) {
if (err.message && err.message.includes('Extension context invalidated')) {
console.warn('Extension context invalidated during event listener setup');
} else {
console.error('Error setting up filter event listener:', err);
}
[dateFilter, subjectFilter, justificationFilter].forEach((filter) => {
if (filter) {
filter.addEventListener("change", filterAbsences);
}
});
filterAbsences();
} catch (err) {
if (err.message && err.message.includes('Extension context invalidated')) {
console.warn('Extension context invalidated during filter setup');
} else {
console.error('Error setting up filters:', err);
}
console.error("Error setting up filters:", err);
}
}
function updateDayGroupsVisibility() {
document.querySelectorAll(".day-group").forEach((group) => {
const visibleCards = group.querySelectorAll('.absence-card:not([style*="display: none"])');
const dayCount = group.querySelector('.day-count');
if (visibleCards.length > 0) {
group.style.display = "";
if (dayCount) {
dayCount.textContent = `${visibleCards.length} ${LanguageManager.t('absences.hours')}`;
}
} else {
group.style.display = "none";
}
});
}
if (window.location.href.includes('/Hianyzas/Hianyzasok')) {
transformAbsencesPage().catch(error => {
console.error(LanguageManager.t('absences.page_transform_error'), error);
function updateStatistics() {
try {
const visibleCards = document.querySelectorAll('.absence-card:not([style*="display: none"])');
const totalVisible = visibleCards.length;
const justifiedVisible = Array.from(visibleCards).filter(
card => card.dataset.status === 'justified'
).length;
const unjustifiedVisible = Array.from(visibleCards).filter(
card => card.dataset.status === 'unjustified'
).length;
const pendingVisible = Array.from(visibleCards).filter(
card => card.dataset.status === 'pending'
).length;
const statCards = document.querySelectorAll(".stat-card");
statCards.forEach(card => {
const type = card.dataset.type;
const numberEl = card.querySelector('.stat-number');
if (numberEl) {
switch(type) {
case 'total':
numberEl.textContent = totalVisible;
break;
case 'justified':
numberEl.textContent = justifiedVisible;
break;
case 'unjustified':
numberEl.textContent = unjustifiedVisible;
break;
case 'pending':
numberEl.textContent = pendingVisible;
break;
}
}
});
} catch (err) {
console.error("Error updating statistics:", err);
}
}
if (window.location.href.includes("/Hianyzas/Hianyzasok")) {
transformAbsencesPage().catch((error) => {
console.error(LanguageManager.t("absences.page_transform_error"), error);
});
}

View File

@@ -37,6 +37,8 @@ h2 {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr));
gap: 20px;
justify-content: center;
justify-items: center;
}
.widget-card {
@@ -50,6 +52,7 @@ h2 {
height: 100%;
border: none;
min-height: 400px;
width: 100%;
}
.widget-header {
@@ -140,9 +143,10 @@ h2 {
font-weight: 600;
font-size: 16px;
line-height: 1.3;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
word-wrap: break-word;
overflow-wrap: break-word;
hyphens: auto;
max-width: 100%;
}
.widget-subtitle {
@@ -150,6 +154,8 @@ h2 {
font-weight: 500;
font-size: 14px;
line-height: 1.2;
word-wrap: break-word;
overflow-wrap: break-word;
}
.widget-content {
@@ -186,6 +192,8 @@ h2 {
font-size: 12px;
font-weight: 400;
margin-top: 4px;
word-wrap: break-word;
overflow-wrap: break-word;
}
.grade-date {
@@ -196,20 +204,67 @@ h2 {
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%;
}
.widget-meta {
.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;
}
@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;
}
}
@media (min-width: 769px) {
.news-author {
max-width: 180px;
white-space: normal;
line-height: 1.3;
}
}
.widget-empty {
@@ -236,21 +291,27 @@ h2 {
font-size: 16px;
font-weight: 600;
margin-bottom: 8px;
word-wrap: break-word;
overflow-wrap: break-word;
hyphens: auto;
line-height: 1.4;
}
.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 {
color: var(--accent-accent);
font-size: 12px;
font-weight: 500;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.more-link {
margin-top: auto;
display: inline-flex;
@@ -294,13 +355,15 @@ h2 {
}
.user-dropdown-btn {
background: none;
background: var(--button-secondaryFill);
border: none;
cursor: pointer;
padding: 8px;
padding: 8px 12px;
display: flex;
align-items: center;
color: var(--text-primary);
border-radius: 12px;
box-shadow: 0px 1px var(--shadow-blur, 2px) 0px var(--accent-shadow);
}
.user-dropdown {
@@ -331,6 +394,7 @@ h2 {
.dropdown-item:hover {
background-color: var(--accent-15);
text-decoration: none;
}
.dropdown-item svg {
@@ -349,14 +413,6 @@ h2 {
line-height: 1.4;
color: var(--text-secondary);
}
.exam-type {
color: var(--accent-accent);
font-size: 12px;
font-weight: 500;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.more-link {
margin-top: auto;
display: inline-flex;

View File

@@ -1,44 +1,63 @@
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;
if (typeof window.LanguageManager !== 'undefined') {
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'
"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'
"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 `${LanguageManager.t(monthKeys[month - 1])} ${day}.`;
}
},
};
class DashboardDataManager {
@@ -48,175 +67,216 @@ class DashboardDataManager {
absences: [],
notes: [],
upcomingExams: [],
news: []
news: [],
};
}
extractGradeData() {
const gradeRows = document.querySelectorAll('#legutobbiErtekelesek tr:not(:first-child)');
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 gradeRows = document.querySelectorAll(
"#legutobbiErtekelesek tr:not(:first-child)",
);
const [fullSubject, date] = gradeInfo.split('\n').map(str => str.trim());
const { subject, type, dateInSubject } = this.parseSubjectInformation(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;
return {
value: DashboardUtils.formatGradeValue(gradeValue),
subject,
date: DashboardUtils.parseDate(date),
type: type || LanguageManager.t('dashboard.evaluation'),
dateInSubject: dateInSubject || null
};
}).filter(Boolean);
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 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 {
subject: subjectPart,
type: '',
dateInSubject: datePart
type: "",
dateInSubject: datePart,
};
}
const months = [
LanguageManager.t('months.january'), LanguageManager.t('months.february'), LanguageManager.t('months.march'),
LanguageManager.t('months.april'), LanguageManager.t('months.may'), LanguageManager.t('months.june'),
LanguageManager.t('months.july'), LanguageManager.t('months.august'), LanguageManager.t('months.september'),
LanguageManager.t('months.october'), LanguageManager.t('months.november'), LanguageManager.t('months.december')
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 fallbackMonthPattern = new RegExp(months.join("|"), "i");
const monthMatch = fullSubject.match(fallbackMonthPattern);
if (monthMatch) {
const monthIndex = fullSubject.lastIndexOf(monthMatch[0]);
return {
subject: fullSubject.substring(0, monthIndex).trim(),
type: fullSubject.substring(monthIndex).trim()
type: fullSubject.substring(monthIndex).trim(),
};
}
return { subject: fullSubject, type: '' };
return { subject: fullSubject, type: "" };
}
extractAbsenceData() {
const absenceRows = document.querySelectorAll('#legutobbiMulasztasok tr:not(:first-child)');
this.dashboardData.absences = Array.from(absenceRows).map(row => {
const spans = row.querySelectorAll('span');
if (spans.length < 4) return null;
const absenceRows = document.querySelectorAll(
"#legutobbiMulasztasok tr:not(:first-child)",
);
return {
date: spans[0]?.textContent?.trim() || '',
day: spans[2]?.textContent?.trim() || '',
type: spans[1]?.textContent?.trim() || '',
count: spans[3]?.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);
}
extractNoteData() {
const noteRows = document.querySelectorAll('#legutobbiFeljegyzesek tr:not(:first-child)');
this.dashboardData.notes = Array.from(noteRows).map(row => {
const spans = row.querySelectorAll('span');
if (spans.length < 3) return null;
const noteRows = document.querySelectorAll(
"#legutobbiFeljegyzesek tr:not(:first-child)",
);
return {
title: spans[0]?.textContent?.trim() || '',
author: spans[1]?.textContent?.trim() || '',
date: spans[2]?.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);
}
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;
const examRows = document.querySelectorAll(
"#legutobbiBejelentettSzamonkeres 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.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',
method: "GET",
credentials: "include",
headers: {
'Accept': 'application/json, text/javascript, */*; q=0.01',
'X-Requested-With': 'XMLHttpRequest'
}
Accept: "application/json, text/javascript, */*; q=0.01",
"X-Requested-With": "XMLHttpRequest",
},
});
if (!response.ok) {
throw new Error(`API request failed with status: ${response.status}`);
}
const data = await response.json();
if (!data.FaliujsagElemek || !Array.isArray(data.FaliujsagElemek)) {
return;
}
data.FaliujsagElemek.forEach((item, index) => {
let formattedDate = '';
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+)\)\//);
const match = item.Idopont.match(/\/Date\((\d+)\)\//);
if (match) {
const timestamp = parseInt(match[1]);
const date = new Date(timestamp);
formattedDate = date.toLocaleDateString('hu-HU');
formattedDate = date.toLocaleDateString("hu-HU");
}
}
if (!formattedDate) {
formattedDate = new Date().toLocaleDateString('hu-HU');
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();
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',
content: cleanContent || "Nincs elérhető tartalom",
date: formattedDate,
author: `${item.Nev || 'Ismeretlen'} (${item.Munkakor || 'Ismeretlen'})`
author: `${item.Nev || "Ismeretlen"} (${item.Munkakor || "Ismeretlen"})`,
};
this.dashboardData.news.push(newsItem);
});
} catch (error) {
console.error('❌ Error fetching news from API:', error);
console.error("❌ Error fetching news from API:", error);
}
}
@@ -231,37 +291,64 @@ class DashboardDataManager {
}
class DashboardRenderer {
constructor(data) {
constructor(data, settings = {}) {
this.baseData = data;
this.settings = settings;
}
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);
}
generateMainContent() {
const cards = [];
if (!this.settings.hideGrades) {
cards.push(this.createGradeCard());
}
if (!this.settings.hideAbsences) {
cards.push(this.createAbsenceCard());
}
if (!this.settings.hideNotes) {
cards.push(this.createNoteCard());
}
if (!this.settings.hideExams) {
cards.push(this.createExamCard());
}
cards.push(this.createNewsCard());
if (cards.length === 1) {
cards.unshift(this.createGradeCard());
}
return `
<main class="kreta-main">
<div class="grid-container">
${this.createGradeCard()}
${this.createAbsenceCard()}
${this.createNoteCard()}
${this.createExamCard()}
${this.createNewsCard()}
${cards.join('')}
</div>
</main>
`;
}
createNewsCard() {
const newsItems = this.data.news.map(news => `
const newsItems = this.data.news
.map(
(news) => `
<div class="widget-item news-item">
<div class="widget-row">
<div class="widget-details news-details">
@@ -269,18 +356,27 @@ class DashboardRenderer {
<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>` : ''}
${news.date ? `<div class="widget-date news-date">${news.date}</div>` : ""}
${news.author ? `<div class="widget-author news-author">${news.author}</div>` : ""}
</div>
</div>
</div>
`).join('');
`,
)
.join("");
return this.createCard(LanguageManager.t('dashboard.news'), newsItems || LanguageManager.t('dashboard.not_supported'), '/Intezmeny/Faliujsag', LanguageManager.t('dashboard.all_news'));
return this.createCard(
LanguageManager.t("dashboard.news"),
newsItems || LanguageManager.t("dashboard.not_supported"),
"/Intezmeny/Faliujsag",
LanguageManager.t("dashboard.all_news"),
);
}
createGradeCard() {
const gradeItems = this.data.grades.map(grade => `
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>
@@ -288,62 +384,96 @@ class DashboardRenderer {
<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>` : ''}
${grade.dateInSubject || grade.date ? `<div class="widget-subtitle grade-date">${grade.dateInSubject || grade.date}</div>` : ""}
</div>
</div>
</div>
</div>
`).join('');
`,
)
.join("");
return this.createCard(LanguageManager.t('dashboard.grades'), gradeItems, '/TanuloErtekeles/Osztalyzatok', LanguageManager.t('dashboard.all_grades'));
return this.createCard(
LanguageManager.t("dashboard.grades"),
gradeItems,
"/TanuloErtekeles/Osztalyzatok",
LanguageManager.t("dashboard.all_grades"),
);
}
createAbsenceCard() {
const absenceItems = this.data.absences.map(absence => `
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>` : ''}
${absence.day ? `<div class="widget-date">${absence.day}</div>` : ""}
</div>
</div>
`).join('');
`,
)
.join("");
return this.createCard(LanguageManager.t('dashboard.absences'), absenceItems, '/Hianyzas/Hianyzasok', LanguageManager.t('dashboard.all_absences'));
return this.createCard(
LanguageManager.t("dashboard.absences"),
absenceItems,
"/Hianyzas/Hianyzasok",
LanguageManager.t("dashboard.all_absences"),
);
}
createNoteCard() {
const noteItems = this.data.notes.map(note => `
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>` : ''}
${note.author ? `<div class="widget-author note-author">${note.author}</div>` : ""}
</div>
</div>
`).join('');
`,
)
.join("");
return this.createCard(LanguageManager.t('dashboard.notes'), noteItems, '/TanuloErtekeles/InformaciokFeljegyzesek', LanguageManager.t('dashboard.all_messages'));
return this.createCard(
LanguageManager.t("dashboard.notes"),
noteItems,
"/TanuloErtekeles/InformaciokFeljegyzesek",
LanguageManager.t("dashboard.all_messages"),
);
}
createExamCard() {
const examItems = this.data.upcomingExams.map(exam => `
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 class="widget-subtitle exam-type">${exam.type || ""}</div>
</div>
<div class="widget-date exam-date">${DashboardUtils.formatHungarianDate(exam.date)}</div>
</div>
</div>
`).join('');
`,
)
.join("");
return this.createCard(LanguageManager.t('dashboard.exams'), examItems, '/Tanulo/TanuloBejelentettSzamonkeresek', LanguageManager.t('dashboard.all_exams'));
return this.createCard(
LanguageManager.t("dashboard.exams"),
examItems,
"/Tanulo/TanuloBejelentettSzamonkeresek",
LanguageManager.t("dashboard.all_exams"),
);
}
createCard(title, content, linkHref, linkText) {
@@ -353,7 +483,7 @@ class DashboardRenderer {
<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>`}
${content || `<div class="widget-empty">${LanguageManager.t("dashboard.not_supported")}</div>`}
</div>
<div class="widget-footer">
<a href="${linkHref}" class="widget-link more-link">
@@ -367,15 +497,32 @@ class DashboardRenderer {
`;
}
render() {
document.body.innerHTML = `
<div class="kreta-container">
${createTemplate.header()}
${this.generateMainContent()}
</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();
}
}
@@ -384,37 +531,61 @@ class DashboardApplication {
this.init();
}
async loadBulletinSettings() {
try {
const settings = await storageManager.get("pageSettings_bulletin", {});
return {
hideGrades: settings.hideGrades || false,
hideAbsences: settings.hideAbsences || false,
hideNotes: settings.hideNotes || false,
hideExams: settings.hideExams || false
};
} catch (error) {
console.error("Error loading bulletin settings:", error);
return {
hideGrades: false,
hideAbsences: false,
hideNotes: false,
hideExams: false
};
}
}
async init() {
if (!window.location.href.includes('/Intezmeny/Faliujsag')) {
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 (
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));
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');
const newsContainer = document.querySelector(
".faliujsag-lista, #faliujsagLista",
);
if (newsContainer) {
const possibleSelectors = [
'.nb-item',
'.news-item',
'.faliujsag-item',
'.list-group-item',
'li',
".nb-item",
".news-item",
".faliujsag-item",
".list-group-item",
"li",
'div[class*="item"]',
'div[class*="news"]'
'div[class*="news"]',
];
for (const selector of possibleSelectors) {
const items = newsContainer.querySelectorAll(selector);
if (items.length > 0) {
@@ -422,30 +593,34 @@ class DashboardApplication {
break;
}
}
if (!newsItemsFound) {
if (newsContainer.children.length > 0 || newsContainer.textContent.trim().length > 0) {
if (
newsContainer.children.length > 0 ||
newsContainer.textContent.trim().length > 0
) {
newsItemsFound = true;
}
}
}
if (!newsItemsFound) {
attempts++;
await new Promise(resolve => setTimeout(resolve, 100));
await new Promise((resolve) => setTimeout(resolve, 100));
}
}
try {
const dataManager = new DashboardDataManager();
const dashboardData = await dataManager.extractAllData();
createTemplate.importFonts();
const renderer = new DashboardRenderer(dashboardData);
renderer.render();
const bulletinSettings = await this.loadBulletinSettings();
const renderer = new DashboardRenderer(dashboardData, bulletinSettings);
await renderer.render();
} catch (error) {
console.error('Error initializing dashboard:', error);
console.error("Error initializing dashboard:", error);
}
}
}
new DashboardApplication();
new DashboardApplication();

BIN
fonts/Icons.woff2 Normal file

Binary file not shown.

View File

@@ -1,292 +1,57 @@
* {
box-sizing: border-box;
margin: 0;
padding: 0;
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 {
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;
background-color:var(--background) !important;
}
.forgot-container {
width: 90%;
max-width: 500px;
padding: 20px;
margin: 0 auto;
.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%;
}
.forgot-card {
background: var(--card-card);
padding: 24px;
margin-bottom: 16px;
border-radius: 24px;
box-shadow: 0px 1px var(--shadow-blur) 0px var(--accent-shadow);
.maintenance-logo {
width:128px;
height:128px;
margin:0 auto 2rem;
}
.forgot-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;
.maintenance-title {
font-size:1.5rem;
font-weight:600;
margin-bottom:1rem;
color:var(--accent-accent);
font-family:'Montserrat',sans-serif;
}
.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;
justify-content: center;
gap: 12px;
.maintenance-message {
font-size:1rem;
line-height:1.5;
margin-bottom:1.5rem;
color:var(--text-primary);
font-family:'Figtree',sans-serif;
}
.logo {
width: 48px;
height: 48px;
border-radius: 12px;
.maintenance-footer {
font-size:0.875rem;
color:var(--text-secondary);
margin-top:2rem;
font-family:'Figtree',sans-serif;
}
.forgot-title {
color: var(--text-primary);
text-align: center;
font-family: Montserrat;
font-size: 24px;
font-style: normal;
font-weight: 700;
line-height: normal;
margin-bottom: 24px;
.maintenance-cactus {
position:fixed;
bottom:0px;
right:20px;
width:120px;
height:120px;
opacity:1;
z-index:1000;
}
.forgot-form {
width: 100%;
=======
.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;
margin-bottom: 8px;
color: var(--text-primary);
font-family: Montserrat;
font-size: 14px;
font-weight: 600;
}
.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;
width: 100%;
font-size: 16px;
transition: border-color 0.2s ease;
}
.form-control:focus {
outline: none;
border-color: var(--accent-accent) !important;
}
.form-control::placeholder {
color: var(--text-secondary) !important;
}
.form-control.error {
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;
}
.error-message.show {
display: block;
animation: fadeIn 0.2s ease;
border-color: var(--accent);
}
.form-actions {
margin-top: 24px;
display: flex;
flex-direction: column;
gap: 16px;
}
.help-link {
color: var(--text-primary);
font-size: 14px;
font-weight: 500;
text-decoration: none;
transition: color 0.2s ease;
text-align: center;
}
.help-link:hover {
color: var(--text-teritary);
}
.btn-submit {
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-submit:hover {
background: var(--text-teritary);
}
.btn-submit:disabled {
opacity: 0.7;
cursor: not-allowed;
}
.message {
padding: 12px 16px;
border-radius: 12px;
margin-bottom: 16px;
font-size: 14px;
font-weight: 500;
animation: fadeIn 0.3s ease;
text-align: center;
}
.message.success {
background: var(--success-card);
color: var(--success-text);
border: 1px solid var(--success-accent);
}
.message.error {
background: var(--error-card);
color: var(--error-text);
border: 1px solid var(--error-accent);
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(-4px); }
to { opacity: 1; transform: translateY(0); }
}
@media (max-width: 480px) {
.forgot-container {
padding: 16px;
}
.forgot-card {
padding: 20px;
}
.forgot-header {
margin-bottom: 20px;
}
.logo-text {
font-size: 18px;
}
.forgot-title {
font-size: 20px;
}
.form-control {
height: 44px;
font-size: 14px;
}
.btn-submit {
padding: 10px;
font-size: 14px;
}
.help-link {
font-size: 12px;
}
}
@media (max-width: 600px) {
.forgot-container {
padding: 16px;
}
.forgot-card {
padding: 20px;
}
.form-actions {
flex-direction: column;
gap: 16px;
align-items: stretch;
}
}

View File

@@ -1,53 +1,17 @@
(() => {
const renderRecaptcha = () => {
const container = document.getElementById('recaptcha-container');
if (container && typeof grecaptcha !== 'undefined' && grecaptcha.render) {
try {
grecaptcha.render('recaptcha-container', {
'sitekey': '6LfKURIqAAAAAD5bF2evQ-_Sf6MRrOkUEBwb_mMy',
'theme': 'light'
});
} catch (error) {
console.error('Error rendering reCAPTCHA:', error);
}
}
};
// reCAPTCHA functionality removed for security compliance
const loadDependencies = async () => {
if (typeof cookieManager === 'undefined') {
const cookieScript = document.createElement('script');
cookieScript.src = chrome.runtime.getURL('tools/cookieManager.js');
document.head.appendChild(cookieScript);
await new Promise(resolve => {
cookieScript.onload = resolve;
});
}
if (typeof LanguageManager === 'undefined') {
const langScript = document.createElement('script');
langScript.src = chrome.runtime.getURL('global/language.js');
document.head.appendChild(langScript);
await new Promise(resolve => {
langScript.onload = resolve;
});
await new Promise(resolve => setTimeout(resolve, 100));
}
if (!document.querySelector('script[src*="recaptcha"]')) {
await new Promise((resolve) => {
window.onRecaptchaLoad = resolve;
const script = document.createElement('script');
script.src = 'https://www.google.com/recaptcha/api.js?hl=hu&onload=onRecaptchaLoad&render=explicit';
document.head.appendChild(script);
});
}
// reCAPTCHA functionality removed for security compliance
// Extension now works without external script dependencies
};
const createPageStructure = () => {
document.body.innerHTML = `
// 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>
@@ -64,7 +28,7 @@
<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>
@@ -88,7 +52,7 @@
<div class="error-message" data-i18n="forgotpassword.email_required">Az e-mail cím megadása kötelező</div>
</div>
<div id="recaptcha-container"></div>
<!-- reCAPTCHA container removed for security compliance -->
<div class="form-actions">
<a href="/Adminisztracio/Login" class="help-link" data-i18n="forgotpassword.back_to_login">
@@ -101,19 +65,31 @@
</form>
</div>
</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');
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);
if (message.action === "toggleTheme") {
document.documentElement.setAttribute(
"data-theme",
message.darkMode ? "dark" : "light",
);
localStorage.setItem("darkMode", message.darkMode);
}
});
@@ -121,34 +97,34 @@
let attempts = 0;
const maxAttempts = 50;
const waitForLanguageManager = () => {
return new Promise((resolve) => {
const checkLanguageManager = () => {
attempts++;
if (typeof LanguageManager !== 'undefined' && LanguageManager.t) {
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');
console.warn("LanguageManager not available, using fallback texts");
resolve();
}
};
checkLanguageManager();
});
};
await waitForLanguageManager();
if (typeof LanguageManager !== 'undefined' && LanguageManager.t) {
const elements = document.querySelectorAll('[data-i18n]');
elements.forEach(element => {
const key = element.getAttribute('data-i18n');
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');
const attr = element.getAttribute("data-i18n-attr");
if (attr) {
element.setAttribute(attr, translation);
} else {
@@ -157,172 +133,158 @@
}
});
}
setTimeout(() => {
renderRecaptcha();
}, 500);
// 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);
const setupFormValidation = () => {
const form = document.getElementById("forgotForm");
const inputs = form.querySelectorAll(".form-control");
setTimeout(() => {
if (messageDiv.parentNode) {
messageDiv.remove();
}
}, 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;
}
inputs.forEach((input) => {
input.addEventListener("input", () => {
validateInput(input);
});
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');
}
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();
}
}, 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;
}
});
let recaptchaResponse = '';
if (typeof grecaptcha !== 'undefined') {
recaptchaResponse = grecaptcha.getResponse();
if (!recaptchaResponse) {
showMessage(LanguageManager.t('forgotpassword.recaptcha_required'), true);
isValid = false;
}
} else {
showMessage('reCAPTCHA nem töltődött be. Kérjük, frissítse az oldalt!', 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;
}
if (!isValid) {
return;
}
// reCAPTCHA validation removed for security compliance
const submitButton = form.querySelector('.btn-submit');
const originalText = submitButton.textContent;
submitButton.disabled = true;
submitButton.textContent = LanguageManager.t('loading.text') || 'Küldés...';
if (!isValid) {
return;
}
try {
const formData = new FormData(form);
const submitButton = form.querySelector(".btn-submit");
const originalText = submitButton.textContent;
submitButton.disabled = true;
submitButton.textContent = LanguageManager.t("loading.text") || "Küldés...";
if (typeof grecaptcha !== 'undefined') {
formData.append('ReCaptcha', grecaptcha.getResponse());
}
const response = await fetch('/Adminisztracio/ElfelejtettJelszo/LinkKuldes', {
method: 'POST',
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'
}
});
"X-Requested-With": "XMLHttpRequest",
},
},
);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const result = await response.json();
if (result.Success) {
showMessage(LanguageManager.t('forgotpassword.success_message'));
form.reset();
if (typeof grecaptcha !== 'undefined') {
grecaptcha.reset();
}
setTimeout(() => {
window.location.href = '/Adminisztracio/Login';
}, 3000);
} else {
showMessage(result.Message || LanguageManager.t('forgotpassword.error_message'), true);
if (typeof grecaptcha !== 'undefined') {
grecaptcha.reset();
}
}
} catch (error) {
console.error('Password reset error:', error);
showMessage(LanguageManager.t('forgotpassword.error_message'), true);
if (typeof grecaptcha !== 'undefined') {
grecaptcha.reset();
}
} finally {
submitButton.disabled = false;
submitButton.textContent = originalText;
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
};
if (window.location.href.includes('/Adminisztracio/ElfelejtettJelszo')) {
transformForgotPasswordPage().catch(console.error);
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);
}
})();

View File

@@ -1,131 +1,189 @@
(function() {
let currentLanguage = 'hu';
let translations = {};
async function setLanguage(language) {
(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 {
currentLanguage = language;
cookieManager.set('languagePreference', language);
localStorage.setItem('languagePreference', language);
await loadTranslations(language);
applyTranslations();
window.dispatchEvent(new CustomEvent('languageChanged', {
detail: { language: language }
}));
chrome.runtime.sendMessage({
action: 'languageChanged',
language: language
}).catch(() => {});
} catch (error) {}
const fallbackUrl = chrome.runtime.getURL("i18n/hu.json");
const response = await fetch(fallbackUrl);
translations = await response.json();
} catch (fallbackError) {}
}
}
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 applyTranslations() {
const elements = document.querySelectorAll('[data-i18n]');
elements.forEach(element => {
const key = element.getAttribute('data-i18n');
const translation = getTranslation(key);
if (translation && translation !== key) {
const attr = element.getAttribute('data-i18n-attr');
if (attr) {
element.setAttribute(attr, translation);
} else {
element.textContent = translation;
}
}
});
}
function getTranslation(keyPath, fallback = '') {
const keys = keyPath.split('.');
let value = translations;
for (const key of keys) {
if (value && typeof value === 'object' && key in value) {
value = value[key];
} else {
return fallback || keyPath;
}
}
return typeof value === 'string' ? value : fallback || keyPath;
}
async function initializeLanguage() {
const cookieLanguage = cookieManager.get('languagePreference');
const localStorageLanguage = localStorage.getItem('languagePreference');
const language = cookieLanguage || localStorageLanguage || 'hu';
await setLanguage(language);
if (cookieLanguage !== localStorageLanguage) {
if (cookieLanguage) {
localStorage.setItem('languagePreference', cookieLanguage);
} else if (localStorageLanguage) {
cookieManager.set('languagePreference', localStorageLanguage);
}
}
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initializeLanguage);
} else {
initializeLanguage();
}
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.action === 'changeLanguage') {
setLanguage(message.language);
sendResponse({ success: true });
}
if (message.action === 'getLanguage') {
sendResponse({ language: currentLanguage });
}
return true;
}
});
}
window.LanguageManager = {
getCurrentLanguage: () => currentLanguage,
changeLanguage: setLanguage,
t: getTranslation,
getAvailableLanguages: () => [
{ code: 'hu', name: 'Magyar' },
{ code: 'en', name: 'English' }
]
};
})();
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,89 +1,57 @@
@font-face {
font-family: 'Montserrat';
src: url('chrome-extension://__MSG_@@extension_id__/fonts/Montserrat-Regular.woff2') format('woff2');
font-weight: 400;
font-style: normal;
}
@font-face {
font-family: 'Montserrat';
src: url('chrome-extension://__MSG_@@extension_id__/fonts/Montserrat-Medium.woff2') format('woff2');
font-weight: 500;
font-style: normal;
}
@font-face {
font-family: 'Montserrat';
src: url('chrome-extension://__MSG_@@extension_id__/fonts/Montserrat-SemiBold.woff2') format('woff2');
font-weight: 600;
font-style: normal;
}
@font-face {
font-family: 'Figtree';
src: url('chrome-extension://__MSG_@@extension_id__/fonts/Figtree-Regular.woff2') format('woff2');
font-weight: 400;
font-style: normal;
}
body.maintenance-mode {
margin: 0;
padding: 0;
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
background-color: var(--background);
color: var(--text-primary);
font-family: 'Figtree', 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-family: 'Montserrat', sans-serif;
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;
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;
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;
}
position:fixed;
bottom:0px;
right:20px;
width:120px;
height:120px;
opacity:1;
z-index:1000;
}

View File

@@ -1,99 +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');
const bodyText = document.body ? document.body.textContent : '';
const maintenanceContent = document.querySelector(".login_content");
const bodyText = document.body ? document.body.textContent : "";
const specificMaintenanceMessage = 'Kedves Felhasználók! A KRÉTA rendszer jelenleg frissítés alatt van, hamarosan újra elérhetővé válik. Köszönjük türelmüket és megértésüket! KRÉTA Csapat';
const hasSpecificMessage = bodyText.includes('Kedves Felhasználók!') &&
bodyText.includes('A KRÉTA rendszer jelenleg frissítés alatt van') &&
bodyText.includes('KRÉTA Csapat');
const 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 hasGeneralMaintenance =
maintenanceContent &&
(maintenanceContent.textContent.includes("frissítés alatt") ||
maintenanceContent.textContent.includes("under maintenance"));
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();
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 overlays = document.querySelectorAll('.modalBckgroundMain, .loading-screen, #KretaProgressBar');
overlays.forEach(overlay => overlay.remove());
};
removeLoadingElements();
setTimeout(removeLoadingElements, 100);
const removeLoadingElements = () => {
const loadingScreen = document.querySelector(".loading-screen");
if (loadingScreen) loadingScreen.remove();
const existingStyles = document.querySelectorAll('link[rel="stylesheet"], style');
existingStyles.forEach(style => style.remove());
const kretaProgressBar = document.querySelector("#KretaProgressBar");
if (kretaProgressBar) kretaProgressBar.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 modalBackground = document.querySelector(".modalBckgroundMain");
if (modalBackground) modalBackground.remove();
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);
}
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,125 +1,114 @@
.kreta-header {
padding: clamp(1rem, 3vw, 2rem);
display: grid;
grid-template-columns: minmax(300px, 400px) 1fr minmax(200px, 300px);
padding: 1rem 2rem;
display: flex;
align-items: center;
gap: 1rem;
justify-content: space-between;
background-color: var(--background);
gap: 1.5rem;
}
.school-info {
margin: 0;
display: flex;
flex-direction: column;
min-width: 200px;
flex-shrink: 0;
}
.logo-text {
color: var(--text-primary);
font-size: 24px;
font-size: 22px;
font-weight: 600;
margin: 0 0 0.5rem;
margin: 0;
display: flex;
align-items: center;
gap: 0.5rem;
}
.logo {
width: 24px;
height: 24px;
border-radius: 8px;
margin-right: 0.5rem;
}
.school-details {
color: var(--text-secondary);
font-size: 14px;
font-size: 13px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 300px;
margin-top: 0.25rem;
}
.kreta-nav {
padding: 0 clamp(0.5rem, 3vw, 1.5rem);
position: sticky;
top: 0;
z-index: 100;
display: flex;
justify-content: center;
flex: 1;
}
.nav-links {
display: flex;
gap: clamp(0.5rem, 2vw, 1rem);
padding: 0.25rem;
gap: 0.5rem;
align-items: center;
}
.nav-item {
display: flex;
align-items: center;
padding: clamp(0.5rem, 1.5vw, 1rem) 0.5rem;
padding: 10px 16px;
color: var(--text-secondary);
text-decoration: none;
font-weight: 500;
font-size: 14px;
white-space: nowrap;
border-radius: 8px;
border-radius: 12px;
transition: all 0.2s ease;
gap: 0.5rem;
text-decoration: none;
}
.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);
}
.nav-item:hover {
color: var(--text-primary);
background-color: var(--hover);
border-radius: 8px;
text-decoration: none;
}
.nav-item.active:hover {
color: var(--accent-accent);
background-color: var(--accent-hover);
color: var(--text-primary);
background-color: var(--accent-15);
text-decoration: none;
}
.nav-item.active {
color: var(--accent-accent);
background: var(--accent-15);
}
.nav-item img,
.nav-item svg {
width: 24px;
height: 24px;
width: 20px;
height: 20px;
}
.nav-item.active svg path {
fill: var(--accent-accent);
.nav-item.active img {
filter: var(--icon-filter) !important;
}
.user-profile {
position: relative;
justify-self: flex-end;
flex-shrink: 0;
}
.user-dropdown-btn {
display: flex;
align-items: center;
gap: 1rem;
background: none;
gap: 0.75rem;
background: var(--button-secondaryFill);
border: none;
cursor: pointer;
padding: 0.5rem;
border-radius: 8px;
transition: background-color 0.2s;
padding: 8px 12px;
border-radius: 12px;
transition: all 0.2s ease;
box-shadow: 0px 1px var(--shadow-blur, 2px) 0px var(--accent-shadow);
}
.user-dropdown-btn:hover {
background: var(--hover);
background: var(--accent-15);
}
.user-info {
@@ -129,26 +118,54 @@
.user-name {
display: block;
color: var(--text-primary);
font-size: 16px;
font-size: 14px;
font-weight: 500;
}
.nav-logout-timer {
display: block;
color: var(--text-secondary);
font-size: 14px;
font-size: 12px;
}
.user-avatar-icon {
width: 32px;
height: 32px;
border-radius: 50%;
background: var(--accent-15);
display: flex;
align-items: center;
justify-content: center;
}
.user-avatar-icon svg {
width: 18px;
height: 18px;
fill: var(--accent-accent);
}
.dropdown-arrow {
width: 16px;
height: 16px;
fill: var(--text-secondary);
transition: transform 0.2s ease;
}
.user-dropdown-btn.open .dropdown-arrow {
transform: rotate(180deg);
}
.user-dropdown {
position: absolute;
top: 100%;
top: calc(100% + 8px);
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;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
min-width: 180px;
display: none;
z-index: 1000;
overflow: hidden;
}
.user-dropdown.show {
@@ -160,23 +177,33 @@
display: flex;
align-items: center;
gap: 0.75rem;
padding: 0.75rem 1rem;
padding: 12px 16px;
color: var(--text-primary);
text-decoration: none;
transition: background-color 0.2s;
font-size: 14px;
}
.dropdown-item:hover {
background: var(--hover);
color: var(--accent-accent);
border-radius: 8px;
text-decoration: none;
}
.dropdown-item img {
width: 18px;
height: 18px;
filter: var(--icon-filter);
}
.dropdown-item:hover img {
filter: none;
}
@keyframes dropdownShow {
from {
opacity: 0;
transform: translateY(-10px);
transform: translateY(-8px);
}
to {
opacity: 1;
@@ -184,218 +211,281 @@
}
}
@media (max-width: 1200px) {
.kreta-header {
grid-template-columns: minmax(250px, 350px) 1fr minmax(180px, 250px);
@keyframes dropdownShowUp {
from {
opacity: 0;
transform: translateY(8px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* Hamburger menu styles */
.nav-toggle {
.mobile-header,
.mobile-bottom-nav,
.mobile-user-dropdown {
display: none;
background: none;
border: none;
cursor: pointer;
padding: 0.5rem;
border-radius: 8px;
transition: background-color 0.2s;
}
.nav-toggle:hover {
background: var(--hover);
}
.nav-toggle svg {
width: 24px;
height: 24px;
fill: var(--text-primary);
}
@media (max-width: 768px) {
@media (max-width: 900px) {
.kreta-header {
grid-template-columns: 1fr auto auto;
grid-template-areas:
"school toggle user"
"nav nav nav";
padding: 1rem;
gap: 0.5rem;
display: none;
}
.nav-toggle {
display: block;
grid-area: toggle;
}
.school-info {
grid-area: school;
max-width: none;
.mobile-header {
display: flex;
align-items: center;
gap: 0.5rem;
justify-content: center;
padding: 1rem;
background-color: var(--background);
gap: 1rem;
}
.logo-text {
margin: 0;
font-size: 18px;
.mobile-header .school-info {
align-items: center;
text-align: center;
min-width: auto;
}
.school-details {
font-size: 11px;
max-width: 200px;
.mobile-header .logo-text {
font-size: 20px;
justify-content: center;
}
.kreta-nav {
grid-area: nav;
padding: 0;
margin-top: 0.5rem;
display: none;
.mobile-header .school-details {
max-width: 280px;
text-align: center;
}
.kreta-nav.show {
.mobile-bottom-nav {
display: flex;
animation: slideDown 0.3s ease;
position: fixed;
bottom: 0;
left: 0;
right: 0;
background: var(--background);
padding: 8px 16px;
padding-bottom: calc(8px + env(safe-area-inset-bottom, 0px));
z-index: 1000;
box-shadow: 0 -2px 16px rgba(0, 0, 0, 0.1);
justify-content: space-between;
align-items: center;
gap: 8px;
border-radius: 20px 20px 0 0;
}
.kreta-nav::-webkit-scrollbar {
display: none;
}
.nav-links {
.mobile-nav-item {
display: flex;
flex-direction: column;
width: 100%;
gap: 0.5rem;
align-items: center;
justify-content: center;
flex: 1;
padding: 8px 4px;
color: var(--text-secondary);
text-decoration: none;
font-size: 10px;
font-weight: 500;
border-radius: 12px;
transition: all 0.2s ease;
gap: 4px;
background: var(--card-card);
box-shadow: 0px 1px var(--shadow-blur, 2px) 0px var(--accent-shadow);
}
.mobile-nav-item:hover {
color: var(--text-primary);
background: var(--accent-15);
text-decoration: none;
}
.mobile-nav-item.active {
color: var(--accent-accent);
background: var(--accent-15);
}
.mobile-nav-item img {
width: 22px;
height: 22px;
transition: filter 0.2s ease;
}
.mobile-nav-item.active img {
filter: var(--icon-filter) !important;
}
.mobile-nav-item.active {
color: var(--accent-accent) !important;
background: var(--accent-15);
}
.mobile-user-btn {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
flex: 1;
padding: 8px 4px;
color: var(--text-secondary);
background: var(--card-card);
box-shadow: 0px 1px var(--shadow-blur, 2px) 0px var(--accent-shadow);
border: none;
font-size: 10px;
font-weight: 500;
border-radius: 12px;
transition: all 0.2s ease;
gap: 4px;
cursor: pointer;
}
.mobile-user-btn:hover {
color: var(--text-primary);
background: var(--accent-15);
}
.mobile-user-btn.active {
color: var(--accent-accent);
background: var(--accent-15);
}
.mobile-user-btn .user-avatar-icon {
width: 22px;
height: 22px;
}
.mobile-user-btn .user-avatar-icon svg {
width: 14px;
height: 14px;
}
.mobile-user-dropdown {
position: fixed;
bottom: calc(70px + env(safe-area-inset-bottom, 0px));
right: 12px;
background: var(--card-card);
border-radius: 12px;
padding: 1rem;
box-shadow: 0px 1px var(--shadow-blur) 0px var(--accent-shadow);
box-shadow: 0 -4px 24px rgba(0, 0, 0, 0.15);
min-width: 200px;
display: none;
z-index: 1001;
overflow: hidden;
}
.nav-item {
width: 100%;
justify-content: flex-start;
padding: 0.75rem;
.mobile-user-dropdown.show {
display: block;
animation: dropdownShowUp 0.2s ease;
}
.mobile-dropdown-header {
padding: 12px 16px;
border-bottom: 1px solid var(--hover);
text-align: center;
}
.mobile-dropdown-header .user-name {
font-size: 14px;
font-weight: 600;
color: var(--text-primary);
}
.mobile-dropdown-header .nav-logout-timer {
font-size: 12px;
color: var(--text-secondary);
margin-top: 2px;
}
.mobile-dropdown-item {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 12px 16px;
color: var(--text-primary);
text-decoration: none;
transition: background-color 0.2s;
font-size: 14px;
}
.user-profile {
grid-area: user;
.mobile-dropdown-item:hover {
background: var(--hover);
color: var(--accent-accent);
text-decoration: none;
}
.user-info {
text-align: right;
max-width: 120px;
.mobile-dropdown-item img {
width: 18px;
height: 18px;
filter: var(--icon-filter);
}
.user-name {
font-size: 13px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
.mobile-dropdown-item:hover img {
filter: none;
}
.nav-logout-timer {
font-size: 11px;
body {
padding-bottom: calc(70px + env(safe-area-inset-bottom, 0px));
}
}
@media (max-width: 480px) {
.kreta-header {
grid-template-columns: 1fr auto auto;
grid-template-areas:
"school toggle user"
"nav nav nav";
.mobile-header {
padding: 0.75rem;
gap: 0.25rem;
}
.school-info {
min-width: 0;
flex: 1;
.mobile-header .logo-text {
font-size: 18px;
}
.logo-text {
font-size: 16px;
.mobile-header .school-details {
font-size: 11px;
max-width: 220px;
}
.school-details {
font-size: 10px;
max-width: 150px;
.mobile-nav-item {
padding: 6px 4px;
font-size: 9px;
}
.kreta-nav {
display: none;
.mobile-nav-item img {
width: 20px;
height: 20px;
}
.kreta-nav.show {
display: flex;
animation: slideDown 0.3s ease;
.mobile-user-btn {
padding: 6px 4px;
font-size: 9px;
}
.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;
.mobile-user-btn .user-avatar-icon {
width: 20px;
height: 20px;
}
}
@media (max-width: 360px) {
.kreta-header {
padding: 0.5rem;
.mobile-header .logo-text {
font-size: 16px;
}
.logo-text {
font-size: 14px;
.mobile-header .school-details {
font-size: 10px;
max-width: 180px;
}
.school-details {
font-size: 9px;
max-width: 120px;
.mobile-nav-item span {
display: none;
}
.user-info {
max-width: 80px;
.mobile-user-btn span {
display: none;
}
.user-name {
font-size: 11px;
.mobile-nav-item img {
width: 24px;
height: 24px;
}
.nav-logout-timer {
font-size: 9px;
}
}
@keyframes slideDown {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
.mobile-user-btn .user-avatar-icon {
width: 24px;
height: 24px;
}
}

View File

@@ -1,122 +1,127 @@
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: LanguageManager.t('navigation.school_default'),
USER: LanguageManager.t('navigation.user_default'),
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 updateTimer = () => {
const minutes = Math.floor(timeLeft / 60);
const seconds = timeLeft % 60;
const timeText = `${minutes}:${seconds.toString().padStart(2, "0")}`;
updateTimer();
setInterval(updateTimer, 1000);
const desktopTimer = document.querySelector("#logoutTimer");
const mobileTimer = document.querySelector("#mobileLogoutTimer");
if (desktopTimer) desktopTimer.textContent = timeText;
if (mobileTimer) mobileTimer.textContent = timeText;
if (timeLeft <= 0) {
window.location.href = "/Home/Logout";
}
timeLeft--;
};
updateTimer();
setInterval(updateTimer, 1000);
}
document.addEventListener('DOMContentLoaded', () => {
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");
userBtn?.classList.toggle("open");
});
const mobileUserBtn = document.querySelector("#mobileUserBtn");
const mobileUserDropdown = document.querySelector("#mobileUserDropdown");
mobileUserBtn?.addEventListener("click", (e) => {
e.stopPropagation();
mobileUserDropdown?.classList.toggle("show");
mobileUserBtn?.classList.toggle("active");
});
document.addEventListener("click", (e) => {
if (!userBtn?.contains(e.target) && !userDropdown?.contains(e.target)) {
userDropdown?.classList.remove("show");
userBtn?.classList.remove("open");
}
if (!mobileUserBtn?.contains(e.target) && !mobileUserDropdown?.contains(e.target)) {
mobileUserDropdown?.classList.remove("show");
mobileUserBtn?.classList.remove("active");
}
});
const mobileDropdownItems = document.querySelectorAll(".mobile-dropdown-item");
mobileDropdownItems.forEach(item => {
item.addEventListener("click", () => {
mobileUserDropdown?.classList.remove("show");
mobileUserBtn?.classList.remove("active");
});
});
}
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");
});
document.getElementById("mobileSettingsBtn")?.addEventListener("click", (e) => {
e.preventDefault();
e.stopPropagation();
const url = chrome.runtime.getURL("settings/index.html");
window.open(url, "_blank", "width=400,height=600");
});
}
function setupMobileNavigation() {
setTimeout(() => {
const navToggle = document.querySelector('.nav-toggle');
const nav = document.querySelector('.kreta-nav');
if (!navToggle || !nav) {
return;
}
navToggle.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
nav.classList.toggle('show');
});
document.addEventListener('click', (e) => {
if (!nav.contains(e.target) && !navToggle.contains(e.target)) {
nav.classList.remove('show');
}
});
const navItems = document.querySelectorAll('.nav-item');
navItems.forEach(item => {
item.addEventListener('click', () => {
nav.classList.remove('show');
});
});
}, 100);
}
document.addEventListener('DOMContentLoaded', () => {
updateHeaderInfo();
setupUserDropdown();
setupSettingsButton();
setupMobileNavigation();
document.addEventListener("DOMContentLoaded", async () => {
await updateHeaderInfo();
setupUserDropdown();
setupSettingsButton();
});

View File

@@ -1,160 +1,170 @@
: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;
--icon-filter:brightness(0) saturate(100%) invert(76%) sepia(65%) saturate(482%) hue-rotate(35deg) brightness(98%) contrast(89%);
--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;
--icon-filter:brightness(0) saturate(100%) invert(76%) sepia(65%) saturate(482%) hue-rotate(35deg) brightness(98%) contrast(89%);
--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;
--icon-filter:brightness(0) saturate(100%) invert(76%) sepia(65%) saturate(482%) hue-rotate(35deg) brightness(98%) contrast(89%);
--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;
}
::-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);
}
/* SVG ikonok színezése a kiemelő színnel */
.icon-accent,
svg.icon-accent,
img.icon-accent {
filter: var(--icon-filter);
}
* {
transition: background-color 0.2s ease, color 0.2s ease, border-color 0.2s ease;
}
/* Minden SVG-re alkalmazható osztály */
.themed-icon {
filter: var(--icon-filter);
transition: filter 0.2s ease;
}
/* Automatikus ikon színezés - minden icons/ mappából származó kép */
img[src*="icons/"],
img[src*="icons%2F"],
img[src$=".svg"] {
filter: var(--icon-filter);
transition: filter 0.2s ease;
}
/* Warning színű ikonok (narancssárga) */
img[src*="icons/dkt.svg"],
img[src*="icons%2Fdkt.svg"],
img[src*="icons/assigment.svg"],
img[src*="icons%2Fassigment.svg"],
img[src$="dkt.svg"],
img[src$="assigment.svg"] {
filter: brightness(0) saturate(100%) invert(67%) sepia(44%) saturate(1057%) hue-rotate(349deg) brightness(101%) contrast(101%) !important;
}
/* Error színű ikonok (piros/rózsaszín) */
img[src*="icons/logout.svg"],
img[src*="icons%2Flogout.svg"],
img[src$="logout.svg"] {
filter: brightness(0) saturate(100%) invert(52%) sepia(75%) saturate(1637%) hue-rotate(306deg) brightness(101%) contrast(101%) !important;
}

View File

@@ -1,132 +1,322 @@
(() => {
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(() => {
let customThemes = [];
});
} catch (error) {
console.error('Error setting theme:', error);
}
async function loadCustomThemes() {
try {
const saved = await storageManager.get("customThemes", []);
customThemes = Array.isArray(saved) ? saved : [];
} catch (error) {
console.error("Error loading custom themes:", error);
customThemes = [];
}
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 applyCustomThemeColors(theme) {
const root = document.documentElement;
const isDark = theme.mode === "dark";
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);
}
}
}
root.style.setProperty("--background", theme.colors.background);
root.style.setProperty("--background-0", theme.colors.background + "00");
root.style.setProperty("--card-card", theme.colors.card);
root.style.setProperty("--card-translucent", theme.colors.card + "80");
root.style.setProperty("--accent-accent", theme.colors.accent);
root.style.setProperty("--text-primary", theme.colors.text);
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initializeTheme);
// Származtatott színek
root.style.setProperty("--text-secondary", theme.colors.text + "cc");
root.style.setProperty("--text-teritary", theme.colors.text + "80");
root.style.setProperty("--accent-15", theme.colors.accent + "26");
root.style.setProperty("--button-secondaryFill", isDark ? lightenColor(theme.colors.card, 10) : darkenColor(theme.colors.card, 5));
root.style.setProperty("--accent-secondary", isDark ? lightenColor(theme.colors.accent, 20) : darkenColor(theme.colors.accent, 20));
root.style.setProperty("--shadow-blur", isDark ? "0" : "2px");
root.style.setProperty("--accent-shadow", isDark ? "#0000" : theme.colors.accent + "26");
// SVG ikon filter beállítása a kiemelő szín alapján
root.style.setProperty("--icon-filter", hexToFilter(theme.colors.accent));
}
// Hex szín átalakítása CSS filterré
function hexToFilter(hex) {
// Hex -> RGB
const r = parseInt(hex.slice(1, 3), 16);
const g = parseInt(hex.slice(3, 5), 16);
const b = parseInt(hex.slice(5, 7), 16);
// RGB -> HSL
const rNorm = r / 255;
const gNorm = g / 255;
const bNorm = b / 255;
const max = Math.max(rNorm, gNorm, bNorm);
const min = Math.min(rNorm, gNorm, bNorm);
let h, s, l = (max + min) / 2;
if (max === min) {
h = s = 0;
} else {
initializeTheme();
const d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch (max) {
case rNorm: h = ((gNorm - bNorm) / d + (gNorm < bNorm ? 6 : 0)) / 6; break;
case gNorm: h = ((bNorm - rNorm) / d + 2) / 6; break;
case bNorm: h = ((rNorm - gNorm) / d + 4) / 6; break;
}
}
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.action === 'changeTheme') {
setTheme(message.theme);
sendResponse({ success: true });
const hue = Math.round(h * 360);
const saturation = Math.round(s * 100);
const lightness = Math.round(l * 100);
// Filter létrehozása
// Ez egy egyszerűsített megközelítés - a pontos szín reprodukálásához
// komplexebb számítás kellene, de ez megfelelő a legtöbb esetben
const brightnessVal = lightness > 50 ? 1 + (lightness - 50) / 100 : 0.5 + lightness / 100;
const saturateVal = saturation > 0 ? 1 + saturation / 100 : 0;
return `brightness(0) saturate(100%) invert(${lightness}%) sepia(${saturation}%) saturate(${Math.min(500, saturation * 5)}%) hue-rotate(${hue}deg) brightness(${brightnessVal}) contrast(${90 + saturation / 10}%)`;
}
function lightenColor(color, percent) {
const num = parseInt(color.replace("#", ""), 16);
const amt = Math.round(2.55 * percent);
const R = Math.min(255, (num >> 16) + amt);
const G = Math.min(255, ((num >> 8) & 0x00ff) + amt);
const B = Math.min(255, (num & 0x0000ff) + amt);
return "#" + (0x1000000 + R * 0x10000 + G * 0x100 + B).toString(16).slice(1);
}
function darkenColor(color, percent) {
const num = parseInt(color.replace("#", ""), 16);
const amt = Math.round(2.55 * percent);
const R = Math.max(0, (num >> 16) - amt);
const G = Math.max(0, ((num >> 8) & 0x00ff) - amt);
const B = Math.max(0, (num & 0x0000ff) - amt);
return "#" + (0x1000000 + R * 0x10000 + G * 0x100 + B).toString(16).slice(1);
}
function clearCustomThemeStyles() {
const root = document.documentElement;
const customProps = [
"--background", "--background-0", "--card-card", "--card-translucent",
"--accent-accent", "--text-primary", "--text-secondary", "--text-teritary",
"--accent-15", "--button-secondaryFill", "--accent-secondary",
"--shadow-blur", "--accent-shadow", "--icon-filter"
];
customProps.forEach(prop => root.style.removeProperty(prop));
}
async function setTheme(theme) {
try {
// Töröljük az előző egyéni téma stílusait
clearCustomThemeStyles();
document.documentElement.setAttribute("data-theme", theme);
await storageManager.set("themePreference", theme);
// Ha egyéni téma, alkalmazzuk a színeket
if (theme.startsWith("custom-")) {
await loadCustomThemes();
const themeId = theme.replace("custom-", "");
const customTheme = customThemes.find(t => t.id === themeId);
if (customTheme) {
applyCustomThemeColors(customTheme);
}
if (message.action === 'getTheme') {
const currentTheme = document.documentElement.getAttribute('data-theme') || 'light-green';
sendResponse({ theme: currentTheme });
}
return true;
});
let titleCheckTimeout;
const observer = new MutationObserver((mutations) => {
const currentTheme = document.documentElement.getAttribute('data-theme');
const savedTheme = cookieManager.get('themePreference') || localStorage.getItem('themePreference');
if ((!currentTheme && savedTheme) || (currentTheme !== savedTheme && savedTheme)) {
setTheme(savedTheme);
}
const titleChanged = mutations.some(mutation =>
mutation.type === 'childList' &&
mutation.target === document.head &&
Array.from(mutation.addedNodes).some(node => node.tagName === 'TITLE')
);
if (titleChanged || document.title !== 'Firka - KRÉTA') {
clearTimeout(titleCheckTimeout);
titleCheckTimeout = setTimeout(() => {
if (document.title !== 'Firka - KRÉTA') {
setPageTitleAndFavicon();
}
}, 100);
}
});
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => {
observer.observe(document.documentElement, {
attributes: true,
attributeFilter: ['data-theme']
});
observer.observe(document.head, {
childList: true,
subtree: true
});
});
} else {
observer.observe(document.documentElement, {
attributes: true,
attributeFilter: ['data-theme']
});
observer.observe(document.head, {
childList: true,
subtree: true
});
}
chrome.runtime
.sendMessage({
action: "themeChanged",
theme: theme,
})
.catch(() => {});
} catch (error) {
console.error("Error setting theme:", 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;
}
@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);
}
}
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 loadCustomThemes();
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") {
// Ha egyéni témák is jöttek az üzenetben, frissítsük a lokális listát
if (message.customThemes) {
customThemes = message.customThemes;
}
setTheme(message.theme);
sendResponse({ success: true });
}
if (message.action === "getTheme") {
const currentTheme =
document.documentElement.getAttribute("data-theme") || "light-green";
sendResponse({ theme: currentTheme });
}
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,
});
});
} else {
observer.observe(document.documentElement, {
attributes: true,
attributeFilter: ["data-theme"],
});
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,445 +0,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;
}
@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: 20px;
margin-bottom: 24px;
box-shadow: 0px 1px var(--shadow-blur) 0px var(--accent-shadow);
}
.filter-header {
margin-bottom: 16px;
}
.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-fit, minmax(200px, 1fr));
gap: 16px;
}
.filter-group {
display: flex;
flex-direction: column;
gap: 8px;
}
.filter-group label {
display: flex;
align-items: center;
gap: 8px;
color: var(--text-secondary);
font-size: 14px;
}
.filter-group select,
.filter-group 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;
}
.filter-group select:focus,
.filter-group input:focus {
outline: none;
box-shadow: 0 0 0 2px var(--accent-accent);
}
.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: flex;
flex-direction: column;
gap: 16px;
}
.homework-date-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);
}
.date-header {
padding: 16px;
color: var(--text-primary);
font-weight: 600;
display: flex;
align-items: center;
gap: 8px;
}
.homework-count {
margin-left: auto;
background: var(--accent-accent);
color: var(--button-secondaryFill);
padding: 4px 8px;
border-radius: 12px;
font-size: 14px;
}
.homework-list-items {
padding: 16px;
display: flex;
flex-direction: column;
gap: 12px;
}
.date-header h3 {
font-size: 16px;
font-weight: 600;
color: var(--text-primary);
margin: 0;
}
.homework-item {
display: flex;
flex-direction: column;
gap: 8px;
padding: 12px;
margin: 0 16px 12px 16px;
background: var(--accent-15);
border-radius: 12px;
transition: transform 0.2s ease;
}
.homework-item:hover {
transform: translateX(4px);
}
.homework-item.due-tomorrow {
background: var(--accent-accent);
color: white;
}
.homework-item.due-tomorrow .homework-subject,
.homework-item.due-tomorrow .homework-content,
.homework-item.due-tomorrow .homework-teacher {
color: white;
}
.homework-details {
display: flex;
flex-direction: column;
gap: 4px;
}
.homework-subject {
font-weight: 600;
font-size: 16px;
color: var(--text-primary);
margin-bottom: 4px;
}
.homework-content {
color: var(--text-primary);
font-size: 14px;
margin-bottom: 8px;
line-height: 1.4;
word-wrap: break-word;
overflow-wrap: break-word;
white-space: pre-wrap;
}
.homework-teacher {
color: var(--text-secondary);
font-size: 12px;
font-style: italic;
}
.empty-state {
text-align: center;
padding: 2rem;
color: var(--text-secondary);
display: none;
background: var(--card-card);
border-radius: 24px;
box-shadow: 0px 1px var(--shadow-blur) 0px var(--accent-shadow);
}
.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 fadeIn {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes dropdownShow {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@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,434 +0,0 @@
async function fetchHomeworkData() {
try {
const currentDomain = window.location.hostname;
const apiUrl = `https://${currentDomain}/api/TanuloHaziFeladatApi/GetTanulotHaziFeladatGrid?sort=HaziFeladatHatarido-asc&page=1&pageSize=100&group=&filter=&data=%7B%22RegiHaziFeladatokElrejtese%22%3Afalse%7D&_=${Date.now()}`;
const response = await fetch(apiUrl, {
method: 'GET',
credentials: 'include',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
});
if (!response.ok) {
throw new Error('Network response was not ok');
}
const data = await response.json();
return data;
} catch (error) {
console.error('Error fetching homework data:', error);
return { Data: [], Total: 0 };
}
}
async function collectHomeworkData() {
const apiData = await fetchHomeworkData();
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 = [];
if (apiData.Data && Array.isArray(apiData.Data)) {
apiData.Data.forEach(item => {
homeworkItems.push({
id: item.ID,
subject: item.TantargyNev || '',
teacher: item.TanarNeve || '',
description: item.HaziFeladatSzoveg || '',
createdDate: formatApiDate(item.HaziFeladatRogzitesDatuma),
deadline: formatApiDate(item.HaziFeladatHatarido),
completed: item.MegoldottHF_BOOL || false,
classGroup: item.OsztalyCsoport || ''
});
});
}
const groupedHomework = {};
homeworkItems.forEach(homework => {
const deadlineDate = homework.deadline.split(' ')[0];
if (!groupedHomework[deadlineDate]) {
groupedHomework[deadlineDate] = [];
}
groupedHomework[deadlineDate].push(homework);
});
return { basicData, homeworkItems, groupedHomework };
}
function formatApiDate(dateString) {
if (!dateString) return '';
try {
const date = new Date(dateString);
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const dayNames = [
LanguageManager.t('common.sunday'),
LanguageManager.t('common.monday'),
LanguageManager.t('common.tuesday'),
LanguageManager.t('common.wednesday'),
LanguageManager.t('common.thursday'),
LanguageManager.t('common.friday'),
LanguageManager.t('common.saturday')
];
const dayName = dayNames[date.getDay()];
return `${month}.${day}. (${dayName})`;
} catch (error) {
return dateString;
}
}
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>${LanguageManager.t('homework.filter_title')}</h2>
</div>
<div class="filter-content">
<div class="filter-group">
<label>
<!--<span class="material-icons-round">subject</span>-->
${LanguageManager.t('homework.subject')}
</label>
<select id="subjectFilter">
<option value="">${LanguageManager.t('homework.all_subjects')}</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>-->
${LanguageManager.t('homework.teacher')}
</label>
<select id="teacherFilter">
<option value="">${LanguageManager.t('homework.all_teachers')}</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>-->
${LanguageManager.t('homework.due_date')}
</label>
<select id="deadlineFilter">
<option value="">${LanguageManager.t('homework.all_deadlines')}</option>
<option value="tomorrow">${LanguageManager.t('homework.tomorrow_deadline')}</option>
<option value="thisWeek">${LanguageManager.t('homework.this_week')}</option>
<option value="nextWeek">${LanguageManager.t('homework.next_week')}</option>
</select>
</div>
</div>
</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 partsA = a.split('.');
const partsB = b.split('.');
const monthA = parseInt(partsA[0]) - 1;
const dayA = parseInt(partsA[1]);
const monthB = parseInt(partsB[0]) - 1;
const dayB = parseInt(partsB[1]);
const currentYear = new Date().getFullYear();
const currentMonth = new Date().getMonth();
const yearA = monthA < currentMonth ? currentYear + 1 : currentYear;
const yearB = monthB < currentMonth ? currentYear + 1 : currentYear;
const dateA = new Date(yearA, monthA, dayA);
const dateB = new Date(yearB, monthB, dayB);
return dateB - dateA;
});
if (sortedDates.length === 0) {
return `
<div class="empty-state">
<p>${LanguageManager.t('homework.no_homework')}</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>
<span class="homework-count">${homeworkItems.length} ${LanguageManager.t('homework.items') || 'feladat'}</span>
</div>
<div class="homework-list-items">
${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-details">
<div class="homework-subject">${homework.subject}</div>
<div class="homework-content">${homework.description}</div>
<div class="homework-teacher">${homework.teacher}</div>
</div>
</div>
`;
}).join('')}
</div>
</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 < 2) return dateStr;
const month = parseInt(parts[0].trim()) - 1;
const day = parseInt(parts[1].trim());
const currentYear = today.getFullYear();
const date = new Date(currentYear, month, day);
if (date.toDateString() === today.toDateString()) {
return LanguageManager.t('common.today') + ' - ' + dateStr;
} else if (date.toDateString() === tomorrow.toDateString()) {
return LanguageManager.t('common.tomorrow') + ' - ' + dateStr;
}
const weekdays = [
LanguageManager.t('common.sunday'),
LanguageManager.t('common.monday'),
LanguageManager.t('common.tuesday'),
LanguageManager.t('common.wednesday'),
LanguageManager.t('common.thursday'),
LanguageManager.t('common.friday'),
LanguageManager.t('common.saturday')
];
return `${weekdays[date.getDay()]} - ${dateStr}`;
}
function formatDate(dateStr) {
if (!dateStr) return '';
return dateStr;
}
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>${LanguageManager.t('homework.no_matching_homework')}</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);
});
}

View File

@@ -5,12 +5,49 @@
},
"settings": {
"title": "Settings",
"appearance": "Appearance",
"theme": "Theme",
"language": "Language",
"tabs": {
"home": "Home",
"settings": "Settings",
"about": "About"
},
"page_settings": {
"title": "Page Settings",
"current_page": "Current page",
"no_settings": "No custom settings available for this page.",
"login": {
"hide_system_message": "Hide system message",
"hide_system_message_desc": "Hide the system message shown on the login page",
"hide_school_info": "Hide school name and ID",
"hide_school_info_desc": "The school name and KRÉTA ID will not be displayed"
},
"roleselect": {
"auto_redirect": "Auto redirect",
"auto_redirect_desc": "Automatically redirect to the gradebook",
"hide_school_info": "Hide school and name",
"hide_school_info_desc": "The school name and user name will not be displayed"
},
"bulletin": {
"hide_grades": "Hide grades",
"hide_grades_desc": "Hide the grades card",
"hide_absences": "Hide absences",
"hide_absences_desc": "Hide the absences card",
"hide_notes": "Hide notes",
"hide_notes_desc": "Hide the notes card",
"hide_exams": "Hide announced exams",
"hide_exams_desc": "Hide the announced exams card"
},
"grades": {
"hide_chart": "Hide chart",
"hide_chart_desc": "Hide the grades chart",
"hide_class_average": "Hide class average",
"hide_class_average_desc": "Hide class average values"
}
},
"themes": {
"light_blue": "Light Blue",
"light_green": "Light Green",
"dark_blue": "Dark Blue",
"dark_green": "Dark Green",
"dark_red": "Dark Red",
"dark_purple": "Dark Purple",
@@ -21,6 +58,29 @@
"dark_lime": "Dark Lime",
"dark_indigo": "Dark Indigo"
},
"custom_themes": {
"title": "Custom themes",
"no_themes": "No custom themes yet",
"create": "Create new theme",
"edit": "Edit theme",
"name": "Theme name",
"mode": "Mode",
"dark_mode": "Dark",
"light_mode": "Light",
"colors": "Colors",
"accent_color": "Accent color",
"background_color": "Background color",
"card_color": "Card color",
"text_color": "Text color",
"preview": "Preview",
"share": "Share theme",
"share_description": "Copy the code and share it with others:",
"import": "Import theme",
"import_description": "Paste the theme code:",
"import_error_empty": "Please paste the theme code!",
"import_error_invalid": "Invalid theme code!",
"delete_confirm": "Are you sure you want to delete this theme?"
},
"languages": {
"hu": "Magyar",
"en": "English"
@@ -42,7 +102,6 @@
"grades": "Grades",
"homework": "Homework",
"absences": "Absences",
"other": "Other",
"messages": "Messages",
"profile": "Profile",
"settings": "Settings",
@@ -75,7 +134,9 @@
"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",
@@ -114,7 +175,37 @@
"wednesday": "Wednesday",
"thursday": "Thursday",
"friday": "Friday",
"found_current_week": "Found current week"
"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",
@@ -132,7 +223,15 @@
"no_filtered_homework": "No homework matching the filter criteria.",
"teacher": "Teacher",
"no_matching_homework": "No homework matching the filter criteria.",
"items": "items"
"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",
@@ -154,7 +253,10 @@
"current_month": "Current month",
"last_month": "Last month",
"current_semester": "Current semester",
"last_30_days": "Last 30 days"
"last_30_days": "Last 30 days",
"total_absences": "Total absences",
"topic": "Topic",
"status": "Status"
},
"profile": {
"title": "Profile",
@@ -246,6 +348,35 @@
"success": "Successfully logged out!",
"continue": "Continue"
},
"setup": {
"welcome": "Set up the extension in a few simple steps",
"steps": {
"theme": "Theme",
"language": "Language",
"finish": "Done"
},
"theme": {
"title": "Choose a theme",
"description": "Select the appearance that suits you best"
},
"language": {
"title": "Choose a language",
"description": "Select the language you want to use"
},
"finish": {
"title": "All set!",
"description": "Settings saved successfully. Let's start learning!",
"about": "About",
"about_desc": "Learn more about the project",
"support": "Support",
"support_desc": "Support the development",
"github": "GitHub",
"github_desc": "View the source code",
"discord": "Discord",
"discord_desc": "Join the community",
"start": "Get Started"
}
},
"common": {
"save": "Save",
"cancel": "Cancel",
@@ -294,10 +425,6 @@
"november": "November",
"december": "December"
},
"search": {
"title": "Choose school",
"select_institution": "Please select an institution to continue!"
},
"roleselect": {
"student_book": "Student Book",
"student_description": "View grades, absences, timetable and other information.",
@@ -318,7 +445,7 @@
"description": "Firka is an open source project that creates a custom user interface for the KRÉTA system.",
"support_title": "Support",
"support_description": "If you like our work and want to support development, you can do so in the following way:",
"version": "v1.1.0"
"version": "v1.3.0"
},
"app": {
"title": "Firka - KRÉTA",
@@ -337,7 +464,64 @@
"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 address format",
"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"
}
}
}

View File

@@ -5,8 +5,47 @@
},
"settings": {
"title": "Beállítások",
"appearance": "Megjelenés",
"theme": "Téma",
"language": "Nyelv",
"tabs": {
"home": "Főoldal",
"settings": "Beállítások",
"about": "Névjegy"
},
"page_settings": {
"title": "Oldal beállítások",
"current_page": "Aktuális oldal",
"no_settings": "Ehhez az oldalhoz nincsenek egyéni beállítások.",
"login": {
"hide_system_message": "Rendszerüzenet elrejtése",
"hide_system_message_desc": "A bejelentkezési oldalon megjelenő rendszerüzenet elrejtése",
"hide_school_info": "Iskola nevének és azonosítójának elrejtése",
"hide_school_info_desc": "Az iskola neve és KRÉTA azonosítója nem jelenik meg"
},
"roleselect": {
"auto_redirect": "Automatikus továbblépés",
"auto_redirect_desc": "Automatikusan átirányít az ellenőrzőkönyvre",
"hide_school_info": "Iskola és név elrejtése",
"hide_school_info_desc": "Az iskola neve és a felhasználó neve nem jelenik meg"
},
"bulletin": {
"hide_grades": "Értékelések elrejtése",
"hide_grades_desc": "Az értékeléseid kártya elrejtése",
"hide_absences": "Mulasztások elrejtése",
"hide_absences_desc": "A mulasztások kártya elrejtése",
"hide_notes": "Feljegyzések elrejtése",
"hide_notes_desc": "A feljegyzések kártya elrejtése",
"hide_exams": "Bejelentett dolgozatok elrejtése",
"hide_exams_desc": "A bejelentett dolgozatok kártya elrejtése"
},
"grades": {
"hide_chart": "Grafikon elrejtése",
"hide_chart_desc": "A jegyek grafikonjának elrejtése",
"hide_class_average": "Osztályátlag elrejtése",
"hide_class_average_desc": "Az osztályátlag értékek elrejtése"
}
},
"themes": {
"light_blue": "Világos Kék",
"light_green": "Világos Zöld",
@@ -21,6 +60,29 @@
"dark_lime": "Sötét Lime",
"dark_indigo": "Sötét Indigó"
},
"custom_themes": {
"title": "Egyéni témák",
"no_themes": "Még nincsenek egyéni témák",
"create": "Új téma létrehozása",
"edit": "Téma szerkesztése",
"name": "Téma neve",
"mode": "Mód",
"dark_mode": "Sötét",
"light_mode": "Világos",
"colors": "Színek",
"accent_color": "Kiemelő szín",
"background_color": "Háttér szín",
"card_color": "Kártya szín",
"text_color": "Szöveg szín",
"preview": "Előnézet",
"share": "Téma megosztása",
"share_description": "Másold ki a kódot és oszd meg másokkal:",
"import": "Téma importálása",
"import_description": "Illeszd be a téma kódot:",
"import_error_empty": "Kérlek illeszd be a téma kódot!",
"import_error_invalid": "Érvénytelen téma kód!",
"delete_confirm": "Biztosan törölni szeretnéd ezt a témát?"
},
"languages": {
"hu": "Magyar",
"en": "English"
@@ -42,7 +104,6 @@
"grades": "Jegyek",
"homework": "Házi feladatok",
"absences": "Mulasztások",
"other": "Egyéb",
"messages": "Üzenetek",
"profile": "Profil",
"settings": "Beállítások",
@@ -75,7 +136,9 @@
"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",
@@ -114,25 +177,63 @@
"wednesday": "Szerda",
"thursday": "Csütörtök",
"friday": "Péntek",
"found_current_week": "Megtalált jelenlegi hét"
"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és",
"all_subjects": "Összes tantárgy",
"all_teachers": "Összes tanár",
"all_deadlines": "Összes határidő",
"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": "Jövő 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": "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",
@@ -154,7 +255,10 @@
"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"
"last_30_days": "Utolsó 30 nap",
"total_absences": "Összes hiányzás",
"topic": "Téma",
"status": "Állapot"
},
"profile": {
"title": "Profil",
@@ -246,6 +350,35 @@
"success": "Sikeres kijelentkezés!",
"continue": "Tovább"
},
"setup": {
"welcome": "Állítsd be a bővítményt néhány egyszerű lépésben",
"steps": {
"theme": "Téma",
"language": "Nyelv",
"finish": "Kész"
},
"theme": {
"title": "Válassz témát",
"description": "Válaszd ki a számodra legmegfelelőbb megjelenést"
},
"language": {
"title": "Válassz nyelvet",
"description": "Válaszd ki a használni kívánt nyelvet"
},
"finish": {
"title": "Minden kész!",
"description": "A beállítások sikeresen mentve. Indulhat a tanulás!",
"about": "Rólunk",
"about_desc": "Tudj meg többet a projektről",
"support": "Támogatás",
"support_desc": "Támogasd a fejlesztést",
"github": "GitHub",
"github_desc": "Nézd meg a forráskódot",
"discord": "Discord",
"discord_desc": "Csatlakozz a közösséghez",
"start": "Kezdés"
}
},
"common": {
"save": "Mentés",
"cancel": "Mégse",
@@ -294,10 +427,6 @@
"november": "november",
"december": "december"
},
"search": {
"title": "Válassz iskolát",
"select_institution": "Kérjük, válasszon egy intézményt a folytatáshoz!"
},
"roleselect": {
"student_book": "Ellenőrzőkönyv",
"student_description": "Jegyek, hiányzások, órarended és egyéb információk megtekintése.",
@@ -318,7 +447,7 @@
"description": "A Firka egy nyílt forráskódú projekt, amely a KRÉTA rendszerhez készít saját felhasználói felületet.",
"support_title": "Támogatás",
"support_description": "Ha tetszik a munkánk és szeretnéd támogatni a fejlesztést, az alábbi módon teheted meg:",
"version": "v1.1.0"
"version": "v1.3.0"
},
"app": {
"title": "Firka - KRÉTA",
@@ -339,5 +468,62 @@
"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="currentColor" 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: 288 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="currentColor"/></g></svg>

After

Width:  |  Height:  |  Size: 804 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="currentColor" d="M4 7v2h16V7a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2"/><path stroke="currentColor" 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: 386 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="currentColor" 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: 294 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="currentColor" 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: 294 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="currentColor"/></g></svg>

After

Width:  |  Height:  |  Size: 445 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="currentColor"/></g></svg>

After

Width:  |  Height:  |  Size: 477 B

View File

@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 24 24"><path fill="#A7DC22" d="M8 3a1 1 0 0 1 1-1h6a1 1 0 1 1 0 2H9a1 1 0 0 1-1-1M3 14a9 9 0 0 1 14.618-7.032l.675-.675a1 1 0 1 1 1.414 1.414l-.675.675A9 9 0 1 1 3 14m10-4a1 1 0 1 0-2 0v4a1 1 0 1 0 2 0z" clip-rule="evenodd"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 24 24"><path fill="currentColor" d="M8 3a1 1 0 0 1 1-1h6a1 1 0 1 1 0 2H9a1 1 0 0 1-1-1M3 14a9 9 0 0 1 14.618-7.032l.675-.675a1 1 0 1 1 1.414 1.414l-.675.675A9 9 0 1 1 3 14m10-4a1 1 0 1 0-2 0v4a1 1 0 1 0 2 0z" clip-rule="evenodd"/></svg>

Before

Width:  |  Height:  |  Size: 309 B

After

Width:  |  Height:  |  Size: 314 B

View File

@@ -1,5 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none">
<g opacity="0.5">
<path d="M15.8334 5.83333L14.7143 6.9525M14.7143 6.9525C13.464 5.70223 11.7682 4.99983 10.0001 4.99983C8.23193 4.99983 6.5362 5.70223 5.28592 6.9525C4.03565 8.20278 3.33325 9.89851 3.33325 11.6667C3.33325 13.4348 4.03565 15.1306 5.28592 16.3808C6.5362 17.6311 8.23193 18.3335 10.0001 18.3335C11.7682 18.3335 13.464 17.6311 14.7143 16.3808C15.9645 15.1306 16.6669 13.4348 16.6669 11.6667C16.6669 9.89851 15.9645 8.20278 14.7143 6.9525ZM10.0001 8.33333V11.6667M7.50009 2.5H12.5001" stroke="#6E8F1B" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M15.8334 5.83333L14.7143 6.9525M14.7143 6.9525C13.464 5.70223 11.7682 4.99983 10.0001 4.99983C8.23193 4.99983 6.5362 5.70223 5.28592 6.9525C4.03565 8.20278 3.33325 9.89851 3.33325 11.6667C3.33325 13.4348 4.03565 15.1306 5.28592 16.3808C6.5362 17.6311 8.23193 18.3335 10.0001 18.3335C11.7682 18.3335 13.464 17.6311 14.7143 16.3808C15.9645 15.1306 16.6669 13.4348 16.6669 11.6667C16.6669 9.89851 15.9645 8.20278 14.7143 6.9525ZM10.0001 8.33333V11.6667M7.50009 2.5H12.5001" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 696 B

After

Width:  |  Height:  |  Size: 701 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="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14 8H9m2 4H9"/><path fill="currentColor" 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="currentColor" 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: 593 B

View File

@@ -1,3 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none">
<path d="M16.6666 15.8333V8.74999C16.6666 8.62062 16.6365 8.49302 16.5786 8.37731C16.5208 8.2616 16.4367 8.16095 16.3333 8.08332L10.4999 3.70832C10.3557 3.60014 10.1802 3.54166 9.99992 3.54166C9.81961 3.54166 9.64417 3.60014 9.49992 3.70832L3.66659 8.08332C3.56309 8.16095 3.47909 8.2616 3.42123 8.37731C3.36337 8.49302 3.33325 8.62062 3.33325 8.74999V15.8333C3.33325 16.0543 3.42105 16.2663 3.57733 16.4226C3.73361 16.5789 3.94557 16.6667 4.16659 16.6667H7.49992C7.72093 16.6667 7.93289 16.5789 8.08917 16.4226C8.24545 16.2663 8.33325 16.0543 8.33325 15.8333V13.3333C8.33325 13.1123 8.42105 12.9003 8.57733 12.7441C8.73361 12.5878 8.94557 12.5 9.16658 12.5H10.8333C11.0543 12.5 11.2662 12.5878 11.4225 12.7441C11.5788 12.9003 11.6666 13.1123 11.6666 13.3333V15.8333C11.6666 16.0543 11.7544 16.2663 11.9107 16.4226C12.0669 16.5789 12.2789 16.6667 12.4999 16.6667H15.8333C16.0543 16.6667 16.2662 16.5789 16.4225 16.4226C16.5788 16.2663 16.6666 16.0543 16.6666 15.8333Z" fill="#A7DC22" stroke="#A7DC22" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M16.6666 15.8333V8.74999C16.6666 8.62062 16.6365 8.49302 16.5786 8.37731C16.5208 8.2616 16.4367 8.16095 16.3333 8.08332L10.4999 3.70832C10.3557 3.60014 10.1802 3.54166 9.99992 3.54166C9.81961 3.54166 9.64417 3.60014 9.49992 3.70832L3.66659 8.08332C3.56309 8.16095 3.47909 8.2616 3.42123 8.37731C3.36337 8.49302 3.33325 8.62062 3.33325 8.74999V15.8333C3.33325 16.0543 3.42105 16.2663 3.57733 16.4226C3.73361 16.5789 3.94557 16.6667 4.16659 16.6667H7.49992C7.72093 16.6667 7.93289 16.5789 8.08917 16.4226C8.24545 16.2663 8.33325 16.0543 8.33325 15.8333V13.3333C8.33325 13.1123 8.42105 12.9003 8.57733 12.7441C8.73361 12.5878 8.94557 12.5 9.16658 12.5H10.8333C11.0543 12.5 11.2662 12.5878 11.4225 12.7441C11.5788 12.9003 11.6666 13.1123 11.6666 13.3333V15.8333C11.6666 16.0543 11.7544 16.2663 11.9107 16.4226C12.0669 16.5789 12.2789 16.6667 12.4999 16.6667H15.8333C16.0543 16.6667 16.2662 16.5789 16.4225 16.4226C16.5788 16.2663 16.6666 16.0543 16.6666 15.8333Z" fill="currentColor" stroke="currentColor" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -1,5 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none">
<g opacity="0.5">
<path d="M16.6668 15.8333V8.74999C16.6668 8.62062 16.6367 8.49302 16.5789 8.37731C16.521 8.2616 16.437 8.16095 16.3335 8.08332L10.5002 3.70832C10.3559 3.60014 10.1805 3.54166 10.0002 3.54166C9.81985 3.54166 9.64441 3.60014 9.50016 3.70832L3.66683 8.08332C3.56333 8.16095 3.47933 8.2616 3.42147 8.37731C3.36362 8.49302 3.3335 8.62062 3.3335 8.74999V15.8333C3.3335 16.0543 3.42129 16.2663 3.57757 16.4226C3.73385 16.5789 3.94582 16.6667 4.16683 16.6667H7.50016C7.72118 16.6667 7.93314 16.5789 8.08942 16.4226C8.2457 16.2663 8.3335 16.0543 8.3335 15.8333V13.3333C8.3335 13.1123 8.42129 12.9003 8.57757 12.7441C8.73385 12.5878 8.94582 12.5 9.16683 12.5H10.8335C11.0545 12.5 11.2665 12.5878 11.4228 12.7441C11.579 12.9003 11.6668 13.1123 11.6668 13.3333V15.8333C11.6668 16.0543 11.7546 16.2663 11.9109 16.4226C12.0672 16.5789 12.2791 16.6667 12.5002 16.6667H15.8335C16.0545 16.6667 16.2665 16.5789 16.4228 16.4226C16.579 16.2663 16.6668 16.0543 16.6668 15.8333Z" stroke="#6E8F1B" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M16.6668 15.8333V8.74999C16.6668 8.62062 16.6367 8.49302 16.5789 8.37731C16.521 8.2616 16.437 8.16095 16.3335 8.08332L10.5002 3.70832C10.3559 3.60014 10.1805 3.54166 10.0002 3.54166C9.81985 3.54166 9.64441 3.60014 9.50016 3.70832L3.66683 8.08332C3.56333 8.16095 3.47933 8.2616 3.42147 8.37731C3.36362 8.49302 3.3335 8.62062 3.3335 8.74999V15.8333C3.3335 16.0543 3.42129 16.2663 3.57757 16.4226C3.73385 16.5789 3.94582 16.6667 4.16683 16.6667H7.50016C7.72118 16.6667 7.93314 16.5789 8.08942 16.4226C8.2457 16.2663 8.3335 16.0543 8.3335 15.8333V13.3333C8.3335 13.1123 8.42129 12.9003 8.57757 12.7441C8.73385 12.5878 8.94582 12.5 9.16683 12.5H10.8335C11.0545 12.5 11.2665 12.5878 11.4228 12.7441C11.579 12.9003 11.6668 13.1123 11.6668 13.3333V15.8333C11.6668 16.0543 11.7546 16.2663 11.9109 16.4226C12.0672 16.5789 12.2791 16.6667 12.5002 16.6667H15.8335C16.0545 16.6667 16.2665 16.5789 16.4228 16.4226C16.579 16.2663 16.6668 16.0543 16.6668 15.8333Z" stroke="currentColor" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

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="currentColor" d="M9 7h9v11a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2V7z"/><path stroke="currentColor" 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: 394 B

View File

@@ -1,5 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" width="30" height="23" viewBox="0 0 30 23" fill="none">
<path d="M11 12.8333C13.9455 12.8333 16.3333 10.4455 16.3333 7.49996C16.3333 4.55444 13.9455 2.16663 11 2.16663C8.05444 2.16663 5.66663 4.55444 5.66663 7.49996C5.66663 10.4455 8.05444 12.8333 11 12.8333Z" fill="#FFA046" stroke="#FFA046" stroke-width="2.66667" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M11 12.8333C5.84529 12.8333 1.66663 16.4146 1.66663 20.8333H20.3333C20.3333 16.4146 16.1546 12.8333 11 12.8333Z" fill="#FFA046" stroke="#FFA046" stroke-width="2.66667" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M19 12.8333C19.8912 12.8332 20.7681 12.6099 21.5508 12.1836C22.3334 11.7573 22.9967 11.1417 23.4801 10.3931C23.9636 9.64441 24.2517 8.78654 24.3182 7.89785C24.3847 7.00915 24.2274 6.11796 23.8607 5.30569C23.4941 4.49343 22.9297 3.786 22.2193 3.24802C21.5088 2.71005 20.6748 2.35869 19.7935 2.22604C18.9123 2.09339 18.0118 2.18369 17.1745 2.48867C16.3371 2.79366 15.5895 3.30362 15 3.97196M19 12.8333C17.924 12.8333 16.196 12.4426 15 11.1866M19 12.8333C24.1547 12.8333 28.3333 16.4146 28.3333 20.8333H20.3333" stroke="#FFA046" stroke-width="2.66667" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M11 12.8333C13.9455 12.8333 16.3333 10.4455 16.3333 7.49996C16.3333 4.55444 13.9455 2.16663 11 2.16663C8.05444 2.16663 5.66663 4.55444 5.66663 7.49996C5.66663 10.4455 8.05444 12.8333 11 12.8333Z" fill="currentColor" stroke="currentColor" stroke-width="2.66667" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M11 12.8333C5.84529 12.8333 1.66663 16.4146 1.66663 20.8333H20.3333C20.3333 16.4146 16.1546 12.8333 11 12.8333Z" fill="currentColor" stroke="currentColor" stroke-width="2.66667" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M19 12.8333C19.8912 12.8332 20.7681 12.6099 21.5508 12.1836C22.3334 11.7573 22.9967 11.1417 23.4801 10.3931C23.9636 9.64441 24.2517 8.78654 24.3182 7.89785C24.3847 7.00915 24.2274 6.11796 23.8607 5.30569C23.4941 4.49343 22.9297 3.786 22.2193 3.24802C21.5088 2.71005 20.6748 2.35869 19.7935 2.22604C18.9123 2.09339 18.0118 2.18369 17.1745 2.48867C16.3371 2.79366 15.5895 3.30362 15 3.97196M19 12.8333C17.924 12.8333 16.196 12.4426 15 11.1866M19 12.8333C24.1547 12.8333 28.3333 16.4146 28.3333 20.8333H20.3333" stroke="currentColor" stroke-width="2.66667" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

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="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m3 3l18 18"/><path fill="currentColor" 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: 641 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="currentColor" 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: 458 B

View File

@@ -1,3 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none">
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.8335 1.66666C5.17045 1.66666 4.53457 1.93005 4.06573 2.39889C3.59689 2.86773 3.3335 3.50362 3.3335 4.16666V16.7817C3.33342 17.0006 3.39087 17.2158 3.50008 17.4056C3.6093 17.5953 3.76645 17.7531 3.95582 17.8631C4.14518 17.973 4.3601 18.0313 4.57906 18.0321C4.79803 18.0329 5.01336 17.9761 5.2035 17.8675L9.58683 15.3625C9.71272 15.2906 9.85519 15.2528 10.0002 15.2528C10.1451 15.2528 10.2876 15.2906 10.4135 15.3625L14.7968 17.8675C14.987 17.9761 15.2023 18.0329 15.4213 18.0321C15.6402 18.0313 15.8552 17.973 16.0445 17.8631C16.2339 17.7531 16.391 17.5953 16.5002 17.4056C16.6095 17.2158 16.6669 17.0006 16.6668 16.7817V4.16666C16.6668 3.50362 16.4034 2.86773 15.9346 2.39889C15.4658 1.93005 14.8299 1.66666 14.1668 1.66666H5.8335Z" fill="#A7DC22"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.8335 1.66666C5.17045 1.66666 4.53457 1.93005 4.06573 2.39889C3.59689 2.86773 3.3335 3.50362 3.3335 4.16666V16.7817C3.33342 17.0006 3.39087 17.2158 3.50008 17.4056C3.6093 17.5953 3.76645 17.7531 3.95582 17.8631C4.14518 17.973 4.3601 18.0313 4.57906 18.0321C4.79803 18.0329 5.01336 17.9761 5.2035 17.8675L9.58683 15.3625C9.71272 15.2906 9.85519 15.2528 10.0002 15.2528C10.1451 15.2528 10.2876 15.2906 10.4135 15.3625L14.7968 17.8675C14.987 17.9761 15.2023 18.0329 15.4213 18.0321C15.6402 18.0313 15.8552 17.973 16.0445 17.8631C16.2339 17.7531 16.391 17.5953 16.5002 17.4056C16.6095 17.2158 16.6669 17.0006 16.6668 16.7817V4.16666C16.6668 3.50362 16.4034 2.86773 15.9346 2.39889C15.4658 1.93005 14.8299 1.66666 14.1668 1.66666H5.8335Z" fill="currentColor"/>
</svg>

Before

Width:  |  Height:  |  Size: 906 B

After

Width:  |  Height:  |  Size: 911 B

View File

@@ -1,5 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none">
<g opacity="0.5">
<path d="M14.1667 2.5H5.83341C5.39139 2.5 4.96746 2.67559 4.6549 2.98816C4.34234 3.30072 4.16675 3.72464 4.16675 4.16667V16.7817C4.16677 16.8546 4.18596 16.9263 4.22238 16.9895C4.25881 17.0528 4.3112 17.1053 4.37431 17.1419C4.43742 17.1785 4.50904 17.198 4.58201 17.1982C4.65497 17.1984 4.72672 17.1795 4.79008 17.1433L9.17342 14.6392C9.42519 14.4953 9.71013 14.4197 10.0001 14.4197C10.29 14.4197 10.575 14.4953 10.8267 14.6392L15.2101 17.1442C15.2735 17.1804 15.3453 17.1993 15.4184 17.199C15.4914 17.1987 15.5631 17.1793 15.6262 17.1426C15.6894 17.1058 15.7417 17.0532 15.7781 16.9898C15.8145 16.9265 15.8335 16.8547 15.8334 16.7817V4.16667C15.8334 3.72464 15.6578 3.30072 15.3453 2.98816C15.0327 2.67559 14.6088 2.5 14.1667 2.5Z" stroke="#6E8F1B" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M14.1667 2.5H5.83341C5.39139 2.5 4.96746 2.67559 4.6549 2.98816C4.34234 3.30072 4.16675 3.72464 4.16675 4.16667V16.7817C4.16677 16.8546 4.18596 16.9263 4.22238 16.9895C4.25881 17.0528 4.3112 17.1053 4.37431 17.1419C4.43742 17.1785 4.50904 17.198 4.58201 17.1982C4.65497 17.1984 4.72672 17.1795 4.79008 17.1433L9.17342 14.6392C9.42519 14.4953 9.71013 14.4197 10.0001 14.4197C10.29 14.4197 10.575 14.4953 10.8267 14.6392L15.2101 17.1442C15.2735 17.1804 15.3453 17.1993 15.4184 17.199C15.4914 17.1987 15.5631 17.1793 15.6262 17.1426C15.6894 17.1058 15.7417 17.0532 15.7781 16.9898C15.8145 16.9265 15.8335 16.8547 15.8334 16.7817V4.16667C15.8334 3.72464 15.6578 3.30072 15.3453 2.98816C15.0327 2.67559 14.6088 2.5 14.1667 2.5Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 949 B

After

Width:  |  Height:  |  Size: 954 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="currentColor"/></g></svg>

After

Width:  |  Height:  |  Size: 350 B

View File

@@ -1,4 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" viewBox="0 0 28 28" fill="none">
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.5174 0.419898C12.1146 0.240744 12.7454 0.203682 13.3595 0.311674C13.9736 0.419665 14.5539 0.669717 15.0542 1.04187C15.5544 1.41403 15.9608 1.89798 16.2407 2.45509C16.5207 3.0122 16.6666 3.62706 16.6667 4.25057V23.7492C16.6666 24.3727 16.5207 24.9876 16.2407 25.5447C15.9608 26.1018 15.5544 26.5858 15.0542 26.9579C14.5539 27.3301 13.9736 27.5801 13.3595 27.6881C12.7454 27.7961 12.1146 27.7591 11.5174 27.5799L3.51735 25.1799C2.69349 24.9328 1.97123 24.4266 1.45773 23.7366C0.944226 23.0466 0.666828 22.2094 0.666687 21.3492V6.65057C0.666828 5.79043 0.944226 4.95325 1.45773 4.26321C1.97123 3.57318 2.69349 3.06704 3.51735 2.8199L11.5174 0.419898ZM18 3.33323C18 2.97961 18.1405 2.64047 18.3905 2.39042C18.6406 2.14037 18.9797 1.9999 19.3334 1.9999H23.3334C24.3942 1.9999 25.4116 2.42133 26.1618 3.17147C26.9119 3.92162 27.3334 4.93903 27.3334 5.9999V7.33323C27.3334 7.68685 27.1929 8.02599 26.9428 8.27604C26.6928 8.52609 26.3536 8.66656 26 8.66656C25.6464 8.66656 25.3073 8.52609 25.0572 8.27604C24.8072 8.02599 24.6667 7.68685 24.6667 7.33323V5.9999C24.6667 5.64628 24.5262 5.30714 24.2762 5.05709C24.0261 4.80704 23.687 4.66656 23.3334 4.66656H19.3334C18.9797 4.66656 18.6406 4.52609 18.3905 4.27604C18.1405 4.02599 18 3.68685 18 3.33323ZM26 19.3332C26.3536 19.3332 26.6928 19.4737 26.9428 19.7238C27.1929 19.9738 27.3334 20.3129 27.3334 20.6666V21.9999C27.3334 23.0608 26.9119 24.0782 26.1618 24.8283C25.4116 25.5785 24.3942 25.9999 23.3334 25.9999H19.3334C18.9797 25.9999 18.6406 25.8594 18.3905 25.6094C18.1405 25.3593 18 25.0202 18 24.6666C18 24.3129 18.1405 23.9738 18.3905 23.7238C18.6406 23.4737 18.9797 23.3332 19.3334 23.3332H23.3334C23.687 23.3332 24.0261 23.1928 24.2762 22.9427C24.5262 22.6927 24.6667 22.3535 24.6667 21.9999V20.6666C24.6667 20.3129 24.8072 19.9738 25.0572 19.7238C25.3073 19.4737 25.6464 19.3332 26 19.3332ZM10 12.6666C9.6464 12.6666 9.30726 12.807 9.05721 13.0571C8.80716 13.3071 8.66669 13.6463 8.66669 13.9999C8.66669 14.3535 8.80716 14.6927 9.05721 14.9427C9.30726 15.1928 9.6464 15.3332 10 15.3332H10.0014C10.355 15.3332 10.6941 15.1928 10.9442 14.9427C11.1942 14.6927 11.3347 14.3535 11.3347 13.9999C11.3347 13.6463 11.1942 13.3071 10.9442 13.0571C10.6941 12.807 10.355 12.6666 10.0014 12.6666H10Z" fill="#FF54A1"/>
<path d="M19.3334 13.9999H26M26 13.9999L23.3334 11.3333M26 13.9999L23.3334 16.6666" stroke="#FF54A1" stroke-width="2.66667" stroke-linecap="round" stroke-linejoin="round"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.5174 0.419898C12.1146 0.240744 12.7454 0.203682 13.3595 0.311674C13.9736 0.419665 14.5539 0.669717 15.0542 1.04187C15.5544 1.41403 15.9608 1.89798 16.2407 2.45509C16.5207 3.0122 16.6666 3.62706 16.6667 4.25057V23.7492C16.6666 24.3727 16.5207 24.9876 16.2407 25.5447C15.9608 26.1018 15.5544 26.5858 15.0542 26.9579C14.5539 27.3301 13.9736 27.5801 13.3595 27.6881C12.7454 27.7961 12.1146 27.7591 11.5174 27.5799L3.51735 25.1799C2.69349 24.9328 1.97123 24.4266 1.45773 23.7366C0.944226 23.0466 0.666828 22.2094 0.666687 21.3492V6.65057C0.666828 5.79043 0.944226 4.95325 1.45773 4.26321C1.97123 3.57318 2.69349 3.06704 3.51735 2.8199L11.5174 0.419898ZM18 3.33323C18 2.97961 18.1405 2.64047 18.3905 2.39042C18.6406 2.14037 18.9797 1.9999 19.3334 1.9999H23.3334C24.3942 1.9999 25.4116 2.42133 26.1618 3.17147C26.9119 3.92162 27.3334 4.93903 27.3334 5.9999V7.33323C27.3334 7.68685 27.1929 8.02599 26.9428 8.27604C26.6928 8.52609 26.3536 8.66656 26 8.66656C25.6464 8.66656 25.3073 8.52609 25.0572 8.27604C24.8072 8.02599 24.6667 7.68685 24.6667 7.33323V5.9999C24.6667 5.64628 24.5262 5.30714 24.2762 5.05709C24.0261 4.80704 23.687 4.66656 23.3334 4.66656H19.3334C18.9797 4.66656 18.6406 4.52609 18.3905 4.27604C18.1405 4.02599 18 3.68685 18 3.33323ZM26 19.3332C26.3536 19.3332 26.6928 19.4737 26.9428 19.7238C27.1929 19.9738 27.3334 20.3129 27.3334 20.6666V21.9999C27.3334 23.0608 26.9119 24.0782 26.1618 24.8283C25.4116 25.5785 24.3942 25.9999 23.3334 25.9999H19.3334C18.9797 25.9999 18.6406 25.8594 18.3905 25.6094C18.1405 25.3593 18 25.0202 18 24.6666C18 24.3129 18.1405 23.9738 18.3905 23.7238C18.6406 23.4737 18.9797 23.3332 19.3334 23.3332H23.3334C23.687 23.3332 24.0261 23.1928 24.2762 22.9427C24.5262 22.6927 24.6667 22.3535 24.6667 21.9999V20.6666C24.6667 20.3129 24.8072 19.9738 25.0572 19.7238C25.3073 19.4737 25.6464 19.3332 26 19.3332ZM10 12.6666C9.6464 12.6666 9.30726 12.807 9.05721 13.0571C8.80716 13.3071 8.66669 13.6463 8.66669 13.9999C8.66669 14.3535 8.80716 14.6927 9.05721 14.9427C9.30726 15.1928 9.6464 15.3332 10 15.3332H10.0014C10.355 15.3332 10.6941 15.1928 10.9442 14.9427C11.1942 14.6927 11.3347 14.3535 11.3347 13.9999C11.3347 13.6463 11.1942 13.3071 10.9442 13.0571C10.6941 12.807 10.355 12.6666 10.0014 12.6666H10Z" fill="currentColor"/>
<path d="M19.3334 13.9999H26M26 13.9999L23.3334 11.3333M26 13.9999L23.3334 16.6666" stroke="currentColor" stroke-width="2.66667" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

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="currentColor"/></g></svg>

After

Width:  |  Height:  |  Size: 334 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="currentColor"><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: 353 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><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>

Before

Width:  |  Height:  |  Size: 327 B

View File

@@ -1,3 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32" fill="none">
<path fill-rule="evenodd" clip-rule="evenodd" d="M4 6.66663C4 5.60576 4.42143 4.58834 5.17157 3.8382C5.92172 3.08805 6.93913 2.66663 8 2.66663H24C25.0609 2.66663 26.0783 3.08805 26.8284 3.8382C27.5786 4.58834 28 5.60576 28 6.66663V25.3333C28 26.3942 27.5786 27.4116 26.8284 28.1617C26.0783 28.9119 25.0609 29.3333 24 29.3333H8C6.93913 29.3333 5.92172 28.9119 5.17157 28.1617C4.42143 27.4116 4 26.3942 4 25.3333V6.66663ZM10.6667 6.66663V16L13.724 12.9426C13.974 12.6927 14.3131 12.5522 14.6667 12.5522C15.0202 12.5522 15.3593 12.6927 15.6093 12.9426L18.6667 16V6.66663C18.6667 6.313 18.5262 5.97387 18.2761 5.72382C18.0261 5.47377 17.687 5.33329 17.3333 5.33329H12C11.6464 5.33329 11.3072 5.47377 11.0572 5.72382C10.8071 5.97387 10.6667 6.313 10.6667 6.66663Z" fill="#A7DC22"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M4 6.66663C4 5.60576 4.42143 4.58834 5.17157 3.8382C5.92172 3.08805 6.93913 2.66663 8 2.66663H24C25.0609 2.66663 26.0783 3.08805 26.8284 3.8382C27.5786 4.58834 28 5.60576 28 6.66663V25.3333C28 26.3942 27.5786 27.4116 26.8284 28.1617C26.0783 28.9119 25.0609 29.3333 24 29.3333H8C6.93913 29.3333 5.92172 28.9119 5.17157 28.1617C4.42143 27.4116 4 26.3942 4 25.3333V6.66663ZM10.6667 6.66663V16L13.724 12.9426C13.974 12.6927 14.3131 12.5522 14.6667 12.5522C15.0202 12.5522 15.3593 12.6927 15.6093 12.9426L18.6667 16V6.66663C18.6667 6.313 18.5262 5.97387 18.2761 5.72382C18.0261 5.47377 17.687 5.33329 17.3333 5.33329H12C11.6464 5.33329 11.3072 5.47377 11.0572 5.72382C10.8071 5.97387 10.6667 6.313 10.6667 6.66663Z" fill="currentColor"/>
</svg>

Before

Width:  |  Height:  |  Size: 881 B

After

Width:  |  Height:  |  Size: 886 B

View File

@@ -1,5 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none">
<g opacity="0.5">
<path fill-rule="evenodd" clip-rule="evenodd" d="M1.66699 5.00018C1.66699 4.34271 2.19998 3.80972 2.85745 3.80972H17.1429C17.8004 3.80972 18.3334 4.34271 18.3334 5.00018C18.3334 5.65765 17.8004 6.19064 17.1429 6.19064H2.85745C2.19998 6.19064 1.66699 5.65765 1.66699 5.00018ZM1.66699 9.76201C1.66699 9.10454 2.19998 8.57155 2.85745 8.57155H17.1429C17.8004 8.57155 18.3334 9.10454 18.3334 9.76201C18.3334 10.4195 17.8004 10.9525 17.1429 10.9525H2.85745C2.19998 10.9525 1.66699 10.4195 1.66699 9.76201ZM1.66699 14.5238C1.66699 13.8664 2.19998 13.3334 2.85745 13.3334H17.1429C17.8004 13.3334 18.3334 13.8664 18.3334 14.5238C18.3334 15.1813 17.8004 15.7143 17.1429 15.7143H2.85745C2.19998 15.7143 1.66699 15.1813 1.66699 14.5238Z" fill="#6E8F1B"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M1.66699 5.00018C1.66699 4.34271 2.19998 3.80972 2.85745 3.80972H17.1429C17.8004 3.80972 18.3334 4.34271 18.3334 5.00018C18.3334 5.65765 17.8004 6.19064 17.1429 6.19064H2.85745C2.19998 6.19064 1.66699 5.65765 1.66699 5.00018ZM1.66699 9.76201C1.66699 9.10454 2.19998 8.57155 2.85745 8.57155H17.1429C17.8004 8.57155 18.3334 9.10454 18.3334 9.76201C18.3334 10.4195 17.8004 10.9525 17.1429 10.9525H2.85745C2.19998 10.9525 1.66699 10.4195 1.66699 9.76201ZM1.66699 14.5238C1.66699 13.8664 2.19998 13.3334 2.85745 13.3334H17.1429C17.8004 13.3334 18.3334 13.8664 18.3334 14.5238C18.3334 15.1813 17.8004 15.7143 17.1429 15.7143H2.85745C2.19998 15.7143 1.66699 15.1813 1.66699 14.5238Z" fill="currentColor"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 876 B

After

Width:  |  Height:  |  Size: 881 B

1
icons/pending.svg Normal file
View File

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

After

Width:  |  Height:  |  Size: 348 B

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="currentColor"><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: 253 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="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 12h7m7 0h-7m0 0V5m0 7v7"/></svg>

After

Width:  |  Height:  |  Size: 227 B

View File

@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" viewBox="0 0 28 28" fill="none"><g fill-rule="evenodd" clip-rule="evenodd" fill="#A7DC22" stroke="#A7DC22" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="8" r="5" fill="#A7DC22"/><path d="M20 21a8 8 0 1 0-16 0"/></g></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" viewBox="0 0 28 28" fill="none"><g fill-rule="evenodd" clip-rule="evenodd" fill="currentColor" stroke="currentColor" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="8" r="5" fill="currentColor"/><path d="M20 21a8 8 0 1 0-16 0"/></g></svg>

Before

Width:  |  Height:  |  Size: 328 B

After

Width:  |  Height:  |  Size: 343 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="currentColor" 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: 431 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="currentColor" 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: 359 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="currentColor"/></g></svg>

After

Width:  |  Height:  |  Size: 596 B

View File

@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" viewBox="0 0 28 28" fill="none"><path fill="#A7DC22" fill-rule="evenodd" d="M9.024 2.783A1 1 0 0 1 10 2h4a1 1 0 0 1 .976.783l.44 1.981q.6.285 1.14.66l1.938-.61a1 1 0 0 1 1.166.454l2 3.464a1 1 0 0 1-.19 1.237l-1.497 1.373a8 8 0 0 1 0 1.316l1.497 1.373a1 1 0 0 1 .19 1.237l-2 3.464a1 1 0 0 1-1.166.454l-1.937-.61q-.54.375-1.14.66l-.44 1.98A1 1 0 0 1 14 22h-4a1 1 0 0 1-.976-.783l-.44-1.981q-.6-.285-1.14-.66l-1.938.61a1 1 0 0 1-1.166-.454l-2-3.464a1 1 0 0 1 .19-1.237l1.497-1.373a8 8 0 0 1 0-1.316L2.53 9.97a1 1 0 0 1-.19-1.237l2-3.464a1 1 0 0 1 1.166-.454l1.937.61q.54-.375 1.14-.66l.44-1.98zM12 15a3 3 0 1 0 0-6a3 3 0 0 0 0 6" clip-rule="evenodd"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" viewBox="0 0 28 28" fill="none"><path fill="currentColor" fill-rule="evenodd" d="M9.024 2.783A1 1 0 0 1 10 2h4a1 1 0 0 1 .976.783l.44 1.981q.6.285 1.14.66l1.938-.61a1 1 0 0 1 1.166.454l2 3.464a1 1 0 0 1-.19 1.237l-1.497 1.373a8 8 0 0 1 0 1.316l1.497 1.373a1 1 0 0 1 .19 1.237l-2 3.464a1 1 0 0 1-1.166.454l-1.937-.61q-.54.375-1.14.66l-.44 1.98A1 1 0 0 1 14 22h-4a1 1 0 0 1-.976-.783l-.44-1.981q-.6-.285-1.14-.66l-1.938.61a1 1 0 0 1-1.166-.454l-2-3.464a1 1 0 0 1 .19-1.237l1.497-1.373a8 8 0 0 1 0-1.316L2.53 9.97a1 1 0 0 1-.19-1.237l2-3.464a1 1 0 0 1 1.166-.454l1.937.61q.54-.375 1.14-.66l.44-1.98zM12 15a3 3 0 1 0 0-6a3 3 0 0 0 0 6" clip-rule="evenodd"/></svg>

Before

Width:  |  Height:  |  Size: 717 B

After

Width:  |  Height:  |  Size: 722 B

View File

@@ -1,4 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="18" viewBox="0 0 16 18" fill="none">
<path d="M1.3335 4.83332V6.49999H14.6668V4.83332C14.6668 4.3913 14.4912 3.96737 14.1787 3.65481C13.8661 3.34225 13.4422 3.16666 13.0002 3.16666H3.00016C2.55814 3.16666 2.13421 3.34225 1.82165 3.65481C1.50909 3.96737 1.3335 4.3913 1.3335 4.83332Z" fill="#A7DC22"/>
<path d="M11.3335 3.16667H13.0002C13.4422 3.16667 13.8661 3.34226 14.1787 3.65482C14.4912 3.96738 14.6668 4.39131 14.6668 4.83333V6.5H1.3335V4.83333C1.3335 4.39131 1.50909 3.96738 1.82165 3.65482C2.13421 3.34226 2.55814 3.16667 3.00016 3.16667H4.66683M11.3335 3.16667V1.5M11.3335 3.16667H4.66683M4.66683 3.16667V1.5M1.3335 6.91667V14.8333C1.3335 15.2754 1.50909 15.6993 1.82165 16.0118C2.13421 16.3244 2.55814 16.5 3.00016 16.5H13.0002C13.4422 16.5 13.8661 16.3244 14.1787 16.0118C14.4912 15.6993 14.6668 15.2754 14.6668 14.8333V6.91667" stroke="#A7DC22" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M1.3335 4.83332V6.49999H14.6668V4.83332C14.6668 4.3913 14.4912 3.96737 14.1787 3.65481C13.8661 3.34225 13.4422 3.16666 13.0002 3.16666H3.00016C2.55814 3.16666 2.13421 3.34225 1.82165 3.65481C1.50909 3.96737 1.3335 4.3913 1.3335 4.83332Z" fill="currentColor"/>
<path d="M11.3335 3.16667H13.0002C13.4422 3.16667 13.8661 3.34226 14.1787 3.65482C14.4912 3.96738 14.6668 4.39131 14.6668 4.83333V6.5H1.3335V4.83333C1.3335 4.39131 1.50909 3.96738 1.82165 3.65482C2.13421 3.34226 2.55814 3.16667 3.00016 3.16667H4.66683M11.3335 3.16667V1.5M11.3335 3.16667H4.66683M4.66683 3.16667V1.5M1.3335 6.91667V14.8333C1.3335 15.2754 1.50909 15.6993 1.82165 16.0118C2.13421 16.3244 2.55814 16.5 3.00016 16.5H13.0002C13.4422 16.5 13.8661 16.3244 14.1787 16.0118C14.4912 15.6993 14.6668 15.2754 14.6668 14.8333V6.91667" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 991 B

After

Width:  |  Height:  |  Size: 1001 B

View File

@@ -1,5 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none">
<g opacity="0.5">
<path d="M3.33325 7.5V15.8333C3.33325 16.2754 3.50885 16.6993 3.82141 17.0118C4.13397 17.3244 4.55789 17.5 4.99992 17.5H14.9999C15.4419 17.5 15.8659 17.3244 16.1784 17.0118C16.491 16.6993 16.6666 16.2754 16.6666 15.8333V7.5M3.33325 7.5V5.83333C3.33325 5.39131 3.50885 4.96738 3.82141 4.65482C4.13397 4.34226 4.55789 4.16667 4.99992 4.16667H6.66659M3.33325 7.5H16.6666M16.6666 7.5V5.83333C16.6666 5.39131 16.491 4.96738 16.1784 4.65482C15.8659 4.34226 15.4419 4.16667 14.9999 4.16667H13.3333M6.66659 4.16667H13.3333M6.66659 4.16667V2.5M13.3333 4.16667V2.5" stroke="#6E8F1B" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M3.33325 7.5V15.8333C3.33325 16.2754 3.50885 16.6993 3.82141 17.0118C4.13397 17.3244 4.55789 17.5 4.99992 17.5H14.9999C15.4419 17.5 15.8659 17.3244 16.1784 17.0118C16.491 16.6993 16.6666 16.2754 16.6666 15.8333V7.5M3.33325 7.5V5.83333C3.33325 5.39131 3.50885 4.96738 3.82141 4.65482C4.13397 4.34226 4.55789 4.16667 4.99992 4.16667H6.66659M3.33325 7.5H16.6666M16.6666 7.5V5.83333C16.6666 5.39131 16.491 4.96738 16.1784 4.65482C15.8659 4.34226 15.4419 4.16667 14.9999 4.16667H13.3333M6.66659 4.16667H13.3333M6.66659 4.16667V2.5M13.3333 4.16667V2.5" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 772 B

After

Width:  |  Height:  |  Size: 777 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="currentColor"/></g></svg>

After

Width:  |  Height:  |  Size: 405 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="currentColor" 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: 266 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 53 KiB

BIN
images/firefoxact.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 213 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

After

Width:  |  Height:  |  Size: 96 KiB

BIN
images/firka_logo_128_o.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

BIN
images/firka_logo_o.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -1,360 +1,306 @@
: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;
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;
margin-bottom: 12px;
}
.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: var(--icon-filter);
}
.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,51 +1,70 @@
async function transformLoginPage() {
try {
if (document.readyState !== 'complete') {
await new Promise(resolve => {
window.addEventListener('load', resolve);
while (
typeof window.LanguageManager === "undefined" ||
!window.LanguageManager.t("login.username_placeholder") ||
window.LanguageManager.t("login.username_placeholder") === "login.username_placeholder"
) {
await new Promise((resolve) => setTimeout(resolve, 50));
}
if (document.readyState !== "complete") {
await new Promise((resolve) => {
window.addEventListener("load", resolve);
});
}
const existingForm = document.querySelector('form');
const loginSettings = await loadLoginSettings();
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(`${LanguageManager.t('login.kreta_id')}: `, '');
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>
${!loginSettings.hideSchoolInfo ? `<h1 class="school-name">${schoolInfo.name}</h1>
<div class="school-details">
${schoolInfo.kretaId ? `<div>${schoolInfo.kretaId}</div>` : ''}
${schoolInfo.omCode ? `<div>${LanguageManager.t('login.kreta_id')}: ${schoolInfo.omCode}</div>` : ''}
</div>
${schoolInfo.kretaId ? `<div>${schoolInfo.kretaId}</div>` : ""}
${schoolInfo.omCode ? `<div>${LanguageManager.t("login.kreta_id")}: ${schoolInfo.omCode}</div>` : ""}
</div>` : ''}
</div>
<form class="login-form" method="post" action="${formData.action}" id="loginForm" novalidate>
@@ -57,133 +76,164 @@ async function transformLoginPage() {
<div class="form-group">
<input class="form-control" type="text" id="UserName" name="UserName"
placeholder="${LanguageManager.t('login.username_placeholder')}" maxlength="256" autocomplete="username" required value="${formData.userName}">
<div class="error-message">${LanguageManager.t('login.username_required')}</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="${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">
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">${LanguageManager.t('login.password_required')}</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">${LanguageManager.t('login.login_button')}</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">${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>
<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 && !loginSettings.hideSystemMessage
? `
<div class="system-message">
<h4>${LanguageManager.t('login.system_message')}</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">${LanguageManager.t('login.privacy_policy')}</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();
}
async function loadLoginSettings() {
try {
const settings = await storageManager.get("pageSettings_login", {});
return {
hideSystemMessage: settings.hideSystemMessage || false,
hideSchoolInfo: settings.hideSchoolInfo || false
};
} catch (error) {
console.error("Error loading login settings:", error);
return {
hideSystemMessage: false,
hideSchoolInfo: false
};
}
}
if (window.location.href.includes('idp.e-kreta.hu/Account/Login')) {
transformLoginPage().catch(error => {
console.error('Error:', error);
});
}
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.action === "pageSettingChanged" && message.pageType === "login") {
transformLoginPage();
}
});
if (window.location.href.includes("idp.e-kreta.hu/Account/Login")) {
(async () => {
while (typeof window.LanguageManager === "undefined") {
await new Promise((resolve) => setTimeout(resolve, 50));
}
transformLoginPage().catch((error) => {
console.error("Error transforming login page:", error);
});
})();
}

View File

@@ -1,301 +1,254 @@
: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: var(--icon-filter);
}
.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">${LanguageManager.t('twofactor.title')}</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="${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">
placeholder="${LanguageManager.t("twofactor.code_placeholder")}" maxlength="256" autocomplete="off" required autofocus>
<button type="button" class="show-password" aria-label="${LanguageManager.t("twofactor.show_code")}">
<img src="${chrome.runtime.getURL("icons/eye-off.svg")}" alt="Show password" class="icon-eye">
</button>
<div class="error-message">${LanguageManager.t('twofactor.code_required')}</div>
<div class="error-message">${LanguageManager.t("twofactor.code_required")}</div>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="trustDevice" name="TrustDevice" value="true">
<label class="form-check-label" for="trustDevice">
${LanguageManager.t('twofactor.trust_device')}
${LanguageManager.t("twofactor.trust_device")}
</label>
<input name="TrustDevice" type="hidden" value="false">
</div>
<div class="d-flex justify-content-center mb-3 mt-4">
<button type="submit" class="btn-kreta">${LanguageManager.t('twofactor.verify_button')}</button>
<button type="submit" class="btn-kreta">${LanguageManager.t("twofactor.verify_button")}</button>
</div>
<div class="d-flex justify-content-center mt-3">
<span class="subtext">
${LanguageManager.t('twofactor.no_access')}
${LanguageManager.t("twofactor.no_access")}
<button type="submit" class="btn-link" formaction="/account/loginwithrecoverycode">
${LanguageManager.t('twofactor.recovery_code')}
${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">${LanguageManager.t('login.privacy_policy')}</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">${LanguageManager.t('twofactor.verifying')}</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.2.3",
"version": "1.4.4",
"description": "KRÉTA webes verziójának újraírása",
"icons": {
"128": "images/firka_logo_128.png"
@@ -12,111 +12,221 @@
"128": "images/firka_logo_128.png"
}
},
"permissions": [
"storage",
"tabs"
],
"background": {
"service_worker": "tools/background.js"
},
"browser_specific_settings": {
"gecko": {
"id": "firxa@zan1456.hu",
"strict_min_version": "58.0"
"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/cookieManager.js"
],
"matches": ["<all_urls>"]
}],
"web_accessible_resources": [
{
"resources": [
"settings/*",
"setup/*",
"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/*"],
"js": ["tools/loadingScreen.js, tools/cookieManager.js", "tools/helper.js", "tools/createTemplate.js",
"global/language.js", "global/maintenance.js", "global/theme.js", "global/navigation.js"],
"css": ["tools/loadingScreen.css", "global/theme.css", "global/navigation.css", "global/maintenance.css"],
"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"]
},
{
"matches": ["https://idp.e-kreta.hu/account/loginwithtwofactor*"],
"js": ["login/twofactor.js"],
"css": ["login/twofactor.css"]
},
{
"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_end"
},
{
"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": ["forgotpassword/forgotpassword.js"],
"css": ["forgotpassword/forgotpassword.css"],
"run_at": "document_end"
},
{
"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*"
"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"
],
"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"
}
]
}

233
manifest_fox.json Normal file
View File

@@ -0,0 +1,233 @@
{
"manifest_version": 3,
"name": "Firxa",
"version": "1.4.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",
"tabs"
],
"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"
}
]
}

683
messages/messages.css Normal file
View File

@@ -0,0 +1,683 @@
* {
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 {
justify-content: space-between;
flex-wrap: nowrap;
gap: 6px;
order: 1;
}
.bulk-actions-right {
justify-content: space-between;
flex-wrap: wrap;
gap: 6px;
order: 2;
}
.bulk-btn,
.view-toggle button {
width: 32px;
height: 32px;
flex: 1;
}
.bulk-btn img,
.view-toggle button img {
width: 18px;
height: 18px;
}
}
@media (max-width: 480px) {
.bulk-btn,
.view-toggle button {
width: 28px;
height: 28px;
flex: 1;
}
.bulk-btn img,
.view-toggle button img {
width: 16px;
height: 16px;
}
.bulk-actions-left {
order: 1;
}
.bulk-actions-right {
order: 2;
}
}
.messages-grid {
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">×</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);
const closeBtn = modalContent.querySelector('.modal-close');
closeBtn.addEventListener('click', closeMessageModal);
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();
}
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(LanguageManager.t('profile.email_required'));
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(LanguageManager.t('profile.invalid_email'));
return;
}
if (phone && !isValidPhone(phone)) {
alert(LanguageManager.t('profile.invalid_phone'));
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(LanguageManager.t('profile.contacts_saved'));
} else {
throw new Error(LanguageManager.t('profile.contacts_save_error'));
}
} catch (error) {
console.error('Error saving contacts:', error);
alert(LanguageManager.t('profile.save_error'));
}
});
}
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(LanguageManager.t('profile.settings_saved'));
} else {
throw new Error(LanguageManager.t('profile.settings_save_error'));
}
} catch (error) {
console.error('Error saving settings:', error);
alert(LanguageManager.t('profile.save_error'));
}
});
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(LanguageManager.t('profile.fill_all_fields'));
return;
}
if (newPassword !== confirmPassword) {
alert(LanguageManager.t('profile.passwords_not_match'));
return;
}
if (newPassword.length < 8) {
alert(LanguageManager.t('profile.password_min_length'));
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(LanguageManager.t('profile.password_changed'));
document.getElementById('currentPassword').value = '';
document.getElementById('newPassword').value = '';
document.getElementById('confirmPassword').value = '';
} else {
throw new Error(LanguageManager.t('profile.password_change_error'));
}
} catch (error) {
console.error('Error changing password:', error);
alert(LanguageManager.t('profile.password_change_error'));
}
});
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,354 +1,324 @@
* {
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(--card-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;
}
gap:0.5rem;
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,60 +1,84 @@
(() => {
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();
return setInterval(updateTimer, 1000);
};
async function loadRoleselectSettings() {
try {
const settings = await storageManager.get("pageSettings_roleselect", {
autoRedirect: false,
hideSchoolInfo: true
});
return {
autoRedirect: settings.autoRedirect || false,
hideSchoolInfo: settings.hideSchoolInfo !== undefined ? settings.hideSchoolInfo : true
};
} catch (error) {
console.error("Error loading roleselect settings:", error);
return {
autoRedirect: false,
hideSchoolInfo: true
};
}
}
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(LanguageManager.t('roleselect.role_change_error'));
console.error("Error changing role:", error);
alert(LanguageManager.t("roleselect.role_change_error"));
}
};
const createHTML = (schoolCode, fullSchoolName, userName) => `
const createHTML = (schoolCode, fullSchoolName, userName, settings) => `
<div class="kreta-container">
<header class="kreta-header">
<div class="school-info">
<p class="logo-text">
<img src=${chrome.runtime.getURL('images/firka_logo.png')} alt="Firka" class="logo">
<img src=${chrome.runtime.getURL("images/firka_logo.png")} alt="Firka" class="logo">
Firka
</p>
<div class="school-details">
<span>${schoolCode || ''} - ${fullSchoolName || 'Iskola'}</span>
</div>
${!settings.hideSchoolInfo ? `<div class="school-details">
<span>${schoolCode || ""} - ${fullSchoolName || "Iskola"}</span>
</div>` : ''}
</div>
<div class="user-profile">
<div class="user-info">
${!settings.hideSchoolInfo ? `<div class="user-info">
<span class="user-name">${userName}</span>
<span class="logout-timer" id="logoutTimer">5:00</span>
</div>
</div>` : `<div class="user-info">
<span class="logout-timer" id="logoutTimer">5:00</span>
</div>`}
</div>
</header>
@@ -63,11 +87,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">
${LanguageManager.t('roleselect.student_book')}
<div class="role-description">${LanguageManager.t('roleselect.student_description')}</div>
${LanguageManager.t("roleselect.student_book")}
<div class="role-description">${LanguageManager.t("roleselect.student_description")}</div>
</div>
</div>
</div>
@@ -75,21 +99,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">
${LanguageManager.t('roleselect.dkt_title')}
<div class="role-description">${LanguageManager.t('roleselect.dkt_description')}</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">
${LanguageManager.t('roleselect.logout_title')}
<div class="role-description">${LanguageManager.t('roleselect.logout_description')}</div>
${LanguageManager.t("roleselect.logout_title")}
<div class="role-description">${LanguageManager.t("roleselect.logout_description")}</div>
</div>
</div>
</div>
@@ -100,65 +124,84 @@
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 settings = await loadRoleselectSettings();
if (settings.autoRedirect) {
handleRoleChange("Ellenorzo");
return;
}
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() || LanguageManager.t('common.username');
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,
settings,
), '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,222 +1,199 @@
/* Hide original elements */
header, footer, .page-title, .card-kreta {
display: none !important;
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;
}
.logos {
width:32px;
height:32px;
}
.logo {
width: 32px;
height: 32px;
display: none !important;
}
.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 = 'logos';
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(LanguageManager.t('search.select_institution'));
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);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -17,105 +17,267 @@
<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 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>
<span data-i18n="settings.theme">Téma</span>
<div class="tab-navigation">
<button class="tab-button active" data-tab="home">
<span class="material-icons-round">home</span>
<span data-i18n="settings.tabs.home">Főoldal</span>
</button>
<button class="tab-button" data-tab="settings">
<span class="material-icons-round">tune</span>
<span data-i18n="settings.tabs.settings">Beállítások</span>
</button>
<button class="tab-button" data-tab="about">
<span class="material-icons-round">info</span>
<span data-i18n="settings.tabs.about">Névjegy</span>
</button>
</div>
<div class="tab-content active" id="tab-home">
<div class="settings-card">
<h2 data-i18n="settings.appearance">Megjelenés</h2>
<div class="settings-group">
<div class="setting-section">
<div class="setting-header">
<span class="material-icons-round">palette</span>
<span data-i18n="settings.theme">Téma</span>
</div>
<div class="theme-grid">
<button class="theme-option" data-theme="light-green">
<div class="theme-preview light-green">
<div class="preview-header"></div>
<div class="preview-content">
<div class="preview-card"></div>
</div>
</div>
<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>
<div class="preview-content">
<div class="preview-card"></div>
</div>
</div>
<span class="theme-name" data-i18n="settings.themes.dark_green">Sötét Zöld</span>
</button>
</div>
<div class="custom-themes-section">
<div class="custom-themes-header">
<span data-i18n="settings.custom_themes.title">Egyéni témák</span>
<div class="custom-themes-buttons">
<button class="theme-btn" id="addCustomTheme" title="Új téma">
<span class="material-icons-round">add</span>
</button>
<button class="theme-btn" id="importCustomTheme" title="Téma importálása">
<span class="material-icons-round">download</span>
</button>
</div>
</div>
<div class="theme-grid custom-themes-grid" id="customThemesGrid">
</div>
</div>
</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" data-i18n="settings.themes.light_blue">Világos Kék</span>
</button>
<button class="theme-option" data-theme="light-green">
<div class="theme-preview light-green">
<div class="preview-header"></div>
<div class="preview-content">
<div class="preview-card"></div>
</div>
</div>
<span class="theme-name" data-i18n="settings.themes.light_green">Világos Zöld</span>
</button>
<button class="theme-option" data-theme="dark-blue">
<div class="theme-preview dark-blue">
<div class="preview-header"></div>
<div class="preview-content">
<div class="preview-card"></div>
</div>
</div>
<span class="theme-name" data-i18n="settings.themes.dark_blue">Sötét Kék</span>
</button>
<button class="theme-option" data-theme="dark-green">
<div class="theme-preview dark-green">
<div class="preview-header"></div>
<div class="preview-content">
<div class="preview-card"></div>
</div>
</div>
<span class="theme-name" data-i18n="settings.themes.dark_green">Sötét Zöld</span>
</button>
</div>
</div>
<div class="setting-section">
<div class="setting-header">
<span class="material-icons-round">language</span>
<span data-i18n="settings.language">Nyelv</span>
</div>
<div class="language-grid">
<button class="language-option" data-language="hu">
<span class="language-name" data-i18n="settings.languages.hu">Magyar</span>
</button>
<button class="language-option" data-language="en">
<span class="language-name" data-i18n="settings.languages.en">English</span>
</button>
<div 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>
</div>
</div>
</div>
<div class="about-card">
<h2 data-i18n="settings.about.title">Névjegy</h2>
<div class="about-content">
<p data-i18n="settings.about.description">A Firka egy nyílt forráskódú projekt, amely a KRÉTA rendszerhez készít saját felhasználói felületet.</p>
<a href="https://github.com/QwIT-Development/" target="_blank" class="github-link">
<span class="material-icons-round">code</span>
<span data-i18n="settings.about.github">GitHub</span>
</a>
</div>
</div>
<div class="support-card">
<h2 data-i18n="settings.support.title">Támogatás</h2>
<div class="support-content">
<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>
<span data-i18n="settings.support.kofi">Ko-Fi</span>
</a>
<div class="tab-content" id="tab-settings">
<div class="settings-card">
<h2 data-i18n="settings.page_settings.title">Oldal beállítások</h2>
<div class="page-settings-info">
<div class="current-page-indicator">
<span class="material-icons-round">web</span>
<span id="currentPageName" data-i18n="settings.page_settings.current_page">Aktuális oldal</span>
<span id="currentPageValue" class="page-badge">-</span>
</div>
</div>
<div id="pageSpecificSettings" class="page-specific-settings">
<div class="no-settings-placeholder">
<span class="material-icons-round">settings_suggest</span>
<p data-i18n="settings.page_settings.no_settings">Ehhez az oldalhoz nincsenek egyéni beállítások.</p>
</div>
</div>
</div>
</div>
<footer class="popup-footer">
<div class="version-info" id="version">v1.1.0</div>
</footer>
<div class="tab-content" id="tab-about">
<div class="about-card">
<h2 data-i18n="settings.about.title">Névjegy</h2>
<div class="about-content">
<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 data-i18n="settings.support.title">Támogatás</h2>
<div class="support-content">
<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>
<span data-i18n="settings.support.kofi">Ko-Fi</span>
</a>
</div>
</div>
</div>
<div class="version-info" id="version">v1.4.0</div>
</div>
</div>
<script src="../tools/cookieManager.js"></script>
<div class="modal-overlay" id="themeEditorModal">
<div class="modal-content theme-editor-modal">
<div class="modal-header">
<h3 id="themeEditorTitle" data-i18n="settings.custom_themes.create">Új téma létrehozása</h3>
<button class="modal-close" id="closeThemeEditor">
<span class="material-icons-round">close</span>
</button>
</div>
<div class="modal-body">
<div class="theme-form">
<div class="form-group">
<label for="themeName" data-i18n="settings.custom_themes.name">Téma neve</label>
<input type="text" id="themeName" placeholder="Pl. Kék éjszaka">
</div>
<div class="form-group">
<label data-i18n="settings.custom_themes.mode">Mód</label>
<div class="mode-selector">
<button class="mode-option active" data-mode="dark">
<span class="material-icons-round">dark_mode</span>
<span data-i18n="settings.custom_themes.dark_mode">Sötét</span>
</button>
<button class="mode-option" data-mode="light">
<span class="material-icons-round">light_mode</span>
<span data-i18n="settings.custom_themes.light_mode">Világos</span>
</button>
</div>
</div>
<div class="color-section">
<h4 data-i18n="settings.custom_themes.colors">Színek</h4>
<div class="color-group">
<label data-i18n="settings.custom_themes.accent_color">Kiemelő szín</label>
<div class="color-input-wrapper">
<input type="color" id="accentColor" value="#A7DC22">
<input type="text" class="color-hex" id="accentColorHex" value="#A7DC22">
</div>
</div>
<div class="color-group">
<label data-i18n="settings.custom_themes.background_color">Háttér szín</label>
<div class="color-input-wrapper">
<input type="color" id="backgroundColor" value="#0D1202">
<input type="text" class="color-hex" id="backgroundColorHex" value="#0D1202">
</div>
</div>
<div class="color-group">
<label data-i18n="settings.custom_themes.card_color">Kártya szín</label>
<div class="color-input-wrapper">
<input type="color" id="cardColor" value="#141905">
<input type="text" class="color-hex" id="cardColorHex" value="#141905">
</div>
</div>
<div class="color-group">
<label data-i18n="settings.custom_themes.text_color">Szöveg szín</label>
<div class="color-input-wrapper">
<input type="color" id="textColor" value="#EAF7CC">
<input type="text" class="color-hex" id="textColorHex" value="#EAF7CC">
</div>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn-secondary" id="cancelThemeEditor" data-i18n="common.cancel">Mégse</button>
<button class="btn-primary" id="saveTheme" data-i18n="common.save">Mentés</button>
</div>
</div>
</div>
<div class="modal-overlay" id="shareThemeModal">
<div class="modal-content share-modal">
<div class="modal-header">
<h3 data-i18n="settings.custom_themes.share">Téma megosztása</h3>
<button class="modal-close" id="closeShareModal">
<span class="material-icons-round">close</span>
</button>
</div>
<div class="modal-body">
<p data-i18n="settings.custom_themes.share_description">Másold ki a kódot és oszd meg másokkal:</p>
<div class="share-code-wrapper">
<textarea id="shareCode" readonly></textarea>
<button class="copy-btn" id="copyShareCode">
<span class="material-icons-round">content_copy</span>
</button>
</div>
</div>
<div class="modal-footer">
<button class="btn-primary" id="closeShareModalBtn" data-i18n="common.close">Bezárás</button>
</div>
</div>
</div>
<div class="modal-overlay" id="importThemeModal">
<div class="modal-content import-modal">
<div class="modal-header">
<h3 data-i18n="settings.custom_themes.import">Téma importálása</h3>
<button class="modal-close" id="closeImportModal">
<span class="material-icons-round">close</span>
</button>
</div>
<div class="modal-body">
<p data-i18n="settings.custom_themes.import_description">Illeszd be a téma kódot:</p>
<textarea id="importCode" placeholder="Illeszd be a téma kódját ide..."></textarea>
<p class="error-message" id="importError"></p>
</div>
<div class="modal-footer">
<button class="btn-secondary" id="cancelImport" data-i18n="common.cancel">Mégse</button>
<button class="btn-primary" id="confirmImport" data-i18n="settings.custom_themes.import">Importálás</button>
</div>
</div>
</div>
<script src="../tools/storageManager.js"></script>
<script src="../global/language.js"></script>
<script src="index.js"></script>
</body>

File diff suppressed because it is too large Load Diff

462
setup/setup.css Normal file
View File

@@ -0,0 +1,462 @@
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Montserrat', sans-serif;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 1rem;
background: var(--background);
color: var(--text-primary);
}
.setup-container {
width: 100%;
max-width: 700px;
animation: fadeIn 0.5s ease;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.setup-card {
background: var(--card-card);
border-radius: 20px;
padding: 2rem;
box-shadow: 0 4px 20px var(--accent-shadow);
}
.setup-header {
text-align: center;
margin-bottom: 2rem;
}
.setup-logo {
width: 60px;
height: 60px;
margin-bottom: 1rem;
}
.setup-title {
font-size: 2rem;
font-weight: 700;
color: var(--text-primary);
margin-bottom: 0.5rem;
}
.setup-subtitle {
font-size: 1rem;
color: var(--text-secondary);
}
.progress-bar {
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 2rem;
padding: 0 1rem;
}
.progress-step {
display: flex;
flex-direction: column;
align-items: center;
gap: 0.4rem;
position: relative;
}
.progress-circle {
width: 40px;
height: 40px;
border-radius: 50%;
background: var(--button-secondaryFill);
border: 2px solid var(--text-teritary);
display: flex;
align-items: center;
justify-content: center;
font-weight: 600;
font-size: 1rem;
color: var(--text-teritary);
transition: all 0.3s ease;
}
.progress-step.active .progress-circle {
background: var(--accent-accent);
border-color: var(--accent-accent);
color: white;
transform: scale(1.1);
}
.progress-step.completed .progress-circle {
background: var(--accent-accent);
border-color: var(--accent-accent);
color: white;
}
.progress-label {
font-size: 0.875rem;
color: var(--text-secondary);
font-weight: 500;
}
.progress-step.active .progress-label {
color: var(--accent-accent);
font-weight: 600;
}
.progress-line {
width: 80px;
height: 2px;
background: var(--text-teritary);
margin: 0 0.5rem;
margin-bottom: 1.5rem;
}
.setup-content {
min-height: 320px;
position: relative;
}
.setup-step {
display: none;
animation: slideIn 0.4s ease;
}
.setup-step.active {
display: block;
}
@keyframes slideIn {
from {
opacity: 0;
transform: translateX(20px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
.step-icon {
text-align: center;
margin-bottom: 1rem;
}
.step-icon .material-icons-round {
font-size: 3rem;
color: var(--accent-accent);
}
.step-icon.success .material-icons-round {
font-size: 3.5rem;
color: var(--success);
}
.step-title {
text-align: center;
font-size: 1.5rem;
font-weight: 700;
color: var(--text-primary);
margin-bottom: 0.4rem;
}
.step-description {
text-align: center;
font-size: 0.9rem;
color: var(--text-secondary);
margin-bottom: 1.5rem;
}
.theme-options {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
gap: 1rem;
margin-top: 1rem;
}
.theme-card {
background: var(--button-secondaryFill);
border: 3px solid transparent;
border-radius: 12px;
padding: 1rem;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
flex-direction: column;
align-items: center;
gap: 0.8rem;
position: relative;
}
.theme-card:hover {
transform: translateY(-4px);
box-shadow: 0 8px 24px var(--accent-shadow);
}
.theme-card.selected {
border-color: var(--accent-accent);
background: var(--accent-15);
}
.theme-icon {
position: absolute;
top: 1rem;
right: 1rem;
color: var(--text-secondary);
font-size: 1.5rem;
}
.theme-card.selected .theme-icon {
color: var(--accent-accent);
}
.theme-preview {
width: 100%;
height: 100px;
border-radius: 10px;
overflow: hidden;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.theme-preview.light-green {
background: #FAFFF0;
}
.theme-preview.dark-green {
background: #0D1202;
}
.preview-header {
height: 30%;
display: flex;
align-items: center;
padding: 0.5rem;
}
.theme-preview.light-green .preview-header {
background: #F3FBDE;
}
.theme-preview.dark-green .preview-header {
background: #141905;
}
.preview-content {
height: 70%;
padding: 0.5rem;
}
.preview-card {
width: 100%;
height: 100%;
border-radius: 6px;
}
.theme-preview.light-green .preview-card {
background: white;
}
.theme-preview.dark-green .preview-card {
background: #1a2207;
}
.theme-name {
font-weight: 600;
color: var(--text-primary);
font-size: 1.1rem;
}
.language-options {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
gap: 1rem;
margin-top: 1rem;
}
.language-card {
background: var(--button-secondaryFill);
border: 3px solid transparent;
border-radius: 12px;
padding: 1.5rem;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
flex-direction: column;
align-items: center;
gap: 0.8rem;
}
.language-card:hover {
transform: translateY(-4px);
box-shadow: 0 8px 24px var(--accent-shadow);
}
.language-card.selected {
border-color: var(--accent-accent);
background: var(--accent-15);
}
.language-flag {
font-size: 3rem;
}
.language-name {
font-weight: 600;
color: var(--text-primary);
font-size: 1.1rem;
}
.finish-links {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 0.8rem;
margin-top: 1rem;
}
.finish-link {
background: var(--button-secondaryFill);
border: 2px solid transparent;
border-radius: 10px;
padding: 1rem;
display: flex;
align-items: center;
gap: 0.8rem;
text-decoration: none;
color: var(--text-primary);
transition: all 0.3s ease;
}
.finish-link:hover {
border-color: var(--accent-accent);
background: var(--accent-15);
transform: translateY(-2px);
}
.finish-link .material-icons-round {
font-size: 2rem;
color: var(--accent-accent);
flex-shrink: 0;
}
.link-content {
display: flex;
flex-direction: column;
gap: 0.25rem;
}
.link-title {
font-weight: 600;
font-size: 1rem;
color: var(--text-primary);
}
.link-description {
font-size: 0.8rem;
color: var(--text-secondary);
}
.setup-actions {
display: flex;
justify-content: space-between;
margin-top: 2rem;
gap: 1rem;
}
.actions-spacer {
flex: 1;
}
.btn-primary,
.btn-secondary {
padding: 1rem 2rem;
border-radius: 12px;
font-weight: 600;
font-size: 1rem;
cursor: pointer;
transition: all 0.3s ease;
border: none;
display: flex;
align-items: center;
gap: 0.5rem;
font-family: 'Montserrat', sans-serif;
}
.btn-primary {
background: var(--accent-accent);
color: white;
}
.btn-primary:hover {
background: var(--accent-secondary);
transform: translateY(-2px);
box-shadow: 0 4px 12px var(--accent-shadow);
}
.btn-secondary {
background: var(--button-secondaryFill);
color: var(--text-primary);
border: 2px solid var(--text-teritary);
}
.btn-secondary:hover {
border-color: var(--accent-accent);
transform: translateY(-2px);
}
.btn-primary .material-icons-round,
.btn-secondary .material-icons-round {
font-size: 1.25rem;
}
@media (max-width: 768px) {
body {
padding: 0.5rem;
}
.setup-card {
padding: 1.5rem;
}
.setup-title {
font-size: 1.75rem;
}
.step-title {
font-size: 1.3rem;
}
.progress-bar {
padding: 0;
}
.progress-line {
width: 30px;
}
.progress-circle {
width: 36px;
height: 36px;
}
.theme-options,
.language-options {
grid-template-columns: 1fr;
}
.finish-links {
grid-template-columns: 1fr;
}
}

159
setup/setup.html Normal file
View File

@@ -0,0 +1,159 @@
<!DOCTYPE html>
<html lang="hu">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Firxa - Kezdeti beállítások</title>
<link rel="icon" type="image/png" href="../images/firka_logo_128.png">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons+Round" rel="stylesheet">
<link rel="stylesheet" href="setup.css">
<link rel="stylesheet" href="../global/theme.css">
</head>
<body>
<div class="setup-container">
<div class="setup-card">
<div class="setup-header">
<img src="../images/firka_logo.png" alt="Firka" class="setup-logo">
<h1 class="setup-title">Üdvözöl a Firxa!</h1>
<p class="setup-subtitle" data-i18n="setup.welcome">Állítsd be a bővítményt néhány egyszerű lépésben</p>
</div>
<div class="progress-bar">
<div class="progress-step active" data-step="1">
<div class="progress-circle">1</div>
<span class="progress-label" data-i18n="setup.steps.theme">Téma</span>
</div>
<div class="progress-line"></div>
<div class="progress-step" data-step="2">
<div class="progress-circle">2</div>
<span class="progress-label" data-i18n="setup.steps.language">Nyelv</span>
</div>
<div class="progress-line"></div>
<div class="progress-step" data-step="3">
<div class="progress-circle">3</div>
<span class="progress-label" data-i18n="setup.steps.finish">Kész</span>
</div>
</div>
<div class="setup-content">
<div class="setup-step active" data-step="1">
<div class="step-icon">
<span class="material-icons-round">palette</span>
</div>
<h2 class="step-title" data-i18n="setup.theme.title">Válassz témát</h2>
<p class="step-description" data-i18n="setup.theme.description">Válaszd ki a számodra legmegfelelőbb megjelenést</p>
<div class="theme-options">
<button class="theme-card" data-theme="light-green">
<div class="theme-preview light-green">
<div class="preview-header"></div>
<div class="preview-content">
<div class="preview-card"></div>
</div>
</div>
<span class="theme-name" data-i18n="settings.themes.light_green">Világos Zöld</span>
<span class="theme-icon material-icons-round">light_mode</span>
</button>
<button class="theme-card" data-theme="dark-green">
<div class="theme-preview dark-green">
<div class="preview-header"></div>
<div class="preview-content">
<div class="preview-card"></div>
</div>
</div>
<span class="theme-name" data-i18n="settings.themes.dark_green">Sötét Zöld</span>
<span class="theme-icon material-icons-round">dark_mode</span>
</button>
</div>
</div>
<div class="setup-step" data-step="2">
<div class="step-icon">
<span class="material-icons-round">language</span>
</div>
<h2 class="step-title" data-i18n="setup.language.title">Válassz nyelvet</h2>
<p class="step-description" data-i18n="setup.language.description">Válaszd ki a használni kívánt nyelvet</p>
<div class="language-options">
<button class="language-card" data-language="hu">
<span class="language-flag">🇭🇺</span>
<span class="language-name" data-i18n="settings.languages.hu">Magyar</span>
</button>
<button class="language-card" data-language="en">
<span class="language-flag">🇬🇧</span>
<span class="language-name" data-i18n="settings.languages.en">English</span>
</button>
</div>
</div>
<div class="setup-step" data-step="3">
<div class="step-icon success">
<span class="material-icons-round">check_circle</span>
</div>
<h2 class="step-title" data-i18n="setup.finish.title">Minden kész!</h2>
<p class="step-description" data-i18n="setup.finish.description">A beállítások sikeresen mentve. Indulhat a tanulás!</p>
<div class="finish-links">
<a href="https://firka.app" target="_blank" class="finish-link">
<span class="material-icons-round">info</span>
<div class="link-content">
<span class="link-title" data-i18n="setup.finish.about">Weboldal</span>
<span class="link-description" data-i18n="setup.finish.about_desc">Tudj meg többet a projektről</span>
</div>
</a>
<a href="https://ko-fi.com/zan1456" target="_blank" class="finish-link">
<span class="material-icons-round">favorite</span>
<div class="link-content">
<span class="link-title" data-i18n="setup.finish.support">Támogatás</span>
<span class="link-description" data-i18n="setup.finish.support_desc">Támogasd a fejlesztést</span>
</div>
</a>
<a href="https://github.com/QwIT-Development/" target="_blank" class="finish-link">
<span class="material-icons-round">code</span>
<div class="link-content">
<span class="link-title" data-i18n="setup.finish.github">GitHub</span>
<span class="link-description" data-i18n="setup.finish.github_desc">Nézd meg a forráskódot</span>
</div>
</a>
<a href="https://discord.gg/firka-1111649116020285532" target="_blank" class="finish-link">
<span class="material-icons-round">forum</span>
<div class="link-content">
<span class="link-title" data-i18n="setup.finish.discord">Discord</span>
<span class="link-description" data-i18n="setup.finish.discord_desc">Csatlakozz a közösséghez</span>
</div>
</a>
</div>
</div>
</div>
<div class="setup-actions">
<button class="btn-secondary" id="backBtn" style="display: none;">
<span class="material-icons-round">arrow_back</span>
<span data-i18n="common.back">Vissza</span>
</button>
<div class="actions-spacer"></div>
<button class="btn-primary" id="nextBtn">
<span data-i18n="common.next">Tovább</span>
<span class="material-icons-round">arrow_forward</span>
</button>
<button class="btn-primary" id="finishBtn" style="display: none;">
<span data-i18n="setup.finish.start">Kezdés</span>
<span class="material-icons-round">check</span>
</button>
</div>
</div>
</div>
<script src="../tools/storageManager.js"></script>
<script src="../global/language.js"></script>
<script src="setup.js"></script>
</body>
</html>

146
setup/setup.js Normal file
View File

@@ -0,0 +1,146 @@
let currentStep = 1;
const totalSteps = 3;
let selectedTheme = 'light-green';
let selectedLanguage = 'hu';
document.addEventListener('DOMContentLoaded', async () => {
selectedTheme = await storageManager.get('themePreference', 'light-green');
selectedLanguage = await storageManager.get('languagePreference', 'hu');
initializeThemeSelection();
initializeLanguageSelection();
document.getElementById('nextBtn').addEventListener('click', nextStep);
document.getElementById('backBtn').addEventListener('click', previousStep);
document.getElementById('finishBtn').addEventListener('click', finishSetup);
document.querySelectorAll('.theme-card').forEach(card => {
card.addEventListener('click', () => selectTheme(card.dataset.theme));
});
document.querySelectorAll('.language-card').forEach(card => {
card.addEventListener('click', () => selectLanguage(card.dataset.language));
});
applyTheme(selectedTheme);
});
function initializeThemeSelection() {
const themeCards = document.querySelectorAll('.theme-card');
themeCards.forEach(card => {
if (card.dataset.theme === selectedTheme) {
card.classList.add('selected');
}
});
}
function initializeLanguageSelection() {
const languageCards = document.querySelectorAll('.language-card');
languageCards.forEach(card => {
if (card.dataset.language === selectedLanguage) {
card.classList.add('selected');
}
});
}
function selectTheme(theme) {
selectedTheme = theme;
document.querySelectorAll('.theme-card').forEach(card => {
card.classList.remove('selected');
});
document.querySelector(`[data-theme="${theme}"]`).classList.add('selected');
applyTheme(theme);
}
function selectLanguage(language) {
selectedLanguage = language;
document.querySelectorAll('.language-card').forEach(card => {
card.classList.remove('selected');
});
document.querySelector(`[data-language="${language}"]`).classList.add('selected');
}
function applyTheme(theme) {
document.documentElement.setAttribute('data-theme', theme);
}
function nextStep() {
if (currentStep < totalSteps) {
saveCurrentStepSettings();
currentStep++;
updateStepDisplay();
}
}
function previousStep() {
if (currentStep > 1) {
currentStep--;
updateStepDisplay();
}
}
async function saveCurrentStepSettings() {
if (currentStep === 1) {
await storageManager.set('themePreference', selectedTheme);
} else if (currentStep === 2) {
await storageManager.set('language', selectedLanguage);
await storageManager.set('languagePreference', selectedLanguage);
if (window.LanguageManager) {
await window.LanguageManager.changeLanguage(selectedLanguage);
}
}
}
function updateStepDisplay() {
document.querySelectorAll('.progress-step').forEach(step => {
const stepNumber = parseInt(step.dataset.step);
if (stepNumber < currentStep) {
step.classList.add('completed');
step.classList.remove('active');
} else if (stepNumber === currentStep) {
step.classList.add('active');
step.classList.remove('completed');
} else {
step.classList.remove('active', 'completed');
}
});
document.querySelectorAll('.setup-step').forEach(step => {
if (parseInt(step.dataset.step) === currentStep) {
step.classList.add('active');
} else {
step.classList.remove('active');
}
});
const backBtn = document.getElementById('backBtn');
const nextBtn = document.getElementById('nextBtn');
const finishBtn = document.getElementById('finishBtn');
if (currentStep === 1) {
backBtn.style.display = 'none';
} else {
backBtn.style.display = 'flex';
}
if (currentStep === totalSteps) {
nextBtn.style.display = 'none';
finishBtn.style.display = 'flex';
} else {
nextBtn.style.display = 'flex';
finishBtn.style.display = 'none';
}
}
async function finishSetup() {
await saveCurrentStepSettings();
await storageManager.set('setupCompleted', true);
window.location.href = 'https://intezmenykereso.e-kreta.hu/';
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

118
tools/background.js Normal file
View File

@@ -0,0 +1,118 @@
chrome.runtime.onInstalled.addListener(async (details) => {
if (details.reason === 'install') {
const setupCompleted = await chrome.storage.sync.get('firka_setupCompleted');
if (!setupCompleted.firka_setupCompleted) {
chrome.tabs.create({
url: chrome.runtime.getURL('setup/setup.html')
});
}
}
});
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,140 +1,171 @@
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 navItems = [
{ href: `${baseUrl}/Intezmeny/Faliujsag`, page: "dashboard", path: "/Intezmeny/Faliujsag", icon: "dashboard", label: LanguageManager.t("navigation.dashboard") },
{ href: `${baseUrl}/TanuloErtekeles/Osztalyzatok`, page: "grades", path: "/TanuloErtekeles/Osztalyzatok", icon: "grades", label: LanguageManager.t("navigation.grades") },
{ href: `${baseUrl}/Orarend/InformaciokOrarend`, page: "timetable", path: "/Orarend/InformaciokOrarend", icon: "timetable", label: LanguageManager.t("navigation.timetable") },
{ href: `${baseUrl}/Hianyzas/Hianyzasok`, page: "absences", path: "/Hianyzas/Hianyzasok", icon: "absences", label: LanguageManager.t("navigation.absences") },
{ href: "https://eugyintezes.e-kreta.hu/api/bff/login", page: "messages", path: "/" || "/uzenetek", icon: "messages", label: LanguageManager.t("navigation.messages") }
];
const desktopNavItems = navItems.map(item => {
const isActive = location.pathname === item.path;
return `<a href="${item.href}" data-page="${item.page}" class="nav-item ${isActive ? "active" : ""}">
<img src="${chrome.runtime.getURL("icons/" + item.icon + "-" + (isActive ? "active" : "inactive") + ".svg")}" alt="${item.label}">
${item.label}
</a>`;
}).join("");
const mobileNavItems = navItems.map(item => {
const isActive = location.pathname === item.path;
return `<a href="${item.href}" data-page="${item.page}" class="mobile-nav-item ${isActive ? "active" : ""}">
<img src="${chrome.runtime.getURL("icons/" + item.icon + "-" + (isActive ? "active" : "inactive") + ".svg")}" alt="${item.label}">
<span>${item.label}</span>
</a>`;
}).join("");
const userAvatarSvg = `<svg viewBox="0 0 24 24"><path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/></svg>`;
const element = `
<header class="kreta-header">
<div class="school-info">
<p class="logo-text">
<img src="${chrome.runtime.getURL("images/firka_logo.png")}" alt="Firka" class="logo">
Firka
</p>
<div class="school-details" title="${schoolNameFull}">
${shortenedSchoolName}
</div>
</div>
<nav class="kreta-nav">
<div class="nav-links">
${desktopNavItems}
</div>
</nav>
const element = `<header class="kreta-header">
<div class="school-info">
<p class="logo-text">
<img src="${chrome.runtime.getURL('images/firka_logo.png')}" alt="Firka" class="logo">
Firka
</p>
<div class="school-details" title="${schoolNameFull}">
${shortenedSchoolName}
</div>
<div class="user-profile">
<button class="user-dropdown-btn">
<div class="user-info">
<span class="user-name">${data.userData.name}</span>
<span class="nav-logout-timer" id="logoutTimer">${data.userData.time}</span>
</div>
<button class="nav-toggle" aria-label="${LanguageManager.t('navigation.nav_toggle')}">
<svg viewBox="0 0 24 24">
<path d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z"/>
</svg>
</button>
<nav class="kreta-nav">
<div class="nav-links">
<a href="/Intezmeny/Faliujsag" data-page="dashboard" class="nav-item ${( location.pathname == '/Intezmeny/Faliujsag' ? 'active' : '')}">
<img src="${chrome.runtime.getURL('icons/dashboard-' + ( location.pathname == '/Intezmeny/Faliujsag' ? 'active' : 'inactive') + '.svg')}" alt="${LanguageManager.t('navigation.dashboard')}">
${LanguageManager.t('navigation.dashboard')}
</a>
<a href="/TanuloErtekeles/Osztalyzatok" data-page="grades" class="nav-item ${( location.pathname == '/TanuloErtekeles/Osztalyzatok' ? 'active' : '')}">
<img src="${chrome.runtime.getURL('icons/grades-' + ( location.pathname == '/TanuloErtekeles/Osztalyzatok' ? 'active' : 'inactive') + '.svg')}" alt="${LanguageManager.t('navigation.grades')}">
${LanguageManager.t('navigation.grades')}
</a>
<a href="/Orarend/InformaciokOrarend" data-page="timetable" class="nav-item ${( location.pathname == '/Orarend/InformaciokOrarend' ? 'active' : '')}">
<img src="${chrome.runtime.getURL('icons/timetable-' + ( location.pathname == '/Orarend/InformaciokOrarend' ? 'active' : 'inactive') + '.svg')}" alt="${LanguageManager.t('navigation.timetable')}">
${LanguageManager.t('navigation.timetable')}
</a>
<a href="/Hianyzas/Hianyzasok" data-page="absences" class="nav-item ${( location.pathname == '/Hianyzas/Hianyzasok' ? 'active' : '')}">
<img src="${chrome.runtime.getURL('icons/absences-' + ( location.pathname == '/Hianyzas/Hianyzasok' ? 'active' : 'inactive') + '.svg')}" alt="${LanguageManager.t('navigation.absences')}">
${LanguageManager.t('navigation.absences')}
</a>
<a href="/Tanulo/TanuloHaziFeladat" data-page="other" class="nav-item ${( location.pathname == '/Tanulo/TanuloHaziFeladat' ? 'active' : '')}">
<img src="${chrome.runtime.getURL('icons/others.svg')}" alt="${LanguageManager.t('navigation.other')}">
${LanguageManager.t('navigation.other')}
</a>
<div class="user-avatar-icon">
${userAvatarSvg}
</div>
</nav>
</button>
<div class="user-dropdown">
<a href="${baseUrl}/Adminisztracio/Profil" data-page="profile" class="dropdown-item">
<img src="${chrome.runtime.getURL("icons/profile.svg")}" alt="${LanguageManager.t("navigation.profile")}">
${LanguageManager.t("navigation.profile")}
</a>
<a href="#" class="dropdown-item" id="settingsBtn">
<img src="${chrome.runtime.getURL("icons/settings.svg")}" alt="${LanguageManager.t("navigation.settings")}">
${LanguageManager.t("navigation.settings")}
</a>
<a href="${baseUrl}/Home/Logout" data-page="logout" class="dropdown-item">
<img src="${chrome.runtime.getURL("icons/logout.svg")}" alt="${LanguageManager.t("navigation.logout")}">
${LanguageManager.t("navigation.logout")}
</a>
</div>
</div>
</header>
<div class="user-profile">
<button class="user-dropdown-btn">
<div class="user-info">
<span class="user-name">${data.userData.name}</span>
<span class="nav-logout-timer" id="logoutTimer">${data.userData.time}</span>
</div>
</button>
<div class="user-dropdown">
<a href="https://bmszc-puskas.e-kreta.hu/Home/Uzenetek" data-page="messages" class="dropdown-item">
<img src="${chrome.runtime.getURL('icons/messages.svg')}" alt="${LanguageManager.t('navigation.messages')}">
${LanguageManager.t('navigation.messages')}
</a>
<a href="/Adminisztracio/Profil" data-page="profile" class="dropdown-item">
<img src="${chrome.runtime.getURL('icons/profile.svg')}" alt="${LanguageManager.t('navigation.profile')}">
${LanguageManager.t('navigation.profile')}
</a>
<a href="#" class="dropdown-item" id="settingsBtn">
<img src="${chrome.runtime.getURL('icons/settings.svg')}" alt="${LanguageManager.t('navigation.settings')}">
${LanguageManager.t('navigation.settings')}
</a>
<a href="/Home/Logout" data-page="logout" class="dropdown-item">
<img src="${chrome.runtime.getURL('icons/logout.svg')}" alt="${LanguageManager.t('navigation.logout')}">
${LanguageManager.t('navigation.logout')}
</a>
</div>
</div>
</header>`
<header class="mobile-header">
<div class="school-info">
<p class="logo-text">
<img src="${chrome.runtime.getURL("images/firka_logo.png")}" alt="Firka" class="logo">
Firka
</p>
<div class="school-details" title="${schoolNameFull}">
${shortenedSchoolName}
</div>
</div>
</header>
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--;
}
};
<nav class="mobile-bottom-nav">
${mobileNavItems}
<button class="mobile-user-btn" id="mobileUserBtn">
<div class="user-avatar-icon">
${userAvatarSvg}
</div>
<span>${LanguageManager.t("navigation.profile")}</span>
</button>
</nav>
setInterval(updateTimer, 1000);
<div class="mobile-user-dropdown" id="mobileUserDropdown">
<div class="mobile-dropdown-header">
<span class="user-name">${data.userData.name}</span>
<span class="nav-logout-timer" id="mobileLogoutTimer">${data.userData.time}</span>
</div>
<a href="${baseUrl}/Adminisztracio/Profil" data-page="profile" class="mobile-dropdown-item">
<img src="${chrome.runtime.getURL("icons/profile.svg")}" alt="${LanguageManager.t("navigation.profile")}">
${LanguageManager.t("navigation.profile")}
</a>
<a href="#" class="mobile-dropdown-item" id="mobileSettingsBtn">
<img src="${chrome.runtime.getURL("icons/settings.svg")}" alt="${LanguageManager.t("navigation.settings")}">
${LanguageManager.t("navigation.settings")}
</a>
<a href="${baseUrl}/Home/Logout" data-page="logout" class="mobile-dropdown-item">
<img src="${chrome.runtime.getURL("icons/logout.svg")}" alt="${LanguageManager.t("navigation.logout")}">
${LanguageManager.t("navigation.logout")}
</a>
</div>
`;
return element;
},
const startTime = parseInt(data.userData.time?.match(/\d+/)?.[0] || "45");
let timeLeft = startTime * 60;
importFonts() {
const links = [
{ rel: 'preconnect', href: 'https://fonts.googleapis.com' },
{ rel: 'preconnect', href: 'https://fonts.gstatic.com', crossorigin: true },
{ rel: 'stylesheet', href: 'https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&display=swap' },
{ rel: 'stylesheet', href: 'https://fonts.googleapis.com/css2?family=Figtree:ital,wght@0,300..900;1,300..900&display=swap' },
{ rel: 'stylesheet', href: 'https://fonts.googleapis.com/icon?family=Material+Icons+Round' }
];
links.forEach(link => {
const linkElement = document.createElement('link');
Object.entries(link).forEach(([key, value]) => {
linkElement[key] = value;
});
document.head.appendChild(linkElement);
});
}
}
const updateTimer = () => {
const minutes = Math.floor(timeLeft / 60);
const seconds = timeLeft % 60;
const timeText = `${minutes}:${seconds.toString().padStart(2, "0")}`;
const desktopTimer = document.getElementById("logoutTimer");
const mobileTimer = document.getElementById("mobileLogoutTimer");
if (desktopTimer) desktopTimer.textContent = timeText;
if (mobileTimer) mobileTimer.textContent = timeText;
if (timeLeft <= 0) {
window.location.href = "/Home/Logout";
} else {
timeLeft--;
}
};
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");
});
document.querySelector("#mobileSettingsBtn")?.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,53 +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" data-i18n="loading.text">Betöltés alatt...</div>
<div class="loading-text2" data-i18n="loading.subtext">Kis türelmet!</div>
</div>
`;
document.body.appendChild(loadingScreen);
document.body.classList.add('loaded');
},
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';
hide() {
document.body.classList.add("loaded");
const loadingScreen = document.querySelector(".loading-screen");
if (loadingScreen) {
loadingScreen.style.opacity = "0";
const removeLoadingScreen = () => {
if (loadingScreen && loadingScreen.parentNode) {
loadingScreen.remove();
}
};
loadingScreen.addEventListener('transitionend', removeLoadingScreen, { once: true });
setTimeout(removeLoadingScreen, 500);
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);
}
},
};