41 Commits

Author SHA1 Message Date
Zan
f00f5f4738 Action bar to messages 2025-11-29 20:46:50 +01:00
Zan
23ceb4f367 Little fixes 2025-11-29 20:46:42 +01:00
Zan
c01a69586a Theme fixes 2025-11-29 19:51:24 +01:00
Zan
a107c51fa2 Mark as seen 2025-11-29 18:59:40 +01:00
Zan
e171ce93b6 Fixes 2025-11-29 18:42:54 +01:00
Zan
de59809872 Get bff for messages page 2025-11-29 18:38:31 +01:00
Zan
004b198f52 Fix for messages page 2025-11-29 18:36:18 +01:00
Zan
d35b50d5e4 -w 2025-10-17 21:10:54 +02:00
Zan
48a5155772 w 2025-10-17 21:10:23 +02:00
Zan
3f3e16b4e5 more tests 2025-10-09 15:01:51 +02:00
Zan
12ecf72052 Temp remove custom themes 2025-10-06 18:21:59 +02:00
Zan
80feac12b2 Update README.md 2025-09-25 16:21:37 +02:00
Zan
757c6b6451 Create manifest_fox.json 2025-09-25 16:20:14 +02:00
Zan
67aa3ed836 Remove icon 2025-09-25 16:18:41 +02:00
Zan
9ac962daf6 Removed homework page from navbar and added messages page 2025-09-24 20:37:33 +02:00
Zan
a446afa153 Removed support for homework page 2025-09-24 20:37:04 +02:00
Zan
8037f2f241 Homework attachment support 2025-09-24 20:28:00 +02:00
Zan
bf16c91a0a Updated 2025-09-23 22:15:12 +02:00
Zan
ca71eba13f Update README.md 2025-09-23 22:12:58 +02:00
Zan
a88a4b2ed6 fixes 2025-09-23 22:06:58 +02:00
Zan
f70f8d682b Added messages page support 2025-09-23 22:05:39 +02:00
Zan
084308abab Shorter theme ID-s 2025-09-23 22:01:14 +02:00
Zan
5a25de4c45 navbar redirects by schoolID 2025-09-23 21:36:24 +02:00
Zan
4180fe823b removed old cookierequests 2025-09-23 20:40:37 +02:00
Zan
636a325470 Removed refilc colors, added theme customisation (WIP) 2025-09-23 19:31:22 +02:00
Zan
0f9bae45a5 Update manifest.json 2025-09-23 18:24:57 +02:00
Zan
86cab01fba Lang fixes 2025-09-15 22:47:04 +02:00
Zan
eb3924f60f Fixes for custom things and mark as done 2025-09-15 22:44:53 +02:00
Zan
8c57152fe4 Use localstorage rather than cookies 2025-09-15 22:17:50 +02:00
Zan
9d877a1d68 Fixed maintenance 2025-09-14 18:17:45 +02:00
Zan
1ca8e24805 Added manual adding for homework 2025-09-14 18:17:17 +02:00
Zan
8a8d81b67f version update 2025-09-10 15:27:49 +02:00
Zan
245142c833 Fix loading remove 2025-09-10 15:27:43 +02:00
Zan
7e1d4cd9a7 Mobile UI fix 2025-09-10 15:27:18 +02:00
Zan
f29f7a2366 Mobile UI fix 2025-09-10 15:27:01 +02:00
Zan
6e0d3b6b7f Link underline remove 2025-09-10 15:26:52 +02:00
Zan
9fd0190ab8 Elmaradt órák megjelenítése 2025-09-09 22:16:53 +02:00
Zan
94fbc472f6 Mark homework as done (timetable) 2025-09-09 22:10:50 +02:00
Zan
96be6471f0 Homework in timetable site 2025-09-09 21:52:36 +02:00
Zan
10cabc3889 Update login.js 2025-09-09 21:38:04 +02:00
Zan
6cddce142c Profile page rework 2025-09-09 20:05:05 +02:00
57 changed files with 5751 additions and 2710 deletions

View File

@@ -8,9 +8,9 @@
</p>
<p align="center">
<a href="https://github.com/QwIT-Development/firka-extension/releases">
<!--<a href="https://github.com/QwIT-Development/firka-extension/releases">
<img src="https://img.shields.io/github/downloads-pre/QwIT-Development/firka-extension/latest/total?style=for-the-badge&logo=github&logoColor=EAF7CC&label=Let%C3%B6lt%C3%A9sek&labelColor=141905&color=A7DC22" alt="Downloads">
</a>
</a>-->
<a href="https://discord.gg/6DvjyPAw2T">
<img src="https://img.shields.io/discord/1111649116020285532?style=for-the-badge&logo=discord&logoColor=EAF7CC&label=Discord&labelColor=0D1202&color=A7DC22" alt="Discord">
</a>
@@ -31,7 +31,7 @@
## 📱 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 +39,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 +46,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 +57,17 @@ A bővítmény jelenleg az alábbi e-KRÉTA oldalakat támogatja:
- Bejelentkezés
- Kijelentkezés
- Szerepkörválasztó
- Órarend
- Órarend (Házi feladatok, Számonkérések)
- Faliújság
- Hiányzások
- Házi feladatok
- Jegyek
- Intézménykereső
- Üzenetek
- Profil (Béta)
## 👥 Csapat
- **[Zan1456](https://github.com/Zan1456)** - Vezető Fejlesztő
- **[BalazsManus](https://github.com/olajcsere)** - Fejlesztő
- **[Xou](https://yoursit.ee/xou)** - Designer
## 🤝 Közreműködés

View File

@@ -139,6 +139,7 @@ body {
}
.dropdown-item:hover {
background:var(--button-secondaryFill);
text-decoration:none;
}
.kreta-main {
flex:1;

View File

@@ -1,11 +1,11 @@
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ó",
name: await storageManager.get("userName", "Felhasználónév"),
time:
document.querySelector(".usermenu_timer")?.textContent?.trim() ||
"45:00",
@@ -281,7 +281,7 @@ async function transformAbsencesPage() {
container.className = 'kreta-container';
const headerDiv = document.createElement('div');
const parser = new DOMParser();
const doc = parser.parseFromString(createTemplate.header(), 'text/html');
const doc = parser.parseFromString(await createTemplate.header(), 'text/html');
const tempDiv = doc.body;
while (tempDiv.firstChild) {
headerDiv.appendChild(tempDiv.firstChild);

View File

@@ -219,6 +219,51 @@ h2 {
word-wrap: break-word;
overflow-wrap: break-word;
hyphens: auto;
max-width: 200px;
}
/* Mobile view: author below content */
@media (max-width: 768px) {
.news-item .widget-row {
flex-direction: column;
align-items: stretch;
}
.news-item .widget-meta {
order: 2;
display: flex;
flex-direction: column;
align-items: center;
margin-top: 8px;
}
.news-item .widget-details {
order: 1;
}
.news-author {
align-items: center;
text-align: center;
max-width: 100%;
margin-top: 4px;
}
.widget-date {
order: 1;
}
.news-author {
order: 2;
}
}
/* Desktop view: limit author width and wrap text */
@media (min-width: 769px) {
.news-author {
max-width: 180px;
white-space: normal;
line-height: 1.3;
}
}
.widget-empty {
@@ -346,6 +391,7 @@ h2 {
.dropdown-item:hover {
background-color: var(--accent-15);
text-decoration: none;
}
.dropdown-item svg {

View File

@@ -292,18 +292,22 @@ class DashboardDataManager {
class DashboardRenderer {
constructor(data) {
this.baseData = data;
}
async init() {
this.data = {
...data,
...this.baseData,
schoolInfo: {
name:
cookieManager.get(COOKIE_KEYS.SCHOOL_NAME) || DEFAULT_VALUES.SCHOOL,
id: cookieManager.get(COOKIE_KEYS.SCHOOL_CODE) || "",
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,
name: await storageManager.get("userName", "Felhasználónév"),
time:
document.querySelector(".usermenu_timer")?.textContent?.trim() ||
DEFAULT_VALUES.TIMER,
"45:00",
},
};
this.schoolNameFull = `${this.data.schoolInfo.id} - ${this.data.schoolInfo.name}`;
@@ -475,14 +479,15 @@ class DashboardRenderer {
`;
}
render() {
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(createTemplate.header(), 'text/html');
const headerDoc = parser.parseFromString(await createTemplate.header(), 'text/html');
const headerContent = headerDoc.body;
while (headerContent.firstChild) {
headerDiv.appendChild(headerContent.firstChild);
@@ -573,7 +578,7 @@ class DashboardApplication {
const dashboardData = await dataManager.extractAllData();
const renderer = new DashboardRenderer(dashboardData);
renderer.render();
await renderer.render();
} catch (error) {
console.error("Error initializing dashboard:", error);
}

View File

@@ -6,7 +6,8 @@
try {
currentLanguage = language;
cookieManager.set("languagePreference", language);
await storageManager.set("languagePreference", language);
localStorage.setItem("languagePreference", language);
await loadTranslations(language);
@@ -82,20 +83,25 @@
}
async function initializeLanguage() {
const cookieLanguage = cookieManager.get("languagePreference");
const localStorageLanguage = localStorage.getItem("languagePreference");
try {
const storageLanguage = await storageManager.get("languagePreference");
const localStorageLanguage = localStorage.getItem("languagePreference");
const language = storageLanguage || localStorageLanguage || "hu";
const language = cookieLanguage || localStorageLanguage || "hu";
await setLanguage(language);
loadTranslationsForPage();
await setLanguage(language);
loadTranslationsForPage();
if (cookieLanguage !== localStorageLanguage) {
if (cookieLanguage) {
localStorage.setItem("languagePreference", cookieLanguage);
} else if (localStorageLanguage) {
cookieManager.set("languagePreference", localStorageLanguage);
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();
}
}
@@ -121,8 +127,7 @@
function loadTranslationsForPage() {
try {
applyTranslations();
// Figyeljük a DOM változásokat és alkalmazzuk a fordításokat új elemekre
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.type === 'childList') {
@@ -142,8 +147,7 @@
}
}
});
// Ha maga az elem is tartalmaz data-i18n attribútumot
if (node.hasAttribute && node.hasAttribute('data-i18n')) {
const key = node.getAttribute('data-i18n');
const translation = getTranslation(key);

View File

@@ -9,12 +9,7 @@ function checkMaintenancePage() {
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 &&

View File

@@ -49,15 +49,17 @@
.nav-item {
display:flex;
align-items:center;
padding:clamp(0.5rem,1.5vw,1rem) 0.5rem;
padding:8px 14px 8px 12px;
color:var(--text-secondary);
text-decoration:none;
font-weight:500;
white-space:nowrap;
border-radius:8px;
border-radius:20px;
transition:all 0.2s ease;
gap:0.5rem;
text-decoration:none;
background:var(--button-secondaryFill);
box-shadow:0px 1px var(--shadow-blur,2px) 0px var(--accent-shadow);
}
.nav-item.active {
display:flex;
@@ -70,13 +72,13 @@
}
.nav-item:hover {
color:var(--text-primary);
background-color:var(--hover);
background-color:var(--accent-15);
border-radius:8px;
text-decoration:none;
}
.nav-item.active:hover {
color:var(--accent-accent);
background-color:var(--accent-hover);
background-color:var(--accent-15);
text-decoration:none;
}
.nav-item img,.nav-item svg {
@@ -146,6 +148,7 @@
background:var(--hover);
color:var(--accent-accent);
border-radius:8px;
text-decoration:none;
}
@keyframes dropdownShow {
from {

View File

@@ -11,16 +11,16 @@ const DEFAULT_VALUES = {
TIMER: "45:00",
};
function updateHeaderInfo() {
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:
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,
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,
@@ -59,8 +59,8 @@ function startLogoutTimer(timeString) {
setInterval(updateTimer, 1000);
}
document.addEventListener("DOMContentLoaded", () => {
updateHeaderInfo();
document.addEventListener("DOMContentLoaded", async () => {
await updateHeaderInfo();
});
function setupUserDropdown() {
@@ -169,8 +169,8 @@ function setupMobileNavigation() {
}, 100);
}
document.addEventListener("DOMContentLoaded", () => {
updateHeaderInfo();
document.addEventListener("DOMContentLoaded", async () => {
await updateHeaderInfo();
setupUserDropdown();
setupSettingsButton();
setupMobileNavigation();

View File

@@ -1,18 +1,18 @@
:root {
--background:#DAE4F7;
--background-0:#dae4f700;
--background:#FAFFF0;
--background-0:#fafff000;
--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;
--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;
@@ -66,40 +66,6 @@
--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;

View File

@@ -1,16 +1,12 @@
(() => {
function setTheme(theme) {
async 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);
document.documentElement.setAttribute("data-theme", theme);
await storageManager.set("themePreference", theme);
chrome.runtime
.sendMessage({
action: "themeChanged",
theme: actualTheme,
theme: theme,
})
.catch(() => {});
} catch (error) {
@@ -104,22 +100,18 @@
}
}
function initializeTheme() {
const cookieTheme = cookieManager.get("themePreference");
const localStorageTheme = localStorage.getItem("themePreference");
async function initializeTheme() {
try {
const theme = await storageManager.get("themePreference", "light-green");
const theme = cookieTheme || localStorageTheme || "light-green";
setTheme(theme);
setPageTitleAndFavicon();
importFonts();
if (cookieTheme !== localStorageTheme) {
if (cookieTheme) {
localStorage.setItem("themePreference", cookieTheme);
} else if (localStorageTheme) {
cookieManager.set("themePreference", localStorageTheme);
}
await setTheme(theme);
setPageTitleAndFavicon();
importFonts();
} catch (error) {
console.error("Error initializing theme:", error);
await setTheme("light-green");
setPageTitleAndFavicon();
importFonts();
}
}
@@ -146,17 +138,20 @@
let titleCheckTimeout;
const observer = new MutationObserver((mutations) => {
const observer = new MutationObserver(async (mutations) => {
const currentTheme = document.documentElement.getAttribute("data-theme");
const savedTheme =
cookieManager.get("themePreference") ||
localStorage.getItem("themePreference");
try {
const savedTheme = await storageManager.get("themePreference");
if (
(!currentTheme && savedTheme) ||
(currentTheme !== savedTheme && savedTheme)
) {
setTheme(savedTheme);
if (
(!currentTheme && savedTheme) ||
(currentTheme !== savedTheme && savedTheme)
) {
await setTheme(savedTheme);
}
} catch (error) {
console.error("Error checking theme in observer:", error);
}
const titleChanged = mutations.some(

View File

@@ -317,6 +317,7 @@ body {
}
.dropdown-item:hover {
background:var(--button-secondaryFill);
text-decoration:none;
}
.kreta-main {
flex:1;

View File

@@ -11,7 +11,7 @@
window.currentGradesData = gradesData;
document.body.innerHTML = '';
const parser = new DOMParser();
const doc = parser.parseFromString(generatePageHTML(
const doc = parser.parseFromString(await generatePageHTML(
gradesData,
studentAverage,
classAverage,
@@ -61,24 +61,24 @@
}
const data = await response.json();
return processAPIGradesData(data);
return await processAPIGradesData(data);
} catch (error) {
console.error("Error fetching grades from API:", error);
return extractGradesDataFromDOM();
return await extractGradesDataFromDOM();
}
}
function processAPIGradesData(apiData) {
async function processAPIGradesData(apiData) {
const subjects = [];
if (!apiData.Data || !Array.isArray(apiData.Data)) {
return {
schoolInfo: {
id: cookieManager.get("schoolCode") || "",
name: cookieManager.get("schoolName") || "Iskola",
id: await storageManager.get("schoolCode", ""),
name: await storageManager.get("schoolName", "OM azonosító - Iskola neve"),
},
userData: {
name: cookieManager.get("userName") || "Felhasználó",
name: await storageManager.get("userName", "Felhasználónév"),
time:
document.querySelector(".usermenu_timer")?.textContent?.trim() ||
"45:00",
@@ -195,11 +195,11 @@
return {
schoolInfo: {
id: cookieManager.get("schoolCode") || "",
name: cookieManager.get("schoolName") || "Iskola",
id: await storageManager.get("schoolCode", ""),
name: await storageManager.get("schoolName", "Iskola"),
},
userData: {
name: cookieManager.get("userName") || "Felhasználó",
name: await storageManager.get("userName", "Felhasználó"),
time:
document.querySelector(".usermenu_timer")?.textContent?.trim() ||
"45:00",
@@ -208,7 +208,7 @@
};
}
function extractGradesDataFromDOM() {
async function extractGradesDataFromDOM() {
const subjects = [];
const rows = document.querySelectorAll(
"#Osztalyzatok_7895TanuloErtekelesByTanuloGrid tbody tr",
@@ -293,11 +293,11 @@
return {
schoolInfo: {
id: cookieManager.get("schoolCode") || "",
name: cookieManager.get("schoolName") || "Iskola",
id: await storageManager.get("schoolCode", ""),
name: await storageManager.get("schoolName", "Iskola"),
},
userData: {
name: cookieManager.get("userName") || "Felhasználó",
name: await storageManager.get("userName", "Felhasználó"),
time:
document.querySelector(".usermenu_timer")?.textContent?.trim() ||
"45:00",
@@ -376,7 +376,7 @@
return distribution;
}
function generatePageHTML(data, studentAverage, classAverage) {
async function generatePageHTML(data, studentAverage, classAverage) {
const totalGrades = data.subjects.reduce(
(sum, subject) => sum + subject.grades.length,
0,
@@ -393,7 +393,7 @@
return `
<div class="kreta-container">
${createTemplate.header()}
${await createTemplate.header()}
<main class="kreta-main">
<div class="grades-overview">

View File

@@ -1,753 +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);
}
/* Checkbox oszlop stílusai */
.checkbox-header {
width: 50px;
text-align: center;
font-size: 16px;
}
.checkbox-cell {
width: 50px;
text-align: center;
padding: 8px !important;
}
.homework-checkbox {
width: 20px;
height: 20px;
cursor: pointer;
appearance: none;
border: 2px solid var(--accent-30);
border-radius: 4px;
background-color: var(--card-card);
position: relative;
transition: all 0.2s ease;
display: flex;
align-items: center;
justify-content: center;
}
.homework-checkbox:hover {
border-color: var(--accent);
background-color: var(--accent-15);
transform: scale(1.05);
}
.homework-checkbox:checked {
background-color: var(--accent);
border-color: var(--accent);
}
.homework-checkbox:checked::after {
content: '✓';
color: white;
font-size: 14px;
font-weight: bold;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.homework-checkbox:focus {
outline: 2px solid var(--accent);
outline-offset: 2px;
}
.homework-checkbox:active {
transform: scale(0.95);
}
/* Megjelölt házi feladatok stílusai */
.table-row.user-completed {
opacity: 0.6;
background-color: var(--accent-15) !important;
}
.table-row.user-completed .table-cell {
color: var(--text-secondary);
}
.table-row.user-completed .status-badge.completed {
background-color: var(--success);
color: white;
}
/* Mobil nézet checkbox stílusai */
@media (max-width: 768px) {
.checkbox-header {
width: 40px;
font-size: 14px;
}
.checkbox-cell {
width: 40px;
padding: 6px !important;
}
.homework-checkbox {
width: 18px;
height: 18px;
}
.homework-checkbox:checked::after {
font-size: 12px;
}
.table-row.user-completed {
opacity: 0.7;
}
}
.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;
}
.stats-overview {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 16px;
margin-bottom: 24px;
}
.stat-card {
background: var(--card-card);
border-radius: 16px;
padding: 20px;
text-align: center;
box-shadow: 0px 1px var(--shadow-blur) 0px var(--accent-shadow);
transition: transform 0.2s ease;
}
.stat-card:hover {
transform: translateY(-2px);
}
.stat-number {
font-size: 32px;
font-weight: 700;
color: var(--accent-accent);
margin-bottom: 8px;
}
.stat-label {
font-size: 14px;
color: var(--text-secondary);
font-weight: 500;
}
.stat-card.urgent .stat-number {
color: #ff4757;
}
.stat-card.completed .stat-number {
color: #2ed573;
}
.homework-container {
background: var(--card-card);
border-radius: 24px;
overflow: hidden;
box-shadow: 0px 1px var(--shadow-blur) 0px var(--accent-shadow);
}
.homework-table {
width: 100%;
border-collapse: collapse;
}
.table-header {
background: var(--accent-15);
font-weight: 600;
color: var(--text-primary);
}
.table-header th {
padding: 16px;
text-align: left;
font-size: 14px;
border-bottom: 1px solid var(--accent-15);
}
.table-row {
border-bottom: 1px solid var(--accent-15);
transition: background-color 0.2s ease;
}
.table-row:hover {
background-color: var(--accent-15);
}
.table-row.due-tomorrow {
background-color: rgba(255, 71, 87, 0.1);
}
.table-row.due-tomorrow:hover {
background-color: rgba(255, 71, 87, 0.2);
}
.table-cell {
padding: 16px;
vertical-align: top;
border-bottom: 1px solid var(--accent-15);
}
.date-cell {
font-weight: 600;
color: var(--text-primary);
min-width: 120px;
}
.subject-cell {
font-weight: 500;
color: var(--accent-accent);
min-width: 120px;
}
.description-cell {
max-width: 300px;
word-wrap: break-word;
line-height: 1.4;
}
.teacher-cell {
color: var(--text-secondary);
font-style: italic;
min-width: 120px;
}
.status-cell {
text-align: center;
min-width: 100px;
}
.status-badge {
padding: 4px 12px;
border-radius: 12px;
font-size: 12px;
font-weight: 500;
text-transform: uppercase;
}
.status-badge.pending {
background-color: rgba(255, 193, 7, 0.2);
color: #ffc107;
}
.status-badge.urgent {
background-color: rgba(255, 71, 87, 0.2);
color: #ff4757;
}
.status-badge.completed {
background-color: rgba(46, 213, 115, 0.2);
color: #2ed573;
}
.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;
}
.stats-overview {
grid-template-columns: repeat(2, 1fr);
}
.table-cell {
padding: 12px;
font-size: 14px;
}
.description-cell {
max-width: 200px;
}
}
@media (max-width: 480px) {
.homework-container {
overflow-x: hidden;
}
.homework-table,
.table-header,
.table-row,
.table-cell {
display: block;
}
.table-header {
display: none;
}
.date-group {
margin-bottom: 24px;
}
.date-group-header {
background: var(--accent-accent);
color: white;
padding: 12px 16px;
font-weight: 600;
font-size: 16px;
border-radius: 12px 12px 0 0;
margin-bottom: 0;
}
.date-group-content {
background: var(--card-card);
border: 1px solid var(--accent-15);
border-radius: 0 0 12px 12px;
overflow: hidden;
}
.table-row {
display: block;
width: 100%;
margin-bottom: 0;
padding: 16px;
border-bottom: 1px solid var(--accent-15);
background: var(--card-card);
}
.table-row:last-child {
border-bottom: none;
}
.table-cell {
display: flex;
justify-content: space-between;
align-items: flex-start;
padding: 8px 0;
border-bottom: 1px solid var(--accent-15);
}
.table-cell:last-child {
border-bottom: none;
}
.table-cell::before {
content: attr(data-label);
font-weight: 600;
color: var(--text-secondary);
font-size: 12px;
text-transform: uppercase;
flex-shrink: 0;
}
.description-cell {
max-width: none;
white-space: normal;
text-overflow: initial;
overflow: visible;
text-align: right;
flex: 1;
}
.stats-overview {
grid-template-columns: 1fr;
}
}
@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,712 +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 = '';
const kretaContainer = document.createElement('div');
kretaContainer.className = 'kreta-container';
const headerDiv = document.createElement('div');
const parser = new DOMParser();
const headerDoc = parser.parseFromString(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 filterCard = document.createElement('div');
filterCard.className = 'filter-card';
const filterHeader = document.createElement('div');
filterHeader.className = 'filter-header';
const filterTitle = document.createElement('h2');
filterTitle.textContent = LanguageManager.t('homework.filter_title');
filterHeader.appendChild(filterTitle);
const filterContent = document.createElement('div');
filterContent.className = 'filter-content';
const subjectGroup = document.createElement('div');
subjectGroup.className = 'filter-group';
const subjectLabel = document.createElement('label');
subjectLabel.textContent = LanguageManager.t('homework.subject');
const subjectSelect = document.createElement('select');
subjectSelect.id = 'subjectFilter';
const allSubjectsOption = document.createElement('option');
allSubjectsOption.value = '';
allSubjectsOption.textContent = LanguageManager.t('homework.all_subjects');
subjectSelect.appendChild(allSubjectsOption);
[...new Set(homeworkItems.map((item) => item.subject))]
.sort()
.forEach((subject) => {
const option = document.createElement('option');
option.value = subject;
option.textContent = subject;
subjectSelect.appendChild(option);
});
subjectGroup.appendChild(subjectLabel);
subjectGroup.appendChild(subjectSelect);
const teacherGroup = document.createElement('div');
teacherGroup.className = 'filter-group';
const teacherLabel = document.createElement('label');
teacherLabel.textContent = LanguageManager.t('homework.teacher');
const teacherSelect = document.createElement('select');
teacherSelect.id = 'teacherFilter';
const allTeachersOption = document.createElement('option');
allTeachersOption.value = '';
allTeachersOption.textContent = LanguageManager.t('homework.all_teachers');
teacherSelect.appendChild(allTeachersOption);
[...new Set(homeworkItems.map((item) => item.teacher))]
.sort()
.forEach((teacher) => {
const option = document.createElement('option');
option.value = teacher;
option.textContent = teacher;
teacherSelect.appendChild(option);
});
teacherGroup.appendChild(teacherLabel);
teacherGroup.appendChild(teacherSelect);
const deadlineGroup = document.createElement('div');
deadlineGroup.className = 'filter-group';
const deadlineLabel = document.createElement('label');
deadlineLabel.textContent = LanguageManager.t('homework.due_date');
const deadlineSelect = document.createElement('select');
deadlineSelect.id = 'deadlineFilter';
const deadlineOptions = [
{ value: '', text: LanguageManager.t('homework.all_deadlines') },
{ value: 'tomorrow', text: LanguageManager.t('homework.tomorrow_deadline') },
{ value: 'thisWeek', text: LanguageManager.t('homework.this_week') },
{ value: 'nextWeek', text: LanguageManager.t('homework.next_week') }
];
deadlineOptions.forEach(({ value, text }) => {
const option = document.createElement('option');
option.value = value;
option.textContent = text;
deadlineSelect.appendChild(option);
});
deadlineGroup.appendChild(deadlineLabel);
deadlineGroup.appendChild(deadlineSelect);
filterContent.appendChild(subjectGroup);
filterContent.appendChild(teacherGroup);
filterContent.appendChild(deadlineGroup);
filterCard.appendChild(filterHeader);
filterCard.appendChild(filterContent);
const statsOverview = document.createElement('div');
statsOverview.className = 'stats-overview';
statsOverview.id = 'statsOverview';
const statCards = [
{ id: 'totalHomework', label: LanguageManager.t('homework.total_homework'), className: '' },
{ id: 'urgentHomework', label: LanguageManager.t('homework.urgent_homework'), className: 'urgent' },
{ id: 'completedHomework', label: LanguageManager.t('homework.completed_homework'), className: 'completed' },
{ id: 'pendingHomework', label: LanguageManager.t('homework.pending_homework'), className: '' }
];
statCards.forEach(({ id, label, className }) => {
const statCard = document.createElement('div');
statCard.className = `stat-card ${className}`.trim();
const statNumber = document.createElement('div');
statNumber.className = 'stat-number';
statNumber.id = id;
statNumber.textContent = '0';
const statLabel = document.createElement('div');
statLabel.className = 'stat-label';
statLabel.textContent = label;
statCard.appendChild(statNumber);
statCard.appendChild(statLabel);
statsOverview.appendChild(statCard);
});
const homeworkContainer = document.createElement('div');
homeworkContainer.className = 'homework-container';
const homeworkTable = document.createElement('table');
homeworkTable.className = 'homework-table';
homeworkTable.id = 'homeworkTable';
const thead = document.createElement('thead');
thead.className = 'table-header';
const headerRow = document.createElement('tr');
const headers = [
'✓',
LanguageManager.t('homework.due_date'),
LanguageManager.t('homework.subject'),
LanguageManager.t('homework.description'),
LanguageManager.t('homework.teacher'),
LanguageManager.t('homework.status')
];
headers.forEach((headerText, index) => {
const th = document.createElement('th');
th.textContent = headerText;
if (index === 0) {
th.className = 'checkbox-header';
}
headerRow.appendChild(th);
});
thead.appendChild(headerRow);
const tbody = document.createElement('tbody');
tbody.id = 'homeworkTableBody';
const homeworkHTML = generateHomeworkHTML(homeworkItems);
if (homeworkHTML.trim()) {
const parser = new DOMParser();
const doc = parser.parseFromString(`<table><tbody>${homeworkHTML}</tbody></table>`, 'text/html');
const tempTbody = doc.querySelector('tbody');
while (tempTbody.firstChild) {
tbody.appendChild(tempTbody.firstChild);
}
} else {
const emptyRow = document.createElement('tr');
const emptyCell = document.createElement('td');
emptyCell.colSpan = 5;
emptyCell.textContent = LanguageManager.t('homework.no_homework') || 'Nincs házi feladat';
emptyCell.style.textAlign = 'center';
emptyCell.style.padding = '20px';
emptyRow.appendChild(emptyCell);
tbody.appendChild(emptyRow);
}
homeworkTable.appendChild(thead);
homeworkTable.appendChild(tbody);
homeworkContainer.appendChild(homeworkTable);
main.appendChild(filterCard);
main.appendChild(statsOverview);
main.appendChild(homeworkContainer);
kretaContainer.appendChild(main);
document.body.appendChild(kretaContainer);
setupFilters(homeworkItems, groupedHomework);
setupUserDropdown();
setupMobileNavigation();
setupMobileGrouping();
updateStatistics();
loadingScreen.hide();
}
function generateHomeworkHTML(homeworkItems) {
const sortedHomework = homeworkItems.sort((a, b) => {
const dateA = new Date(
a.deadline.split(" ")[0].split(".").reverse().join("-"),
);
const dateB = new Date(
b.deadline.split(" ")[0].split(".").reverse().join("-"),
);
return dateA - dateB;
});
return sortedHomework
.map((homework) => {
const isUrgent = isTomorrow(homework.deadline);
const isUserCompleted = getHomeworkCompletionStatus(homework.id);
const status = homework.completed || isUserCompleted
? "completed"
: isUrgent
? "urgent"
: "pending";
const statusText = homework.completed || isUserCompleted
? LanguageManager.t("homework.completed")
: isUrgent
? LanguageManager.t("homework.urgent")
: LanguageManager.t("homework.pending");
return `
<tr class="table-row ${isUrgent ? "due-tomorrow" : ""} ${isUserCompleted ? "user-completed" : ""}"
data-subject="${homework.subject}"
data-teacher="${homework.teacher}"
data-deadline="${homework.deadline}"
data-homework-id="${homework.id}">
<td class="table-cell checkbox-cell">
<input type="checkbox" class="homework-checkbox"
data-homework-id="${homework.id}"
${isUserCompleted ? "checked" : ""}
title="${isUserCompleted ? 'Megcsinált házi - kattints a visszaállításhoz' : 'Kattints a megjelöléshez'}">
</td>
<td class="table-cell date-cell" data-label="${LanguageManager.t("homework.due_date")}:">${homework.deadline}</td>
<td class="table-cell subject-cell" data-label="${LanguageManager.t("homework.subject")}:">${homework.subject}</td>
<td class="table-cell description-cell" data-label="${LanguageManager.t("homework.description")}:">${homework.description}</td>
<td class="table-cell teacher-cell" data-label="${LanguageManager.t("homework.teacher")}:">${homework.teacher}</td>
<td class="table-cell status-cell" data-label="${LanguageManager.t("homework.status")}:">
<span class="status-badge ${status}">${statusText}</span>
</td>
</tr>
`;
})
.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 applyFilters = () => {
const selectedSubject = subjectFilter.value;
const selectedTeacher = teacherFilter.value;
const selectedDeadline = deadlineFilter.value;
const tableRows = document.querySelectorAll(".table-row");
tableRows.forEach((row) => {
const subject = row.getAttribute("data-subject");
const teacher = row.getAttribute("data-teacher");
const deadline = row.getAttribute("data-deadline");
let showRow = true;
if (selectedSubject && subject !== selectedSubject) {
showRow = false;
}
if (selectedTeacher && teacher !== selectedTeacher) {
showRow = false;
}
if (selectedDeadline) {
const parts = deadline.split(" ")[0].split(".");
if (parts.length >= 2) {
const month = parseInt(parts[0].trim()) - 1;
const day = parseInt(parts[1].trim());
const currentYear = new Date().getFullYear();
const date = new Date(currentYear, 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()
) {
showRow = false;
} else if (
selectedDeadline === "thisWeek" &&
(date < startOfWeek || date > endOfWeek)
) {
showRow = false;
} else if (
selectedDeadline === "nextWeek" &&
(date < startOfNextWeek || date > endOfNextWeek)
) {
showRow = false;
}
}
}
row.style.display = showRow ? "" : "none";
});
updateDateGroupsVisibility();
updateStatistics();
setupHomeworkCheckboxes();
};
subjectFilter.addEventListener("change", applyFilters);
teacherFilter.addEventListener("change", applyFilters);
deadlineFilter.addEventListener("change", applyFilters);
}
function updateStatistics() {
const visibleRows = document.querySelectorAll(
'.table-row:not([style*="display: none"])',
);
let totalCount = 0;
let urgentCount = 0;
let completedCount = 0;
let pendingCount = 0;
visibleRows.forEach((row) => {
totalCount++;
if (row.classList.contains("due-tomorrow")) {
urgentCount++;
}
const statusBadge = row.querySelector(".status-badge");
if (statusBadge) {
if (statusBadge.classList.contains("completed")) {
completedCount++;
} else if (statusBadge.classList.contains("pending")) {
pendingCount++;
}
}
});
document.getElementById("totalHomework").textContent = totalCount;
document.getElementById("urgentHomework").textContent = urgentCount;
document.getElementById("completedHomework").textContent = completedCount;
document.getElementById("pendingHomework").textContent = pendingCount;
}
function setupMobileGrouping() {
function handleResize() {
if (window.innerWidth <= 480) {
createMobileGroups();
} else {
removeMobileGroups();
}
}
window.addEventListener("resize", handleResize);
handleResize();
}
function createMobileGroups() {
const tableBody = document.getElementById("homeworkTableBody");
const rows = Array.from(tableBody.querySelectorAll(".table-row"));
if (rows.length === 0) return;
const groupedByDate = {};
rows.forEach((row) => {
const deadline = row.getAttribute("data-deadline");
const dateKey = deadline.split(" ")[0];
if (!groupedByDate[dateKey]) {
groupedByDate[dateKey] = [];
}
groupedByDate[dateKey].push(row);
});
tableBody.innerHTML = "";
Object.keys(groupedByDate)
.sort()
.forEach((date) => {
const dateGroup = document.createElement("div");
dateGroup.className = "date-group";
dateGroup.setAttribute("data-date", date);
const header = document.createElement("div");
header.className = "date-group-header";
header.textContent = formatDateHeader(date);
const content = document.createElement("div");
content.className = "date-group-content";
groupedByDate[date].forEach((row) => {
content.appendChild(row);
});
dateGroup.appendChild(header);
dateGroup.appendChild(content);
tableBody.appendChild(dateGroup);
});
setupHomeworkCheckboxes();
}
function removeMobileGroups() {
const tableBody = document.getElementById("homeworkTableBody");
const dateGroups = tableBody.querySelectorAll(".date-group");
if (dateGroups.length === 0) return;
const allRows = [];
dateGroups.forEach((group) => {
const rows = group.querySelectorAll(".table-row");
rows.forEach((row) => allRows.push(row));
});
tableBody.innerHTML = "";
allRows.forEach((row) => tableBody.appendChild(row));
setupHomeworkCheckboxes();
}
function updateDateGroupsVisibility() {
const dateGroups = document.querySelectorAll(".date-group");
dateGroups.forEach((group) => {
const visibleRows = group.querySelectorAll(
'.table-row:not([style*="display: none"])',
);
group.style.display = visibleRows.length > 0 ? "block" : "none";
});
}
function getHomeworkCompletionStatus(homeworkId) {
const completedHomework = cookieManager.get('completedHomework');
if (!completedHomework) return false;
try {
const completedList = JSON.parse(completedHomework);
return completedList.includes(homeworkId.toString());
} catch (error) {
console.error('Error parsing completed homework cookie:', error);
return false;
}
}
function setHomeworkCompletionStatus(homeworkId, isCompleted) {
let completedHomework = cookieManager.get('completedHomework');
let completedList = [];
if (completedHomework) {
try {
completedList = JSON.parse(completedHomework);
} catch (error) {
console.error('Error parsing completed homework cookie:', error);
completedList = [];
}
}
const homeworkIdStr = homeworkId.toString();
if (isCompleted) {
if (!completedList.includes(homeworkIdStr)) {
completedList.push(homeworkIdStr);
}
} else {
completedList = completedList.filter(id => id !== homeworkIdStr);
}
cookieManager.set('completedHomework', JSON.stringify(completedList));
}
function setupHomeworkCheckboxes() {
const checkboxes = document.querySelectorAll('.homework-checkbox');
checkboxes.forEach(checkbox => {
checkbox.addEventListener('change', function() {
const homeworkId = this.getAttribute('data-homework-id');
const isChecked = this.checked;
const row = this.closest('.table-row');
setHomeworkCompletionStatus(homeworkId, isChecked);
if (isChecked) {
row.classList.add('user-completed');
this.title = 'Megcsinált házi - kattints a visszaállításhoz';
} else {
row.classList.remove('user-completed');
this.title = 'Kattints a megjelöléshez';
}
const statusBadge = row.querySelector('.status-badge');
if (statusBadge) {
if (isChecked) {
statusBadge.className = 'status-badge completed';
statusBadge.textContent = LanguageManager.t('homework.completed');
} else {
const isUrgent = row.classList.contains('due-tomorrow');
if (isUrgent) {
statusBadge.className = 'status-badge urgent';
statusBadge.textContent = LanguageManager.t('homework.urgent');
} else {
statusBadge.className = 'status-badge pending';
statusBadge.textContent = LanguageManager.t('homework.pending');
}
}
}
updateStatistics();
});
});
}
if (window.location.href.includes("/Tanulo/TanuloHaziFeladat")) {
transformHomeworkPage().then(() => {
setupHomeworkCheckboxes();
}).catch((error) => {
console.error("Error transforming homework page:", error);
});
}

View File

@@ -8,9 +8,7 @@
"theme": "Theme",
"language": "Language",
"themes": {
"light_blue": "Light Blue",
"light_green": "Light Green",
"dark_blue": "Dark Blue",
"dark_green": "Dark Green",
"dark_red": "Dark Red",
"dark_purple": "Dark Purple",
@@ -34,6 +32,33 @@
"title": "Support",
"description": "If you like our work and would like to support the development, you can do so in the following way:",
"kofi": "Ko-Fi"
},
"custom_theme": {
"title": "Custom Theme",
"create": "Create New Theme",
"import": "Import Theme",
"editor_title": "Theme Editor",
"import_title": "Import Theme",
"name": "Theme name",
"background": "Background",
"text": "Text",
"accent": "Accent colors",
"main_background": "Main background",
"card_background": "Card background",
"primary_text": "Primary text",
"secondary_text": "Secondary text",
"primary_accent": "Primary accent",
"secondary_accent": "Secondary accent",
"preview": "Preview",
"save": "Save",
"cancel": "Cancel",
"import_string": "Theme ID",
"apply": "Apply",
"edit": "Edit",
"export": "Export",
"delete": "Delete",
"no_themes": "No custom themes yet",
"delete_confirm": "Are you sure you want to delete the \"{name}\" theme?"
}
},
"navigation": {
@@ -42,7 +67,6 @@
"grades": "Grades",
"homework": "Homework",
"absences": "Absences",
"other": "Other",
"messages": "Messages",
"profile": "Profile",
"settings": "Settings",
@@ -75,6 +99,7 @@
"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",
@@ -116,7 +141,36 @@
"thursday": "Thursday",
"friday": "Friday",
"found_current_week": "Found current week",
"open_homework": "Open homework"
"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",
@@ -307,10 +361,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.",
@@ -331,7 +381,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",
@@ -350,7 +400,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

@@ -34,6 +34,33 @@
"title": "Támogatás",
"description": "Ha tetszik a munkánk és szeretnéd támogatni a fejlesztést, az alábbi módon teheted meg:",
"kofi": "Ko-Fi"
},
"custom_theme": {
"title": "Egyéni Téma",
"create": "Új Téma Létrehozása",
"import": "Téma Importálása",
"editor_title": "Téma Szerkesztő",
"import_title": "Téma Importálása",
"name": "Téma neve",
"background": "Háttér",
"text": "Szöveg",
"accent": "Kiemelő színek",
"main_background": "Fő háttér",
"card_background": "Kártya háttér",
"primary_text": "Elsődleges szöveg",
"secondary_text": "Másodlagos szöveg",
"primary_accent": "Elsődleges kiemelő",
"secondary_accent": "Másodlagos kiemelő",
"preview": "Előnézet",
"save": "Mentés",
"cancel": "Mégse",
"import_string": "Téma azonosító",
"apply": "Alkalmaz",
"edit": "Szerkeszt",
"export": "Export",
"delete": "Töröl",
"no_themes": "Még nincsenek egyéni témák",
"delete_confirm": "Biztosan törölni szeretnéd a \"{name}\" témát?"
}
},
"navigation": {
@@ -42,7 +69,6 @@
"grades": "Jegyek",
"homework": "Házi feladatok",
"absences": "Mulasztások",
"other": "Egyéb",
"messages": "Üzenetek",
"profile": "Profil",
"settings": "Beállítások",
@@ -75,6 +101,7 @@
"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",
@@ -116,7 +143,36 @@
"thursday": "Csütörtök",
"friday": "Péntek",
"found_current_week": "Megtalált jelenlegi hét",
"open_homework": "Ugrás a házi feladatokhoz"
"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",
@@ -307,10 +363,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.",
@@ -331,7 +383,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",
@@ -352,5 +404,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"
}
}
}

1
icons/delete.svg Normal file
View File

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

After

Width:  |  Height:  |  Size: 384 B

View File

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

Before

Width:  |  Height:  |  Size: 458 B

After

Width:  |  Height:  |  Size: 631 B

View File

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

Before

Width:  |  Height:  |  Size: 395 B

After

Width:  |  Height:  |  Size: 453 B

View File

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

After

Width:  |  Height:  |  Size: 329 B

View File

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

After

Width:  |  Height:  |  Size: 348 B

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

1
icons/pipa.svg Normal file
View File

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

After

Width:  |  Height:  |  Size: 248 B

1
icons/plus.svg Normal file
View File

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

After

Width:  |  Height:  |  Size: 222 B

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

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

After

Width:  |  Height:  |  Size: 426 B

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

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

After

Width:  |  Height:  |  Size: 354 B

1
icons/select.svg Normal file
View File

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

After

Width:  |  Height:  |  Size: 591 B

1
icons/trash.svg Normal file
View File

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

After

Width:  |  Height:  |  Size: 400 B

1
icons/undo.svg Normal file
View File

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

After

Width:  |  Height:  |  Size: 261 B

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

View File

@@ -12,13 +12,6 @@
--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;

View File

@@ -110,9 +110,7 @@ async function transformLoginPage() {
</div>
`;
// 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(newHTML, 'text/html');
const tempDiv = doc.body;

View File

@@ -12,13 +12,6 @@
--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;

View File

@@ -104,10 +104,8 @@ 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) {
}
}

View File

@@ -1,10 +1,10 @@
(() => {
function transformLogoutPage() {
async function transformLogoutPage() {
const theme =
cookieManager.get("themePreference") ||
await storageManager.get("themePreference", null) ||
localStorage.getItem("themePreference") ||
"light-green";
const instituteCode = cookieManager.get("schoolSubdomain");
const instituteCode = await storageManager.get("schoolSubdomain", null);
document.documentElement.setAttribute("data-theme", theme);
const newHTML = `
@@ -65,8 +65,18 @@
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", transformLogoutPage);
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.3.1",
"version": "1.3.5",
"description": "KRÉTA webes verziójának újraírása",
"icons": {
"128": "images/firka_logo_128.png"
@@ -12,6 +12,12 @@
"128": "images/firka_logo_128.png"
}
},
"permissions": [
"storage"
],
"background": {
"service_worker": "tools/background.js"
},
"browser_specific_settings": {
"gecko": {
"id": "firxa@zan1456.hu",
@@ -31,7 +37,8 @@
"icons/*.svg",
"grades/chart.js",
"i18n/*.json",
"tools/cookieManager.js"
"tools/storageManager.js",
"tools/storageTest.js"
],
"matches": [
"<all_urls>"
@@ -52,13 +59,14 @@
"https://*.e-kreta.hu/Adminisztracio/Profil*",
"https://*.e-kreta.hu/Tanulo/TanuloHaziFeladat*",
"https://*.e-kreta.hu/Adminisztracio/ElfelejtettJelszo*",
"https://intezmenykereso.e-kreta.hu/*"
"https://intezmenykereso.e-kreta.hu/*",
"https://eugyintezes.e-kreta.hu/*"
],
"js": [
"global/language.js",
"global/theme.js",
"tools/loadingScreen.js",
"tools/cookieManager.js",
"tools/storageManager.js",
"tools/helper.js",
"tools/createTemplate.js",
"global/maintenance.js",
@@ -181,18 +189,6 @@
],
"run_at": "document_end"
},
{
"matches": [
"https://*.e-kreta.hu/Tanulo/TanuloHaziFeladat*"
],
"js": [
"homework/homework.js"
],
"css": [
"homework/homework.css"
],
"run_at": "document_end"
},
{
"matches": [
"https://*.e-kreta.hu/Adminisztracio/ElfelejtettJelszo*"
@@ -216,6 +212,19 @@
"search/search.css"
],
"run_at": "document_end"
},
{
"matches": [
"https://eugyintezes.e-kreta.hu/*",
"https://eugyintezes.e-kreta.hu/"
],
"js": [
"messages/messages.js"
],
"css": [
"messages/messages.css"
],
"run_at": "document_start"
}
]
}

232
manifest_fox.json Normal file
View File

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

669
messages/messages.css Normal file
View File

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

670
messages/messages.js Normal file
View File

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

View File

@@ -1,362 +1,485 @@
* {
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);
background-color:var(--background) !important;
font-family:"Montserrat",serif !important;
min-height:100vh;
font-size:16px;
margin: 0;
padding: 0;
color: var(--text-primary) !important;
background-color: var(--background) !important;
font-family: 'Montserrat', sans-serif !important;
min-height: 100vh;
font-size: 16px;
}
.kreta-container {
min-height:100vh;
display:flex;
flex-direction:column;
.page-wrapper {
background-color: var(--background) !important;
min-height: 100vh;
}
.kreta-header {
padding:clamp(1rem,3vw,2rem);
display:grid;
grid-template-columns:minmax(300px,400px) 1fr minmax(200px,300px);
align-items:center;
gap:1rem;
.main-wrapper {
display: flex;
flex-direction: column;
min-height: 100vh;
background-color: var(--background) !important;
}
.school-info {
display:flex;
flex-direction:column;
gap:0.5rem;
.main-content {
flex: 1;
padding: 0;
background-color: var(--background) !important;
}
.logo-text {
color:var(--text-primary);
font-size:24px;
font-weight:600;
display:flex;
align-items:center;
margin:0;
.content-content {
background-color: var(--background) !important;
min-height: auto !important;
padding: 0;
}
.logo {
width:24px;
border-radius:8px;
margin-right:8px;
.content-container {
max-width: 1200px;
margin: 0 auto;
padding: clamp(1rem, 3vw, 2rem);
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;
.firka-header {
padding: clamp(1rem, 3vw, 2rem);
display: flex;
align-items: center;
gap: 1rem;
background-color: var(--background);
border-bottom: 1px solid var(--accent-15);
}
.user-profile {
position:relative;
justify-self:flex-end;
.back-button {
display: flex;
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);
}
.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;
.back-button:hover {
background: var(--button-secondaryFill);
transform: translateY(-1px);
box-shadow: 0px 2px var(--shadow-blur, 4px) 0px var(--accent-shadow);
}
.user-dropdown-btn:hover {
background:var(--card-card);
.back-button svg {
width: 16px;
height: 16px;
fill: var(--text-primary);
}
.user-info {
text-align:right;
.page-title {
color: var(--text-primary);
font-size: 24px;
font-weight: 600;
margin: 0;
}
.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;
.k-content {
background-color: var(--background) !important;
}
.user-dropdown.show {
display:block;
animation:dropdownShow 0.2s ease;
.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;
}
.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;
.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;
}
.dropdown-item:hover {
background:var(--button-secondaryFill);
.k-tabstrip {
background: var(--card-card) !important;
border: none !important;
}
.kreta-main {
flex:1;
padding:clamp(1rem,3vw,2rem);
max-width:800px;
margin:0 auto;
width:100%;
.k-tabstrip-items {
background: var(--card-card) !important;
border: none !important;
border-radius: 24px 24px 0 0 !important;
padding: 0 20px !important;
}
.card {
background:var(--card-card);
border-radius:24px;
overflow:hidden;
box-shadow:0px 1px var(--shadow-blur) 0px var(--accent-shadow);
.k-tabstrip-items .k-item {
background: transparent !important;
border: none !important;
margin: 0 !important;
border-radius: 0 !important;
}
.card h2 {
font-size:18px;
font-weight:600;
color:var(--text-primary);
padding:1.5rem;
padding-bottom:0.5rem;
background-color:var(--card-card);
.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;
}
.profile-tabs {
padding:1.5rem;
.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;
}
.tab-headers {
display:flex;
gap:0.5rem;
margin-bottom:1.5rem;
border-bottom:1px solid var(--card-translucent);
overflow-x:auto;
scrollbar-width:none;
.k-tabstrip-items .k-item:hover .k-link {
color: var(--text-primary) !important;
}
.tab-headers::-webkit-scrollbar {
display:none;
.k-tabstrip .k-content {
background: var(--card-card) !important;
border: none !important;
padding: 20px !important;
border-radius: 0 0 24px 24px !important;
}
.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;
form {
background: transparent !important;
}
.tab-header.active {
color:var(--accent-accent);
border-bottom-color:var(--accent-accent);
.container-fluid.details {
background: transparent !important;
margin-bottom: 2rem;
}
.tab-content {
display:none;
background:var(--button-secondaryFill);
border-radius:8px;
padding:1.5rem;
.row {
margin-bottom: 1rem;
align-items: center;
}
.tab-content.active {
display:block;
.windowInputLabel {
color: var(--text-primary) !important;
font-family: 'Montserrat', sans-serif !important;
font-weight: 500 !important;
font-size: 14px !important;
margin: 0 !important;
}
.form-group {
margin-bottom:1rem;
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;
}
.form-label {
display:block;
color:var(--text-secondary);
font-size:14px;
margin-bottom:0.5rem;
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;
}
.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;
.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;
}
.form-control:focus {
outline:none;
border-color:var(--accent-accent);
.k-checkbox:checked {
background: var(--accent-accent) !important;
border-color: var(--accent-accent) !important;
}
.form-text {
font-size:12px;
color:var(--text-secondary);
margin-top:0.25rem;
.k-checkbox:checked::after {
content: '✓';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: white;
font-size: 12px;
font-weight: bold;
}
.security-content {
display:flex;
flex-direction:column;
gap:1.5rem;
.k-checkbox-label {
margin-left: 8px !important;
color: var(--text-primary) !important;
font-family: 'Montserrat', sans-serif !important;
cursor: pointer !important;
}
.step-card {
background:var(--button-secondaryFill);
border-radius:12px;
padding:1.5rem;
animation:fadeIn 0.3s ease;
.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;
}
.step-card h3 {
color:var(--text-primary);
font-size:16px;
font-weight:600;
margin-bottom:1rem;
.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;
}
.step-card p {
color:var(--text-secondary);
font-size:14px;
margin-bottom:1rem;
line-height:1.5;
.k-button:active,
button:active {
transform: translateY(0) !important;
}
.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;
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;
}
.btn-copy:hover {
color:var(--text-primary);
background:var(--button-secondaryFill);
}
.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;
}
.btn-save:hover {
background:var(--accent-secondary);
@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);
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
to {
opacity:1;
transform:translateY(0);
.k-tabstrip-wrapper {
animation: fadeIn 0.5s ease forwards;
}
}@keyframes dropdownShow {
from {
opacity:0;
transform:translateY(-10px);
.k-widget,
.k-header {
background: var(--card-card) !important;
color: var(--text-primary) !important;
border: none !important;
}
to {
opacity:1;
transform:translateY(0);
.k-state-default {
background: transparent !important;
border: none !important;
}
}@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-state-active {
background: transparent !important;
}
.school-info {
grid-area:school;
.k-overlay,
.k-window,
.k-notification {
display: none !important;
}
.user-profile {
grid-area:user;
.main-content .content-content {
display: block !important;
}
.app-grid {
grid-template-columns:1fr;
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;
}
.profile-tabs {
padding:1rem;
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;
}
.tab-content {
padding:1rem;
.hidden-contact-info {
display: none !important;
visibility: hidden !important;
}
}.material-icons-round {
font-size:20px;
vertical-align:middle;
.hidden-tab {
display: none !important;
}
::-webkit-scrollbar {
width:8px;
height:8px;
#ProfilTab-3 {
padding: 20px;
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
min-height: 500px;
}
::-webkit-scrollbar-track {
background:var(--background);
#ProfilTab-3 .container-fluid {
background: white;
border-radius: 12px;
padding: 30px;
margin-top: 20px;
}
::-webkit-scrollbar-thumb {
background:var(--text-secondary);
border-radius:4px;
#ProfilTab-3 h4 {
color: #2c3e50;
font-weight: 600;
margin-bottom: 25px;
padding-bottom: 10px;
border-bottom: 3px solid #3498db;
display: inline-block;
}
::-webkit-scrollbar-thumb:hover {
background:var(--text-primary);
#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,417 +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() {
'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();
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;
}
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) {
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 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) {
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) {
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);
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';
});
}
}
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>
`;
}
function addBackButton() {
if (document.getElementById('firka-back-button')) {
return;
}
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);
async function init() {
if (window.location.pathname.includes("/Adminisztracio/Profil")) {
document.body.innerHTML = '';
const parser = new DOMParser();
const doc = parser.parseFromString(createProfileHTML(), 'text/html');
const tempDiv = doc.body;
while (tempDiv.firstChild) {
document.body.appendChild(tempDiv.firstChild);
}
setupUserDropdown();
setupMobileNavigation();
setupEventListeners();
setupContactForm();
}
}
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

@@ -187,7 +187,7 @@ body {
.logout-card:active {
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 {
:root[data-theme="light-green"] .role-card:hover {
box-shadow:0px 1px var(--shadow-blur) 0px var(--accent-shadow);
}
::-webkit-scrollbar {
@@ -253,10 +253,14 @@ to {
}
.side-roles {
grid-template-columns:1fr;
gap:1rem;
gap:0.75rem;
}
.role-card {
padding:1.5rem;
padding:1rem;
gap:1rem;
}
.role-card[data-role="Ellenorzo"],.role-card[data-role="DKT"],.logout-card {
height:200px;
}
.role-icon {
width:40px;
@@ -267,9 +271,53 @@ to {
height:24px;
}
.role-text {
font-size:1.2rem;
font-size:1.5rem;
}
.role-description {
font-size:1rem;
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

@@ -120,12 +120,12 @@
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();

View File

@@ -126,9 +126,11 @@ form {
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;
text-decoration:none !important;
}
.dropdown-item.active {
background-color:var(--accent-accent) !important;

View File

@@ -20,10 +20,10 @@ if (
setTimeout(initializeTransformation, 1000);
}
function applyFirkaStyling() {
async function applyFirkaStyling() {
try {
const theme =
cookieManager.get("themePreference") ||
await storageManager.get("themePreference") ||
localStorage.getItem("themePreference") ||
"light-green";
document.documentElement.setAttribute("data-theme", theme);

View File

@@ -68,8 +68,19 @@ h2 {
display:flex;
align-items:center;
gap:8px;
font-weight:600;
font-size:16px;
color:var(--text-primary);
font-weight:500;
}
.custom-theme-editor .setting-header {
justify-content:space-between;
}
.setting-header > div:first-child {
display:flex;
align-items:center;
gap:8px;
}
.theme-grid,.language-grid {
display:grid;
@@ -140,16 +151,9 @@ h2 {
color:var(--text-primary);
font-size:12px;
font-weight:500;
font-family: "Montserrat", serif;
}
.theme-preview.light-blue {
background:#DAE4F7;
}
.theme-preview.light-blue .preview-header {
background:#EDF3FF;
}
.theme-preview.light-blue .preview-card {
background:#FBFCFF;
}
.theme-preview.light-green {
background:#FAFFF0;
}
@@ -159,15 +163,7 @@ h2 {
.theme-preview.light-green .preview-card {
background:#FEFFFD;
}
.theme-preview.dark-blue {
background:#070A0E;
}
.theme-preview.dark-blue .preview-header {
background:#0F131B;
}
.theme-preview.dark-blue .preview-card {
background:#131822;
}
.theme-preview.dark-green {
background:#0D1202;
}
@@ -201,7 +197,14 @@ h2 {
.about-content p,.support-content p {
margin-bottom:12px;
}
.github-link {
.about-links {
display: flex;
flex-direction: column;
gap: 8px;
margin-top: 8px;
}
.about-link {
display:inline-flex;
align-items:center;
gap:6px;
@@ -209,8 +212,10 @@ h2 {
text-decoration:none;
font-weight:500;
transition:color 0.2s;
padding: 4px 0;
}
.github-link:hover {
.about-link:hover {
color:var(--accent-secondary);
}
.support-buttons {
@@ -243,6 +248,357 @@ h2 {
.version-info {
color:var(--text-secondary);
font-size:12px;
text-align:center;
}
.theme-editor-controls {
display: flex;
gap: 8px;
}
.icon-btn {
background: var(--button-secondaryFill);
border: none !important;
border-radius: 8px;
width: 36px;
height: 36px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.2s ease;
color: var(--text-primary);
}
.icon-btn:hover {
background: var(--accent-accent);
color: white;
transform: translateY(-1px);
}
.icon-btn .material-icons-round {
font-size: 18px;
}
.btn-primary, .btn-secondary {
padding: 8px 16px;
border-radius: 8px;
border: none;
font-family: inherit;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s ease;
}
.btn-primary {
background: var(--accent-accent);
color: white;
}
.btn-primary:hover {
background: var(--accent-secondary);
}
.btn-secondary {
background: var(--button-secondaryFill);
color: var(--text-primary);
border: 1px solid var(--accent-15);
}
.btn-secondary:hover {
background: var(--accent-15);
}
.custom-themes-list {
display: flex;
flex-direction: column;
gap: 8px;
}
.custom-theme-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px;
background: var(--button-secondaryFill);
border-radius: 8px;
border: 1px solid var(--accent-15);
}
.custom-theme-info {
display: flex;
align-items: center;
gap: 12px;
}
.custom-theme-preview-mini {
width: 40px;
height: 24px;
border-radius: 4px;
display: flex;
overflow: hidden;
}
.custom-theme-preview-mini .color-strip {
flex: 1;
height: 100%;
}
.custom-theme-name {
font-weight: 500;
color: var(--text-primary);
}
.custom-theme-actions {
display: flex;
gap: 4px;
}
.action-btn {
padding: 6px;
border: none;
border-radius: 4px;
background: var(--accent-15);
color: var(--text-primary);
cursor: pointer;
font-size: 16px;
transition: background 0.2s ease;
display: flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
}
.action-btn .material-icons-round {
font-size: 16px;
}
.action-btn:hover {
background: var(--accent-accent);
color: white;
}
.action-btn.delete:hover {
background: var(--error-accent);
}
.theme-editor-modal, .import-modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.modal-content {
background: var(--card-card);
border-radius: 16px;
width: 90%;
max-width: 500px;
max-height: 90vh;
overflow-y: auto;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
}
.modal-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 16px;
border-bottom: 1px solid var(--accent-15);
}
.modal-header h3 {
margin: 0;
color: var(--text-primary);
font-size: 18px;
}
.close-modal {
background: none;
border: none;
color: var(--text-secondary);
cursor: pointer;
padding: 4px;
border-radius: 4px;
transition: background 0.2s ease;
}
.close-modal:hover {
background: var(--accent-15);
}
.modal-body {
padding: 16px;
}
.modal-footer {
display: flex;
gap: 8px;
justify-content: flex-end;
padding: 16px;
border-top: 1px solid var(--accent-15);
}
.theme-name-input {
margin-bottom: 20px;
}
.theme-name-input label {
display: block;
margin-bottom: 4px;
color: var(--text-primary);
font-weight: 500;
}
.theme-name-input input {
width: 100%;
padding: 8px 12px;
border: 1px solid var(--accent-15);
border-radius: 8px;
background: var(--button-secondaryFill);
color: var(--text-primary);
font-family: inherit;
}
.color-groups {
display: flex;
flex-direction: column;
gap: 20px;
margin-bottom: 20px;
}
.color-group h4 {
margin: 0 0 12px 0;
color: var(--text-primary);
font-size: 16px;
}
.color-inputs {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
}
.color-input-group {
display: flex;
flex-direction: column;
gap: 4px;
}
.color-input-group label {
color: var(--text-secondary);
font-size: 12px;
font-weight: 500;
}
.color-input-group input[type="color"] {
width: 100%;
height: 40px;
border: 1px solid var(--accent-15);
border-radius: 8px;
background: var(--card-card);
cursor: pointer;
padding: 4px;
}
.color-input-group input[type="color"]::-webkit-color-swatch-wrapper {
padding: 0;
border-radius: 6px;
overflow: hidden;
}
.color-input-group input[type="color"]::-webkit-color-swatch {
border: none;
border-radius: 6px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.theme-preview-container h4 {
margin: 0 0 12px 0;
color: var(--text-primary);
font-size: 16px;
}
.custom-theme-preview {
border: 1px solid var(--accent-15);
border-radius: 8px;
overflow: hidden;
height: 120px;
position: relative;
}
.custom-theme-preview .preview-header {
height: 30px;
background: var(--preview-background, var(--background));
border-bottom: 1px solid var(--accent-15);
}
.custom-theme-preview .preview-content {
padding: 12px;
background: var(--preview-background, var(--background));
}
.custom-theme-preview .preview-card {
background: var(--preview-card, var(--card-card));
border-radius: 8px;
padding: 12px;
height: 100%;
display: flex;
flex-direction: column;
gap: 8px;
}
.preview-text-primary {
color: var(--preview-text-primary, var(--text-primary));
font-weight: 600;
font-size: 14px;
}
.preview-text-secondary {
color: var(--preview-text-secondary, var(--text-secondary));
font-size: 12px;
}
.preview-accent {
background: var(--preview-accent, var(--accent-accent));
color: white;
padding: 6px 12px;
border-radius: 6px;
font-size: 12px;
font-weight: 500;
display: inline-block;
margin-top: 4px;
}
.import-input {
display: flex;
flex-direction: column;
gap: 8px;
}
.import-input label {
color: var(--text-primary);
font-weight: 500;
}
.import-input textarea {
width: 100%;
padding: 12px;
border: 1px solid var(--accent-15);
border-radius: 8px;
background: var(--button-secondaryFill);
color: var(--text-primary);
font-family: inherit;
resize: vertical;
min-height: 80px;
}
.material-icons-round {
font-size:18px;

View File

@@ -31,15 +31,6 @@
<span data-i18n="settings.theme">Téma</span>
</div>
<div class="theme-grid">
<button class="theme-option" data-theme="default">
<div class="theme-preview light-blue">
<div class="preview-header"></div>
<div class="preview-content">
<div class="preview-card"></div>
</div>
</div>
<span class="theme-name" 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>
@@ -49,15 +40,7 @@
</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>
@@ -69,7 +52,128 @@
</button>
</div>
</div>
<!--
<div class="setting-section">
<div class="setting-header">
<div>
<span class="material-icons-round">brush</span>
<span data-i18n="settings.custom_theme.title">Egyéni Téma</span>
</div>
<div class="theme-editor-controls">
<button class="icon-btn" id="createCustomTheme" title="Új Téma Létrehozása">
<span class="material-icons-round">add</span>
</button>
<button class="icon-btn" id="importTheme" title="Téma Importálása">
<span class="material-icons-round">file_download</span>
</button>
</div>
</div>
<div class="custom-theme-editor">
<div class="custom-themes-list" id="customThemesList">
</div>
<div class="theme-editor-modal" id="themeEditorModal" style="display: none;">
<div class="modal-content">
<div class="modal-header">
<h3 data-i18n="settings.custom_theme.editor_title">Téma Szerkesztő</h3>
<button class="close-modal" id="closeThemeEditor">
<span class="material-icons-round">close</span>
</button>
</div>
<div class="modal-body">
<div class="theme-name-input">
<label for="themeName" data-i18n="settings.custom_theme.name">Téma neve:</label>
<input type="text" id="themeName" placeholder="Saját téma" maxlength="30">
</div>
<div class="color-groups">
<div class="color-group">
<h4 data-i18n="settings.custom_theme.background">Háttér</h4>
<div class="color-inputs">
<div class="color-input-group">
<label for="background" data-i18n="settings.custom_theme.main_background">Fő háttér:</label>
<input type="color" id="background" value="#DAE4F7">
</div>
<div class="color-input-group">
<label for="cardCard" data-i18n="settings.custom_theme.card_background">Kártya háttér:</label>
<input type="color" id="cardCard" value="#EDF3FF">
</div>
</div>
</div>
<div class="color-group">
<h4 data-i18n="settings.custom_theme.text">Szöveg</h4>
<div class="color-inputs">
<div class="color-input-group">
<label for="textPrimary" data-i18n="settings.custom_theme.primary_text">Elsődleges szöveg:</label>
<input type="color" id="textPrimary" value="#050B15">
</div>
<div class="color-input-group">
<label for="textSecondary" data-i18n="settings.custom_theme.secondary_text">Másodlagos szöveg:</label>
<input type="color" id="textSecondary" value="#050B15">
</div>
</div>
</div>
<div class="color-group">
<h4 data-i18n="settings.custom_theme.accent">Kiemelő színek</h4>
<div class="color-inputs">
<div class="color-input-group">
<label for="accentAccent" data-i18n="settings.custom_theme.primary_accent">Elsődleges kiemelő:</label>
<input type="color" id="accentAccent" value="#3673EE">
</div>
<div class="color-input-group">
<label for="accentSecondary" data-i18n="settings.custom_theme.secondary_accent">Másodlagos kiemelő:</label>
<input type="color" id="accentSecondary" value="#1C469A">
</div>
</div>
</div>
</div>
<div class="theme-preview-container">
<h4 data-i18n="settings.custom_theme.preview">Előnézet</h4>
<div class="custom-theme-preview" id="customThemePreview">
<div class="preview-content">
<div class="preview-card">
<div class="preview-text-primary">Elsődleges szöveg</div>
<div class="preview-text-secondary">Másodlagos szöveg</div>
<div class="preview-accent">Kiemelő szín</div>
</div>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn-secondary" id="cancelThemeEdit" data-i18n="settings.custom_theme.cancel">Mégse</button>
<button class="btn-primary" id="saveTheme" data-i18n="settings.custom_theme.save">Mentés</button>
</div>
</div>
</div>
<div class="import-modal" id="importModal" style="display: none;">
<div class="modal-content">
<div class="modal-header">
<h3 data-i18n="settings.custom_theme.import_title">Téma Importálása</h3>
<button class="close-modal" id="closeImportModal">
<span class="material-icons-round">close</span>
</button>
</div>
<div class="modal-body">
<div class="import-input">
<label for="themeImportString" data-i18n="settings.custom_theme.import_string">Téma azonosítója:</label>
<textarea id="themeImportString" placeholder="Illeszd be ide a téma azonosítóját..." rows="4"></textarea>
</div>
</div>
<div class="modal-footer">
<button class="btn-secondary" id="cancelImport" data-i18n="settings.custom_theme.cancel">Mégse</button>
<button class="btn-primary" id="confirmImport" data-i18n="settings.custom_theme.import">Importálás</button>
</div>
</div>
</div>
</div>
</div>
-->
<div class="setting-section">
<div class="setting-header">
<span class="material-icons-round">language</span>
@@ -91,10 +195,20 @@
<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 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>
@@ -112,10 +226,10 @@
</div>
<footer class="popup-footer">
<div class="version-info" id="version">v1.1.0</div>
<div class="version-info" id="version">v1.3.0</div>
</footer>
</div>
<script src="../tools/cookieManager.js"></script>
<script src="../tools/storageManager.js"></script>
<script src="../global/language.js"></script>
<script src="index.js"></script>
</body>

View File

@@ -3,66 +3,9 @@ document.addEventListener("DOMContentLoaded", async () => {
await new Promise((resolve) => setTimeout(resolve, 10));
}
function isThemeDisabled(theme) {
const blueThemesUnlocked =
localStorage.getItem("blueThemesUnlocked") === "true";
return (
(theme === "default" ||
theme === "light-blue" ||
theme === "dark-blue") &&
!blueThemesUnlocked
);
}
function updateThemeAvailability() {
const blueThemesUnlocked =
localStorage.getItem("blueThemesUnlocked") === "true";
document.querySelectorAll(".theme-option").forEach((button) => {
const theme = button.dataset.theme;
if (
theme === "default" ||
theme === "light-blue" ||
theme === "dark-blue"
) {
if (blueThemesUnlocked) {
button.style.display = "block";
button.classList.remove("disabled");
button.removeAttribute("disabled");
} else {
button.style.display = "none";
}
}
});
}
function getCookie(name) {
const cookieName = `${name}=`;
const decodedCookie = decodeURIComponent(document.cookie);
const cookieArray = decodedCookie.split(";");
for (let i = 0; i < cookieArray.length; i++) {
let cookie = cookieArray[i];
while (cookie.charAt(0) === " ") {
cookie = cookie.substring(1);
}
if (cookie.indexOf(cookieName) === 0) {
return cookie.substring(cookieName.length, cookie.length);
}
}
return null;
}
function setCookie(name, value, days = 365) {
const date = new Date();
date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000);
const expires = `expires=${date.toUTCString()}`;
document.cookie = `${name}=${value}; ${expires}; path=/; domain=.e-kreta.hu`;
}
function getCurrentTheme() {
return (
localStorage.getItem("themePreference") ||
getCookie("themePreference") ||
"light-green"
);
}
@@ -72,13 +15,12 @@ document.addEventListener("DOMContentLoaded", async () => {
const theme = button.dataset.theme;
button.classList.toggle("active", theme === currentTheme);
});
updateThemeAvailability();
}
function getCurrentLanguage() {
return (
localStorage.getItem("languagePreference") ||
getCookie("languagePreference") ||
"hu"
);
}
@@ -91,7 +33,6 @@ document.addEventListener("DOMContentLoaded", async () => {
}
async function applyLanguage(language) {
setCookie("languagePreference", language);
localStorage.setItem("languagePreference", language);
updateLanguageButtons(language);
@@ -108,7 +49,6 @@ document.addEventListener("DOMContentLoaded", async () => {
}
async function applyTheme(theme) {
setCookie("themePreference", theme);
localStorage.setItem("themePreference", theme);
document.documentElement.setAttribute("data-theme", theme);
@@ -154,11 +94,6 @@ document.addEventListener("DOMContentLoaded", async () => {
let initialTheme = getCurrentTheme();
if (isThemeDisabled(initialTheme)) {
initialTheme = "light-green";
}
updateThemeAvailability();
await applyTheme(initialTheme);
const initialLanguage = getCurrentLanguage();
@@ -175,53 +110,6 @@ document.addEventListener("DOMContentLoaded", async () => {
const versionElement = document.getElementById("version");
versionElement.textContent = `v${manifest.version}`;
let clickCount = 0;
versionElement.addEventListener("click", () => {
clickCount++;
if (clickCount >= 5) {
localStorage.setItem("blueThemesUnlocked", "true");
updateThemeAvailability();
const notification = document.createElement("div");
notification.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
background: var(--accent-accent);
color: white;
padding: 12px 20px;
border-radius: 8px;
font-family: 'Montserrat', sans-serif;
font-weight: 500;
z-index: 10000;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
animation: slideIn 0.3s ease-out;
`;
notification.textContent =
window.LanguageManager.t("common.success") +
": " +
window.LanguageManager.t("settings.blue_themes_unlocked");
const style = document.createElement("style");
style.textContent = `
@keyframes slideIn {
from { transform: translateX(100%); opacity: 0; }
to { transform: translateX(0); opacity: 1; }
}
`;
document.head.appendChild(style);
document.body.appendChild(notification);
setTimeout(() => {
notification.remove();
style.remove();
}, 3000);
clickCount = 0;
}
});
themeButtons.forEach((button) => {
button.addEventListener("mouseover", () => {
if (!button.hasAttribute("disabled")) {
@@ -247,4 +135,436 @@ document.addEventListener("DOMContentLoaded", async () => {
window.addEventListener("languageChanged", (event) => {
updateLanguageButtons(event.detail.language);
});
class CustomThemeManager {
constructor() {
this.customThemes = this.loadCustomThemes();
this.currentEditingTheme = null;
this.initializeEventListeners();
this.renderCustomThemes();
}
loadCustomThemes() {
const stored = localStorage.getItem('customThemes');
return stored ? JSON.parse(stored) : [];
}
saveCustomThemes() {
localStorage.setItem('customThemes', JSON.stringify(this.customThemes));
}
generateThemeId() {
const existingIds = this.customThemes.map(theme => theme.id);
let counter = 1;
let id;
do {
id = 'custom-' + counter;
counter++;
} while (existingIds.includes(id));
return id;
}
createTheme(name, colors) {
const theme = {
id: this.generateThemeId(),
name: name,
colors: colors,
created: new Date().toISOString()
};
this.customThemes.push(theme);
this.saveCustomThemes();
return theme;
}
updateTheme(id, name, colors) {
const index = this.customThemes.findIndex(t => t.id === id);
if (index !== -1) {
this.customThemes[index] = {
...this.customThemes[index],
name: name,
colors: colors,
updated: new Date().toISOString()
};
this.saveCustomThemes();
return this.customThemes[index];
}
return null;
}
deleteTheme(id) {
this.customThemes = this.customThemes.filter(t => t.id !== id);
this.saveCustomThemes();
const currentTheme = getCurrentTheme();
if (currentTheme === id) {
this.applyTheme('light-green');
}
}
applyTheme(themeId) {
if (themeId.startsWith('custom-')) {
const theme = this.customThemes.find(t => t.id === themeId);
if (theme) {
this.applyCustomThemeColors(theme.colors);
localStorage.setItem("themePreference", themeId);
}
} else {
document.documentElement.setAttribute('data-theme', themeId);
localStorage.setItem("themePreference", themeId);
}
updateThemeButtons(themeId);
}
applyCustomThemeColors(colors) {
const root = document.documentElement;
root.removeAttribute('data-theme');
Object.entries(colors).forEach(([key, value]) => {
const cssVar = this.convertToCSSVariable(key);
root.style.setProperty(cssVar, value);
});
}
convertToCSSVariable(key) {
return '--' + key.replace(/([A-Z])/g, '-$1').toLowerCase();
}
exportTheme(themeId) {
const theme = this.customThemes.find(t => t.id === themeId);
if (!theme) return null;
const colors = theme.colors;
const colorString = [
colors.background.substring(1),
colors.cardCard.substring(1),
colors.textPrimary.substring(1),
colors.textSecondary.substring(1),
colors.accentAccent.substring(1),
colors.accentSecondary.substring(1)
].join('');
return theme.name + '|' + colorString;
}
importTheme(themeString) {
try {
const parts = themeString.split('|');
if (parts.length !== 2) {
throw new Error('Invalid theme format - expected name|colors');
}
const [themeName, colorString] = parts;
if (colorString.length !== 36) {
throw new Error('Invalid theme format - expected 36 hex characters');
}
const hexPattern = /^[0-9A-Fa-f]{36}$/;
if (!hexPattern.test(colorString)) {
throw new Error('Invalid hex color format');
}
const colors = [];
for (let i = 0; i < 36; i += 6) {
colors.push(colorString.substring(i, i + 6));
}
const themeColors = {
background: '#' + colors[0],
cardCard: '#' + colors[1],
textPrimary: '#' + colors[2],
textSecondary: '#' + colors[3],
accentAccent: '#' + colors[4],
accentSecondary: '#' + colors[5]
};
const importedTheme = this.createTheme(
themeName + ' (Importált)',
themeColors
);
this.renderCustomThemes();
return importedTheme;
} catch (error) {
console.error('Theme import failed:', error);
return null;
}
}
renderCustomThemes() {
/*const container = document.getElementById('customThemesList');
container.innerHTML = '';
if (this.customThemes.length === 0) {
const emptyMessage = window.translations?.settings?.custom_theme?.no_themes || 'Még nincsenek egyéni témák';
container.innerHTML = `<p style="color: var(--text-secondary); font-size: 12px; text-align: center; padding: 20px;">${emptyMessage}</p>`;
return;
}
this.customThemes.forEach(theme => {
const themeElement = this.createThemeElement(theme);
container.appendChild(themeElement);
});*/
}
createThemeElement(theme) {
const element = document.createElement('div');
element.className = 'custom-theme-item';
const colors = theme.colors;
const previewColors = [
colors.background || '#DAE4F7',
colors.cardCard || '#EDF3FF',
colors.accentAccent || '#3673EE',
colors.textPrimary || '#050B15'
];
const translations = window.translations?.settings?.custom_theme || {};
element.innerHTML = `
<div class="custom-theme-info">
<div class="custom-theme-preview-mini">
${previewColors.map(color => `<div class="color-strip" style="background: ${color}"></div>`).join('')}
</div>
<span class="custom-theme-name">${theme.name}</span>
</div>
<div class="custom-theme-actions">
<button class="action-btn apply" data-theme-id="${theme.id}" title="${translations.apply || 'Alkalmaz'}">
<span class="material-icons-round">check</span>
</button>
<button class="action-btn edit" data-theme-id="${theme.id}" title="${translations.edit || 'Szerkeszt'}">
<span class="material-icons-round">edit</span>
</button>
<button class="action-btn export" data-theme-id="${theme.id}" title="${translations.export || 'Export'}">
<span class="material-icons-round">file_upload</span>
</button>
<button class="action-btn delete" data-theme-id="${theme.id}" title="${translations.delete || 'Töröl'}">
<span class="material-icons-round">delete</span>
</button>
</div>
`;
element.querySelector('.apply').addEventListener('click', () => {
this.applyTheme(theme.id);
});
element.querySelector('.edit').addEventListener('click', () => {
this.editTheme(theme.id);
});
element.querySelector('.export').addEventListener('click', () => {
this.showExportModal(theme.id);
});
element.querySelector('.delete').addEventListener('click', () => {
const confirmMessage = window.translations?.settings?.custom_theme?.delete_confirm || `Biztosan törölni szeretnéd a "${theme.name}" témát?`;
if (confirm(confirmMessage.replace('{name}', theme.name))) {
this.deleteTheme(theme.id);
this.renderCustomThemes();
}
});
return element;
}
editTheme(themeId) {
const theme = this.customThemes.find(t => t.id === themeId);
if (!theme) return;
this.currentEditingTheme = theme;
this.openThemeEditor(theme);
}
openThemeEditor(theme = null) {
const modal = document.getElementById('themeEditorModal');
const nameInput = document.getElementById('themeName');
if (theme) {
nameInput.value = theme.name;
this.loadThemeColorsToEditor(theme.colors);
} else {
nameInput.value = '';
this.loadDefaultColorsToEditor();
}
this.updatePreview();
modal.style.display = 'flex';
}
loadThemeColorsToEditor(colors) {
Object.entries(colors).forEach(([key, value]) => {
const input = document.getElementById(key);
if (input) {
input.value = value;
}
});
}
loadDefaultColorsToEditor() {
const defaults = {
background: '#DAE4F7',
cardCard: '#EDF3FF',
textPrimary: '#050B15',
textSecondary: '#050B15',
accentAccent: '#3673EE',
accentSecondary: '#1C469A'
};
this.loadThemeColorsToEditor(defaults);
}
updatePreview() {
const preview = document.getElementById('customThemePreview');
const colors = this.getColorsFromEditor();
preview.style.setProperty('--preview-background', colors.background);
preview.style.setProperty('--preview-card', colors.cardCard);
preview.style.setProperty('--preview-text-primary', colors.textPrimary);
preview.style.setProperty('--preview-text-secondary', colors.textSecondary);
preview.style.setProperty('--preview-accent', colors.accentAccent);
}
getColorsFromEditor() {
return {
background: document.getElementById('background').value,
cardCard: document.getElementById('cardCard').value,
textPrimary: document.getElementById('textPrimary').value,
textSecondary: document.getElementById('textSecondary').value,
accentAccent: document.getElementById('accentAccent').value,
accentSecondary: document.getElementById('accentSecondary').value
};
}
saveThemeFromEditor() {
const name = document.getElementById('themeName').value.trim();
if (!name) {
alert('Add meg a téma nevét!');
return;
}
const colors = this.getColorsFromEditor();
if (this.currentEditingTheme) {
this.updateTheme(this.currentEditingTheme.id, name, colors);
} else {
this.createTheme(name, colors);
}
this.closeThemeEditor();
this.renderCustomThemes();
}
closeThemeEditor() {
document.getElementById('themeEditorModal').style.display = 'none';
this.currentEditingTheme = null;
}
showExportModal(themeId) {
const exportString = this.exportTheme(themeId);
if (exportString) {
const theme = this.customThemes.find(t => t.id === themeId);
prompt(`${theme.name} téma export azonosítója (másold ki):`, exportString);
}
}
showImportModal() {
document.getElementById('importModal').style.display = 'flex';
}
closeImportModal() {
document.getElementById('importModal').style.display = 'none';
document.getElementById('themeImportString').value = '';
}
importThemeFromModal() {
const importString = document.getElementById('themeImportString').value.trim();
if (!importString) {
alert('Add meg az azonosítót!');
return;
}
const importedTheme = this.importTheme(importString);
if (importedTheme) {
alert(`"${importedTheme.name}" téma sikeresen importálva!`);
this.closeImportModal();
} else {
alert('Hiba történt az importálás során. Ellenőrizd az azonosítót!');
}
}
initializeEventListeners() {
/*document.getElementById('createCustomTheme').addEventListener('click', () => {
this.openThemeEditor();
});
document.getElementById('importTheme').addEventListener('click', () => {
this.showImportModal();
});
document.getElementById('closeThemeEditor').addEventListener('click', () => {
this.closeThemeEditor();
});
document.getElementById('cancelThemeEdit').addEventListener('click', () => {
this.closeThemeEditor();
});
document.getElementById('saveTheme').addEventListener('click', () => {
this.saveThemeFromEditor();
});
document.getElementById('closeImportModal').addEventListener('click', () => {
this.closeImportModal();
});
document.getElementById('cancelImport').addEventListener('click', () => {
this.closeImportModal();
});
document.getElementById('confirmImport').addEventListener('click', () => {
this.importThemeFromModal();
});
const colorInputs = ['background', 'cardCard', 'textPrimary', 'textSecondary', 'accentAccent', 'accentSecondary'];
colorInputs.forEach(inputId => {
document.getElementById(inputId).addEventListener('input', () => {
this.updatePreview();
});
});
document.getElementById('themeEditorModal').addEventListener('click', (e) => {
if (e.target.id === 'themeEditorModal') {
this.closeThemeEditor();
}
});
document.getElementById('importModal').addEventListener('click', (e) => {
if (e.target.id === 'importModal') {
this.closeImportModal();
}
});*/
}
}
const customThemeManager = new CustomThemeManager();
const originalGetCurrentTheme = getCurrentTheme;
getCurrentTheme = function() {
const theme = originalGetCurrentTheme();
if (theme && theme.startsWith('custom-')) {
return theme;
}
return theme;
};
const originalUpdateThemeButtons = updateThemeButtons;
updateThemeButtons = function(currentTheme) {
originalUpdateThemeButtons(currentTheme);
document.querySelectorAll('.custom-theme-item').forEach(item => {
const applyBtn = item.querySelector('.apply');
const themeId = applyBtn.dataset.themeId;
item.classList.toggle('active', themeId === currentTheme);
});
};
const currentTheme = getCurrentTheme();
if (currentTheme && currentTheme.startsWith('custom-')) {
customThemeManager.applyTheme(currentTheme);
}
});

View File

@@ -107,7 +107,7 @@ body {
transition:background-color 0.2s;
}
.user-dropdown-btn:hover {
background:var(--card-card);
background:var(--hover);
}
.user-info {
text-align:right;
@@ -119,7 +119,7 @@ body {
margin-top:0.5rem;
background:var(--card-card);
border-radius:12px;
box-shadow:0 4px 6px -1px var(--accent-shadow);
box-shadow:0 4px 6px -1px rgba(0,0,0,0.1);
width:200px;
display:none;
z-index:1000;
@@ -138,7 +138,10 @@ body {
transition:background-color 0.2s;
}
.dropdown-item:hover {
background:var(--button-secondaryFill);
background:var(--hover);
color:var(--accent-accent);
border-radius:8px;
text-decoration:none;
}
.kreta-main {
flex:1;
@@ -359,9 +362,10 @@ body {
justify-content:center;
margin:16px auto;
background:var(--card-card);
border-radius:24px;
border-radius:12px;
box-shadow:0px 1px var(--shadow-blur) 0px var(--accent-shadow);
max-width:800px;
padding:20px;
padding:16px;
}
.week-selector-container {
width:100%;
@@ -393,7 +397,7 @@ body {
display:flex;
align-items:center;
justify-content:center;
gap:12px;
gap:clamp(8px,2vw,12px);
max-width:100%;
}
.week-nav-btn {
@@ -744,9 +748,10 @@ body {
opacity:1;
}
.modal-content {
background:var(--card-card);
background:var(--card-card) !important;
border-radius:24px;
border-width:0 !important;
box-shadow: 0 1px var(--shadow-blur) 0 var(--accent-shadow);
width:100%;
max-width:500px;
max-height:90vh;
@@ -766,6 +771,7 @@ body {
align-items:center;
padding:1.5rem;
background:var(--button-secondaryFill);
border: none !important;
}
.modal-title {
font-size:18px;
@@ -781,9 +787,46 @@ body {
border-radius:8px;
transition:all 0.2s ease;
}
.modal-header-buttons {
display: flex;
align-items: center;
gap: 0.5rem;
}
.modal-add-btn {
background: none;
border: none;
color: var(--text-secondary);
cursor: pointer;
padding: 0.5rem;
border-radius: 8px;
transition: all 0.2s ease;
display: flex;
align-items: center;
justify-content: center;
}
.modal-add-btn:hover {
background: var(--background);
color: var(--text-primary);
}
.modal-close {
background: none;
border: none;
color: var(--text-secondary);
cursor: pointer;
padding: 0.5rem;
border-radius: 8px;
transition: all 0.2s ease;
display: flex;
align-items: center;
justify-content: center;
}
.modal-close:hover {
background:var(--background);
color:var(--text-primary);
background: var(--background);
color: var(--text-primary);
}
.modal-body {
padding:1.5rem;
@@ -826,6 +869,352 @@ body {
.test-section h4 {
color:var(--warning-accent);
}
.custom-homework-section h4,
.custom-tests-section h4 {
color: var(--accent-accent);
}
.custom-homework-list,
.custom-tests-list {
display: flex;
flex-direction: column;
gap: 0.75rem;
}
.custom-homework-item,
.custom-test-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0.75rem;
background: var(--background);
border-radius: 8px;
border: 1px solid var(--background-0);
transition: all 0.2s ease;
}
.custom-homework-item.completed,
.custom-test-item.completed {
opacity: 0.6;
}
.custom-homework-item.completed .homework-text,
.custom-test-item.completed .test-text {
text-decoration: line-through;
}
.homework-text,
.test-text {
flex: 1;
color: var(--text-primary);
font-size: 14px;
line-height: 1.4;
}
.homework-actions,
.test-actions {
display: flex;
align-items: center;
gap: 0.5rem;
}
.homework-complete-btn,
.test-complete-btn,
.homework-delete-btn,
.test-delete-btn {
background: none;
border: none;
cursor: pointer;
padding: 0.25rem;
border-radius: 4px;
transition: all 0.2s ease;
display: flex;
align-items: center;
justify-content: center;
}
.homework-complete-btn:hover,
.test-complete-btn:hover {
background: var(--accent-accent-20);
}
.homework-delete-btn:hover,
.test-delete-btn:hover {
background: var(--error-20);
}
.empty-message {
color: var(--text-secondary);
font-style: italic;
text-align: center;
padding: 1rem;
margin: 0;
}
.add-item-modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
display: flex !important;
align-items: center !important;
justify-content: center !important;
z-index: 10001;
opacity: 0;
visibility: hidden;
transition: all 0.3s ease;
}
.add-item-modal.show {
opacity: 1;
visibility: visible;
}
.add-modal-content {
background: var(--button-secondaryFill);
border-radius: 12px;
width: 90%;
max-width: 400px;
max-height: 90vh;
overflow-y: auto;
position: relative;
transform: translateY(20px);
opacity: 0;
transition: all 0.3s ease;
}
.add-item-modal.show .add-modal-content {
transform: translateY(0);
opacity: 1;
}
.add-modal-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1.5rem;
border-bottom: 1px solid var(--background-0);
}
.add-modal-header h3 {
font-size: 18px;
font-weight: 600;
color: var(--text-primary);
margin: 0;
}
.add-modal-close {
background: none;
border: none;
color: var(--text-secondary);
cursor: pointer;
padding: 0.5rem;
border-radius: 8px;
transition: all 0.2s ease;
display: flex;
align-items: center;
justify-content: center;
}
.add-modal-close:hover {
background: var(--background);
color: var(--text-primary);
}
.add-modal-body {
padding: 1.5rem;
display: flex;
flex-direction: column;
gap: 1rem;
}
.type-selection,
.text-input {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.type-selection label,
.text-input label {
color: var(--text-primary);
font-weight: 500;
font-size: 14px;
}
.type-select {
padding: 0.75rem;
border: 1px solid var(--background-0);
border-radius: 8px;
background: var(--background);
color: var(--text-primary);
font-size: 14px;
transition: all 0.2s ease;
}
.type-select:focus {
outline: none;
border-color: var(--accent-accent);
}
.item-text {
padding: 0.75rem;
border: 1px solid var(--background-0);
border-radius: 8px;
background: var(--background);
color: var(--text-primary);
font-size: 14px;
font-family: inherit;
resize: vertical;
min-height: 80px;
transition: all 0.2s ease;
}
.item-text:focus {
outline: none;
border-color: var(--accent-accent);
}
.item-text::placeholder {
color: var(--text-secondary);
}
.add-modal-actions {
display: flex;
gap: 0.75rem;
justify-content: flex-end;
margin-top: 0.5rem;
}
.cancel-btn,
.save-btn {
padding: 0.75rem 1.5rem;
border: none;
border-radius: 8px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s ease;
}
.cancel-btn {
background: var(--background);
color: var(--text-secondary);
border: 1px solid var(--background-0);
}
.cancel-btn:hover {
background: var(--background-0);
color: var(--text-primary);
}
.save-btn {
background: var(--accent-accent);
color: white;
}
.save-btn:hover {
background: var(--accent-accent-80);
}
@media (max-width: 768px) {
.lesson-modal {
z-index: 10000;
}
.add-item-modal {
z-index: 10001;
}
.modal-content {
width: 95%;
max-width: none;
margin: 1rem;
}
.add-modal-content {
width: 95%;
max-width: none;
margin: 1rem;
}
.modal-header,
.add-modal-header {
padding: 1rem;
}
.modal-body,
.add-modal-body {
padding: 1rem;
}
.modal-header-buttons {
gap: 0.25rem;
}
.modal-add-btn,
.modal-close,
.add-modal-close {
padding: 0.375rem;
}
.modal-add-btn img,
.modal-close img,
.add-modal-close img {
width: 20px !important;
height: 20px !important;
}
.custom-homework-item,
.custom-test-item {
padding: 0.5rem;
flex-direction: column;
align-items: flex-start;
gap: 0.5rem;
}
.homework-actions,
.test-actions {
align-self: flex-end;
}
.homework-text,
.test-text {
font-size: 13px;
}
.add-modal-actions {
flex-direction: column;
gap: 0.5rem;
}
.cancel-btn,
.save-btn {
width: 100%;
padding: 0.875rem;
}
}
@media (min-width: 769px) {
.lesson-modal {
z-index: 10000;
}
.add-item-modal {
z-index: 10001;
}
.modal-content {
position: relative;
}
.add-modal-content {
position: relative;
}
}
.tema-section h4 {
color:var(--primary-accent);
}
@@ -835,18 +1224,167 @@ body {
border-radius:8px;
color:var(--text-primary);
}
.homework-attachments {
margin-top: 1rem;
padding-top: 1rem;
border-top: 1px solid var(--background-0);
}
.attachments-list {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.attachment-item {
display: flex;
align-items: center;
justify-content: center;
padding: 0.75rem;
background-color: var(--accent-15);
border-radius: 6px;
border: 1px solid var(--background-0);
cursor: pointer;
transition: all 0.2s ease;
text-align: center;
}
.attachment-item:hover {
background-color: var(--background-0);
transform: translateY(-1px);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.attachment-item:active {
transform: translateY(0);
background-color: var(--accent-accent);
opacity: 0.8;
}
.test-details-loading {
color:var(--text-secondary);
font-style:italic;
margin-top:0.5rem;
}
.homework-details-loading {
color:var(--text-secondary);
font-style:italic;
margin-top:0.5rem;
}
.test-details {
color:var(--text-primary);
margin-top:0.5rem;
padding:0.75rem;
background:var(--background-0);
}
.homework-details {
color:var(--text-primary);
padding:0.75rem;
background:var(--background-0);
border-radius:6px;
border-left:3px solid var(--warning-accent);
margin-bottom:0.75rem;
}
.homework-details p {
margin:0.5rem 0;
line-height:1.4;
}
.homework-details p:first-child {
margin-top:0;
}
.homework-details p:last-child {
margin-bottom:0;
}
.homework-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
}
.homework-header h4 {
margin: 0;
display: flex;
align-items: center;
gap: 8px;
}
.homework-completion-header-btn {
background: transparent;
border: 2px solid var(--border-color);
border-radius: 50%;
width: 32px;
height: 32px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s ease;
opacity: 0.6;
}
.homework-completion-header-btn:hover {
opacity: 1;
border-color: var(--accent-accent);
background: var(--background-hover);
}
.homework-completion-header-btn.completed {
background: var(--success-color, #4caf50);
border-color: var(--success-color, #4caf50);
opacity: 1;
}
.homework-completion-header-btn.completed:hover {
background: var(--success-color-hover, #45a049);
border-color: var(--success-color-hover, #45a049);
}
.homework-completion-header-btn img {
filter: var(--icon-filter, none);
}
.homework-completion-header-btn.completed img {
filter: brightness(0) invert(1);
}
.homework-completion {
margin-top:15px;
text-align:center;
}
.homework-completion-btn {
background:var(--background-secondary);
border:2px solid var(--border-color);
color:var(--text-primary);
padding:10px 20px;
border-radius:8px;
cursor:pointer;
font-size:14px;
font-weight:500;
transition:all 0.2s ease;
display:inline-flex;
align-items:center;
gap:8px;
}
.homework-completion-btn:hover {
background:var(--background-hover);
border-color:var(--accent-accent);
}
.homework-completion-btn.completed {
background:var(--success-background, #e8f5e8);
border-color:var(--success-color, #4caf50);
color:var(--success-color, #4caf50);
}
.homework-completion-btn.completed:hover {
background:var(--success-background-hover, #d4edda);
}
.homework-completion-btn span {
font-size:16px;
font-weight:bold;
}
.test-details p {
@@ -996,4 +1534,4 @@ to {
padding-top:16px;
transition:gap 0.2s ease;
font-size:clamp(0.875rem,1.5vw,1rem);
}
}

File diff suppressed because it is too large Load Diff

106
tools/background.js Normal file
View File

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

View File

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

View File

@@ -1,19 +1,20 @@
const createTemplate = {
header() {
async header() {
const data = {
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ó",
name: await storageManager.get("userName", "Felhasználónév"),
time:
document.querySelector(".usermenu_timer")?.textContent?.trim() ||
"45:00",
email: cookieManager.get("userEmail") || "",
},
};
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);
@@ -36,25 +37,25 @@ const createTemplate = {
<nav class="kreta-nav">
<div class="nav-links">
<a href="/Intezmeny/Faliujsag" data-page="dashboard" class="nav-item ${location.pathname == "/Intezmeny/Faliujsag" ? "active" : ""}">
<a href="${baseUrl}/Intezmeny/Faliujsag" data-page="dashboard" class="nav-item ${location.pathname == "/Intezmeny/Faliujsag" ? "active" : ""}">
<img src="${chrome.runtime.getURL("icons/dashboard-" + (location.pathname == "/Intezmeny/Faliujsag" ? "active" : "inactive") + ".svg")}" alt="${LanguageManager.t("navigation.dashboard")}">
${LanguageManager.t("navigation.dashboard")}
</a>
<a href="/TanuloErtekeles/Osztalyzatok" data-page="grades" class="nav-item ${location.pathname == "/TanuloErtekeles/Osztalyzatok" ? "active" : ""}">
<a href="${baseUrl}/TanuloErtekeles/Osztalyzatok" data-page="grades" class="nav-item ${location.pathname == "/TanuloErtekeles/Osztalyzatok" ? "active" : ""}">
<img src="${chrome.runtime.getURL("icons/grades-" + (location.pathname == "/TanuloErtekeles/Osztalyzatok" ? "active" : "inactive") + ".svg")}" alt="${LanguageManager.t("navigation.grades")}">
${LanguageManager.t("navigation.grades")}
</a>
<a href="/Orarend/InformaciokOrarend" data-page="timetable" class="nav-item ${location.pathname == "/Orarend/InformaciokOrarend" ? "active" : ""}">
<a href="${baseUrl}/Orarend/InformaciokOrarend" data-page="timetable" class="nav-item ${location.pathname == "/Orarend/InformaciokOrarend" ? "active" : ""}">
<img src="${chrome.runtime.getURL("icons/timetable-" + (location.pathname == "/Orarend/InformaciokOrarend" ? "active" : "inactive") + ".svg")}" alt="${LanguageManager.t("navigation.timetable")}">
${LanguageManager.t("navigation.timetable")}
</a>
<a href="/Hianyzas/Hianyzasok" data-page="absences" class="nav-item ${location.pathname == "/Hianyzas/Hianyzasok" ? "active" : ""}">
<a href="${baseUrl}/Hianyzas/Hianyzasok" data-page="absences" class="nav-item ${location.pathname == "/Hianyzas/Hianyzasok" ? "active" : ""}">
<img src="${chrome.runtime.getURL("icons/absences-" + (location.pathname == "/Hianyzas/Hianyzasok" ? "active" : "inactive") + ".svg")}" alt="${LanguageManager.t("navigation.absences")}">
${LanguageManager.t("navigation.absences")}
</a>
<a href="/Tanulo/TanuloHaziFeladat" data-page="other" class="nav-item ${location.pathname == "/Tanulo/TanuloHaziFeladat" ? "active" : ""}">
<img src="${chrome.runtime.getURL("icons/others.svg")}" alt="${LanguageManager.t("navigation.other")}">
${LanguageManager.t("navigation.other")}
<a href="https://eugyintezes.e-kreta.hu/api/bff/login" data-page="messages" class="nav-item ${location.pathname == "/" ? "active" : ""}">
<img src="${chrome.runtime.getURL("icons/messages-" + (location.pathname == "/uzenetek" ? "active" : "inactive") + ".svg")}" alt="${LanguageManager.t("navigation.messages")}">
${LanguageManager.t("navigation.messages")}
</a>
</div>
</nav>
@@ -67,11 +68,7 @@ const createTemplate = {
</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">
<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>
@@ -79,7 +76,7 @@ const createTemplate = {
<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">
<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>

View File

@@ -65,7 +65,13 @@ window.addEventListener("DOMContentLoaded", () => {
});
}
if (urls.some((url) => url.includes(location.pathname))) {
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);
}
},
};