mirror of
https://github.com/QwIT-Development/firka-extension.git
synced 2026-06-12 11:51:39 +02:00
Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a94c06d893 | ||
|
|
7cd8c065b3 | ||
|
|
d44be3b029 | ||
|
|
3243ebdc2e | ||
|
|
0f69a1583b | ||
|
|
15ab433064 | ||
|
|
4aab9afebd | ||
|
|
e0df578dca | ||
|
|
dd320d3dc4 | ||
|
|
5248cfc12e | ||
|
|
1f34cacf25 | ||
|
|
f35d29f56c | ||
|
|
1429943dbe | ||
|
|
2ca1c9949a | ||
|
|
1086086552 | ||
|
|
c0b5ed3e46 | ||
|
|
430bed67c2 | ||
|
|
f75eff8c09 | ||
|
|
f52017585f | ||
|
|
a40b5db6f9 | ||
|
|
f6a52b73de | ||
|
|
75d6cc4676 | ||
|
|
0dbb3402a8 | ||
|
|
64c54171cb | ||
|
|
3d399ee8be | ||
|
|
431103d2ff | ||
|
|
8c45ee58ea | ||
|
|
0b9897e0eb | ||
|
|
d72a8b519d | ||
|
|
53af1d4c1e | ||
|
|
2329ba2cab | ||
|
|
578da8fb38 | ||
|
|
3275b79adf |
@@ -1,8 +1,9 @@
|
||||
<p align="center">
|
||||
<img src="https://i.imgur.com/WugwlzI.png" width="150">
|
||||
<img width="150" height="150" alt="firka_logo_128" src="https://github.com/QwIT-Development/firka-extension/blob/main/images/firka_logo_128.png?raw=true" />
|
||||
<h1 align="center">Firka extension</h1>
|
||||
</p>
|
||||
|
||||
|
||||
<p align="center">
|
||||
Modern, testre szabható felhasználói felület az e-KRÉTA rendszerhez
|
||||
</p>
|
||||
@@ -23,8 +24,8 @@
|
||||
<a href="https://chromewebstore.google.com/detail/firxa/emafoaifbfppcccgfmpcoheonhjnpldj?hl=hu">
|
||||
<img src="https://github.com/QwIT-Development/firka-extension/blob/main/images/chrome.png?raw=true" alt="Elérhető a Chrome Web Store-on" width="200">
|
||||
</a>
|
||||
<a href="https://addons.mozilla.org/hu/firefox/addon/firxa/">
|
||||
<img src="https://github.com/QwIT-Development/firka-extension/blob/main/images/firefox.png?raw=true" alt="Elérhető a Firefox add-ons oldalon" width="200">
|
||||
<a href="https://github.com/QwIT-Development/firka-extension/releases">
|
||||
<img src="https://github.com/QwIT-Development/firka-extension/blob/main/images/firefoxact.png?raw=true" alt="Elérhető a Firefox add-ons oldalon" width="200">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
||||
@@ -29,131 +29,51 @@ body {
|
||||
}
|
||||
|
||||
.absences-page {
|
||||
display: grid;
|
||||
grid-template-columns: 300px 1fr;
|
||||
grid-template-rows: 1fr;
|
||||
grid-template-areas:
|
||||
"sidebar content";
|
||||
gap: 24px;
|
||||
min-height: calc(100vh - 200px);
|
||||
}
|
||||
|
||||
.absences-sidebar {
|
||||
grid-area: sidebar;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
position: sticky;
|
||||
top: 24px;
|
||||
height: fit-content;
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
.filter-card {
|
||||
.stats-section {
|
||||
background: var(--card-card);
|
||||
border-radius: 20px;
|
||||
padding: 24px;
|
||||
box-shadow: 0px 1px var(--shadow-blur) 0px var(--accent-shadow);
|
||||
}
|
||||
|
||||
.filter-header {
|
||||
.stats-title {
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
color: var(--text-primary);
|
||||
margin-bottom: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.filter-header h2 {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
margin: 0;
|
||||
background-color: #00000000 !important;
|
||||
}
|
||||
|
||||
.filter-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.filter-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.filter-group label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
color: var(--text-secondary);
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.filter-group label img {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.filter-input {
|
||||
padding: 12px 16px;
|
||||
border: 2px solid transparent;
|
||||
border-radius: 12px;
|
||||
background: var(--button-secondaryFill);
|
||||
color: var(--text-primary);
|
||||
font-family: inherit;
|
||||
font-size: 14px;
|
||||
transition: all 0.2s ease;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.filter-input:hover {
|
||||
background: var(--accent-15);
|
||||
}
|
||||
|
||||
.filter-input:focus {
|
||||
outline: none;
|
||||
border-color: var(--accent-accent);
|
||||
background: var(--button-secondaryFill);
|
||||
}
|
||||
|
||||
.stats-section {
|
||||
grid-area: stats;
|
||||
background-color: transparent !important;
|
||||
margin-top: 0px !important;
|
||||
}
|
||||
|
||||
.stats-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
grid-template-columns: repeat(6, 1fr);
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
background: var(--card-card);
|
||||
background: var(--button-secondaryFill);
|
||||
border-radius: 16px;
|
||||
padding: 20px;
|
||||
padding: 20px 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
box-shadow: 0px 1px var(--shadow-blur) 0px var(--accent-shadow);
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
min-height: 100px;
|
||||
border: 2px solid transparent;
|
||||
}
|
||||
|
||||
.stat-card:hover {
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.stat-icon {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
margin-bottom: 8px;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.stat-number {
|
||||
font-size: 32px;
|
||||
font-weight: 700;
|
||||
@@ -166,18 +86,46 @@ body {
|
||||
color: var(--accent-accent);
|
||||
}
|
||||
|
||||
.stat-card.total {
|
||||
border-color: var(--accent-15);
|
||||
}
|
||||
|
||||
.stat-card.absence .stat-number {
|
||||
color: var(--accent-accent);
|
||||
}
|
||||
|
||||
.stat-card.late .stat-number {
|
||||
color: var(--grades-2);
|
||||
}
|
||||
|
||||
.stat-card.late {
|
||||
border-color: var(--grades-background-2);
|
||||
}
|
||||
|
||||
.stat-card.justified .stat-number {
|
||||
color: var(--grades-4);
|
||||
}
|
||||
|
||||
.stat-card.justified {
|
||||
border-color: var(--grades-background-4);
|
||||
}
|
||||
|
||||
.stat-card.unjustified .stat-number {
|
||||
color: var(--grades-1);
|
||||
}
|
||||
|
||||
.stat-card.unjustified {
|
||||
border-color: var(--grades-background-1);
|
||||
}
|
||||
|
||||
.stat-card.pending .stat-number {
|
||||
color: var(--grades-3);
|
||||
}
|
||||
|
||||
.stat-card.pending {
|
||||
border-color: var(--grades-background-3);
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
color: var(--text-secondary);
|
||||
font-size: 12px;
|
||||
@@ -186,163 +134,369 @@ body {
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.absences-content {
|
||||
grid-area: content;
|
||||
.calendar-legend {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 24px;
|
||||
flex-wrap: wrap;
|
||||
gap: 16px;
|
||||
padding: 16px 24px;
|
||||
background: var(--card-card);
|
||||
border-radius: 16px;
|
||||
box-shadow: 0px 1px var(--shadow-blur) 0px var(--accent-shadow);
|
||||
}
|
||||
|
||||
.day-group {
|
||||
.legend-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.legend-color {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.legend-color.status-justified {
|
||||
background: var(--grades-background-4);
|
||||
border: 2px solid var(--grades-4);
|
||||
}
|
||||
|
||||
.legend-color.status-pending {
|
||||
background: var(--grades-background-3);
|
||||
border: 2px solid var(--grades-3);
|
||||
}
|
||||
|
||||
.legend-color.status-unjustified {
|
||||
background: var(--grades-background-1);
|
||||
border: 2px solid var(--grades-1);
|
||||
}
|
||||
|
||||
.legend-color.type-late-legend {
|
||||
background: var(--grades-background-2);
|
||||
border: 2px dashed var(--grades-2);
|
||||
}
|
||||
|
||||
.legend-item span {
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.calendar-section {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.month-container {
|
||||
background: var(--card-card);
|
||||
border-radius: 20px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0px 1px var(--shadow-blur) 0px var(--accent-shadow);
|
||||
}
|
||||
|
||||
.day-header {
|
||||
.month-header {
|
||||
background: var(--button-secondaryFill);
|
||||
color: white;
|
||||
padding: 16px 20px;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.calendar-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(7, 1fr);
|
||||
gap: 4px;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.calendar-day-header {
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: var(--text-secondary);
|
||||
padding: 8px 0;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.calendar-day {
|
||||
aspect-ratio: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 10px;
|
||||
background: var(--button-secondaryFill);
|
||||
position: relative;
|
||||
min-height: 40px;
|
||||
transition: transform 0.15s ease, box-shadow 0.15s ease;
|
||||
}
|
||||
|
||||
.calendar-day.empty {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.calendar-day.future {
|
||||
background: var(--accent-15);
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.calendar-day.future .day-number {
|
||||
color: var(--text-teritary);
|
||||
}
|
||||
|
||||
.calendar-day.today .day-number {
|
||||
color: var(--accent-accent);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.calendar-day .day-number {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: var(--text-primary);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.calendar-day.has-absence {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.calendar-day.has-absence:hover {
|
||||
transform: scale(1.1);
|
||||
box-shadow: 0 4px 12px var(--accent-shadow);
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.calendar-day.status-justified {
|
||||
background: var(--grades-background-4);
|
||||
border: 2px solid var(--grades-4);
|
||||
}
|
||||
|
||||
.calendar-day.status-justified .day-number {
|
||||
color: var(--grades-4);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.calendar-day.status-pending {
|
||||
background: var(--grades-background-3);
|
||||
border: 2px solid var(--grades-3);
|
||||
}
|
||||
|
||||
.calendar-day.status-pending .day-number {
|
||||
color: var(--grades-3);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.calendar-day.status-unjustified {
|
||||
background: var(--grades-background-1);
|
||||
border: 2px solid var(--grades-1);
|
||||
}
|
||||
|
||||
.calendar-day.status-unjustified .day-number {
|
||||
color: var(--grades-1);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.calendar-day.type-late {
|
||||
border-style: dashed;
|
||||
}
|
||||
|
||||
.calendar-day.type-mixed {
|
||||
border-style: double;
|
||||
border-width: 3px;
|
||||
}
|
||||
|
||||
.absence-count {
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
right: 2px;
|
||||
background: var(--accent-accent);
|
||||
color: white;
|
||||
padding: 16px 24px;
|
||||
font-size: 9px;
|
||||
font-weight: 700;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.calendar-day.status-justified .absence-count {
|
||||
background: var(--grades-4);
|
||||
}
|
||||
|
||||
.calendar-day.status-pending .absence-count {
|
||||
background: var(--grades-3);
|
||||
}
|
||||
|
||||
.calendar-day.status-unjustified .absence-count {
|
||||
background: var(--grades-1);
|
||||
}
|
||||
|
||||
.absence-modal-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
backdrop-filter: blur(4px);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 10000;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s ease;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.absence-modal-overlay.visible {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.absence-modal {
|
||||
background: var(--card-card);
|
||||
border-radius: 24px;
|
||||
max-width: 500px;
|
||||
width: 100%;
|
||||
max-height: 80vh;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
||||
transform: scale(0.95);
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
|
||||
.absence-modal-overlay.visible .absence-modal {
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
background: var(--button-secondaryFill);
|
||||
border-bottom: 1px solid #00000000 !important;
|
||||
color: white;
|
||||
padding: 14px 18px !important;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.day-date {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
.modal-header h2 {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
margin: 0;
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
.day-date img {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
filter: brightness(0) invert(1);
|
||||
.modal-content {
|
||||
background-color: var(--card-card) !important;
|
||||
padding: 16px;
|
||||
overflow-y: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.day-count {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
padding: 4px 12px;
|
||||
border-radius: 20px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.day-absences {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
|
||||
gap: 16px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.absence-card {
|
||||
.modal-absence-card {
|
||||
background: var(--button-secondaryFill);
|
||||
border-radius: 16px;
|
||||
padding: 20px;
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr minmax(auto, max-content);
|
||||
grid-template-rows: auto auto;
|
||||
grid-template-areas:
|
||||
"lesson subject status"
|
||||
"lesson topic status";
|
||||
gap: 8px 12px;
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
border: 1px solid var(--accent-15);
|
||||
border-radius: 12px;
|
||||
padding: 12px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
border-left: 3px solid transparent;
|
||||
}
|
||||
|
||||
.absence-card:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px var(--accent-shadow);
|
||||
}
|
||||
|
||||
.absence-lesson {
|
||||
grid-area: lesson;
|
||||
.modal-card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
background: var(--accent-15);
|
||||
border-radius: 12px;
|
||||
font-size: 20px;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.modal-lesson {
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
color: var(--accent-accent);
|
||||
min-width: 24px;
|
||||
}
|
||||
|
||||
.type-indicator {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 3px 8px 1px 8px;
|
||||
border-radius: 12px;
|
||||
font-size: 10px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.type-indicator.absence {
|
||||
background: var(--accent-15);
|
||||
color: var(--accent-accent);
|
||||
}
|
||||
|
||||
.absence-subject {
|
||||
grid-area: subject;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
align-self: end;
|
||||
.type-indicator.late {
|
||||
background: var(--grades-background-2);
|
||||
color: var(--grades-2);
|
||||
}
|
||||
|
||||
.absence-topic {
|
||||
grid-area: topic;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
align-self: start;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.absence-status {
|
||||
grid-area: status;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.status-badge {
|
||||
.modal-status-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 8px 14px;
|
||||
border-radius: 24px;
|
||||
font-size: 11px;
|
||||
padding: 3px 8px;
|
||||
border-radius: 12px;
|
||||
font-size: 10px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.2px;
|
||||
white-space: nowrap;
|
||||
flex-shrink: 0;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.status-badge img {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.status-badge.justified {
|
||||
.modal-status-badge.justified {
|
||||
background: var(--grades-background-4);
|
||||
color: var(--grades-4);
|
||||
}
|
||||
|
||||
.status-badge.justified img {
|
||||
filter: brightness(0) saturate(100%) invert(76%) sepia(43%) saturate(609%) hue-rotate(53deg) brightness(101%) contrast(92%);
|
||||
}
|
||||
|
||||
.status-badge.unjustified {
|
||||
background: var(--grades-background-1);
|
||||
color: var(--grades-1);
|
||||
}
|
||||
|
||||
.status-badge.unjustified .material-icons-round {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.status-badge.pending {
|
||||
.modal-status-badge.pending {
|
||||
background: var(--grades-background-3);
|
||||
color: var(--grades-3);
|
||||
}
|
||||
|
||||
.status-badge.pending .material-icons-round {
|
||||
font-size: 16px;
|
||||
.modal-status-badge.unjustified {
|
||||
background: var(--grades-background-1);
|
||||
color: var(--grades-1);
|
||||
}
|
||||
|
||||
.modal-subject {
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.modal-info-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
font-size: 12px;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.modal-info-item {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.modal-info-item:not(:last-child)::after {
|
||||
content: '•';
|
||||
margin-left: 6px;
|
||||
color: var(--text-teritary);
|
||||
}
|
||||
|
||||
.modal-minutes {
|
||||
color: var(--grades-2);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
@@ -375,14 +529,9 @@ body {
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
.absences-page {
|
||||
grid-template-columns: 260px 1fr;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.day-absences {
|
||||
grid-template-columns: 1fr;
|
||||
@media (max-width: 1200px) {
|
||||
.stats-grid {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -395,41 +544,18 @@ body {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.absences-page {
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: auto 1fr;
|
||||
grid-template-areas:
|
||||
"sidebar"
|
||||
"content";
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.absences-sidebar {
|
||||
position: static;
|
||||
}
|
||||
|
||||
.filter-card {
|
||||
.stats-section {
|
||||
padding: 20px;
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
.filter-content {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.filter-group label {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.filter-input {
|
||||
padding: 10px 12px;
|
||||
font-size: 13px;
|
||||
.stats-title {
|
||||
font-size: 18px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.stats-grid {
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
@@ -447,33 +573,78 @@ body {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.day-group {
|
||||
.calendar-section {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.calendar-legend {
|
||||
gap: 12px;
|
||||
padding: 14px 18px;
|
||||
}
|
||||
|
||||
.legend-item span {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.month-container {
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
.day-header {
|
||||
padding: 14px 18px;
|
||||
.month-header {
|
||||
padding: 14px 16px;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.calendar-grid {
|
||||
gap: 3px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.calendar-day {
|
||||
min-height: 36px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.calendar-day .day-number {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.absence-count {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
font-size: 8px;
|
||||
}
|
||||
|
||||
.absence-modal {
|
||||
max-width: 100%;
|
||||
max-height: 90vh;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
padding: 12px 16px;
|
||||
}
|
||||
|
||||
.modal-header h2 {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
padding: 12px;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.modal-absence-card {
|
||||
padding: 10px;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.modal-subject {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.day-absences {
|
||||
padding: 14px;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.absence-card {
|
||||
padding: 16px;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.absence-lesson {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.absence-subject {
|
||||
font-size: 15px;
|
||||
.modal-info-row {
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -483,26 +654,13 @@ body {
|
||||
}
|
||||
|
||||
.absences-page {
|
||||
gap: 12px;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.filter-card {
|
||||
.stats-section {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.filter-header {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.filter-header h2 {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.filter-content {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.stats-grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 8px;
|
||||
@@ -523,63 +681,35 @@ body {
|
||||
letter-spacing: 0.3px;
|
||||
}
|
||||
|
||||
.day-header {
|
||||
padding: 12px 16px;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.day-date {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.day-count {
|
||||
font-size: 12px;
|
||||
padding: 3px 10px;
|
||||
}
|
||||
|
||||
.day-absences {
|
||||
padding: 12px;
|
||||
.calendar-legend {
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.absence-card {
|
||||
padding: 14px;
|
||||
grid-template-columns: auto 1fr;
|
||||
grid-template-rows: auto auto auto;
|
||||
grid-template-areas:
|
||||
"lesson subject"
|
||||
"lesson topic"
|
||||
"status status";
|
||||
gap: 6px 12px;
|
||||
.calendar-grid {
|
||||
gap: 2px;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.absence-lesson {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
font-size: 16px;
|
||||
grid-row: span 2;
|
||||
.calendar-day {
|
||||
min-height: 32px;
|
||||
}
|
||||
|
||||
.absence-status {
|
||||
justify-content: flex-start;
|
||||
margin-top: 8px;
|
||||
padding-top: 10px;
|
||||
border-top: 1px solid var(--accent-15);
|
||||
}
|
||||
|
||||
.status-badge {
|
||||
padding: 6px 12px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.absence-subject {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.absence-topic {
|
||||
.calendar-day .day-number {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.absence-modal-overlay {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.modal-absence-card {
|
||||
padding: 14px;
|
||||
}
|
||||
|
||||
.modal-subject {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
@@ -593,17 +723,15 @@ body {
|
||||
}
|
||||
}
|
||||
|
||||
.day-group {
|
||||
.month-container {
|
||||
animation: fadeIn 0.3s ease forwards;
|
||||
}
|
||||
|
||||
.day-group:nth-child(1) { animation-delay: 0.05s; }
|
||||
.day-group:nth-child(2) { animation-delay: 0.1s; }
|
||||
.day-group:nth-child(3) { animation-delay: 0.15s; }
|
||||
.day-group:nth-child(4) { animation-delay: 0.2s; }
|
||||
.day-group:nth-child(5) { animation-delay: 0.25s; }
|
||||
|
||||
|
||||
.month-container:nth-child(1) { animation-delay: 0.05s; }
|
||||
.month-container:nth-child(2) { animation-delay: 0.1s; }
|
||||
.month-container:nth-child(3) { animation-delay: 0.15s; }
|
||||
.month-container:nth-child(4) { animation-delay: 0.2s; }
|
||||
.month-container:nth-child(5) { animation-delay: 0.25s; }
|
||||
|
||||
.loading-overlay {
|
||||
position: fixed;
|
||||
|
||||
@@ -15,7 +15,7 @@ async function collectAbsencesData() {
|
||||
try {
|
||||
const currentDomain = window.location.hostname;
|
||||
const response = await fetch(
|
||||
`https://${currentDomain}/api/HianyzasokApi/GetHianyzasGrid?sort=MulasztasDatum-desc&page=1&pageSize=100&group=&filter=&data=%7B%7D&_=${Date.now()}`,
|
||||
`https://${currentDomain}/api/HianyzasokApi/GetHianyzasGrid?sort=MulasztasDatum-desc&page=1&pageSize=500&group=&filter=&data=%7B%7D&_=${Date.now()}`,
|
||||
{
|
||||
method: "GET",
|
||||
credentials: "include",
|
||||
@@ -45,530 +45,449 @@ async function collectAbsencesData() {
|
||||
justificationStatus = "unjustified";
|
||||
}
|
||||
|
||||
const absenceType = item.MulasztasTipus === 1500 ? 'absence' : item.MulasztasTipus === 1499 ? 'late' : 'other';
|
||||
|
||||
absences.push({
|
||||
date: formattedDate,
|
||||
rawDate: date,
|
||||
dateKey: `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, "0")}-${date.getDate().toString().padStart(2, "0")}`,
|
||||
lesson: item.Oraszam?.toString() || "",
|
||||
subject: item.Targy || "",
|
||||
topic: item.Tema || "",
|
||||
type: item.MulasztasTipus_DNAME || "",
|
||||
absenceType: absenceType,
|
||||
absenceTypeId: item.MulasztasTipus,
|
||||
justified: item.Igazolt_BOOL === true,
|
||||
justificationStatus: justificationStatus,
|
||||
purposeful: item.TanoraiCeluMulasztas_BNAME || "",
|
||||
justificationType: item.IgazolasTipus_DNAME || "",
|
||||
minutes: item.KesesPercben || 0,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
const groupedAbsences = absences.reduce((groups, absence) => {
|
||||
const date = absence.date;
|
||||
if (!groups[date]) {
|
||||
groups[date] = [];
|
||||
const groupedByDate = absences.reduce((groups, absence) => {
|
||||
const key = absence.dateKey;
|
||||
if (!groups[key]) {
|
||||
groups[key] = [];
|
||||
}
|
||||
groups[date].push(absence);
|
||||
groups[key].push(absence);
|
||||
return groups;
|
||||
}, {});
|
||||
|
||||
|
||||
Object.keys(groupedAbsences).forEach(date => {
|
||||
groupedAbsences[date].sort((a, b) => parseInt(a.lesson) - parseInt(b.lesson));
|
||||
Object.keys(groupedByDate).forEach(date => {
|
||||
groupedByDate[date].sort((a, b) => parseInt(a.lesson) - parseInt(b.lesson));
|
||||
});
|
||||
|
||||
return { basicData, absences, groupedAbsences };
|
||||
return { basicData, absences, groupedByDate };
|
||||
} catch (error) {
|
||||
console.error("Hiba az API hívás során:", error);
|
||||
return { basicData, absences: [], groupedAbsences: {} };
|
||||
return { basicData, absences: [], groupedByDate: {} };
|
||||
}
|
||||
}
|
||||
|
||||
function createFilterCard(absences) {
|
||||
const filterCard = document.createElement('div');
|
||||
filterCard.className = 'filter-card';
|
||||
|
||||
const filterHeader = document.createElement('div');
|
||||
filterHeader.className = 'filter-header';
|
||||
const h2 = document.createElement('h2');
|
||||
h2.textContent = LanguageManager.t('absences.filter_title');
|
||||
filterHeader.appendChild(h2);
|
||||
|
||||
const filterContent = document.createElement('div');
|
||||
filterContent.className = 'filter-content';
|
||||
|
||||
const dateGroup = document.createElement('div');
|
||||
dateGroup.className = 'filter-group';
|
||||
|
||||
const dateLabel = document.createElement('label');
|
||||
const dateImg = document.createElement('img');
|
||||
dateImg.src = chrome.runtime.getURL('icons/Calendar.svg');
|
||||
dateImg.alt = 'Dátum';
|
||||
dateLabel.appendChild(dateImg);
|
||||
dateLabel.appendChild(document.createTextNode(' ' + LanguageManager.t('absences.date')));
|
||||
|
||||
const dateInput = document.createElement('input');
|
||||
dateInput.type = 'date';
|
||||
dateInput.id = 'dateFilter';
|
||||
dateInput.className = 'filter-input';
|
||||
|
||||
dateGroup.appendChild(dateLabel);
|
||||
dateGroup.appendChild(dateInput);
|
||||
filterContent.appendChild(dateGroup);
|
||||
|
||||
const subjectGroup = document.createElement('div');
|
||||
subjectGroup.className = 'filter-group';
|
||||
|
||||
const subjectLabel = document.createElement('label');
|
||||
const subjectImg = document.createElement('img');
|
||||
subjectImg.src = chrome.runtime.getURL('icons/Subject.svg');
|
||||
subjectImg.alt = 'Tantárgy';
|
||||
subjectLabel.appendChild(subjectImg);
|
||||
subjectLabel.appendChild(document.createTextNode(' ' + LanguageManager.t('absences.subject')));
|
||||
|
||||
const subjectSelect = document.createElement('select');
|
||||
subjectSelect.id = 'subjectFilter';
|
||||
subjectSelect.className = 'filter-input';
|
||||
|
||||
const defaultSubjectOption = document.createElement('option');
|
||||
defaultSubjectOption.value = '';
|
||||
defaultSubjectOption.textContent = LanguageManager.t('absences.all_subjects');
|
||||
subjectSelect.appendChild(defaultSubjectOption);
|
||||
|
||||
const subjects = [...new Set(absences.map(a => a.subject))].sort();
|
||||
subjects.forEach(subject => {
|
||||
const option = document.createElement('option');
|
||||
option.value = subject;
|
||||
option.textContent = subject;
|
||||
subjectSelect.appendChild(option);
|
||||
});
|
||||
|
||||
subjectGroup.appendChild(subjectLabel);
|
||||
subjectGroup.appendChild(subjectSelect);
|
||||
filterContent.appendChild(subjectGroup);
|
||||
|
||||
const justificationGroup = document.createElement('div');
|
||||
justificationGroup.className = 'filter-group';
|
||||
|
||||
const justificationLabel = document.createElement('label');
|
||||
const justificationImg = document.createElement('img');
|
||||
justificationImg.src = chrome.runtime.getURL('icons/BadgeCheck.svg');
|
||||
justificationImg.alt = 'Igazolás';
|
||||
justificationLabel.appendChild(justificationImg);
|
||||
justificationLabel.appendChild(document.createTextNode(' ' + LanguageManager.t('absences.justification')));
|
||||
|
||||
const justificationSelect = document.createElement('select');
|
||||
justificationSelect.id = 'justificationFilter';
|
||||
justificationSelect.className = 'filter-input';
|
||||
|
||||
const justificationOptions = [
|
||||
{ value: '', text: LanguageManager.t('absences.all_types') },
|
||||
{ value: 'justified', text: LanguageManager.t('absences.justified') },
|
||||
{ value: 'unjustified', text: LanguageManager.t('absences.unjustified') },
|
||||
{ value: 'pending', text: LanguageManager.t('absences.pending') }
|
||||
];
|
||||
|
||||
justificationOptions.forEach(optionData => {
|
||||
const option = document.createElement('option');
|
||||
option.value = optionData.value;
|
||||
option.textContent = optionData.text;
|
||||
justificationSelect.appendChild(option);
|
||||
});
|
||||
|
||||
justificationGroup.appendChild(justificationLabel);
|
||||
justificationGroup.appendChild(justificationSelect);
|
||||
filterContent.appendChild(justificationGroup);
|
||||
|
||||
filterCard.appendChild(filterHeader);
|
||||
filterCard.appendChild(filterContent);
|
||||
|
||||
return filterCard;
|
||||
}
|
||||
|
||||
function createStatsSection(absences) {
|
||||
const statsSection = document.createElement('div');
|
||||
statsSection.className = 'stats-section';
|
||||
|
||||
|
||||
const statsTitle = document.createElement('h2');
|
||||
statsTitle.className = 'stats-title';
|
||||
statsTitle.textContent = LanguageManager.t('absences.title');
|
||||
statsSection.appendChild(statsTitle);
|
||||
|
||||
const statsGrid = document.createElement('div');
|
||||
statsGrid.className = 'stats-grid';
|
||||
|
||||
|
||||
const totalAbsences = absences.filter(a => a.absenceType === 'absence').length;
|
||||
const totalLates = absences.filter(a => a.absenceType === 'late').length;
|
||||
const justified = absences.filter(a => a.justificationStatus === 'justified').length;
|
||||
const unjustified = absences.filter(a => a.justificationStatus === 'unjustified').length;
|
||||
const pending = absences.filter(a => a.justificationStatus === 'pending').length;
|
||||
|
||||
const stats = [
|
||||
{
|
||||
type: 'total',
|
||||
number: absences.length,
|
||||
label: LanguageManager.t('absences.total_absences')
|
||||
},
|
||||
{
|
||||
type: 'justified',
|
||||
number: absences.filter(a => a.justificationStatus === 'justified').length,
|
||||
label: LanguageManager.t('absences.justified')
|
||||
},
|
||||
{
|
||||
type: 'unjustified',
|
||||
number: absences.filter(a => a.justificationStatus === 'unjustified').length,
|
||||
label: LanguageManager.t('absences.unjustified')
|
||||
},
|
||||
{
|
||||
type: 'pending',
|
||||
number: absences.filter(a => a.justificationStatus === 'pending').length,
|
||||
label: LanguageManager.t('absences.pending')
|
||||
}
|
||||
{ type: 'total', number: absences.length, label: LanguageManager.t('absences.total_absences') },
|
||||
{ type: 'absence', number: totalAbsences, label: LanguageManager.t('absences.absence_type') },
|
||||
{ type: 'late', number: totalLates, label: LanguageManager.t('absences.late_type') },
|
||||
{ type: 'justified', number: justified, label: LanguageManager.t('absences.justified') },
|
||||
{ type: 'unjustified', number: unjustified, label: LanguageManager.t('absences.unjustified') },
|
||||
{ type: 'pending', number: pending, label: LanguageManager.t('absences.pending') },
|
||||
];
|
||||
|
||||
|
||||
stats.forEach(stat => {
|
||||
const statCard = document.createElement('div');
|
||||
statCard.className = `stat-card ${stat.type}`;
|
||||
statCard.dataset.type = stat.type;
|
||||
|
||||
|
||||
const statNumber = document.createElement('div');
|
||||
statNumber.className = 'stat-number';
|
||||
statNumber.textContent = stat.number;
|
||||
|
||||
|
||||
const statLabel = document.createElement('div');
|
||||
statLabel.className = 'stat-label';
|
||||
statLabel.textContent = stat.label;
|
||||
|
||||
|
||||
statCard.appendChild(statNumber);
|
||||
statCard.appendChild(statLabel);
|
||||
statsGrid.appendChild(statCard);
|
||||
});
|
||||
|
||||
|
||||
statsSection.appendChild(statsGrid);
|
||||
return statsSection;
|
||||
}
|
||||
|
||||
function createDayGroup(date, dayAbsences) {
|
||||
const dayGroup = document.createElement('div');
|
||||
dayGroup.className = 'day-group';
|
||||
dayGroup.dataset.date = date;
|
||||
|
||||
|
||||
const dayHeader = document.createElement('div');
|
||||
dayHeader.className = 'day-header';
|
||||
|
||||
const dayDate = document.createElement('div');
|
||||
dayDate.className = 'day-date';
|
||||
|
||||
const calendarIcon = document.createElement('img');
|
||||
calendarIcon.src = chrome.runtime.getURL('icons/Calendar.svg');
|
||||
calendarIcon.alt = 'Dátum';
|
||||
|
||||
const dateText = document.createElement('span');
|
||||
dateText.textContent = formatDateWithDay(date);
|
||||
|
||||
dayDate.appendChild(calendarIcon);
|
||||
dayDate.appendChild(dateText);
|
||||
|
||||
const dayCount = document.createElement('div');
|
||||
dayCount.className = 'day-count';
|
||||
dayCount.textContent = `${dayAbsences.length} ${LanguageManager.t('absences.hours')}`;
|
||||
|
||||
dayHeader.appendChild(dayDate);
|
||||
dayHeader.appendChild(dayCount);
|
||||
|
||||
const dayAbsencesContainer = document.createElement('div');
|
||||
dayAbsencesContainer.className = 'day-absences';
|
||||
|
||||
dayAbsences.forEach(absence => {
|
||||
const absenceCard = createAbsenceCard(absence);
|
||||
dayAbsencesContainer.appendChild(absenceCard);
|
||||
});
|
||||
|
||||
dayGroup.appendChild(dayHeader);
|
||||
dayGroup.appendChild(dayAbsencesContainer);
|
||||
|
||||
return dayGroup;
|
||||
function getSchoolYearStart() {
|
||||
const now = new Date();
|
||||
const year = now.getMonth() >= 8 ? now.getFullYear() : now.getFullYear() - 1;
|
||||
return new Date(year, 8, 1);
|
||||
}
|
||||
|
||||
function formatDateWithDay(dateStr) {
|
||||
const parts = dateStr.split('.');
|
||||
const year = parseInt(parts[0]);
|
||||
const month = parseInt(parts[1]) - 1;
|
||||
const day = parseInt(parts[2]);
|
||||
|
||||
const date = new Date(year, month, day);
|
||||
const days = [
|
||||
LanguageManager.t('common.sunday'),
|
||||
LanguageManager.t('common.monday'),
|
||||
LanguageManager.t('common.tuesday'),
|
||||
LanguageManager.t('common.wednesday'),
|
||||
LanguageManager.t('common.thursday'),
|
||||
LanguageManager.t('common.friday'),
|
||||
LanguageManager.t('common.saturday')
|
||||
];
|
||||
|
||||
const dayName = days[date.getDay()];
|
||||
return `${dateStr} - ${dayName.charAt(0).toUpperCase() + dayName.slice(1)}`;
|
||||
}
|
||||
function createCalendarSection(groupedByDate, absences) {
|
||||
const calendarSection = document.createElement('div');
|
||||
calendarSection.className = 'calendar-section';
|
||||
|
||||
function createAbsenceCard(absence) {
|
||||
const card = document.createElement('div');
|
||||
card.className = 'absence-card';
|
||||
card.dataset.subject = absence.subject;
|
||||
card.dataset.status = absence.justificationStatus;
|
||||
card.dataset.date = absence.date;
|
||||
const schoolYearStart = getSchoolYearStart();
|
||||
const now = new Date();
|
||||
|
||||
const lessonDiv = document.createElement('div');
|
||||
lessonDiv.className = 'absence-lesson';
|
||||
lessonDiv.textContent = absence.lesson + '.';
|
||||
const months = [];
|
||||
let currentDate = new Date(schoolYearStart);
|
||||
|
||||
const subjectDiv = document.createElement('div');
|
||||
subjectDiv.className = 'absence-subject';
|
||||
subjectDiv.textContent = absence.subject;
|
||||
|
||||
const topicDiv = document.createElement('div');
|
||||
topicDiv.className = 'absence-topic';
|
||||
topicDiv.textContent = absence.topic || '-';
|
||||
topicDiv.title = absence.topic;
|
||||
|
||||
const statusDiv = document.createElement('div');
|
||||
statusDiv.className = 'absence-status';
|
||||
|
||||
const statusBadge = document.createElement('span');
|
||||
statusBadge.className = `status-badge ${absence.justificationStatus}`;
|
||||
|
||||
if (absence.justificationStatus === 'justified') {
|
||||
const img = document.createElement('img');
|
||||
img.src = chrome.runtime.getURL('icons/BadgeCheck.svg');
|
||||
img.alt = 'Igazolt';
|
||||
statusBadge.appendChild(img);
|
||||
statusBadge.appendChild(document.createTextNode(' ' + LanguageManager.t('absences.justified')));
|
||||
} else if (absence.justificationStatus === 'unjustified') {
|
||||
const span = document.createElement('span');
|
||||
span.className = 'material-icons-round';
|
||||
span.textContent = 'cancel';
|
||||
statusBadge.appendChild(span);
|
||||
statusBadge.appendChild(document.createTextNode(' ' + LanguageManager.t('absences.unjustified')));
|
||||
} else {
|
||||
const img = document.createElement('img');
|
||||
img.src = chrome.runtime.getURL('icons/pending.svg');
|
||||
img.alt = 'Függőben';
|
||||
statusBadge.appendChild(img);
|
||||
statusBadge.appendChild(document.createTextNode(' ' + LanguageManager.t('absences.pending')));
|
||||
}
|
||||
|
||||
statusDiv.appendChild(statusBadge);
|
||||
|
||||
card.appendChild(lessonDiv);
|
||||
card.appendChild(subjectDiv);
|
||||
card.appendChild(topicDiv);
|
||||
card.appendChild(statusDiv);
|
||||
|
||||
return card;
|
||||
}
|
||||
|
||||
function createAbsencesContent(groupedAbsences) {
|
||||
const content = document.createElement('div');
|
||||
content.className = 'absences-content';
|
||||
|
||||
const sortedDates = Object.keys(groupedAbsences).sort((a, b) => {
|
||||
const dateA = new Date(a.replace(/\./g, '-').slice(0, -1));
|
||||
const dateB = new Date(b.replace(/\./g, '-').slice(0, -1));
|
||||
return dateB - dateA;
|
||||
});
|
||||
|
||||
if (sortedDates.length === 0) {
|
||||
const emptyState = document.createElement('div');
|
||||
emptyState.className = 'empty-state';
|
||||
|
||||
const emptyIcon = document.createElement('img');
|
||||
emptyIcon.src = chrome.runtime.getURL('icons/BadgeCheck.svg');
|
||||
emptyIcon.alt = 'Nincs hiányzás';
|
||||
|
||||
const emptyTitle = document.createElement('h3');
|
||||
emptyTitle.textContent = LanguageManager.t('absences.title');
|
||||
|
||||
const emptyText = document.createElement('p');
|
||||
emptyText.textContent = LanguageManager.t('dashboard.not_supported');
|
||||
|
||||
emptyState.appendChild(emptyIcon);
|
||||
emptyState.appendChild(emptyTitle);
|
||||
emptyState.appendChild(emptyText);
|
||||
content.appendChild(emptyState);
|
||||
} else {
|
||||
sortedDates.forEach(date => {
|
||||
const dayGroup = createDayGroup(date, groupedAbsences[date]);
|
||||
content.appendChild(dayGroup);
|
||||
while (currentDate <= now) {
|
||||
months.push({
|
||||
year: currentDate.getFullYear(),
|
||||
month: currentDate.getMonth()
|
||||
});
|
||||
currentDate.setMonth(currentDate.getMonth() + 1);
|
||||
}
|
||||
|
||||
return content;
|
||||
|
||||
months.forEach(({ year, month }) => {
|
||||
const monthCalendar = createMonthCalendar(year, month, groupedByDate, absences);
|
||||
calendarSection.appendChild(monthCalendar);
|
||||
});
|
||||
|
||||
return calendarSection;
|
||||
}
|
||||
|
||||
function createMonthCalendar(year, month, groupedByDate, absences) {
|
||||
const monthContainer = document.createElement('div');
|
||||
monthContainer.className = 'month-container';
|
||||
|
||||
const monthHeader = document.createElement('div');
|
||||
monthHeader.className = 'month-header';
|
||||
|
||||
const monthNames = [
|
||||
LanguageManager.t('common.january') || 'Január',
|
||||
LanguageManager.t('common.february') || 'Február',
|
||||
LanguageManager.t('common.march') || 'Március',
|
||||
LanguageManager.t('common.april') || 'Április',
|
||||
LanguageManager.t('common.may') || 'Május',
|
||||
LanguageManager.t('common.june') || 'Június',
|
||||
LanguageManager.t('common.july') || 'Július',
|
||||
LanguageManager.t('common.august') || 'Augusztus',
|
||||
LanguageManager.t('common.september') || 'Szeptember',
|
||||
LanguageManager.t('common.october') || 'Október',
|
||||
LanguageManager.t('common.november') || 'November',
|
||||
LanguageManager.t('common.december') || 'December'
|
||||
];
|
||||
|
||||
monthHeader.textContent = `${monthNames[month]} ${year}`;
|
||||
monthContainer.appendChild(monthHeader);
|
||||
|
||||
const calendarGrid = document.createElement('div');
|
||||
calendarGrid.className = 'calendar-grid';
|
||||
|
||||
const dayNames = [
|
||||
LanguageManager.t('common.mon') || 'H',
|
||||
LanguageManager.t('common.tue') || 'K',
|
||||
LanguageManager.t('common.wed') || 'Sze',
|
||||
LanguageManager.t('common.thu') || 'Cs',
|
||||
LanguageManager.t('common.fri') || 'P',
|
||||
LanguageManager.t('common.sat') || 'Szo',
|
||||
LanguageManager.t('common.sun') || 'V'
|
||||
];
|
||||
|
||||
dayNames.forEach(day => {
|
||||
const dayHeader = document.createElement('div');
|
||||
dayHeader.className = 'calendar-day-header';
|
||||
dayHeader.textContent = day;
|
||||
calendarGrid.appendChild(dayHeader);
|
||||
});
|
||||
|
||||
const firstDay = new Date(year, month, 1);
|
||||
const lastDay = new Date(year, month + 1, 0);
|
||||
const daysInMonth = lastDay.getDate();
|
||||
|
||||
let startDay = firstDay.getDay() - 1;
|
||||
if (startDay < 0) startDay = 6;
|
||||
|
||||
for (let i = 0; i < startDay; i++) {
|
||||
const emptyCell = document.createElement('div');
|
||||
emptyCell.className = 'calendar-day empty';
|
||||
calendarGrid.appendChild(emptyCell);
|
||||
}
|
||||
|
||||
const today = new Date();
|
||||
today.setHours(0, 0, 0, 0);
|
||||
|
||||
for (let day = 1; day <= daysInMonth; day++) {
|
||||
const dateKey = `${year}-${(month + 1).toString().padStart(2, "0")}-${day.toString().padStart(2, "0")}`;
|
||||
const dayAbsences = groupedByDate[dateKey] || [];
|
||||
const currentDayDate = new Date(year, month, day);
|
||||
currentDayDate.setHours(0, 0, 0, 0);
|
||||
|
||||
const dayCell = document.createElement('div');
|
||||
dayCell.className = 'calendar-day';
|
||||
|
||||
if (currentDayDate > today) {
|
||||
dayCell.classList.add('future');
|
||||
}
|
||||
|
||||
if (currentDayDate.getTime() === today.getTime()) {
|
||||
dayCell.classList.add('today');
|
||||
}
|
||||
|
||||
if (dayAbsences.length > 0) {
|
||||
dayCell.classList.add('has-absence');
|
||||
|
||||
const hasUnjustified = dayAbsences.some(a => a.justificationStatus === 'unjustified');
|
||||
const hasPending = dayAbsences.some(a => a.justificationStatus === 'pending');
|
||||
const hasJustified = dayAbsences.some(a => a.justificationStatus === 'justified');
|
||||
const hasAbsence = dayAbsences.some(a => a.absenceType === 'absence');
|
||||
const hasLate = dayAbsences.some(a => a.absenceType === 'late');
|
||||
|
||||
if (hasUnjustified) {
|
||||
dayCell.classList.add('status-unjustified');
|
||||
} else if (hasPending) {
|
||||
dayCell.classList.add('status-pending');
|
||||
} else if (hasJustified) {
|
||||
dayCell.classList.add('status-justified');
|
||||
}
|
||||
|
||||
if (hasAbsence && hasLate) {
|
||||
dayCell.classList.add('type-mixed');
|
||||
} else if (hasLate) {
|
||||
dayCell.classList.add('type-late');
|
||||
}
|
||||
|
||||
dayCell.addEventListener('click', () => {
|
||||
openAbsenceModal(dateKey, dayAbsences);
|
||||
});
|
||||
|
||||
const countBadge = document.createElement('span');
|
||||
countBadge.className = 'absence-count';
|
||||
countBadge.textContent = dayAbsences.length;
|
||||
dayCell.appendChild(countBadge);
|
||||
}
|
||||
|
||||
const dayNumber = document.createElement('span');
|
||||
dayNumber.className = 'day-number';
|
||||
dayNumber.textContent = day;
|
||||
dayCell.appendChild(dayNumber);
|
||||
|
||||
calendarGrid.appendChild(dayCell);
|
||||
}
|
||||
|
||||
monthContainer.appendChild(calendarGrid);
|
||||
return monthContainer;
|
||||
}
|
||||
|
||||
function openAbsenceModal(dateKey, dayAbsences) {
|
||||
const existingModal = document.querySelector('.absence-modal-overlay');
|
||||
if (existingModal) {
|
||||
existingModal.remove();
|
||||
}
|
||||
|
||||
const overlay = document.createElement('div');
|
||||
overlay.className = 'absence-modal-overlay';
|
||||
|
||||
const modal = document.createElement('div');
|
||||
modal.className = 'absence-modal';
|
||||
|
||||
const modalHeader = document.createElement('div');
|
||||
modalHeader.className = 'modal-header';
|
||||
|
||||
const dateParts = dateKey.split('-');
|
||||
const displayDate = new Date(parseInt(dateParts[0]), parseInt(dateParts[1]) - 1, parseInt(dateParts[2]));
|
||||
const dayNames = [
|
||||
LanguageManager.t('common.sunday') || 'Vasárnap',
|
||||
LanguageManager.t('common.monday') || 'Hétfő',
|
||||
LanguageManager.t('common.tuesday') || 'Kedd',
|
||||
LanguageManager.t('common.wednesday') || 'Szerda',
|
||||
LanguageManager.t('common.thursday') || 'Csütörtök',
|
||||
LanguageManager.t('common.friday') || 'Péntek',
|
||||
LanguageManager.t('common.saturday') || 'Szombat'
|
||||
];
|
||||
|
||||
const modalTitle = document.createElement('h2');
|
||||
modalTitle.textContent = `${dateParts[0]}.${dateParts[1]}.${dateParts[2]}. - ${dayNames[displayDate.getDay()]}`;
|
||||
|
||||
modalHeader.appendChild(modalTitle);
|
||||
|
||||
const modalContent = document.createElement('div');
|
||||
modalContent.className = 'modal-content';
|
||||
|
||||
dayAbsences.forEach(absence => {
|
||||
const absenceCard = document.createElement('div');
|
||||
absenceCard.className = `modal-absence-card ${absence.justificationStatus}`;
|
||||
|
||||
const headerRow = document.createElement('div');
|
||||
headerRow.className = 'modal-card-header';
|
||||
|
||||
const lessonSpan = document.createElement('span');
|
||||
lessonSpan.className = 'modal-lesson';
|
||||
lessonSpan.textContent = `${absence.lesson}.`;
|
||||
|
||||
const typeIndicator = document.createElement('span');
|
||||
typeIndicator.className = `type-indicator ${absence.absenceType}`;
|
||||
typeIndicator.textContent = absence.absenceType === 'late'
|
||||
? (LanguageManager.t('absences.late_type') || 'Késés')
|
||||
: (LanguageManager.t('absences.absence_type') || 'Hiányzás');
|
||||
|
||||
const statusBadge = document.createElement('span');
|
||||
statusBadge.className = `modal-status-badge ${absence.justificationStatus}`;
|
||||
let statusText = '';
|
||||
if (absence.justificationStatus === 'justified') {
|
||||
statusText = LanguageManager.t('absences.justified') || 'Igazolt';
|
||||
} else if (absence.justificationStatus === 'unjustified') {
|
||||
statusText = LanguageManager.t('absences.unjustified') || 'Igazolatlan';
|
||||
} else {
|
||||
statusText = LanguageManager.t('absences.pending') || 'Igazolásra vár';
|
||||
}
|
||||
statusBadge.textContent = statusText;
|
||||
|
||||
headerRow.appendChild(lessonSpan);
|
||||
headerRow.appendChild(typeIndicator);
|
||||
headerRow.appendChild(statusBadge);
|
||||
|
||||
const subjectDiv = document.createElement('div');
|
||||
subjectDiv.className = 'modal-subject';
|
||||
subjectDiv.textContent = absence.subject;
|
||||
|
||||
const infoRow = document.createElement('div');
|
||||
infoRow.className = 'modal-info-row';
|
||||
|
||||
if (absence.topic) {
|
||||
const topicSpan = document.createElement('span');
|
||||
topicSpan.className = 'modal-info-item';
|
||||
topicSpan.textContent = absence.topic;
|
||||
infoRow.appendChild(topicSpan);
|
||||
}
|
||||
|
||||
if (absence.justificationType) {
|
||||
const justificationSpan = document.createElement('span');
|
||||
justificationSpan.className = 'modal-info-item';
|
||||
justificationSpan.textContent = absence.justificationType;
|
||||
infoRow.appendChild(justificationSpan);
|
||||
}
|
||||
|
||||
if (absence.absenceType === 'late' && absence.minutes > 0) {
|
||||
const minutesSpan = document.createElement('span');
|
||||
minutesSpan.className = 'modal-info-item modal-minutes';
|
||||
minutesSpan.textContent = `${absence.minutes} ${LanguageManager.t('absences.minutes') || 'perc'}`;
|
||||
infoRow.appendChild(minutesSpan);
|
||||
}
|
||||
|
||||
absenceCard.appendChild(headerRow);
|
||||
absenceCard.appendChild(subjectDiv);
|
||||
if (infoRow.children.length > 0) {
|
||||
absenceCard.appendChild(infoRow);
|
||||
}
|
||||
|
||||
modalContent.appendChild(absenceCard);
|
||||
});
|
||||
|
||||
modal.appendChild(modalHeader);
|
||||
modal.appendChild(modalContent);
|
||||
overlay.appendChild(modal);
|
||||
|
||||
overlay.addEventListener('click', (e) => {
|
||||
if (e.target === overlay) {
|
||||
overlay.remove();
|
||||
}
|
||||
});
|
||||
|
||||
const handleEscape = (e) => {
|
||||
if (e.key === 'Escape') {
|
||||
overlay.remove();
|
||||
document.removeEventListener('keydown', handleEscape);
|
||||
}
|
||||
};
|
||||
document.addEventListener('keydown', handleEscape);
|
||||
|
||||
document.body.appendChild(overlay);
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
overlay.classList.add('visible');
|
||||
});
|
||||
}
|
||||
|
||||
function createLegend() {
|
||||
const legend = document.createElement('div');
|
||||
legend.className = 'calendar-legend';
|
||||
|
||||
const legendItems = [
|
||||
{ class: 'status-justified', label: LanguageManager.t('absences.justified') || 'Igazolt' },
|
||||
{ class: 'status-pending', label: LanguageManager.t('absences.pending') || 'Igazolásra vár' },
|
||||
{ class: 'status-unjustified', label: LanguageManager.t('absences.unjustified') || 'Igazolatlan' },
|
||||
{ class: 'type-late-legend', label: LanguageManager.t('absences.late_type') || 'Késés', isType: true },
|
||||
];
|
||||
|
||||
legendItems.forEach(item => {
|
||||
const legendItem = document.createElement('div');
|
||||
legendItem.className = 'legend-item';
|
||||
|
||||
const legendColor = document.createElement('div');
|
||||
legendColor.className = `legend-color ${item.class}`;
|
||||
|
||||
const legendLabel = document.createElement('span');
|
||||
legendLabel.textContent = item.label;
|
||||
|
||||
legendItem.appendChild(legendColor);
|
||||
legendItem.appendChild(legendLabel);
|
||||
legend.appendChild(legendItem);
|
||||
});
|
||||
|
||||
return legend;
|
||||
}
|
||||
|
||||
async function transformAbsencesPage() {
|
||||
const { basicData, absences, groupedAbsences } = await collectAbsencesData();
|
||||
|
||||
const { basicData, absences, groupedByDate } = await collectAbsencesData();
|
||||
|
||||
document.body.textContent = '';
|
||||
|
||||
|
||||
const container = document.createElement('div');
|
||||
container.className = 'kreta-container';
|
||||
|
||||
const headerDiv = document.createElement('div');
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(await createTemplate.header(), 'text/html');
|
||||
const tempDiv = doc.body;
|
||||
const template = document.createElement('template');
|
||||
template.innerHTML = await createTemplate.header();
|
||||
const tempDiv = template.content;
|
||||
while (tempDiv.firstChild) {
|
||||
headerDiv.appendChild(tempDiv.firstChild);
|
||||
}
|
||||
container.appendChild(headerDiv);
|
||||
|
||||
|
||||
const main = document.createElement('main');
|
||||
main.className = 'kreta-main';
|
||||
|
||||
const pageGrid = document.createElement('div');
|
||||
pageGrid.className = 'absences-page';
|
||||
|
||||
const sidebar = document.createElement('div');
|
||||
sidebar.className = 'absences-sidebar';
|
||||
const filterCard = createFilterCard(absences);
|
||||
sidebar.appendChild(filterCard);
|
||||
|
||||
|
||||
const pageContent = document.createElement('div');
|
||||
pageContent.className = 'absences-page';
|
||||
|
||||
const statsSection = createStatsSection(absences);
|
||||
sidebar.appendChild(statsSection);
|
||||
|
||||
const absencesContent = createAbsencesContent(groupedAbsences);
|
||||
|
||||
pageGrid.appendChild(sidebar);
|
||||
pageGrid.appendChild(absencesContent);
|
||||
|
||||
main.appendChild(pageGrid);
|
||||
pageContent.appendChild(statsSection);
|
||||
|
||||
const legend = createLegend();
|
||||
pageContent.appendChild(legend);
|
||||
|
||||
const calendarSection = createCalendarSection(groupedByDate, absences);
|
||||
pageContent.appendChild(calendarSection);
|
||||
|
||||
main.appendChild(pageContent);
|
||||
container.appendChild(main);
|
||||
document.body.appendChild(container);
|
||||
|
||||
setupUserDropdown();
|
||||
setupFilters(groupedAbsences);
|
||||
|
||||
loadingScreen.hide();
|
||||
}
|
||||
|
||||
function setupFilters(originalGroupedAbsences) {
|
||||
try {
|
||||
const dateFilter = document.getElementById("dateFilter");
|
||||
const subjectFilter = document.getElementById("subjectFilter");
|
||||
const justificationFilter = document.getElementById("justificationFilter");
|
||||
|
||||
if (!dateFilter || !subjectFilter || !justificationFilter) {
|
||||
console.warn("Some filter elements were not found in the DOM");
|
||||
return;
|
||||
}
|
||||
|
||||
const filterAbsences = () => {
|
||||
try {
|
||||
const dateFilterValue = dateFilter.value;
|
||||
const subject = subjectFilter.value;
|
||||
const justified = justificationFilter.value;
|
||||
const selectedDate = dateFilterValue ? new Date(dateFilterValue) : null;
|
||||
|
||||
document.querySelectorAll(".absence-card").forEach((card) => {
|
||||
const dateStr = card.dataset.date;
|
||||
const dateParts = dateStr.split(".");
|
||||
|
||||
if (dateParts.length < 3) {
|
||||
return;
|
||||
}
|
||||
|
||||
const parsedYear = parseInt(dateParts[0].trim(), 10);
|
||||
const parsedMonth = parseInt(dateParts[1].trim(), 10) - 1;
|
||||
const parsedDay = parseInt(dateParts[2].trim(), 10);
|
||||
|
||||
const cardDate = new Date(parsedYear, parsedMonth, parsedDay);
|
||||
|
||||
let showCard = true;
|
||||
|
||||
if (selectedDate) {
|
||||
if (
|
||||
cardDate.getFullYear() !== selectedDate.getFullYear() ||
|
||||
cardDate.getMonth() !== selectedDate.getMonth() ||
|
||||
cardDate.getDate() !== selectedDate.getDate()
|
||||
) {
|
||||
showCard = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (subject && card.dataset.subject !== subject) {
|
||||
showCard = false;
|
||||
}
|
||||
|
||||
if (justified && card.dataset.status !== justified) {
|
||||
showCard = false;
|
||||
}
|
||||
|
||||
card.style.display = showCard ? "" : "none";
|
||||
});
|
||||
|
||||
updateDayGroupsVisibility();
|
||||
|
||||
updateStatistics();
|
||||
} catch (err) {
|
||||
console.error("Error during filtering absences:", err);
|
||||
}
|
||||
};
|
||||
|
||||
[dateFilter, subjectFilter, justificationFilter].forEach((filter) => {
|
||||
if (filter) {
|
||||
filter.addEventListener("change", filterAbsences);
|
||||
}
|
||||
});
|
||||
|
||||
} catch (err) {
|
||||
console.error("Error setting up filters:", err);
|
||||
}
|
||||
}
|
||||
|
||||
function updateDayGroupsVisibility() {
|
||||
document.querySelectorAll(".day-group").forEach((group) => {
|
||||
const visibleCards = group.querySelectorAll('.absence-card:not([style*="display: none"])');
|
||||
const dayCount = group.querySelector('.day-count');
|
||||
|
||||
if (visibleCards.length > 0) {
|
||||
group.style.display = "";
|
||||
if (dayCount) {
|
||||
dayCount.textContent = `${visibleCards.length} ${LanguageManager.t('absences.hours')}`;
|
||||
}
|
||||
} else {
|
||||
group.style.display = "none";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function updateStatistics() {
|
||||
try {
|
||||
const visibleCards = document.querySelectorAll('.absence-card:not([style*="display: none"])');
|
||||
const totalVisible = visibleCards.length;
|
||||
const justifiedVisible = Array.from(visibleCards).filter(
|
||||
card => card.dataset.status === 'justified'
|
||||
).length;
|
||||
const unjustifiedVisible = Array.from(visibleCards).filter(
|
||||
card => card.dataset.status === 'unjustified'
|
||||
).length;
|
||||
const pendingVisible = Array.from(visibleCards).filter(
|
||||
card => card.dataset.status === 'pending'
|
||||
).length;
|
||||
|
||||
const statCards = document.querySelectorAll(".stat-card");
|
||||
statCards.forEach(card => {
|
||||
const type = card.dataset.type;
|
||||
const numberEl = card.querySelector('.stat-number');
|
||||
if (numberEl) {
|
||||
switch(type) {
|
||||
case 'total':
|
||||
numberEl.textContent = totalVisible;
|
||||
break;
|
||||
case 'justified':
|
||||
numberEl.textContent = justifiedVisible;
|
||||
break;
|
||||
case 'unjustified':
|
||||
numberEl.textContent = unjustifiedVisible;
|
||||
break;
|
||||
case 'pending':
|
||||
numberEl.textContent = pendingVisible;
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
console.error("Error updating statistics:", err);
|
||||
}
|
||||
}
|
||||
|
||||
if (window.location.href.includes("/Hianyzas/Hianyzasok")) {
|
||||
transformAbsencesPage().catch((error) => {
|
||||
console.error(LanguageManager.t("absences.page_transform_error"), error);
|
||||
|
||||
@@ -37,6 +37,8 @@ h2 {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr));
|
||||
gap: 20px;
|
||||
justify-content: center;
|
||||
justify-items: center;
|
||||
}
|
||||
|
||||
.widget-card {
|
||||
@@ -50,6 +52,7 @@ h2 {
|
||||
height: 100%;
|
||||
border: none;
|
||||
min-height: 400px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.widget-header {
|
||||
@@ -222,7 +225,6 @@ h2 {
|
||||
max-width: 200px;
|
||||
}
|
||||
|
||||
/* Mobile view: author below content */
|
||||
@media (max-width: 768px) {
|
||||
.news-item .widget-row {
|
||||
flex-direction: column;
|
||||
@@ -257,7 +259,6 @@ h2 {
|
||||
}
|
||||
}
|
||||
|
||||
/* Desktop view: limit author width and wrap text */
|
||||
@media (min-width: 769px) {
|
||||
.news-author {
|
||||
max-width: 180px;
|
||||
|
||||
@@ -291,8 +291,9 @@ class DashboardDataManager {
|
||||
}
|
||||
|
||||
class DashboardRenderer {
|
||||
constructor(data) {
|
||||
constructor(data, settings = {}) {
|
||||
this.baseData = data;
|
||||
this.settings = settings;
|
||||
}
|
||||
|
||||
async init() {
|
||||
@@ -315,14 +316,31 @@ class DashboardRenderer {
|
||||
}
|
||||
|
||||
generateMainContent() {
|
||||
const cards = [];
|
||||
|
||||
if (!this.settings.hideGrades) {
|
||||
cards.push(this.createGradeCard());
|
||||
}
|
||||
if (!this.settings.hideAbsences) {
|
||||
cards.push(this.createAbsenceCard());
|
||||
}
|
||||
if (!this.settings.hideNotes) {
|
||||
cards.push(this.createNoteCard());
|
||||
}
|
||||
if (!this.settings.hideExams) {
|
||||
cards.push(this.createExamCard());
|
||||
}
|
||||
|
||||
cards.push(this.createNewsCard());
|
||||
|
||||
if (cards.length === 1) {
|
||||
cards.unshift(this.createGradeCard());
|
||||
}
|
||||
|
||||
return `
|
||||
<main class="kreta-main">
|
||||
<div class="grid-container">
|
||||
${this.createGradeCard()}
|
||||
${this.createAbsenceCard()}
|
||||
${this.createNoteCard()}
|
||||
${this.createExamCard()}
|
||||
${this.createNewsCard()}
|
||||
${cards.join('')}
|
||||
</div>
|
||||
</main>
|
||||
`;
|
||||
@@ -481,22 +499,22 @@ class DashboardRenderer {
|
||||
|
||||
async render() {
|
||||
await this.init();
|
||||
document.body.innerHTML = '';
|
||||
helper.clearElement(document.body);
|
||||
|
||||
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;
|
||||
const template = document.createElement('template');
|
||||
template.innerHTML = await createTemplate.header();
|
||||
const headerContent = template.content;
|
||||
while (headerContent.firstChild) {
|
||||
headerDiv.appendChild(headerContent.firstChild);
|
||||
}
|
||||
kretaContainer.appendChild(headerDiv);
|
||||
const mainContentDiv = document.createElement('div');
|
||||
const parser2 = new DOMParser();
|
||||
const mainDoc = parser2.parseFromString(this.generateMainContent(), 'text/html');
|
||||
const mainContent = mainDoc.body;
|
||||
const template2 = document.createElement('template');
|
||||
template2.innerHTML = this.generateMainContent();
|
||||
const mainContent = template2.content;
|
||||
while (mainContent.firstChild) {
|
||||
mainContentDiv.appendChild(mainContent.firstChild);
|
||||
}
|
||||
@@ -513,6 +531,26 @@ class DashboardApplication {
|
||||
this.init();
|
||||
}
|
||||
|
||||
async loadBulletinSettings() {
|
||||
try {
|
||||
const settings = await storageManager.get("pageSettings_bulletin", {});
|
||||
return {
|
||||
hideGrades: settings.hideGrades || false,
|
||||
hideAbsences: settings.hideAbsences || false,
|
||||
hideNotes: settings.hideNotes || false,
|
||||
hideExams: settings.hideExams || false
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error loading bulletin settings:", error);
|
||||
return {
|
||||
hideGrades: false,
|
||||
hideAbsences: false,
|
||||
hideNotes: false,
|
||||
hideExams: false
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async init() {
|
||||
if (!window.location.href.includes("/Intezmeny/Faliujsag")) {
|
||||
return;
|
||||
@@ -575,8 +613,9 @@ class DashboardApplication {
|
||||
try {
|
||||
const dataManager = new DashboardDataManager();
|
||||
const dashboardData = await dataManager.extractAllData();
|
||||
const bulletinSettings = await this.loadBulletinSettings();
|
||||
|
||||
const renderer = new DashboardRenderer(dashboardData);
|
||||
const renderer = new DashboardRenderer(dashboardData, bulletinSettings);
|
||||
await renderer.render();
|
||||
} catch (error) {
|
||||
console.error("Error initializing dashboard:", error);
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
body.maintenance-mode {
|
||||
margin:0;
|
||||
padding:0;
|
||||
height:100vh;
|
||||
display:flex;
|
||||
align-items:center;
|
||||
justify-content:center;
|
||||
background-color:var(--background);
|
||||
color:var(--text-primary);
|
||||
font-family:'Figtree',sans-serif;
|
||||
}
|
||||
body {
|
||||
background-color:var(--background) !important;
|
||||
}
|
||||
.maintenance-container {
|
||||
text-align:center;
|
||||
padding:2rem;
|
||||
border-radius:8px;
|
||||
background-color:var(--card-card);
|
||||
box-shadow:0 var(--shadow-blur) 6px var(--accent-shadow);
|
||||
max-width:600px;
|
||||
width:90%;
|
||||
}
|
||||
.maintenance-logo {
|
||||
width:128px;
|
||||
height:128px;
|
||||
margin:0 auto 2rem;
|
||||
}
|
||||
.maintenance-title {
|
||||
font-size:1.5rem;
|
||||
font-weight:600;
|
||||
margin-bottom:1rem;
|
||||
color:var(--accent-accent);
|
||||
font-family:'Montserrat',sans-serif;
|
||||
}
|
||||
.maintenance-message {
|
||||
font-size:1rem;
|
||||
line-height:1.5;
|
||||
margin-bottom:1.5rem;
|
||||
color:var(--text-primary);
|
||||
font-family:'Figtree',sans-serif;
|
||||
}
|
||||
.maintenance-footer {
|
||||
font-size:0.875rem;
|
||||
color:var(--text-secondary);
|
||||
margin-top:2rem;
|
||||
font-family:'Figtree',sans-serif;
|
||||
}
|
||||
.maintenance-cactus {
|
||||
position:fixed;
|
||||
bottom:0px;
|
||||
right:20px;
|
||||
width:120px;
|
||||
height:120px;
|
||||
opacity:1;
|
||||
z-index:1000;
|
||||
}
|
||||
@@ -1,290 +0,0 @@
|
||||
(() => {
|
||||
// reCAPTCHA functionality removed for security compliance
|
||||
|
||||
const loadDependencies = async () => {
|
||||
// reCAPTCHA functionality removed for security compliance
|
||||
// Extension now works without external script dependencies
|
||||
};
|
||||
|
||||
const createPageStructure = () => {
|
||||
// Biztonságos DOM létrehozás innerHTML helyett
|
||||
document.body.innerHTML = '';
|
||||
// Biztonságos HTML parsing DOMParser használatával
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(`
|
||||
<div class="forgot-container">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&display=swap" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons+Round" rel="stylesheet">
|
||||
<style>
|
||||
.g-recaptcha {
|
||||
margin-top: 5px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="forgot-card">
|
||||
<header class="forgot-header">
|
||||
<p class="logo-text">
|
||||
<img src="${chrome.runtime.getURL("images/firka_logo.png")}" alt="Firka" class="logo">
|
||||
Firka
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<h1 class="forgot-title" data-i18n="forgotpassword.title">Elfelejtett jelszó</h1>
|
||||
|
||||
<form id="forgotForm" novalidate>
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="BejelentkezesiNev" data-i18n="forgotpassword.om_id_label">OM azonosító</label>
|
||||
<input type="text" id="BejelentkezesiNev" name="BejelentkezesiNev" class="form-control"
|
||||
data-i18n-attr="placeholder" data-i18n="forgotpassword.om_id_placeholder"
|
||||
placeholder="Adja meg az OM azonosítóját" required>
|
||||
<div class="error-message" data-i18n="forgotpassword.om_id_required">Az OM azonosító megadása kötelező</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="EmailCim" data-i18n="forgotpassword.email_label">E-mail cím</label>
|
||||
<input type="email" id="EmailCim" name="EmailCim" class="form-control"
|
||||
data-i18n-attr="placeholder" data-i18n="forgotpassword.email_placeholder"
|
||||
placeholder="Adja meg az e-mail címét" required>
|
||||
<div class="error-message" data-i18n="forgotpassword.email_required">Az e-mail cím megadása kötelező</div>
|
||||
</div>
|
||||
|
||||
<!-- reCAPTCHA container removed for security compliance -->
|
||||
|
||||
<div class="form-actions">
|
||||
<a href="/Adminisztracio/Login" class="help-link" data-i18n="forgotpassword.back_to_login">
|
||||
Vissza a bejelentkezéshez
|
||||
</a>
|
||||
<button type="submit" class="btn-submit" data-i18n="forgotpassword.reset_button">
|
||||
Jelszó visszaállítása
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
`, 'text/html');
|
||||
const tempDiv = doc.body;
|
||||
|
||||
// Biztonságos DOM hozzáadás
|
||||
while (tempDiv.firstChild) {
|
||||
document.body.appendChild(tempDiv.firstChild);
|
||||
}
|
||||
};
|
||||
|
||||
const transformForgotPasswordPage = async () => {
|
||||
await loadDependencies();
|
||||
|
||||
const isDarkMode = localStorage.getItem("darkMode") === "true";
|
||||
document.documentElement.setAttribute(
|
||||
"data-theme",
|
||||
isDarkMode ? "dark" : "light",
|
||||
);
|
||||
|
||||
chrome.runtime.onMessage.addListener((message) => {
|
||||
if (message.action === "toggleTheme") {
|
||||
document.documentElement.setAttribute(
|
||||
"data-theme",
|
||||
message.darkMode ? "dark" : "light",
|
||||
);
|
||||
localStorage.setItem("darkMode", message.darkMode);
|
||||
}
|
||||
});
|
||||
|
||||
createPageStructure();
|
||||
|
||||
let attempts = 0;
|
||||
const maxAttempts = 50;
|
||||
|
||||
const waitForLanguageManager = () => {
|
||||
return new Promise((resolve) => {
|
||||
const checkLanguageManager = () => {
|
||||
attempts++;
|
||||
if (typeof LanguageManager !== "undefined" && LanguageManager.t) {
|
||||
setTimeout(resolve, 200);
|
||||
} else if (attempts < maxAttempts) {
|
||||
setTimeout(checkLanguageManager, 100);
|
||||
} else {
|
||||
console.warn("LanguageManager not available, using fallback texts");
|
||||
resolve();
|
||||
}
|
||||
};
|
||||
checkLanguageManager();
|
||||
});
|
||||
};
|
||||
|
||||
await waitForLanguageManager();
|
||||
|
||||
if (typeof LanguageManager !== "undefined" && LanguageManager.t) {
|
||||
const elements = document.querySelectorAll("[data-i18n]");
|
||||
elements.forEach((element) => {
|
||||
const key = element.getAttribute("data-i18n");
|
||||
const translation = LanguageManager.t(key);
|
||||
|
||||
if (translation && translation !== key) {
|
||||
const attr = element.getAttribute("data-i18n-attr");
|
||||
if (attr) {
|
||||
element.setAttribute(attr, translation);
|
||||
} else {
|
||||
element.textContent = translation;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// reCAPTCHA rendering removed for security compliance
|
||||
|
||||
setupFormValidation();
|
||||
};
|
||||
|
||||
const setupFormValidation = () => {
|
||||
const form = document.getElementById("forgotForm");
|
||||
const inputs = form.querySelectorAll(".form-control");
|
||||
|
||||
inputs.forEach((input) => {
|
||||
input.addEventListener("input", () => {
|
||||
validateInput(input);
|
||||
});
|
||||
|
||||
input.addEventListener("blur", () => {
|
||||
validateInput(input, true);
|
||||
});
|
||||
});
|
||||
|
||||
form.addEventListener("submit", handleSubmit);
|
||||
};
|
||||
|
||||
const validateInput = (input, showError = false) => {
|
||||
const isValid = input.value.trim().length > 0;
|
||||
const errorElement = input.nextElementSibling;
|
||||
|
||||
if (!isValid && showError) {
|
||||
input.classList.add("error");
|
||||
errorElement?.classList.add("show");
|
||||
} else {
|
||||
input.classList.remove("error");
|
||||
errorElement?.classList.remove("show");
|
||||
}
|
||||
|
||||
return isValid;
|
||||
};
|
||||
|
||||
const showMessage = (message, isError = false) => {
|
||||
const existingMessage = document.querySelector(".message");
|
||||
if (existingMessage) {
|
||||
existingMessage.remove();
|
||||
}
|
||||
|
||||
const messageDiv = document.createElement("div");
|
||||
messageDiv.className = `message ${isError ? "error" : "success"}`;
|
||||
messageDiv.textContent = message;
|
||||
|
||||
const form = document.getElementById("forgotForm");
|
||||
form.insertBefore(messageDiv, form.firstChild);
|
||||
|
||||
setTimeout(() => {
|
||||
if (messageDiv.parentNode) {
|
||||
messageDiv.remove();
|
||||
}
|
||||
}, 5000);
|
||||
};
|
||||
|
||||
const validateEmail = (email) => {
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
return emailRegex.test(email);
|
||||
};
|
||||
|
||||
const handleSubmit = async (event) => {
|
||||
event.preventDefault();
|
||||
|
||||
const form = event.target;
|
||||
const inputs = form.querySelectorAll(".form-control[required]");
|
||||
let isValid = true;
|
||||
|
||||
inputs.forEach((input) => {
|
||||
if (!validateInput(input, true)) {
|
||||
isValid = false;
|
||||
}
|
||||
});
|
||||
|
||||
const emailInput = form.querySelector("#EmailCim");
|
||||
if (emailInput.value && !validateEmail(emailInput.value)) {
|
||||
emailInput.classList.add("error");
|
||||
const errorElement = emailInput.nextElementSibling;
|
||||
if (errorElement) {
|
||||
errorElement.textContent = LanguageManager.t(
|
||||
"forgotpassword.invalid_email",
|
||||
);
|
||||
errorElement.classList.add("show");
|
||||
}
|
||||
isValid = false;
|
||||
}
|
||||
|
||||
// reCAPTCHA validation removed for security compliance
|
||||
|
||||
if (!isValid) {
|
||||
return;
|
||||
}
|
||||
|
||||
const submitButton = form.querySelector(".btn-submit");
|
||||
const originalText = submitButton.textContent;
|
||||
submitButton.disabled = true;
|
||||
submitButton.textContent = LanguageManager.t("loading.text") || "Küldés...";
|
||||
|
||||
try {
|
||||
const formData = new FormData(form);
|
||||
|
||||
// reCAPTCHA data removed for security compliance
|
||||
|
||||
const response = await fetch(
|
||||
"/Adminisztracio/ElfelejtettJelszo/LinkKuldes",
|
||||
{
|
||||
method: "POST",
|
||||
body: formData,
|
||||
headers: {
|
||||
"X-Requested-With": "XMLHttpRequest",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.Success) {
|
||||
showMessage(LanguageManager.t("forgotpassword.success_message"));
|
||||
|
||||
form.reset();
|
||||
|
||||
// reCAPTCHA reset removed
|
||||
|
||||
setTimeout(() => {
|
||||
window.location.href = "/Adminisztracio/Login";
|
||||
}, 3000);
|
||||
} else {
|
||||
showMessage(
|
||||
result.Message || LanguageManager.t("forgotpassword.error_message"),
|
||||
true,
|
||||
);
|
||||
|
||||
// reCAPTCHA reset removed
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Password reset error:", error);
|
||||
showMessage(LanguageManager.t("forgotpassword.error_message"), true);
|
||||
|
||||
// reCAPTCHA reset removed
|
||||
} finally {
|
||||
submitButton.disabled = false;
|
||||
submitButton.textContent = originalText;
|
||||
}
|
||||
};
|
||||
|
||||
if (window.location.href.includes("/Adminisztracio/ElfelejtettJelszo")) {
|
||||
transformForgotPasswordPage().catch(console.error);
|
||||
}
|
||||
})();
|
||||
@@ -184,6 +184,7 @@
|
||||
getAvailableLanguages: () => [
|
||||
{ code: "hu", name: "Magyar" },
|
||||
{ code: "en", name: "English" },
|
||||
{ code: "de", name: "Deutsch" },
|
||||
],
|
||||
};
|
||||
})();
|
||||
|
||||
@@ -45,7 +45,7 @@ function checkMaintenancePage() {
|
||||
);
|
||||
existingStyles.forEach((style) => style.remove());
|
||||
|
||||
body.innerHTML = "";
|
||||
helper.clearElement(body);
|
||||
body.classList.add("maintenance-mode");
|
||||
body.classList.add("theme-enabled");
|
||||
body.classList.add("loaded");
|
||||
|
||||
@@ -14,15 +14,13 @@
|
||||
function applyCustomThemeColors(theme) {
|
||||
const root = document.documentElement;
|
||||
const isDark = theme.mode === "dark";
|
||||
|
||||
|
||||
root.style.setProperty("--background", theme.colors.background);
|
||||
root.style.setProperty("--background-0", theme.colors.background + "00");
|
||||
root.style.setProperty("--card-card", theme.colors.card);
|
||||
root.style.setProperty("--card-translucent", theme.colors.card + "80");
|
||||
root.style.setProperty("--accent-accent", theme.colors.accent);
|
||||
root.style.setProperty("--text-primary", theme.colors.text);
|
||||
|
||||
// Származtatott színek
|
||||
root.style.setProperty("--text-secondary", theme.colors.text + "cc");
|
||||
root.style.setProperty("--text-teritary", theme.colors.text + "80");
|
||||
root.style.setProperty("--accent-15", theme.colors.accent + "26");
|
||||
@@ -30,19 +28,22 @@
|
||||
root.style.setProperty("--accent-secondary", isDark ? lightenColor(theme.colors.accent, 20) : darkenColor(theme.colors.accent, 20));
|
||||
root.style.setProperty("--shadow-blur", isDark ? "0" : "2px");
|
||||
root.style.setProperty("--accent-shadow", isDark ? "#0000" : theme.colors.accent + "26");
|
||||
|
||||
// SVG ikon filter beállítása a kiemelő szín alapján
|
||||
root.style.setProperty("--warning-accent", "#FFA046");
|
||||
root.style.setProperty("--warning-text", isDark ? "#f0b37a" : "#8F531B");
|
||||
root.style.setProperty("--warning-15", "#ffa04626");
|
||||
root.style.setProperty("--warning-card", isDark ? "#201203" : "#FAEBDC");
|
||||
root.style.setProperty("--error-accent", "#FF54A1");
|
||||
root.style.setProperty("--error-text", isDark ? "#f59ec5" : "#8F1B4F");
|
||||
root.style.setProperty("--error-15", "#FF54A126");
|
||||
root.style.setProperty("--error-card", isDark ? "#1e030f" : "#FADCE9");
|
||||
root.style.setProperty("--icon-filter", hexToFilter(theme.colors.accent));
|
||||
}
|
||||
|
||||
// Hex szín átalakítása CSS filterré
|
||||
function hexToFilter(hex) {
|
||||
// Hex -> RGB
|
||||
const r = parseInt(hex.slice(1, 3), 16);
|
||||
const g = parseInt(hex.slice(3, 5), 16);
|
||||
const b = parseInt(hex.slice(5, 7), 16);
|
||||
|
||||
// RGB -> HSL
|
||||
const rNorm = r / 255;
|
||||
const gNorm = g / 255;
|
||||
const bNorm = b / 255;
|
||||
@@ -66,10 +67,6 @@
|
||||
const hue = Math.round(h * 360);
|
||||
const saturation = Math.round(s * 100);
|
||||
const lightness = Math.round(l * 100);
|
||||
|
||||
// Filter létrehozása
|
||||
// Ez egy egyszerűsített megközelítés - a pontos szín reprodukálásához
|
||||
// komplexebb számítás kellene, de ez megfelelő a legtöbb esetben
|
||||
const brightnessVal = lightness > 50 ? 1 + (lightness - 50) / 100 : 0.5 + lightness / 100;
|
||||
const saturateVal = saturation > 0 ? 1 + saturation / 100 : 0;
|
||||
|
||||
@@ -100,20 +97,20 @@
|
||||
"--background", "--background-0", "--card-card", "--card-translucent",
|
||||
"--accent-accent", "--text-primary", "--text-secondary", "--text-teritary",
|
||||
"--accent-15", "--button-secondaryFill", "--accent-secondary",
|
||||
"--shadow-blur", "--accent-shadow", "--icon-filter"
|
||||
"--shadow-blur", "--accent-shadow", "--icon-filter",
|
||||
"--warning-accent", "--warning-text", "--warning-15", "--warning-card",
|
||||
"--error-accent", "--error-text", "--error-15", "--error-card"
|
||||
];
|
||||
customProps.forEach(prop => root.style.removeProperty(prop));
|
||||
}
|
||||
|
||||
async function setTheme(theme) {
|
||||
try {
|
||||
// Töröljük az előző egyéni téma stílusait
|
||||
clearCustomThemeStyles();
|
||||
|
||||
document.documentElement.setAttribute("data-theme", theme);
|
||||
await storageManager.set("themePreference", theme);
|
||||
|
||||
// Ha egyéni téma, alkalmazzuk a színeket
|
||||
|
||||
if (theme.startsWith("custom-")) {
|
||||
await loadCustomThemes();
|
||||
const themeId = theme.replace("custom-", "");
|
||||
@@ -244,7 +241,6 @@
|
||||
|
||||
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
||||
if (message.action === "changeTheme") {
|
||||
// Ha egyéni témák is jöttek az üzenetben, frissítsük a lokális listát
|
||||
if (message.customThemes) {
|
||||
customThemes = message.customThemes;
|
||||
}
|
||||
|
||||
@@ -458,6 +458,9 @@ to {
|
||||
gap:1rem;
|
||||
padding:1rem;
|
||||
}
|
||||
.grade-distribution.centered {
|
||||
justify-content:center;
|
||||
}
|
||||
.grade-count {
|
||||
display:flex;
|
||||
align-items:center;
|
||||
|
||||
119
grades/grades.js
119
grades/grades.js
@@ -1,6 +1,25 @@
|
||||
(() => {
|
||||
let gradesSettings = {
|
||||
hideChart: false,
|
||||
hideClassAverage: false
|
||||
};
|
||||
|
||||
async function loadGradesSettings() {
|
||||
try {
|
||||
const settings = await storageManager.get("pageSettings_grades", {});
|
||||
gradesSettings = {
|
||||
hideChart: settings.hideChart || false,
|
||||
hideClassAverage: settings.hideClassAverage || false
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error loading grades settings:", error);
|
||||
}
|
||||
}
|
||||
|
||||
async function transformGradesPage() {
|
||||
try {
|
||||
await loadGradesSettings();
|
||||
|
||||
const tanuloIdElement = document.querySelector("#TanuloId");
|
||||
const tanuloId = tanuloIdElement ? tanuloIdElement.value : "772481";
|
||||
|
||||
@@ -9,28 +28,31 @@
|
||||
const classAverage = calculateOverallClassAverage(gradesData.subjects);
|
||||
|
||||
window.currentGradesData = gradesData;
|
||||
document.body.innerHTML = '';
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(await generatePageHTML(
|
||||
|
||||
const htmlString = await generatePageHTML(
|
||||
gradesData,
|
||||
studentAverage,
|
||||
classAverage,
|
||||
), 'text/html');
|
||||
const tempDiv = doc.body;
|
||||
while (tempDiv.firstChild) {
|
||||
document.body.appendChild(tempDiv.firstChild);
|
||||
}
|
||||
);
|
||||
|
||||
const template = document.createElement('template');
|
||||
template.innerHTML = htmlString;
|
||||
|
||||
helper.clearElement(document.body);
|
||||
document.body.appendChild(template.content);
|
||||
|
||||
|
||||
setupUserDropdown();
|
||||
|
||||
const script = document.createElement("script");
|
||||
script.src = chrome.runtime.getURL("grades/chart.js");
|
||||
document.head.appendChild(script);
|
||||
if (!gradesSettings.hideChart) {
|
||||
const script = document.createElement("script");
|
||||
script.src = chrome.runtime.getURL("grades/chart.js");
|
||||
document.head.appendChild(script);
|
||||
|
||||
script.onload = () => {
|
||||
setupGradesChart(gradesData.subjects);
|
||||
};
|
||||
script.onload = () => {
|
||||
setupGradesChart(gradesData.subjects);
|
||||
};
|
||||
}
|
||||
|
||||
setupEventListeners();
|
||||
setupGradesListScrolling();
|
||||
@@ -44,9 +66,9 @@
|
||||
async function fetchGradesFromAPI(tanuloId) {
|
||||
try {
|
||||
const currentDomain = window.location.origin;
|
||||
const apiUrl = `${currentDomain}/api/TanuloErtekelesByTanuloApi/GetTanuloErtekelesByTanuloGridTanuloView?sort=&group=&filter=&data=%7B%22tanuloId%22%3A%22${tanuloId}%22%2C%22oktatasiNevelesiFeladatId%22%3A%227895%22%2C%22isOsztalyAtlagMegjelenik%22%3A%22True%22%7D&_=${Date.now()}`;
|
||||
let apiUrl = `${currentDomain}/api/TanuloErtekelesByTanuloApi/GetTanuloErtekelesByTanuloGridTanuloView?sort=&group=&filter=&data=%7B%22tanuloId%22%3A%22${tanuloId}%22%2C%22oktatasiNevelesiFeladatId%22%3A%227895%22%2C%22isOsztalyAtlagMegjelenik%22%3A%22True%22%7D&_=${Date.now()}`;
|
||||
|
||||
const response = await fetch(apiUrl, {
|
||||
let response = await fetch(apiUrl, {
|
||||
method: "GET",
|
||||
credentials: "include",
|
||||
headers: {
|
||||
@@ -59,7 +81,28 @@
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
let data = await response.json();
|
||||
|
||||
if (data.Data && Array.isArray(data.Data) && data.Data.length === 1 && data.Data[0].ID === 0) {
|
||||
console.log("Received only ID: 0, retrying with alternative endpoint...");
|
||||
apiUrl = `${currentDomain}/api/TanuloErtekelesByTanuloApi/GetTanuloErtekelesByTanuloGridTanuloView?sort=&group=&filter=&data=%7B%22tanuloId%22%3A%22${tanuloId}%22%2C%22oktatasiNevelesiFeladatId%22%3A%221160%22%2C%22isOsztalyAtlagMegjelenik%22%3A%22True%22%7D&_=${Date.now()}`;
|
||||
|
||||
response = await fetch(apiUrl, {
|
||||
method: "GET",
|
||||
credentials: "include",
|
||||
headers: {
|
||||
Accept: "application/json, text/javascript, */*; q=0.01",
|
||||
"X-Requested-With": "XMLHttpRequest",
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error on retry! status: ${response.status}`);
|
||||
}
|
||||
|
||||
data = await response.json();
|
||||
}
|
||||
|
||||
return await processAPIGradesData(data);
|
||||
} catch (error) {
|
||||
console.error("Error fetching grades from API:", error);
|
||||
@@ -390,12 +433,16 @@
|
||||
schoolNameFull = `${data.schoolInfo.id} - ${data.schoolInfo.name}`;
|
||||
shortenedSchoolName = helper.shortenSchoolName(schoolNameFull);
|
||||
|
||||
const showClassAverage = !gradesSettings.hideClassAverage && classAverage > 0;
|
||||
const showChart = !gradesSettings.hideChart;
|
||||
|
||||
return `
|
||||
<div class="kreta-container">
|
||||
${await createTemplate.header()}
|
||||
|
||||
<main class="kreta-main">
|
||||
<div class="grades-overview">
|
||||
${showChart ? `
|
||||
<div class="overall-averages card">
|
||||
<div class="chart-header">
|
||||
<div class="chart-title">${LanguageManager.t("grades.chart_title")} (${totalGrades}db)</div>
|
||||
@@ -404,7 +451,7 @@
|
||||
<span class="average-value ${studentAverage < 2 && studentAverage > 0 ? "warning" : ""}">${studentAverage > 0 ? studentAverage.toFixed(2) : "-"}</span>
|
||||
</div>
|
||||
${
|
||||
classAverage > 0
|
||||
showClassAverage
|
||||
? `
|
||||
<div class="average-circle class-average" data-grade="${classGradeLevel}">
|
||||
<span class="average-value">${classAverage.toFixed(2)}</span>
|
||||
@@ -430,6 +477,39 @@
|
||||
.join("")}
|
||||
</div>
|
||||
</div>
|
||||
` : `
|
||||
<div class="overall-averages card">
|
||||
<div class="chart-header">
|
||||
<div class="chart-title">${LanguageManager.t("grades.chart_title")} (${totalGrades}db)</div>
|
||||
<div class="chart-averages">
|
||||
<div class="average-circle my-average" data-grade="${studentGradeLevel}">
|
||||
<span class="average-value ${studentAverage < 2 && studentAverage > 0 ? "warning" : ""}">${studentAverage > 0 ? studentAverage.toFixed(2) : "-"}</span>
|
||||
</div>
|
||||
${
|
||||
showClassAverage
|
||||
? `
|
||||
<div class="average-circle class-average" data-grade="${classGradeLevel}">
|
||||
<span class="average-value">${classAverage.toFixed(2)}</span>
|
||||
</div>
|
||||
`
|
||||
: ""
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="grade-distribution centered">
|
||||
${Object.entries(gradeDistribution)
|
||||
.map(
|
||||
([grade, count]) => `
|
||||
<div class="grade-count grade-${grade}">
|
||||
<span class="grade-value">${grade}</span>
|
||||
<span class="grade-amount">${count}</span>
|
||||
</div>
|
||||
`,
|
||||
)
|
||||
.join("")}
|
||||
</div>
|
||||
</div>
|
||||
`}
|
||||
${
|
||||
yearEndGrades.length > 0
|
||||
? `
|
||||
@@ -635,6 +715,7 @@
|
||||
.reverse();
|
||||
const myGrade = Math.floor(subject.average) || 0;
|
||||
const classGrade = Math.floor(subject.classAverage) || 0;
|
||||
const showClassAvg = !gradesSettings.hideClassAverage && subject.classAverage > 0;
|
||||
|
||||
return `
|
||||
<div class="subject-card card">
|
||||
@@ -647,7 +728,7 @@
|
||||
<span class="average-value">${subject.average > 0 ? subject.average.toFixed(2) : "-"}</span>
|
||||
</div>
|
||||
${
|
||||
subject.classAverage > 0
|
||||
showClassAvg
|
||||
? `
|
||||
<div class="average-circle class-average" data-grade="${classGrade}">
|
||||
<span class="average-value">${subject.classAverage.toFixed(2)}</span>
|
||||
|
||||
559
i18n/de.json
Normal file
559
i18n/de.json
Normal file
@@ -0,0 +1,559 @@
|
||||
{
|
||||
"loading": {
|
||||
"text": "Laden...",
|
||||
"subtext": "Bitte warten!"
|
||||
},
|
||||
"settings": {
|
||||
"title": "Einstellungen",
|
||||
"appearance": "Erscheinungsbild",
|
||||
"theme": "Design",
|
||||
"language": "Sprache",
|
||||
"tabs": {
|
||||
"home": "Startseite",
|
||||
"appearance": "Erscheinungsbild",
|
||||
"settings": "Einstellungen",
|
||||
"about": "Über"
|
||||
},
|
||||
"page_settings": {
|
||||
"title": "Seiteneinstellungen",
|
||||
"current_page": "Aktuelle Seite",
|
||||
"no_settings": "Keine benutzerdefinierten Einstellungen für diese Seite verfügbar.",
|
||||
"login": {
|
||||
"hide_system_message": "Systemmeldung ausblenden",
|
||||
"hide_system_message_desc": "Systemmeldung auf der Anmeldeseite ausblenden",
|
||||
"hide_school_info": "Schulname und ID ausblenden",
|
||||
"hide_school_info_desc": "Der Schulname und die KRÉTA-ID werden nicht angezeigt"
|
||||
},
|
||||
"roleselect": {
|
||||
"auto_redirect": "Automatische Weiterleitung",
|
||||
"auto_redirect_desc": "Automatische Weiterleitung zum Notenbuch",
|
||||
"hide_school_info": "Schule und Name ausblenden",
|
||||
"hide_school_info_desc": "Der Schulname und Benutzername werden nicht angezeigt"
|
||||
},
|
||||
"bulletin": {
|
||||
"hide_grades": "Noten ausblenden",
|
||||
"hide_grades_desc": "Die Notenkarte ausblenden",
|
||||
"hide_absences": "Fehlzeiten ausblenden",
|
||||
"hide_absences_desc": "Die Fehlzeitenkarte ausblenden",
|
||||
"hide_notes": "Notizen ausblenden",
|
||||
"hide_notes_desc": "Die Notizenkarte ausblenden",
|
||||
"hide_exams": "Angekündigte Prüfungen ausblenden",
|
||||
"hide_exams_desc": "Die Karte für angekündigte Prüfungen ausblenden"
|
||||
},
|
||||
"grades": {
|
||||
"hide_chart": "Diagramm ausblenden",
|
||||
"hide_chart_desc": "Das Notendiagramm ausblenden",
|
||||
"hide_class_average": "Klassendurchschnitt ausblenden",
|
||||
"hide_class_average_desc": "Klassendurchschnittswerte ausblenden"
|
||||
}
|
||||
},
|
||||
"themes": {
|
||||
"light_green": "Hellgrün",
|
||||
"dark_green": "Dunkelgrün",
|
||||
"dark_red": "Dunkelrot",
|
||||
"dark_purple": "Dunkellila",
|
||||
"dark_orange": "Dunkelorange",
|
||||
"dark_pink": "Dunkelrosa",
|
||||
"dark_yellow": "Dunkelgelb",
|
||||
"dark_cyan": "Dunkelcyan",
|
||||
"dark_lime": "Dunkellime",
|
||||
"dark_indigo": "Dunkelindigo"
|
||||
},
|
||||
"custom_themes": {
|
||||
"title": "Benutzerdefinierte Designs",
|
||||
"no_themes": "Noch keine benutzerdefinierten Designs",
|
||||
"create": "Neues Design erstellen",
|
||||
"edit": "Design bearbeiten",
|
||||
"name": "Designname",
|
||||
"mode": "Modus",
|
||||
"dark_mode": "Dunkel",
|
||||
"light_mode": "Hell",
|
||||
"colors": "Farben",
|
||||
"accent_color": "Akzentfarbe",
|
||||
"background_color": "Hintergrundfarbe",
|
||||
"card_color": "Kartenfarbe",
|
||||
"text_color": "Textfarbe",
|
||||
"preview": "Vorschau",
|
||||
"share": "Design teilen",
|
||||
"share_description": "Kopieren Sie den Code und teilen Sie ihn mit anderen:",
|
||||
"import": "Design importieren",
|
||||
"import_description": "Fügen Sie den Design-Code ein:",
|
||||
"import_error_empty": "Bitte fügen Sie den Design-Code ein!",
|
||||
"import_error_invalid": "Ungültiger Design-Code!",
|
||||
"delete_confirm": "Möchten Sie dieses Design wirklich löschen?",
|
||||
"manage": "Verwalten"
|
||||
},
|
||||
"languages": {
|
||||
"hu": "Magyar",
|
||||
"en": "English",
|
||||
"de": "Deutsch"
|
||||
},
|
||||
"about": {
|
||||
"title": "Über",
|
||||
"description": "Firka ist ein Open-Source-Projekt, das eine benutzerdefinierte Benutzeroberfläche für das KRÉTA-System erstellt.",
|
||||
"github": "GitHub"
|
||||
},
|
||||
"support": {
|
||||
"title": "Unterstützung",
|
||||
"description": "Wenn Ihnen unsere Arbeit gefällt und Sie die Entwicklung unterstützen möchten, können Sie dies folgendermaßen tun:",
|
||||
"kofi": "Ko-Fi"
|
||||
},
|
||||
"error_reporting": {
|
||||
"title": "Fehlerberichterstattung",
|
||||
"enable": "Fehlerberichterstattung aktivieren",
|
||||
"enable_desc": "Automatische Fehlerberichte an Entwickler senden, um die Erweiterung zu verbessern",
|
||||
"report_issue": "Fehler oder Idee melden",
|
||||
"report_issue_desc": "Öffnen Sie ein GitHub-Issue"
|
||||
}
|
||||
},
|
||||
"navigation": {
|
||||
"dashboard": "Dashboard",
|
||||
"timetable": "Stundenplan",
|
||||
"grades": "Noten",
|
||||
"homework": "Hausaufgaben",
|
||||
"absences": "Fehlzeiten",
|
||||
"messages": "Nachrichten",
|
||||
"profile": "Profil",
|
||||
"settings": "Einstellungen",
|
||||
"logout": "Abmelden",
|
||||
"nav_toggle": "Navigation öffnen"
|
||||
},
|
||||
"dashboard": {
|
||||
"welcome": "Willkommen",
|
||||
"recent_grades": "Letzte Noten",
|
||||
"upcoming_lessons": "Kommende Stunden",
|
||||
"homework_due": "Fällige Hausaufgaben",
|
||||
"news": "Nachrichten",
|
||||
"grades": "Ihre Bewertungen",
|
||||
"absences": "Fehlzeiten",
|
||||
"notes": "Notizen",
|
||||
"exams": "Angekündigte Prüfungen",
|
||||
"all_news": "Alle Nachrichten",
|
||||
"all_grades": "Alle Noten",
|
||||
"all_absences": "Alle Fehlzeiten",
|
||||
"all_messages": "Alle Nachrichten",
|
||||
"all_exams": "Alle Prüfungen",
|
||||
"not_supported": "Derzeit sind keine Daten zum Laden verfügbar",
|
||||
"evaluation": "Bewertung"
|
||||
},
|
||||
"grades": {
|
||||
"title": "Noten",
|
||||
"subject": "Fach",
|
||||
"grade": "Note",
|
||||
"date": "Datum",
|
||||
"teacher": "Lehrer",
|
||||
"average": "Durchschnitt",
|
||||
"chart_title": "Noten",
|
||||
"semester_evaluation": "Semesterbewertung",
|
||||
"semester_evaluations": "Semesterbewertungen",
|
||||
"year_end_evaluations": "Jahresabschlussbewertungen",
|
||||
"semester_average": "Semesterdurchschnitt",
|
||||
"no_grades": "Keine Noten",
|
||||
"september": "September",
|
||||
"october": "Oktober",
|
||||
"november": "November",
|
||||
"december": "Dezember",
|
||||
"january_1": "JanuarI",
|
||||
"january_2": "JanuarII",
|
||||
"february": "Februar",
|
||||
"march": "März",
|
||||
"april": "April",
|
||||
"may": "Mai",
|
||||
"june_1": "JuniI",
|
||||
"june_2": "JuniII"
|
||||
},
|
||||
"timetable": {
|
||||
"title": "Stundenplan",
|
||||
"lesson": "Stunde",
|
||||
"time": "Zeit",
|
||||
"subject": "Fach",
|
||||
"teacher": "Lehrer",
|
||||
"classroom": "Klassenzimmer",
|
||||
"homework_indicator": "Hausaufgaben",
|
||||
"test_indicator": "Prüfung",
|
||||
"teacher_label": "Lehrer:",
|
||||
"substitute_teacher_label": "Vertretungslehrer:",
|
||||
"classroom_label": "Raum:",
|
||||
"time_label": "Zeit:",
|
||||
"status_label": "Status:",
|
||||
"substitution": "Vertretung",
|
||||
"cancelled": "Abgesagt",
|
||||
"has_homework": "Hat Hausaufgaben",
|
||||
"no_lessons_this_week": "Keine Stunden in dieser Woche oder Zeitüberschreitung",
|
||||
"monday": "Montag",
|
||||
"tuesday": "Dienstag",
|
||||
"wednesday": "Mittwoch",
|
||||
"thursday": "Donnerstag",
|
||||
"friday": "Freitag",
|
||||
"found_current_week": "Aktuelle Woche gefunden",
|
||||
"open_homework": "Zu den Hausaufgaben gehen",
|
||||
"all_day": "Ganztägig",
|
||||
"special_day": "Besonderer Tag",
|
||||
"unknown_subject": "Unbekanntes Fach",
|
||||
"lesson_topic": "Stundenthema",
|
||||
"homework_completed": "Hausaufgabe erledigt",
|
||||
"homework_mark_completed": "Als erledigt markieren",
|
||||
"homework_mark_uncompleted": "Erledigt - Klicken zum Rückgängigmachen",
|
||||
"custom_homework": "Eigene Hausaufgaben",
|
||||
"custom_test": "Eigene Prüfung",
|
||||
"add_homework_test": "Hausaufgaben oder Prüfung hinzufügen",
|
||||
"close": "Schließen",
|
||||
"add": "Hinzufügen",
|
||||
"homework_details_loading": "Hausaufgabendetails werden geladen...",
|
||||
"homework_details_error": "Fehler beim Laden der Hausaufgabendetails.",
|
||||
"test_details_loading": "Details werden geladen...",
|
||||
"test_details_error": "Prüfungsdetails konnten nicht geladen werden.",
|
||||
"test_details_error_general": "Fehler beim Laden der Prüfungsdetails.",
|
||||
"custom_homework_title": "Eigene Hausaufgaben:",
|
||||
"custom_tests_title": "Eigene Prüfungen:",
|
||||
"delete_homework_confirm": "Möchten Sie diese Hausaufgabe wirklich löschen?",
|
||||
"delete_test_confirm": "Möchten Sie diese Prüfung wirklich löschen?",
|
||||
"task_label": "Aufgabe:",
|
||||
"deadline_label": "Frist:",
|
||||
"name_label": "Name:",
|
||||
"type_label": "Typ:",
|
||||
"announce_date_label": "Ankündigungsdatum:",
|
||||
"no_name": "Kein Name",
|
||||
"no_type": "Kein Typ angegeben",
|
||||
"no_date": "Kein Datum"
|
||||
},
|
||||
"homework": {
|
||||
"title": "Hausaufgaben",
|
||||
"due_date": "Fälligkeitsdatum",
|
||||
"subject": "Fach",
|
||||
"description": "Beschreibung",
|
||||
"filter_title": "Filter",
|
||||
"all_subjects": "Alle Fächer",
|
||||
"all_teachers": "Alle Lehrer",
|
||||
"all_deadlines": "Alle Fristen",
|
||||
"tomorrow_deadline": "Frist morgen",
|
||||
"this_week": "Diese Woche",
|
||||
"next_week": "Nächste Woche",
|
||||
"no_homework": "Keine anzeigbaren Hausaufgaben.",
|
||||
"no_filtered_homework": "Keine Hausaufgaben, die den Filterkriterien entsprechen.",
|
||||
"teacher": "Lehrer",
|
||||
"no_matching_homework": "Keine Hausaufgaben, die den Filterkriterien entsprechen.",
|
||||
"items": "Element",
|
||||
"status": "Status",
|
||||
"total_homework": "Alle Aufgaben",
|
||||
"urgent_homework": "Dringende Aufgaben",
|
||||
"completed_homework": "Erledigte Aufgaben",
|
||||
"pending_homework": "Ausstehende Aufgaben",
|
||||
"completed": "Abgeschlossen",
|
||||
"urgent": "Dringend",
|
||||
"pending": "Ausstehend"
|
||||
},
|
||||
"absences": {
|
||||
"title": "Fehlzeiten",
|
||||
"date": "Datum",
|
||||
"lesson": "Stunde",
|
||||
"type": "Typ",
|
||||
"justified": "Entschuldigt",
|
||||
"unjustified": "Unentschuldigt",
|
||||
"filter_title": "Filter",
|
||||
"all_subjects": "Alle Fächer",
|
||||
"all_types": "Alle",
|
||||
"pending": "Wartet auf Entschuldigung",
|
||||
"subject": "Fach",
|
||||
"justification": "Entschuldigung",
|
||||
"hours": "Stunden",
|
||||
"page_transform_error": "Fehler bei der Seitentransformation",
|
||||
"time_period": "Zeitraum",
|
||||
"all_periods": "Alle Zeiträume",
|
||||
"current_month": "Aktueller Monat",
|
||||
"last_month": "Letzter Monat",
|
||||
"current_semester": "Aktuelles Semester",
|
||||
"last_30_days": "Letzte 30 Tage",
|
||||
"total_absences": "Alle Fehlzeiten",
|
||||
"topic": "Thema",
|
||||
"status": "Status",
|
||||
"absence_type": "Abwesenheit",
|
||||
"late_type": "Verspätung",
|
||||
"minutes": "Minuten"
|
||||
},
|
||||
"profile": {
|
||||
"title": "Profil",
|
||||
"name": "Name",
|
||||
"class": "Klasse",
|
||||
"school": "Schule",
|
||||
"student_id": "Schüler-ID",
|
||||
"settings_title": "Profileinstellungen",
|
||||
"tab_settings": "Einstellungen",
|
||||
"tab_password": "Passwort ändern",
|
||||
"tab_security": "Sicherheitseinstellungen",
|
||||
"tab_contacts": "Kontakte",
|
||||
"two_factor_description": "Um die Zwei-Faktor-Authentifizierung zu verwenden, installieren Sie eine zeitbasierte Einmalpasswort-App (TOTP):",
|
||||
"android": "Android",
|
||||
"iphone": "iPhone",
|
||||
"enable_2fa": "Zwei-Faktor-Authentifizierung aktivieren",
|
||||
"security_key": "Sicherheitsschlüssel:",
|
||||
"verification_code_label": "Bestätigungscode",
|
||||
"verification_code_help": "Geben Sie den 6-stelligen Code aus der Authentifizierungs-App ein.",
|
||||
"verification_code_placeholder": "123456",
|
||||
"verify_and_activate": "Überprüfen und aktivieren",
|
||||
"backup_codes_description": "Sie können die folgenden Sicherheitscodes zum Anmelden verwenden, wenn Sie keinen Zugriff auf Ihre Authentifizierungs-App haben. Jeder Code kann nur einmal verwendet werden.",
|
||||
"email_label": "E-Mail-Adresse",
|
||||
"email_help": "Die E-Mail-Adresse ist für die Passwort-Erinnerung erforderlich.",
|
||||
"phone_label": "Telefonnummer",
|
||||
"phone_help": "Die Angabe einer Telefonnummer ist optional.",
|
||||
"phone_placeholder": "+49 xxx xxxxxx",
|
||||
"current_password": "Aktuelles Passwort",
|
||||
"new_password": "Neues Passwort",
|
||||
"new_password_help": "Das Passwort muss mindestens 8 Zeichen lang sein.",
|
||||
"confirm_password": "Neues Passwort bestätigen",
|
||||
"change_password": "Passwort ändern",
|
||||
"show_tips": "Tipps anzeigen",
|
||||
"show_tips_help": "Tipps ein-/ausblenden.",
|
||||
"email_required": "Die E-Mail-Adresse ist erforderlich!",
|
||||
"email_invalid": "Bitte geben Sie eine gültige E-Mail-Adresse ein!",
|
||||
"phone_invalid": "Bitte geben Sie eine gültige Telefonnummer ein!",
|
||||
"contacts_saved": "Kontakte erfolgreich gespeichert!",
|
||||
"contacts_save_error": "Fehler beim Speichern. Bitte versuchen Sie es später erneut.",
|
||||
"settings_saved": "Einstellungen erfolgreich gespeichert! Bitte melden Sie sich erneut an, um die Änderungen zu übernehmen.",
|
||||
"settings_save_error": "Fehler beim Speichern. Bitte versuchen Sie es später erneut.",
|
||||
"password_fields_required": "Bitte füllen Sie alle Felder aus!",
|
||||
"passwords_not_match": "Die neuen Passwörter stimmen nicht überein!",
|
||||
"password_too_short": "Das neue Passwort muss mindestens 8 Zeichen lang sein!",
|
||||
"password_changed": "Passwort erfolgreich geändert!",
|
||||
"password_change_error": "Fehler beim Ändern des Passworts. Bitte versuchen Sie es später erneut."
|
||||
},
|
||||
"login": {
|
||||
"title": "Anmelden",
|
||||
"username": "Benutzername",
|
||||
"password": "Passwort",
|
||||
"login_button": "Anmelden",
|
||||
"forgot_password": "Passwort vergessen",
|
||||
"two_factor_title": "Zwei-Faktor-Authentifizierung",
|
||||
"verification_code": "Bestätigungscode",
|
||||
"username_placeholder": "Benutzername",
|
||||
"password_placeholder": "Passwort",
|
||||
"username_required": "Bitte geben Sie Ihren Benutzernamen ein.",
|
||||
"password_required": "Bitte geben Sie Ihr Passwort ein.",
|
||||
"help_login": "Anmeldeprobleme?",
|
||||
"help_link": "Hilfe",
|
||||
"system_message": "Systemmeldung",
|
||||
"privacy_policy": "Datenschutzerklärung",
|
||||
"kreta_id": "KRÉTA-ID",
|
||||
"system_notification": "Systembenachrichtigung"
|
||||
},
|
||||
"forgot_password": {
|
||||
"title": "Passwort vergessen",
|
||||
"om_id": "Ihre OM-ID",
|
||||
"email": "E-Mail-Adresse",
|
||||
"om_id_placeholder": "Geben Sie Ihre OM-ID ein",
|
||||
"email_placeholder": "Geben Sie Ihre E-Mail-Adresse ein",
|
||||
"om_id_required": "Bitte geben Sie Ihre OM-ID ein.",
|
||||
"email_required": "Bitte geben Sie Ihre E-Mail-Adresse ein."
|
||||
},
|
||||
"two_factor": {
|
||||
"title": "Zwei-Faktor-Authentifizierung",
|
||||
"code_placeholder": "Einmalpasswort",
|
||||
"code_required": "Bitte geben Sie das Einmalpasswort ein.",
|
||||
"verify_button": "Code überprüfen",
|
||||
"verifying": "Überprüfung...",
|
||||
"trust_device": "Diesem Gerät vertrauen"
|
||||
},
|
||||
"logout": {
|
||||
"title": "Abmelden",
|
||||
"message": "Möchten Sie sich wirklich abmelden?",
|
||||
"confirm": "Ja",
|
||||
"cancel": "Abbrechen",
|
||||
"success": "Erfolgreich abgemeldet!",
|
||||
"continue": "Weiter"
|
||||
},
|
||||
"setup": {
|
||||
"welcome": "Richten Sie die Erweiterung in wenigen einfachen Schritten ein",
|
||||
"steps": {
|
||||
"theme": "Design",
|
||||
"language": "Sprache",
|
||||
"finish": "Fertig"
|
||||
},
|
||||
"theme": {
|
||||
"title": "Design wählen",
|
||||
"description": "Wählen Sie das für Sie am besten geeignete Erscheinungsbild"
|
||||
},
|
||||
"language": {
|
||||
"title": "Sprache wählen",
|
||||
"description": "Wählen Sie die Sprache, die Sie verwenden möchten"
|
||||
},
|
||||
"finish": {
|
||||
"title": "Alles erledigt!",
|
||||
"description": "Die Einstellungen wurden erfolgreich gespeichert. Viel Erfolg beim Lernen!",
|
||||
"about": "Über uns",
|
||||
"about_desc": "Erfahren Sie mehr über das Projekt",
|
||||
"support": "Unterstützung",
|
||||
"support_desc": "Unterstützen Sie die Entwicklung",
|
||||
"github": "GitHub",
|
||||
"github_desc": "Sehen Sie sich den Quellcode an",
|
||||
"discord": "Discord",
|
||||
"discord_desc": "Treten Sie der Community bei",
|
||||
"start": "Start"
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
"save": "Speichern",
|
||||
"cancel": "Abbrechen",
|
||||
"close": "Schließen",
|
||||
"loading": "Laden...",
|
||||
"error": "Fehler",
|
||||
"success": "Erfolgreich",
|
||||
"warning": "Warnung",
|
||||
"info": "Information",
|
||||
"yes": "Ja",
|
||||
"no": "Nein",
|
||||
"continue": "Weiter",
|
||||
"back": "Zurück",
|
||||
"next": "Weiter",
|
||||
"previous": "Zurück",
|
||||
"all": "Alle",
|
||||
"none": "Keine",
|
||||
"filter": "Filter",
|
||||
"search": "Suchen",
|
||||
"select": "Wählen",
|
||||
"required": "Erforderlich",
|
||||
"optional": "Optional",
|
||||
"api_error": "API-Fehler",
|
||||
"api_load_error": "API-Ladefehler",
|
||||
"monday": "Montag",
|
||||
"tuesday": "Dienstag",
|
||||
"wednesday": "Mittwoch",
|
||||
"thursday": "Donnerstag",
|
||||
"friday": "Freitag",
|
||||
"saturday": "Samstag",
|
||||
"sunday": "Sonntag",
|
||||
"mon": "M",
|
||||
"tue": "D",
|
||||
"wed": "M",
|
||||
"thu": "D",
|
||||
"fri": "F",
|
||||
"sat": "S",
|
||||
"sun": "S",
|
||||
"today": "Heute",
|
||||
"tomorrow": "Morgen",
|
||||
"january": "Januar",
|
||||
"february": "Februar",
|
||||
"march": "März",
|
||||
"april": "April",
|
||||
"may": "Mai",
|
||||
"june": "Juni",
|
||||
"july": "Juli",
|
||||
"august": "August",
|
||||
"september": "September",
|
||||
"october": "Oktober",
|
||||
"november": "November",
|
||||
"december": "Dezember"
|
||||
},
|
||||
"months": {
|
||||
"january": "Januar",
|
||||
"february": "Februar",
|
||||
"march": "März",
|
||||
"april": "April",
|
||||
"may": "Mai",
|
||||
"june": "Juni",
|
||||
"july": "Juli",
|
||||
"august": "August",
|
||||
"september": "September",
|
||||
"october": "Oktober",
|
||||
"november": "November",
|
||||
"december": "Dezember"
|
||||
},
|
||||
"roleselect": {
|
||||
"student_book": "Notenbuch",
|
||||
"student_description": "Noten, Fehlzeiten, Stundenplan und andere Informationen anzeigen.",
|
||||
"dkt_title": "Digitaler Kollaborationsraum (DKT)",
|
||||
"dkt_description": "Klassenzimmerkommunikation und Aufgaben.",
|
||||
"logout_title": "Abmelden",
|
||||
"logout_description": "Vom System abmelden",
|
||||
"role_change_error": "Fehler beim Wechseln der Rolle."
|
||||
},
|
||||
"maintenance": {
|
||||
"title": "Wartung",
|
||||
"message1": "Das KRÉTA-System wird derzeit aktualisiert und wird bald wieder verfügbar sein.",
|
||||
"message2": "Vielen Dank für Ihre Geduld und Ihr Verständnis!",
|
||||
"team": "KRÉTA-Team"
|
||||
},
|
||||
"about": {
|
||||
"title": "Über",
|
||||
"description": "Firka ist ein Open-Source-Projekt, das eine benutzerdefinierte Benutzeroberfläche für das KRÉTA-System erstellt.",
|
||||
"support_title": "Unterstützung",
|
||||
"support_description": "Wenn Ihnen unsere Arbeit gefällt und Sie die Entwicklung unterstützen möchten, können Sie dies folgendermaßen tun:",
|
||||
"version": "v1.3.0"
|
||||
},
|
||||
"app": {
|
||||
"title": "Firka - KRÉTA",
|
||||
"settings_title": "Firxa - Einstellungen"
|
||||
},
|
||||
"forgotpassword": {
|
||||
"title": "Passwort vergessen",
|
||||
"om_id_label": "OM-ID",
|
||||
"om_id_placeholder": "Geben Sie Ihre OM-ID ein",
|
||||
"om_id_required": "Die OM-ID ist erforderlich",
|
||||
"email_label": "E-Mail-Adresse",
|
||||
"email_placeholder": "Geben Sie Ihre E-Mail-Adresse ein",
|
||||
"email_required": "Die E-Mail-Adresse ist erforderlich",
|
||||
"back_to_login": "Zurück zur Anmeldung",
|
||||
"reset_button": "Passwort zurücksetzen",
|
||||
"error_message": "Fehler beim Zurücksetzen des Passworts",
|
||||
"success_message": "Link zum Zurücksetzen des Passworts an Ihre E-Mail-Adresse gesendet",
|
||||
"invalid_data": "Ungültige Daten",
|
||||
"invalid_email": "Ungültiges E-Mail-Format",
|
||||
"recaptcha_required": "Bitte füllen Sie das reCAPTCHA aus"
|
||||
},
|
||||
"modal": {
|
||||
"add_item_title": "Neues Element hinzufügen",
|
||||
"type_label": "Typ:",
|
||||
"homework_option": "Hausaufgaben",
|
||||
"test_option": "Prüfung",
|
||||
"description_label": "Beschreibung:",
|
||||
"cancel": "Abbrechen",
|
||||
"save": "Speichern"
|
||||
},
|
||||
"search": {
|
||||
"choose_school": "Schule wählen",
|
||||
"privacy_policy": "Datenschutzrichtlinie",
|
||||
"title": "Schule wählen",
|
||||
"select_institution": "Bitte wählen Sie eine Einrichtung aus, um fortzufahren!"
|
||||
},
|
||||
"icons": {
|
||||
"cancel": "cancel",
|
||||
"pending": "pending"
|
||||
},
|
||||
"messages": {
|
||||
"title": "Nachrichten",
|
||||
"back": "Zurück",
|
||||
"surveys": "Umfragen",
|
||||
"loading": "Nachrichten werden geladen...",
|
||||
"error": {
|
||||
"title": "Fehler aufgetreten",
|
||||
"description": "Nachrichten konnten nicht geladen werden.",
|
||||
"retry": "Erneut versuchen"
|
||||
},
|
||||
"empty": {
|
||||
"title": "Keine Nachrichten",
|
||||
"description": "Derzeit sind keine eingegangenen Nachrichten vorhanden."
|
||||
},
|
||||
"sender": "Absender",
|
||||
"subject": "Betreff",
|
||||
"date": "Datum",
|
||||
"unread": "Ungelesen",
|
||||
"read": "Gelesen",
|
||||
"message_detail": {
|
||||
"title": "Nachrichtendetails",
|
||||
"loading": "Nachricht wird geladen...",
|
||||
"error": "Fehler beim Laden der Nachricht.",
|
||||
"from": "Von",
|
||||
"to": "An",
|
||||
"subject": "Betreff",
|
||||
"date": "Datum",
|
||||
"content": "Inhalt",
|
||||
"attachments": "Anhänge",
|
||||
"no_attachments": "Keine Anhänge",
|
||||
"reply": "Antworten",
|
||||
"forward": "Weiterleiten",
|
||||
"delete": "Löschen",
|
||||
"mark_read": "Als gelesen markieren",
|
||||
"mark_unread": "Als ungelesen markieren",
|
||||
"back_to_messages": "Zurück zu den Nachrichten"
|
||||
}
|
||||
}
|
||||
}
|
||||
112
i18n/en.json
112
i18n/en.json
@@ -5,8 +5,47 @@
|
||||
},
|
||||
"settings": {
|
||||
"title": "Settings",
|
||||
"appearance": "Appearance",
|
||||
"theme": "Theme",
|
||||
"language": "Language",
|
||||
"tabs": {
|
||||
"appearance": "Appearance",
|
||||
"settings": "Settings",
|
||||
"about": "About"
|
||||
},
|
||||
"page_settings": {
|
||||
"title": "Page Settings",
|
||||
"current_page": "Current page",
|
||||
"no_settings": "No custom settings available for this page.",
|
||||
"login": {
|
||||
"hide_system_message": "Hide system message",
|
||||
"hide_system_message_desc": "Hide the system message shown on the login page",
|
||||
"hide_school_info": "Hide school name and ID",
|
||||
"hide_school_info_desc": "The school name and KRÉTA ID will not be displayed"
|
||||
},
|
||||
"roleselect": {
|
||||
"auto_redirect": "Auto redirect",
|
||||
"auto_redirect_desc": "Automatically redirect to the gradebook",
|
||||
"hide_school_info": "Hide school and name",
|
||||
"hide_school_info_desc": "The school name and user name will not be displayed"
|
||||
},
|
||||
"bulletin": {
|
||||
"hide_grades": "Hide grades",
|
||||
"hide_grades_desc": "Hide the grades card",
|
||||
"hide_absences": "Hide absences",
|
||||
"hide_absences_desc": "Hide the absences card",
|
||||
"hide_notes": "Hide notes",
|
||||
"hide_notes_desc": "Hide the notes card",
|
||||
"hide_exams": "Hide announced exams",
|
||||
"hide_exams_desc": "Hide the announced exams card"
|
||||
},
|
||||
"grades": {
|
||||
"hide_chart": "Hide chart",
|
||||
"hide_chart_desc": "Hide the grades chart",
|
||||
"hide_class_average": "Hide class average",
|
||||
"hide_class_average_desc": "Hide class average values"
|
||||
}
|
||||
},
|
||||
"themes": {
|
||||
"light_green": "Light Green",
|
||||
"dark_green": "Dark Green",
|
||||
@@ -34,17 +73,20 @@
|
||||
"card_color": "Card color",
|
||||
"text_color": "Text color",
|
||||
"preview": "Preview",
|
||||
"share": "Share theme",
|
||||
"share": "Share",
|
||||
"share_description": "Copy the code and share it with others:",
|
||||
"import": "Import theme",
|
||||
"import": "Import",
|
||||
"import_description": "Paste the theme code:",
|
||||
"import_error_empty": "Please paste the theme code!",
|
||||
"import_error_invalid": "Invalid theme code!",
|
||||
"delete_confirm": "Are you sure you want to delete this theme?"
|
||||
"delete_confirm": "Are you sure you want to delete this theme?",
|
||||
"delete": "Delete",
|
||||
"manage": "Manage"
|
||||
},
|
||||
"languages": {
|
||||
"hu": "Magyar",
|
||||
"en": "English"
|
||||
"en": "English",
|
||||
"de": "Deutsch"
|
||||
},
|
||||
"about": {
|
||||
"title": "About",
|
||||
@@ -55,6 +97,13 @@
|
||||
"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"
|
||||
},
|
||||
"error_reporting": {
|
||||
"title": "Error Reporting",
|
||||
"enable": "Enable error reporting",
|
||||
"enable_desc": "Send automatic error reports to developers to help fix the extension",
|
||||
"report_issue": "Report bug or idea",
|
||||
"report_issue_desc": "Open a GitHub issue"
|
||||
}
|
||||
},
|
||||
"navigation": {
|
||||
@@ -217,7 +266,10 @@
|
||||
"last_30_days": "Last 30 days",
|
||||
"total_absences": "Total absences",
|
||||
"topic": "Topic",
|
||||
"status": "Status"
|
||||
"status": "Status",
|
||||
"absence_type": "Absence",
|
||||
"late_type": "Late",
|
||||
"minutes": "minutes"
|
||||
},
|
||||
"profile": {
|
||||
"title": "Profile",
|
||||
@@ -309,6 +361,35 @@
|
||||
"success": "Successfully logged out!",
|
||||
"continue": "Continue"
|
||||
},
|
||||
"setup": {
|
||||
"welcome": "Set up the extension in a few simple steps",
|
||||
"steps": {
|
||||
"theme": "Theme",
|
||||
"language": "Language",
|
||||
"finish": "Done"
|
||||
},
|
||||
"theme": {
|
||||
"title": "Choose a theme",
|
||||
"description": "Select the appearance that suits you best"
|
||||
},
|
||||
"language": {
|
||||
"title": "Choose a language",
|
||||
"description": "Select the language you want to use"
|
||||
},
|
||||
"finish": {
|
||||
"title": "All set!",
|
||||
"description": "Settings saved successfully. Let's start learning!",
|
||||
"about": "About",
|
||||
"about_desc": "Learn more about the project",
|
||||
"support": "Support",
|
||||
"support_desc": "Support the development",
|
||||
"github": "GitHub",
|
||||
"github_desc": "View the source code",
|
||||
"discord": "Discord",
|
||||
"discord_desc": "Join the community",
|
||||
"start": "Get Started"
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
"save": "Save",
|
||||
"cancel": "Cancel",
|
||||
@@ -340,8 +421,27 @@
|
||||
"friday": "Friday",
|
||||
"saturday": "Saturday",
|
||||
"sunday": "Sunday",
|
||||
"mon": "M",
|
||||
"tue": "T",
|
||||
"wed": "W",
|
||||
"thu": "T",
|
||||
"fri": "F",
|
||||
"sat": "S",
|
||||
"sun": "S",
|
||||
"today": "Today",
|
||||
"tomorrow": "Tomorrow"
|
||||
"tomorrow": "Tomorrow",
|
||||
"january": "January",
|
||||
"february": "February",
|
||||
"march": "March",
|
||||
"april": "April",
|
||||
"may": "May",
|
||||
"june": "June",
|
||||
"july": "July",
|
||||
"august": "August",
|
||||
"september": "September",
|
||||
"october": "October",
|
||||
"november": "November",
|
||||
"december": "December"
|
||||
},
|
||||
"months": {
|
||||
"january": "January",
|
||||
|
||||
116
i18n/hu.json
116
i18n/hu.json
@@ -5,8 +5,47 @@
|
||||
},
|
||||
"settings": {
|
||||
"title": "Beállítások",
|
||||
"appearance": "Megjelenés",
|
||||
"theme": "Téma",
|
||||
"language": "Nyelv",
|
||||
"tabs": {
|
||||
"appearance": "Megjelenés",
|
||||
"settings": "Beállítások",
|
||||
"about": "Névjegy"
|
||||
},
|
||||
"page_settings": {
|
||||
"title": "Oldal beállítások",
|
||||
"current_page": "Aktuális oldal",
|
||||
"no_settings": "Ehhez az oldalhoz nincsenek egyéni beállítások.",
|
||||
"login": {
|
||||
"hide_system_message": "Rendszerüzenet elrejtése",
|
||||
"hide_system_message_desc": "A bejelentkezési oldalon megjelenő rendszerüzenet elrejtése",
|
||||
"hide_school_info": "Iskola nevének és azonosítójának elrejtése",
|
||||
"hide_school_info_desc": "Az iskola neve és KRÉTA azonosítója nem jelenik meg"
|
||||
},
|
||||
"roleselect": {
|
||||
"auto_redirect": "Automatikus továbblépés",
|
||||
"auto_redirect_desc": "Automatikusan átirányít az ellenőrzőkönyvre",
|
||||
"hide_school_info": "Iskola és név elrejtése",
|
||||
"hide_school_info_desc": "Az iskola neve és a felhasználó neve nem jelenik meg"
|
||||
},
|
||||
"bulletin": {
|
||||
"hide_grades": "Értékelések elrejtése",
|
||||
"hide_grades_desc": "Az értékeléseid kártya elrejtése",
|
||||
"hide_absences": "Mulasztások elrejtése",
|
||||
"hide_absences_desc": "A mulasztások kártya elrejtése",
|
||||
"hide_notes": "Feljegyzések elrejtése",
|
||||
"hide_notes_desc": "A feljegyzések kártya elrejtése",
|
||||
"hide_exams": "Bejelentett dolgozatok elrejtése",
|
||||
"hide_exams_desc": "A bejelentett dolgozatok kártya elrejtése"
|
||||
},
|
||||
"grades": {
|
||||
"hide_chart": "Grafikon elrejtése",
|
||||
"hide_chart_desc": "A jegyek grafikonjának elrejtése",
|
||||
"hide_class_average": "Osztályátlag elrejtése",
|
||||
"hide_class_average_desc": "Az osztályátlag értékek elrejtése"
|
||||
}
|
||||
},
|
||||
"themes": {
|
||||
"light_blue": "Világos Kék",
|
||||
"light_green": "Világos Zöld",
|
||||
@@ -24,7 +63,7 @@
|
||||
"custom_themes": {
|
||||
"title": "Egyéni témák",
|
||||
"no_themes": "Még nincsenek egyéni témák",
|
||||
"create": "Új téma létrehozása",
|
||||
"create": "Új téma",
|
||||
"edit": "Téma szerkesztése",
|
||||
"name": "Téma neve",
|
||||
"mode": "Mód",
|
||||
@@ -36,17 +75,20 @@
|
||||
"card_color": "Kártya szín",
|
||||
"text_color": "Szöveg szín",
|
||||
"preview": "Előnézet",
|
||||
"share": "Téma megosztása",
|
||||
"share": "Megosztás",
|
||||
"share_description": "Másold ki a kódot és oszd meg másokkal:",
|
||||
"import": "Téma importálása",
|
||||
"import": "Importálás",
|
||||
"import_description": "Illeszd be a téma kódot:",
|
||||
"import_error_empty": "Kérlek illeszd be a téma kódot!",
|
||||
"import_error_invalid": "Érvénytelen téma kód!",
|
||||
"delete_confirm": "Biztosan törölni szeretnéd ezt a témát?"
|
||||
"delete_confirm": "Biztosan törölni szeretnéd ezt a témát?",
|
||||
"delete": "Törlés",
|
||||
"manage": "Kezelés"
|
||||
},
|
||||
"languages": {
|
||||
"hu": "Magyar",
|
||||
"en": "English"
|
||||
"en": "English",
|
||||
"de": "Deutsch"
|
||||
},
|
||||
"about": {
|
||||
"title": "Névjegy",
|
||||
@@ -57,6 +99,13 @@
|
||||
"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"
|
||||
},
|
||||
"error_reporting": {
|
||||
"title": "Hibajelentés",
|
||||
"enable": "Hibajelentés engedélyezése",
|
||||
"enable_desc": "Automatikus hibajelentés küldése a fejlesztőknek a bővítmény javításához",
|
||||
"report_issue": "Hiba vagy ötlet jelentése",
|
||||
"report_issue_desc": "Nyiss egy GitHub issue-t"
|
||||
}
|
||||
},
|
||||
"navigation": {
|
||||
@@ -217,9 +266,12 @@
|
||||
"last_month": "Előző hónap",
|
||||
"current_semester": "Aktuális félév",
|
||||
"last_30_days": "Utolsó 30 nap",
|
||||
"total_absences": "Összes hiányzás",
|
||||
"total_absences": "Összes mulasztás",
|
||||
"topic": "Téma",
|
||||
"status": "Állapot"
|
||||
"status": "Állapot",
|
||||
"absence_type": "Hiányzás",
|
||||
"late_type": "Késés",
|
||||
"minutes": "perc"
|
||||
},
|
||||
"profile": {
|
||||
"title": "Profil",
|
||||
@@ -311,6 +363,35 @@
|
||||
"success": "Sikeres kijelentkezés!",
|
||||
"continue": "Tovább"
|
||||
},
|
||||
"setup": {
|
||||
"welcome": "Állítsd be a bővítményt néhány egyszerű lépésben",
|
||||
"steps": {
|
||||
"theme": "Téma",
|
||||
"language": "Nyelv",
|
||||
"finish": "Kész"
|
||||
},
|
||||
"theme": {
|
||||
"title": "Válassz témát",
|
||||
"description": "Válaszd ki a számodra legmegfelelőbb megjelenést"
|
||||
},
|
||||
"language": {
|
||||
"title": "Válassz nyelvet",
|
||||
"description": "Válaszd ki a használni kívánt nyelvet"
|
||||
},
|
||||
"finish": {
|
||||
"title": "Minden kész!",
|
||||
"description": "A beállítások sikeresen mentve. Indulhat a tanulás!",
|
||||
"about": "Rólunk",
|
||||
"about_desc": "Tudj meg többet a projektről",
|
||||
"support": "Támogatás",
|
||||
"support_desc": "Támogasd a fejlesztést",
|
||||
"github": "GitHub",
|
||||
"github_desc": "Nézd meg a forráskódot",
|
||||
"discord": "Discord",
|
||||
"discord_desc": "Csatlakozz a közösséghez",
|
||||
"start": "Kezdés"
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
"save": "Mentés",
|
||||
"cancel": "Mégse",
|
||||
@@ -342,8 +423,27 @@
|
||||
"friday": "péntek",
|
||||
"saturday": "szombat",
|
||||
"sunday": "vasárnap",
|
||||
"mon": "H",
|
||||
"tue": "K",
|
||||
"wed": "Sze",
|
||||
"thu": "Cs",
|
||||
"fri": "P",
|
||||
"sat": "Szo",
|
||||
"sun": "V",
|
||||
"today": "Ma",
|
||||
"tomorrow": "Holnap"
|
||||
"tomorrow": "Holnap",
|
||||
"january": "Január",
|
||||
"february": "Február",
|
||||
"march": "Március",
|
||||
"april": "Április",
|
||||
"may": "Május",
|
||||
"june": "Június",
|
||||
"july": "Július",
|
||||
"august": "Augusztus",
|
||||
"september": "Szeptember",
|
||||
"october": "Október",
|
||||
"november": "November",
|
||||
"december": "December"
|
||||
},
|
||||
"months": {
|
||||
"january": "január",
|
||||
|
||||
1
icons/contact.svg
Normal file
1
icons/contact.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 24 24"><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 3.223a9.003 9.003 0 0 0-5.605 13.592L3 21l4.185-1.395A9.003 9.003 0 0 0 20.777 14m0-4A9.01 9.01 0 0 0 14 3.223M17 12a5 5 0 0 0-5-5m1 5a1 1 0 0 0-1-1"/></svg>
|
||||
|
After Width: | Height: | Size: 353 B |
1
icons/project.svg
Normal file
1
icons/project.svg
Normal file
@@ -0,0 +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="M3 20h.01M7 20a4 4 0 0 0-4-4m8 4a8 8 0 0 0-8-8"/><path fill="currentColor" d="M19 4H5a2 2 0 0 0-2 2v2.5c4 .167 12 2.7 12 11.5h4a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2"/></g></svg>
|
||||
|
After Width: | Height: | Size: 365 B |
BIN
images/firka_logo_128_c.png
Normal file
BIN
images/firka_logo_128_c.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.4 KiB |
BIN
images/firka_logo_c.png
Normal file
BIN
images/firka_logo_c.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 213 KiB |
@@ -54,7 +54,6 @@ body {
|
||||
flex-direction:column;
|
||||
align-items:center;
|
||||
gap:8px;
|
||||
margin:16px 0;
|
||||
background:var(--card-card) !important;
|
||||
border-bottom:1px solid rgba(0,0,0,0) !important;
|
||||
}
|
||||
@@ -66,6 +65,7 @@ body {
|
||||
font-style:normal;
|
||||
font-weight:700;
|
||||
line-height:normal;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.logo {
|
||||
width:48px;
|
||||
|
||||
@@ -1,11 +1,21 @@
|
||||
async function transformLoginPage() {
|
||||
try {
|
||||
while (
|
||||
typeof window.LanguageManager === "undefined" ||
|
||||
!window.LanguageManager.t("login.username_placeholder") ||
|
||||
window.LanguageManager.t("login.username_placeholder") === "login.username_placeholder"
|
||||
) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 50));
|
||||
}
|
||||
|
||||
if (document.readyState !== "complete") {
|
||||
await new Promise((resolve) => {
|
||||
window.addEventListener("load", resolve);
|
||||
});
|
||||
}
|
||||
|
||||
const loginSettings = await loadLoginSettings();
|
||||
|
||||
const existingForm = document.querySelector("form");
|
||||
const formData = {
|
||||
action: existingForm?.getAttribute("action") || "",
|
||||
@@ -50,11 +60,11 @@ async function transformLoginPage() {
|
||||
<img src=${chrome.runtime.getURL("images/firka_logo.png")} alt="Firka" class="logo">
|
||||
Firka
|
||||
</p>
|
||||
<h1 class="school-name">${schoolInfo.name}</h1>
|
||||
${!loginSettings.hideSchoolInfo ? `<h1 class="school-name">${schoolInfo.name}</h1>
|
||||
<div class="school-details">
|
||||
${schoolInfo.kretaId ? `<div>${schoolInfo.kretaId}</div>` : ""}
|
||||
${schoolInfo.omCode ? `<div>${LanguageManager.t("login.kreta_id")}: ${schoolInfo.omCode}</div>` : ""}
|
||||
</div>
|
||||
</div>` : ''}
|
||||
</div>
|
||||
|
||||
<form class="login-form" method="post" action="${formData.action}" id="loginForm" novalidate>
|
||||
@@ -93,7 +103,7 @@ async function transformLoginPage() {
|
||||
</div>
|
||||
|
||||
${
|
||||
systemMessage
|
||||
systemMessage && !loginSettings.hideSystemMessage
|
||||
? `
|
||||
<div class="system-message">
|
||||
<h4>${LanguageManager.t("login.system_message")}</h4>
|
||||
@@ -110,13 +120,11 @@ async function transformLoginPage() {
|
||||
</div>
|
||||
`;
|
||||
|
||||
document.body.innerHTML = '';
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(newHTML, 'text/html');
|
||||
const tempDiv = doc.body;
|
||||
while (tempDiv.firstChild) {
|
||||
document.body.appendChild(tempDiv.firstChild);
|
||||
}
|
||||
const template = document.createElement('template');
|
||||
template.innerHTML = newHTML;
|
||||
|
||||
helper.clearElement(document.body);
|
||||
document.body.appendChild(template.content);
|
||||
|
||||
setupEventListeners();
|
||||
} catch (error) {
|
||||
@@ -195,7 +203,35 @@ function handleSubmit(event) {
|
||||
form.submit();
|
||||
}
|
||||
|
||||
if (window.location.href.includes("idp.e-kreta.hu/Account/Login")) {
|
||||
transformLoginPage().catch((error) => {
|
||||
});
|
||||
async function loadLoginSettings() {
|
||||
try {
|
||||
const settings = await storageManager.get("pageSettings_login", {});
|
||||
return {
|
||||
hideSystemMessage: settings.hideSystemMessage || false,
|
||||
hideSchoolInfo: settings.hideSchoolInfo || false
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error loading login settings:", error);
|
||||
return {
|
||||
hideSystemMessage: false,
|
||||
hideSchoolInfo: false
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
||||
if (message.action === "pageSettingChanged" && message.pageType === "login") {
|
||||
transformLoginPage();
|
||||
}
|
||||
});
|
||||
|
||||
if (window.location.href.includes("idp.e-kreta.hu/Account/Login")) {
|
||||
(async () => {
|
||||
while (typeof window.LanguageManager === "undefined") {
|
||||
await new Promise((resolve) => setTimeout(resolve, 50));
|
||||
}
|
||||
transformLoginPage().catch((error) => {
|
||||
console.error("Error transforming login page:", error);
|
||||
});
|
||||
})();
|
||||
}
|
||||
|
||||
@@ -83,13 +83,12 @@ async function transformTwoFactorPage() {
|
||||
</div>
|
||||
`;
|
||||
|
||||
document.body.innerHTML = '';
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(newHTML, 'text/html');
|
||||
const tempDiv = doc.body;
|
||||
while (tempDiv.firstChild) {
|
||||
document.body.appendChild(tempDiv.firstChild);
|
||||
}
|
||||
const template = document.createElement('template');
|
||||
template.innerHTML = newHTML;
|
||||
|
||||
helper.clearElement(document.body);
|
||||
document.body.appendChild(template.content);
|
||||
|
||||
applyTheme();
|
||||
setupEventListeners();
|
||||
if (typeof loadingScreen !== "undefined") {
|
||||
@@ -170,7 +169,7 @@ function handleSubmit(event) {
|
||||
const submitButton = form.querySelector(".btn-kreta");
|
||||
if (submitButton) {
|
||||
submitButton.disabled = true;
|
||||
submitButton.innerHTML = '';
|
||||
helper.clearElement(submitButton);
|
||||
const spinnerSpan = document.createElement('span');
|
||||
spinnerSpan.className = 'spinner';
|
||||
const textSpan = document.createElement('span');
|
||||
|
||||
@@ -34,10 +34,10 @@
|
||||
</footer>
|
||||
</div>
|
||||
`;
|
||||
document.body.innerHTML = '';
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(newHTML, 'text/html');
|
||||
const tempDiv = doc.body;
|
||||
helper.clearElement(document.body);
|
||||
const template = document.createElement('template');
|
||||
template.innerHTML = newHTML;
|
||||
const tempDiv = template.content;
|
||||
while (tempDiv.firstChild) {
|
||||
document.body.appendChild(tempDiv.firstChild);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"manifest_version": 3,
|
||||
"name": "Firxa",
|
||||
"version": "1.4.2",
|
||||
"version": "1.4.6",
|
||||
"description": "KRÉTA webes verziójának újraírása",
|
||||
"icons": {
|
||||
"128": "images/firka_logo_128.png"
|
||||
@@ -13,7 +13,8 @@
|
||||
}
|
||||
},
|
||||
"permissions": [
|
||||
"storage"
|
||||
"storage",
|
||||
"tabs"
|
||||
],
|
||||
"background": {
|
||||
"service_worker": "tools/background.js"
|
||||
@@ -31,6 +32,7 @@
|
||||
{
|
||||
"resources": [
|
||||
"settings/*",
|
||||
"setup/*",
|
||||
"global/language.js",
|
||||
"images/*",
|
||||
"fonts/*.woff2",
|
||||
@@ -189,18 +191,6 @@
|
||||
],
|
||||
"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/"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"manifest_version": 3,
|
||||
"name": "Firxa",
|
||||
"version": "1.4.2",
|
||||
"version": "1.4.6",
|
||||
"description": "KRÉTA webes verziójának újraírása",
|
||||
"icons": {
|
||||
"128": "images/firka_logo_128.png"
|
||||
@@ -13,19 +13,18 @@
|
||||
}
|
||||
},
|
||||
"permissions": [
|
||||
"storage"
|
||||
"storage",
|
||||
"tabs"
|
||||
],
|
||||
"background": {
|
||||
"service_worker": "tools/background.js",
|
||||
"scripts": ["tools/background.js"],
|
||||
"persistent": false
|
||||
"scripts": ["tools/background.js"]
|
||||
},
|
||||
"browser_specific_settings": {
|
||||
"gecko": {
|
||||
"id": "firxa@zan1456.hu",
|
||||
"strict_min_version": "109.0",
|
||||
"data_collection_permissions": {
|
||||
"required": ["none"]
|
||||
"required": []
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -33,6 +32,7 @@
|
||||
{
|
||||
"resources": [
|
||||
"settings/*",
|
||||
"setup/*",
|
||||
"global/language.js",
|
||||
"images/*",
|
||||
"fonts/*.woff2",
|
||||
@@ -191,18 +191,6 @@
|
||||
],
|
||||
"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/"
|
||||
|
||||
@@ -440,7 +440,6 @@ body.modal-open {
|
||||
align-items: center;
|
||||
padding: 20px;
|
||||
border-bottom: 1px solid var(--background-0);
|
||||
background: var(--background);
|
||||
border-radius: 12px 12px 0 0;
|
||||
}
|
||||
|
||||
@@ -525,7 +524,7 @@ body.modal-open {
|
||||
}
|
||||
|
||||
.message-info {
|
||||
background-color: var(--card-card);
|
||||
background-color: var(--button-secondaryFill);
|
||||
padding: 15px;
|
||||
border-radius: 6px;
|
||||
margin-bottom: 20px;
|
||||
@@ -564,7 +563,7 @@ body.modal-open {
|
||||
}
|
||||
|
||||
.message-text {
|
||||
background-color: var(--card-card);
|
||||
background-color: var(--button-secondaryFill);
|
||||
border-radius: 6px;
|
||||
padding: 15px;
|
||||
line-height: 1.6;
|
||||
@@ -591,14 +590,14 @@ body.modal-open {
|
||||
}
|
||||
|
||||
.message-attachments {
|
||||
background: #f9f9f9;
|
||||
background: var(--button-secondaryFill);
|
||||
padding: 15px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.message-attachments h4 {
|
||||
margin: 0 0 10px 0;
|
||||
color: #333;
|
||||
color: var(--text-secondary);
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
function sanitizeHTML(html) {
|
||||
const div = document.createElement('div');
|
||||
div.textContent = html;
|
||||
return div.innerHTML;
|
||||
return div.textContent;
|
||||
}
|
||||
|
||||
class APIManager {
|
||||
@@ -159,18 +159,32 @@
|
||||
|
||||
const modalContent = document.createElement('div');
|
||||
modalContent.className = 'modal-content';
|
||||
modalContent.innerHTML = `
|
||||
<div class="modal-header">
|
||||
<h2>Üzenet részletei</h2>
|
||||
<button class="modal-close">×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="loading-content">
|
||||
<div class="loading-spinner"></div>
|
||||
<p>Üzenet betöltése...</p>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
const modalHeader = document.createElement('div');
|
||||
modalHeader.className = 'modal-header';
|
||||
const modalTitle = document.createElement('h2');
|
||||
modalTitle.textContent = 'Üzenet részletei';
|
||||
const modalClose = document.createElement('button');
|
||||
modalClose.className = 'modal-close';
|
||||
modalClose.textContent = '×';
|
||||
modalHeader.appendChild(modalTitle);
|
||||
modalHeader.appendChild(modalClose);
|
||||
|
||||
const modalBody = document.createElement('div');
|
||||
modalBody.className = 'modal-body';
|
||||
const loadingContent = document.createElement('div');
|
||||
loadingContent.className = 'loading-content';
|
||||
const loadingSpinner = document.createElement('div');
|
||||
loadingSpinner.className = 'loading-spinner';
|
||||
const loadingText = document.createElement('p');
|
||||
loadingText.textContent = 'Üzenet betöltése...';
|
||||
loadingContent.appendChild(loadingSpinner);
|
||||
loadingContent.appendChild(loadingText);
|
||||
modalBody.appendChild(loadingContent);
|
||||
|
||||
modalContent.appendChild(modalHeader);
|
||||
modalContent.appendChild(modalBody);
|
||||
|
||||
modalOverlay.appendChild(modalContent);
|
||||
document.body.appendChild(modalOverlay);
|
||||
|
||||
@@ -213,13 +227,24 @@
|
||||
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>
|
||||
`;
|
||||
const modalBody = modalContent.querySelector('.modal-body');
|
||||
helper.clearElement(modalBody);
|
||||
|
||||
const errorContent = document.createElement('div');
|
||||
errorContent.className = 'error-content';
|
||||
const errorTitle = document.createElement('h3');
|
||||
errorTitle.textContent = 'Hiba történt';
|
||||
const errorText = document.createElement('p');
|
||||
errorText.textContent = 'Az üzenet betöltése sikertelen.';
|
||||
const retryBtn = document.createElement('button');
|
||||
retryBtn.className = 'retry-btn';
|
||||
retryBtn.textContent = 'Újrapróbálás';
|
||||
retryBtn.onclick = () => openMessageModal(messageId);
|
||||
|
||||
errorContent.appendChild(errorTitle);
|
||||
errorContent.appendChild(errorText);
|
||||
errorContent.appendChild(retryBtn);
|
||||
modalBody.appendChild(errorContent);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -231,38 +256,90 @@
|
||||
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>
|
||||
`;
|
||||
const modalBody = modalContent.querySelector('.modal-body');
|
||||
helper.clearElement(modalBody);
|
||||
|
||||
const messageDetails = document.createElement('div');
|
||||
messageDetails.className = 'message-details';
|
||||
|
||||
const messageInfo = document.createElement('div');
|
||||
messageInfo.className = 'message-info';
|
||||
|
||||
const senderRow = document.createElement('div');
|
||||
senderRow.className = 'info-row';
|
||||
const senderLabel = document.createElement('span');
|
||||
senderLabel.className = 'info-label';
|
||||
senderLabel.textContent = 'Feladó:';
|
||||
const senderValue = document.createElement('span');
|
||||
senderValue.className = 'info-value';
|
||||
senderValue.textContent = sanitizeHTML(sender);
|
||||
senderRow.appendChild(senderLabel);
|
||||
senderRow.appendChild(senderValue);
|
||||
messageInfo.appendChild(senderRow);
|
||||
|
||||
const dateRow = document.createElement('div');
|
||||
dateRow.className = 'info-row';
|
||||
const dateLabel = document.createElement('span');
|
||||
dateLabel.className = 'info-label';
|
||||
dateLabel.textContent = 'Dátum:';
|
||||
const dateValue = document.createElement('span');
|
||||
dateValue.className = 'info-value';
|
||||
dateValue.textContent = date;
|
||||
dateRow.appendChild(dateLabel);
|
||||
dateRow.appendChild(dateValue);
|
||||
messageInfo.appendChild(dateRow);
|
||||
|
||||
const subjectRow = document.createElement('div');
|
||||
subjectRow.className = 'info-row';
|
||||
const subjectLabel = document.createElement('span');
|
||||
subjectLabel.className = 'info-label';
|
||||
subjectLabel.textContent = 'Tárgy:';
|
||||
const subjectValue = document.createElement('span');
|
||||
subjectValue.className = 'info-value';
|
||||
subjectValue.textContent = sanitizeHTML(subject);
|
||||
subjectRow.appendChild(subjectLabel);
|
||||
subjectRow.appendChild(subjectValue);
|
||||
messageInfo.appendChild(subjectRow);
|
||||
|
||||
messageDetails.appendChild(messageInfo);
|
||||
|
||||
const messageContent = document.createElement('div');
|
||||
messageContent.className = 'message-content';
|
||||
const contentTitle = document.createElement('h4');
|
||||
contentTitle.textContent = 'Üzenet tartalma:';
|
||||
messageContent.appendChild(contentTitle);
|
||||
const messageText = document.createElement('div');
|
||||
messageText.className = 'message-text';
|
||||
messageText.innerHTML = content;
|
||||
|
||||
messageContent.appendChild(messageText);
|
||||
messageDetails.appendChild(messageContent);
|
||||
|
||||
if (message.csatolmanyok && message.csatolmanyok.length > 0) {
|
||||
const messageAttachments = document.createElement('div');
|
||||
messageAttachments.className = 'message-attachments';
|
||||
const attachTitle = document.createElement('h4');
|
||||
attachTitle.textContent = 'Mellékletek:';
|
||||
messageAttachments.appendChild(attachTitle);
|
||||
|
||||
const attachList = document.createElement('ul');
|
||||
message.csatolmanyok.forEach(attachment => {
|
||||
const li = document.createElement('li');
|
||||
const a = document.createElement('a');
|
||||
a.href = '#';
|
||||
a.textContent = sanitizeHTML(attachment.fajlNev || attachment.nev || 'Ismeretlen fájl');
|
||||
a.onclick = (e) => {
|
||||
e.preventDefault();
|
||||
downloadAttachment(attachment.azonosito, attachment.fajlNev || attachment.nev);
|
||||
};
|
||||
li.appendChild(a);
|
||||
attachList.appendChild(li);
|
||||
});
|
||||
messageAttachments.appendChild(attachList);
|
||||
messageDetails.appendChild(messageAttachments);
|
||||
}
|
||||
|
||||
modalBody.appendChild(messageDetails);
|
||||
}
|
||||
|
||||
function closeMessageModal() {
|
||||
@@ -272,47 +349,90 @@
|
||||
}
|
||||
document.body.classList.remove('modal-open');
|
||||
}
|
||||
|
||||
async function downloadAttachment(azonosito, fileName) {
|
||||
try {
|
||||
const response = await chrome.runtime.sendMessage({
|
||||
action: 'download_attachment',
|
||||
azonosito: azonosito,
|
||||
fileName: fileName
|
||||
});
|
||||
|
||||
if (response.success && response.data) {
|
||||
const a = document.createElement('a');
|
||||
a.href = response.data;
|
||||
a.download = response.fileName || 'letoltes';
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
} else {
|
||||
console.error('Melléklet letöltési hiba:', response.error);
|
||||
alert('Nem sikerült letölteni a mellékletet.');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Melléklet letöltési hiba:', error);
|
||||
alert('Hiba történt a melléklet letöltése során.');
|
||||
}
|
||||
}
|
||||
|
||||
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>
|
||||
`;
|
||||
|
||||
const loadingContent = document.createElement('div');
|
||||
loadingContent.className = 'loading-content';
|
||||
const spinner = document.createElement('div');
|
||||
spinner.className = 'loading-spinner';
|
||||
const text = document.createElement('p');
|
||||
text.textContent = LanguageManager.t('messages.loading', 'Üzenetek betöltése...');
|
||||
loadingContent.appendChild(spinner);
|
||||
loadingContent.appendChild(text);
|
||||
loadingDiv.appendChild(loadingContent);
|
||||
|
||||
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');
|
||||
const errorContent = document.createElement('div');
|
||||
errorContent.className = 'error-content';
|
||||
const title = document.createElement('h3');
|
||||
title.textContent = LanguageManager.t('messages.error.title', 'Hiba történt');
|
||||
const desc = document.createElement('p');
|
||||
desc.textContent = LanguageManager.t('messages.error.description', 'Az üzenetek betöltése sikertelen volt.');
|
||||
const retryBtn = document.createElement('button');
|
||||
retryBtn.className = 'retry-btn';
|
||||
retryBtn.textContent = LanguageManager.t('messages.error.retry', 'Újrapróbálás');
|
||||
retryBtn.addEventListener('click', onRetry);
|
||||
|
||||
errorContent.appendChild(title);
|
||||
errorContent.appendChild(desc);
|
||||
errorContent.appendChild(retryBtn);
|
||||
errorDiv.appendChild(errorContent);
|
||||
|
||||
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>
|
||||
`;
|
||||
|
||||
const emptyContent = document.createElement('div');
|
||||
emptyContent.className = 'empty-content';
|
||||
const title = document.createElement('h3');
|
||||
title.textContent = LanguageManager.t('messages.empty.title', 'Nincsenek üzenetek');
|
||||
const desc = document.createElement('p');
|
||||
desc.textContent = LanguageManager.t('messages.empty.description', 'Jelenleg nincsenek elérhető üzenetek.');
|
||||
|
||||
emptyContent.appendChild(title);
|
||||
emptyContent.appendChild(desc);
|
||||
emptyDiv.appendChild(emptyContent);
|
||||
|
||||
return emptyDiv;
|
||||
}
|
||||
|
||||
@@ -332,18 +452,42 @@
|
||||
const subject = message.uzenetTargy || 'Nincs tárgy';
|
||||
const date = formatDate(message.uzenetKuldesDatum);
|
||||
const hasAttachment = message.hasCsatolmany;
|
||||
|
||||
const cardHeader = document.createElement('div');
|
||||
cardHeader.className = 'message-card-header';
|
||||
|
||||
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>' : ''}
|
||||
`;
|
||||
const senderInfo = document.createElement('div');
|
||||
senderInfo.className = 'sender-info';
|
||||
const senderNameSpan = document.createElement('span');
|
||||
senderNameSpan.className = 'sender-name';
|
||||
senderNameSpan.textContent = sanitizeHTML(senderName);
|
||||
senderInfo.appendChild(senderNameSpan);
|
||||
|
||||
if (!message.isElolvasva) {
|
||||
const unreadIndicator = document.createElement('span');
|
||||
unreadIndicator.className = 'unread-indicator';
|
||||
senderInfo.appendChild(unreadIndicator);
|
||||
}
|
||||
|
||||
const messageDate = document.createElement('div');
|
||||
messageDate.className = 'message-date';
|
||||
messageDate.textContent = date;
|
||||
|
||||
cardHeader.appendChild(senderInfo);
|
||||
cardHeader.appendChild(messageDate);
|
||||
card.appendChild(cardHeader);
|
||||
|
||||
const messageSubject = document.createElement('div');
|
||||
messageSubject.className = 'message-subject';
|
||||
messageSubject.textContent = sanitizeHTML(subject);
|
||||
card.appendChild(messageSubject);
|
||||
|
||||
if (hasAttachment) {
|
||||
const attachmentIndicator = document.createElement('div');
|
||||
attachmentIndicator.className = 'attachment-indicator';
|
||||
attachmentIndicator.textContent = '📎';
|
||||
card.appendChild(attachmentIndicator);
|
||||
}
|
||||
|
||||
|
||||
return card;
|
||||
@@ -397,41 +541,111 @@
|
||||
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>
|
||||
`;
|
||||
|
||||
const bulkActionsLeft = document.createElement('div');
|
||||
bulkActionsLeft.className = 'bulk-actions-left';
|
||||
|
||||
const viewToggle = document.createElement('div');
|
||||
viewToggle.className = 'view-toggle';
|
||||
|
||||
const viewInboxBtn = document.createElement('button');
|
||||
viewInboxBtn.id = 'viewInboxBtn';
|
||||
viewInboxBtn.className = currentView === 'inbox' ? 'active' : '';
|
||||
viewInboxBtn.title = 'Beérkezett';
|
||||
const inboxImg = document.createElement('img');
|
||||
inboxImg.src = chrome.runtime.getURL('icons/messages-active.svg');
|
||||
inboxImg.alt = 'Beérkezett';
|
||||
viewInboxBtn.appendChild(inboxImg);
|
||||
|
||||
const viewTrashBtn = document.createElement('button');
|
||||
viewTrashBtn.id = 'viewTrashBtn';
|
||||
viewTrashBtn.className = currentView === 'trash' ? 'active' : '';
|
||||
viewTrashBtn.title = 'Törölt';
|
||||
const trashImg = document.createElement('img');
|
||||
trashImg.src = chrome.runtime.getURL('icons/delete.svg');
|
||||
trashImg.alt = 'Törölt';
|
||||
viewTrashBtn.appendChild(trashImg);
|
||||
|
||||
viewToggle.appendChild(viewInboxBtn);
|
||||
viewToggle.appendChild(viewTrashBtn);
|
||||
bulkActionsLeft.appendChild(viewToggle);
|
||||
|
||||
const toggleSelBtn = document.createElement('button');
|
||||
toggleSelBtn.id = 'toggleSelectionModeBtn';
|
||||
toggleSelBtn.className = 'bulk-btn';
|
||||
toggleSelBtn.title = 'Kijelölés mód';
|
||||
const selectImg = document.createElement('img');
|
||||
selectImg.src = chrome.runtime.getURL('icons/select.svg');
|
||||
selectImg.alt = 'Kijelölés mód';
|
||||
toggleSelBtn.appendChild(selectImg);
|
||||
bulkActionsLeft.appendChild(toggleSelBtn);
|
||||
|
||||
const selectAllBtn = document.createElement('button');
|
||||
selectAllBtn.id = 'selectAllBtn';
|
||||
selectAllBtn.className = 'bulk-btn';
|
||||
selectAllBtn.title = 'Mind kijelöl';
|
||||
const selectAllImg = document.createElement('img');
|
||||
selectAllImg.src = chrome.runtime.getURL('icons/select-all.svg');
|
||||
selectAllImg.alt = 'Mind kijelöl';
|
||||
selectAllBtn.appendChild(selectAllImg);
|
||||
bulkActionsLeft.appendChild(selectAllBtn);
|
||||
|
||||
const clearSelBtn = document.createElement('button');
|
||||
clearSelBtn.id = 'clearSelectionBtn';
|
||||
clearSelBtn.className = 'bulk-btn';
|
||||
clearSelBtn.title = 'Kijelölés törlése';
|
||||
const clearSelImg = document.createElement('img');
|
||||
clearSelImg.src = chrome.runtime.getURL('icons/select-none.svg');
|
||||
clearSelImg.alt = 'Kijelölés törlése';
|
||||
clearSelBtn.appendChild(clearSelImg);
|
||||
bulkActionsLeft.appendChild(clearSelBtn);
|
||||
|
||||
bulk.appendChild(bulkActionsLeft);
|
||||
|
||||
const bulkActionsRight = document.createElement('div');
|
||||
bulkActionsRight.className = 'bulk-actions-right';
|
||||
|
||||
const markReadBtn = document.createElement('button');
|
||||
markReadBtn.id = 'markReadBtn';
|
||||
markReadBtn.className = 'bulk-btn';
|
||||
markReadBtn.title = 'Olvasott';
|
||||
const markReadImg = document.createElement('img');
|
||||
markReadImg.src = chrome.runtime.getURL('icons/eye-on.svg');
|
||||
markReadImg.alt = 'Olvasott';
|
||||
markReadBtn.appendChild(markReadImg);
|
||||
bulkActionsRight.appendChild(markReadBtn);
|
||||
|
||||
const markUnreadBtn = document.createElement('button');
|
||||
markUnreadBtn.id = 'markUnreadBtn';
|
||||
markUnreadBtn.className = 'bulk-btn';
|
||||
markUnreadBtn.title = 'Olvasatlan';
|
||||
const markUnreadImg = document.createElement('img');
|
||||
markUnreadImg.src = chrome.runtime.getURL('icons/eye-off.svg');
|
||||
markUnreadImg.alt = 'Olvasatlan';
|
||||
markUnreadBtn.appendChild(markUnreadImg);
|
||||
bulkActionsRight.appendChild(markUnreadBtn);
|
||||
|
||||
const deleteBtn = document.createElement('button');
|
||||
deleteBtn.id = 'deleteBtn';
|
||||
deleteBtn.className = 'bulk-btn';
|
||||
deleteBtn.title = 'Törlés';
|
||||
const deleteImg = document.createElement('img');
|
||||
deleteImg.src = chrome.runtime.getURL('icons/trash.svg');
|
||||
deleteImg.alt = 'Törlés';
|
||||
deleteBtn.appendChild(deleteImg);
|
||||
bulkActionsRight.appendChild(deleteBtn);
|
||||
|
||||
const restoreBtn = document.createElement('button');
|
||||
restoreBtn.id = 'restoreBtn';
|
||||
restoreBtn.className = 'bulk-btn';
|
||||
restoreBtn.title = 'Visszaállítás';
|
||||
const restoreImg = document.createElement('img');
|
||||
restoreImg.src = chrome.runtime.getURL('icons/undo.svg');
|
||||
restoreImg.alt = 'Visszaállítás';
|
||||
restoreBtn.appendChild(restoreImg);
|
||||
bulkActionsRight.appendChild(restoreBtn);
|
||||
|
||||
bulk.appendChild(bulkActionsRight);
|
||||
container.appendChild(bulk);
|
||||
|
||||
bulk.querySelector('#viewInboxBtn').addEventListener('click', () => switchView('inbox'));
|
||||
@@ -568,13 +782,13 @@ async function switchView(view) {
|
||||
async function transformMessagesPage() {
|
||||
try {
|
||||
await waitForTranslations();
|
||||
document.body.innerHTML = '';
|
||||
helper.clearElement(document.body);
|
||||
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;
|
||||
const template = document.createElement('template');
|
||||
template.innerHTML = await createTemplate.header();
|
||||
const headerContent = template.content;
|
||||
while (headerContent.firstChild) {
|
||||
headerDiv.appendChild(headerContent.firstChild);
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
const backButton = document.createElement('button');
|
||||
backButton.id = 'firka-back-button';
|
||||
backButton.innerHTML = '← Vissza';
|
||||
backButton.textContent = '← Vissza';
|
||||
backButton.style.cssText = `
|
||||
position: static;
|
||||
margin: 20px;
|
||||
|
||||
@@ -18,6 +18,25 @@
|
||||
return setInterval(updateTimer, 1000);
|
||||
};
|
||||
|
||||
async function loadRoleselectSettings() {
|
||||
try {
|
||||
const settings = await storageManager.get("pageSettings_roleselect", {
|
||||
autoRedirect: false,
|
||||
hideSchoolInfo: true
|
||||
});
|
||||
return {
|
||||
autoRedirect: settings.autoRedirect || false,
|
||||
hideSchoolInfo: settings.hideSchoolInfo !== undefined ? settings.hideSchoolInfo : true
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error loading roleselect settings:", error);
|
||||
return {
|
||||
autoRedirect: false,
|
||||
hideSchoolInfo: true
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const handleRoleChange = async (role) => {
|
||||
try {
|
||||
const response = await fetch(
|
||||
@@ -41,7 +60,7 @@
|
||||
}
|
||||
};
|
||||
|
||||
const createHTML = (schoolCode, fullSchoolName, userName) => `
|
||||
const createHTML = (schoolCode, fullSchoolName, userName, settings) => `
|
||||
<div class="kreta-container">
|
||||
<header class="kreta-header">
|
||||
<div class="school-info">
|
||||
@@ -49,15 +68,17 @@
|
||||
<img src=${chrome.runtime.getURL("images/firka_logo.png")} alt="Firka" class="logo">
|
||||
Firka
|
||||
</p>
|
||||
<div class="school-details">
|
||||
${!settings.hideSchoolInfo ? `<div class="school-details">
|
||||
<span>${schoolCode || ""} - ${fullSchoolName || "Iskola"}</span>
|
||||
</div>
|
||||
</div>` : ''}
|
||||
</div>
|
||||
<div class="user-profile">
|
||||
<div class="user-info">
|
||||
${!settings.hideSchoolInfo ? `<div class="user-info">
|
||||
<span class="user-name">${userName}</span>
|
||||
<span class="logout-timer" id="logoutTimer">5:00</span>
|
||||
</div>
|
||||
</div>` : `<div class="user-info">
|
||||
<span class="logout-timer" id="logoutTimer">5:00</span>
|
||||
</div>`}
|
||||
</div>
|
||||
</header>
|
||||
|
||||
@@ -108,6 +129,12 @@
|
||||
window.addEventListener("load", resolve),
|
||||
);
|
||||
}
|
||||
const settings = await loadRoleselectSettings();
|
||||
|
||||
if (settings.autoRedirect) {
|
||||
handleRoleChange("Ellenorzo");
|
||||
return;
|
||||
}
|
||||
|
||||
const schoolNameEl = document.querySelector(".IntezmenyNev");
|
||||
const schoolName = schoolNameEl?.textContent.trim() || "Iskola neve";
|
||||
@@ -127,14 +154,15 @@
|
||||
if (userName) {
|
||||
await storageManager.set("userName", userName);
|
||||
}
|
||||
document.body.innerHTML = '';
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(createHTML(
|
||||
helper.clearElement(document.body);
|
||||
const template = document.createElement('template');
|
||||
template.innerHTML = createHTML(
|
||||
schoolCode,
|
||||
fullSchoolName,
|
||||
userName,
|
||||
), 'text/html');
|
||||
const tempDiv = doc.body;
|
||||
settings,
|
||||
);
|
||||
const tempDiv = template.content;
|
||||
while (tempDiv.firstChild) {
|
||||
document.body.appendChild(tempDiv.firstChild);
|
||||
}
|
||||
|
||||
@@ -57,10 +57,13 @@ body {
|
||||
align-items:center;
|
||||
gap:8px;
|
||||
}
|
||||
.logo {
|
||||
.logos {
|
||||
width:32px;
|
||||
height:32px;
|
||||
}
|
||||
.logo {
|
||||
display: none !important;
|
||||
}
|
||||
.search-title {
|
||||
color:var(--text-primary);
|
||||
text-align:center;
|
||||
|
||||
@@ -47,7 +47,7 @@ async function applyFirkaStyling() {
|
||||
const logoImg = document.createElement('img');
|
||||
logoImg.src = chrome.runtime.getURL('images/firka_logo.png');
|
||||
logoImg.alt = 'Firka';
|
||||
logoImg.className = 'logo';
|
||||
logoImg.className = 'logos';
|
||||
|
||||
logoText.appendChild(logoImg);
|
||||
logoText.appendChild(document.createTextNode('Firka'));
|
||||
|
||||
1061
settings/index.css
1061
settings/index.css
File diff suppressed because it is too large
Load Diff
@@ -22,106 +22,203 @@
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<div class="settings-card">
|
||||
<h2 data-i18n="settings.title">Beállítások</h2>
|
||||
<div class="settings-group">
|
||||
<div class="setting-section">
|
||||
<div class="setting-header">
|
||||
<span class="material-icons-round">palette</span>
|
||||
<span data-i18n="settings.theme">Téma</span>
|
||||
</div>
|
||||
<div class="theme-grid">
|
||||
<button class="theme-option" data-theme="light-green">
|
||||
<div class="theme-preview light-green">
|
||||
<div class="preview-header"></div>
|
||||
<div class="preview-content">
|
||||
<div class="preview-card"></div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="theme-name" data-i18n="settings.themes.light_green">Világos Zöld</span>
|
||||
</button>
|
||||
<div class="tab-navigation">
|
||||
<button class="tab-button active" data-tab="appearance">
|
||||
<span class="material-icons-round">palette</span>
|
||||
<span data-i18n="settings.tabs.appearance">Megjelenés</span>
|
||||
</button>
|
||||
<button class="tab-button" data-tab="settings">
|
||||
<span class="material-icons-round">tune</span>
|
||||
<span data-i18n="settings.tabs.settings">Beállítások</span>
|
||||
</button>
|
||||
<button class="tab-button" data-tab="about">
|
||||
<span class="material-icons-round">info</span>
|
||||
<span data-i18n="settings.tabs.about">Névjegy</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<button class="theme-option" data-theme="dark-green">
|
||||
<div class="theme-preview dark-green">
|
||||
<div class="preview-header"></div>
|
||||
<div class="preview-content">
|
||||
<div class="preview-card"></div>
|
||||
</div>
|
||||
<div class="tab-content active" id="tab-appearance">
|
||||
<div class="settings-card">
|
||||
<div class="settings-group">
|
||||
<div class="setting-section">
|
||||
<div class="setting-header theme-header">
|
||||
<div class="setting-header-left">
|
||||
<span class="material-icons-round">palette</span>
|
||||
<span data-i18n="settings.theme">Téma</span>
|
||||
</div>
|
||||
<span class="theme-name" data-i18n="settings.themes.dark_green">Sötét Zöld</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="custom-themes-section">
|
||||
<div class="custom-themes-header">
|
||||
<span data-i18n="settings.custom_themes.title">Egyéni témák</span>
|
||||
<div class="custom-themes-buttons">
|
||||
<button class="theme-btn" id="addCustomTheme" title="Új téma">
|
||||
<span class="material-icons-round">add</span>
|
||||
</button>
|
||||
<button class="theme-btn" id="importCustomTheme" title="Téma importálása">
|
||||
<span class="material-icons-round">download</span>
|
||||
<div class="theme-manage-dropdown">
|
||||
<button class="theme-manage-btn" id="themeManageBtn">
|
||||
<span class="material-icons-round">settings</span>
|
||||
</button>
|
||||
<div class="theme-manage-menu" id="themeManageMenu">
|
||||
<button class="theme-manage-item" id="addCustomTheme">
|
||||
<span class="material-icons-round">add</span>
|
||||
<span data-i18n="settings.custom_themes.create">Új téma létrehozása</span>
|
||||
</button>
|
||||
<button class="theme-manage-item" id="importCustomTheme">
|
||||
<span class="material-icons-round">download</span>
|
||||
<span data-i18n="settings.custom_themes.import">Importálás</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="theme-grid custom-themes-grid" id="customThemesGrid">
|
||||
<div class="theme-grid" id="allThemesGrid">
|
||||
<button class="theme-option" data-theme="light-green">
|
||||
<div class="theme-preview light-green">
|
||||
<div class="preview-header"></div>
|
||||
<div class="preview-content">
|
||||
<div class="preview-card"></div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="theme-name" data-i18n="settings.themes.light_green">Világos Zöld</span>
|
||||
</button>
|
||||
|
||||
<button class="theme-option" data-theme="dark-green">
|
||||
<div class="theme-preview dark-green">
|
||||
<div class="preview-header"></div>
|
||||
<div class="preview-content">
|
||||
<div class="preview-card"></div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="theme-name" data-i18n="settings.themes.dark_green">Sötét Zöld</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="setting-section">
|
||||
<div class="setting-header">
|
||||
<span class="material-icons-round">language</span>
|
||||
<span data-i18n="settings.language">Nyelv</span>
|
||||
</div>
|
||||
<div class="language-grid">
|
||||
<button class="language-option" data-language="hu">
|
||||
<span class="language-name" data-i18n="settings.languages.hu">Magyar</span>
|
||||
</button>
|
||||
<button class="language-option" data-language="en">
|
||||
<span class="language-name" data-i18n="settings.languages.en">English</span>
|
||||
</button>
|
||||
<button class="language-option" data-language="de">
|
||||
<span class="language-name" data-i18n="settings.languages.de">Deutsch</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="setting-section">
|
||||
<div class="setting-header">
|
||||
<span class="material-icons-round">language</span>
|
||||
<span data-i18n="settings.language">Nyelv</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab-content" id="tab-settings">
|
||||
<div class="settings-card">
|
||||
<h2 data-i18n="settings.page_settings.title">Oldal beállítások</h2>
|
||||
<div class="page-settings-info">
|
||||
<div class="current-page-indicator">
|
||||
<span class="material-icons-round">web</span>
|
||||
<span id="currentPageName" data-i18n="settings.page_settings.current_page">Aktuális oldal</span>
|
||||
<span id="currentPageValue" class="page-badge">-</span>
|
||||
</div>
|
||||
<div class="language-grid">
|
||||
<button class="language-option" data-language="hu">
|
||||
<span class="language-name" data-i18n="settings.languages.hu">Magyar</span>
|
||||
</button>
|
||||
<button class="language-option" data-language="en">
|
||||
<span class="language-name" data-i18n="settings.languages.en">English</span>
|
||||
</button>
|
||||
</div>
|
||||
<div id="pageSpecificSettings" class="page-specific-settings">
|
||||
<div class="no-settings-placeholder">
|
||||
<span class="material-icons-round">settings_suggest</span>
|
||||
<p data-i18n="settings.page_settings.no_settings">Ehhez az oldalhoz nincsenek egyéni beállítások.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="error-reporting-card">
|
||||
<h2 data-i18n="settings.error_reporting.title">Hibajelentés</h2>
|
||||
<div class="error-reporting-content">
|
||||
<div class="setting-item">
|
||||
<div class="setting-item-info">
|
||||
<div class="setting-item-label" data-i18n="settings.error_reporting.enable">Hibajelentés engedélyezése</div>
|
||||
<div class="setting-item-description" data-i18n="settings.error_reporting.enable_desc">Automatikus hibajelentés küldése a fejlesztőknek a bővítmény javításához</div>
|
||||
</div>
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="errorReportingToggle" checked>
|
||||
<span class="toggle-slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="report-issue-button-wrapper">
|
||||
<a href="https://github.com/QwIT-Development/firka-extension/issues/new" target="_blank" class="report-issue-button">
|
||||
<span class="material-icons-round">bug_report</span>
|
||||
<div class="report-issue-text">
|
||||
<span class="report-issue-title" data-i18n="settings.error_reporting.report_issue">Hiba vagy ötlet jelentése</span>
|
||||
<span class="report-issue-desc" data-i18n="settings.error_reporting.report_issue_desc">Nyiss egy GitHub issue-t</span>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="about-card">
|
||||
<h2 data-i18n="settings.about.title">Névjegy</h2>
|
||||
<div class="about-content">
|
||||
<p data-i18n="settings.about.description">A Firka egy nyílt forráskódú projekt, amely a KRÉTA rendszerhez készít saját felhasználói felületet.</p>
|
||||
<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 class="tab-content" id="tab-about">
|
||||
<div class="about-card">
|
||||
<h2 data-i18n="settings.about.title">Névjegy</h2>
|
||||
<div class="about-content">
|
||||
<p data-i18n="settings.about.description">A Firka egy nyílt forráskódú projekt, amely a KRÉTA rendszerhez készít saját felhasználói felületet.</p>
|
||||
<div class="about-links">
|
||||
<a href="https://firka.app" target="_blank" class="about-link">
|
||||
<span class="material-icons-round">language</span>
|
||||
<span>Weboldal</span>
|
||||
</a>
|
||||
<a href="https://github.com/QwIT-Development/" target="_blank" class="about-link">
|
||||
<span class="material-icons-round">code</span>
|
||||
<span data-i18n="settings.about.github">GitHub</span>
|
||||
</a>
|
||||
<a href="https://discord.gg/firka-1111649116020285532" target="_blank" class="about-link">
|
||||
<span class="material-icons-round">forum</span>
|
||||
<span>Discord</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="support-card">
|
||||
<h2 data-i18n="settings.support.title">Támogatás</h2>
|
||||
<div class="support-content">
|
||||
<p data-i18n="settings.support.description">Ha tetszik a munkánk és szeretnéd támogatni a fejlesztést, az alábbi módon teheted meg:</p>
|
||||
<div class="support-buttons">
|
||||
<a href="https://ko-fi.com/zan1456" target="_blank" class="support-button">
|
||||
<span class="material-icons-round">coffee</span>
|
||||
<span data-i18n="settings.support.kofi">Ko-Fi</span>
|
||||
</a>
|
||||
<div class="support-card">
|
||||
<h2 data-i18n="settings.support.title">Támogatás</h2>
|
||||
<div class="support-content">
|
||||
<p data-i18n="settings.support.description">Ha tetszik a munkánk és szeretnéd támogatni a fejlesztést, az alábbi módon teheted meg:</p>
|
||||
<div class="support-buttons">
|
||||
<a href="https://ko-fi.com/zan1456" target="_blank" class="support-button">
|
||||
<span class="material-icons-round">coffee</span>
|
||||
<span data-i18n="settings.support.kofi">Ko-Fi</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="version-info" id="version">v1.4.0</div>
|
||||
</div>
|
||||
|
||||
<footer class="popup-footer">
|
||||
<div class="version-info" id="version">v1.3.0</div>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<div class="modal-overlay" id="updateModal">
|
||||
<div class="modal-content update-modal">
|
||||
<div class="modal-header">
|
||||
<h3 data-i18n="settings.update.title">Új verzió érhető el!</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="update-version-info">
|
||||
<div class="version-badge current">
|
||||
<span class="version-label" data-i18n="settings.update.current_version">Jelenlegi verzió:</span>
|
||||
<span class="version-number" id="currentVersion">-</span>
|
||||
</div>
|
||||
<span class="material-icons-round version-arrow">arrow_forward</span>
|
||||
<div class="version-badge latest">
|
||||
<span class="version-label" data-i18n="settings.update.latest_version">Legújabb verzió:</span>
|
||||
<span class="version-number" id="latestVersion">-</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="update-changelog-section">
|
||||
<h4 data-i18n="settings.update.whats_new">Újdonságok:</h4>
|
||||
<div class="update-changelog" id="updateChangelog">
|
||||
<p data-i18n="settings.update.loading">Betöltés...</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn-secondary" id="dismissUpdate" data-i18n="settings.update.dismiss">Bezárás</button>
|
||||
<a class="btn-primary update-button" id="updateButton" href="#" target="_blank">
|
||||
<span data-i18n="settings.update.download">Letöltés</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal-overlay" id="themeEditorModal">
|
||||
@@ -240,6 +337,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="../tools/helper.js"></script>
|
||||
<script src="../tools/storageManager.js"></script>
|
||||
<script src="../global/language.js"></script>
|
||||
<script src="index.js"></script>
|
||||
|
||||
@@ -5,6 +5,349 @@ document.addEventListener("DOMContentLoaded", async () => {
|
||||
|
||||
let customThemes = [];
|
||||
let editingThemeId = null;
|
||||
let currentPageType = null;
|
||||
|
||||
async function initTabs() {
|
||||
const tabButtons = document.querySelectorAll(".tab-button");
|
||||
const tabContents = document.querySelectorAll(".tab-content");
|
||||
|
||||
const lastTab = localStorage.getItem("settingsLastTab") || "appearance";
|
||||
switchTab(lastTab);
|
||||
|
||||
tabButtons.forEach(button => {
|
||||
button.addEventListener("click", () => {
|
||||
const tabId = button.dataset.tab;
|
||||
switchTab(tabId);
|
||||
localStorage.setItem("settingsLastTab", tabId);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function switchTab(tabId) {
|
||||
const tabButtons = document.querySelectorAll(".tab-button");
|
||||
const tabContents = document.querySelectorAll(".tab-content");
|
||||
|
||||
tabButtons.forEach(btn => {
|
||||
btn.classList.toggle("active", btn.dataset.tab === tabId);
|
||||
});
|
||||
|
||||
tabContents.forEach(content => {
|
||||
content.classList.toggle("active", content.id === `tab-${tabId}`);
|
||||
});
|
||||
|
||||
if (tabId === "settings") {
|
||||
detectCurrentPage();
|
||||
}
|
||||
}
|
||||
|
||||
async function detectCurrentPage() {
|
||||
try {
|
||||
const tabs = await chrome.tabs.query({ active: true, currentWindow: true });
|
||||
if (tabs.length > 0) {
|
||||
const url = tabs[0].url || "";
|
||||
currentPageType = getPageTypeFromUrl(url);
|
||||
updateCurrentPageIndicator(currentPageType);
|
||||
await loadPageSpecificSettings(currentPageType);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error detecting current page:", error);
|
||||
currentPageType = "unknown";
|
||||
updateCurrentPageIndicator(currentPageType);
|
||||
await loadPageSpecificSettings(currentPageType);
|
||||
}
|
||||
}
|
||||
|
||||
function getPageTypeFromUrl(url) {
|
||||
if (url.includes("/Orarend/InformaciokOrarend")) return "timetable";
|
||||
if (url.includes("/TanuloErtekeles/Osztalyzatok")) return "grades";
|
||||
if (url.includes("/Hianyzas/Hianyzasok")) return "absences";
|
||||
if (url.includes("/uzenetek")) return "messages";
|
||||
if (url.includes("/Adminisztracio/Profil")) return "profile";
|
||||
if (url.includes("intezmenykereso.e-kreta.hu/")) return "search";
|
||||
if (url.includes("/Intezmeny/Faliujsag")) return "dashboard";
|
||||
if (url.includes("/Adminisztracio/BelepesKezelo")) return "roleselect";
|
||||
if (url.includes("/ElfelejtettJelszo")) return "forgotpassword";
|
||||
if (url.includes("idp.e-kreta.hu/Account/Login")) return "login";
|
||||
if (url.includes("/Login") || url.includes("/Belepes")) return "login";
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
function getPageDisplayName(pageType) {
|
||||
const pageNames = {
|
||||
"timetable": "Órarend",
|
||||
"grades": "Jegyek",
|
||||
"absences": "Mulasztások",
|
||||
"messages": "Üzenetek",
|
||||
"profile": "Profil",
|
||||
"search": "Intézménykereső",
|
||||
"dashboard": "Faliújság",
|
||||
"roleselect": "Szerepkörválasztó",
|
||||
"login": "Bejelentkezés",
|
||||
"forgotpassword": "Elfelejtett jelszó",
|
||||
"unknown": "Ismeretlen"
|
||||
};
|
||||
return pageNames[pageType] || pageType;
|
||||
}
|
||||
|
||||
function updateCurrentPageIndicator(pageType) {
|
||||
const pageValueEl = document.getElementById("currentPageValue");
|
||||
if (pageValueEl) {
|
||||
pageValueEl.textContent = getPageDisplayName(pageType);
|
||||
}
|
||||
}
|
||||
|
||||
async function getLoginPageSettings() {
|
||||
const settingsKey = "pageSettings_login";
|
||||
const savedSettings = await storageManager.get(settingsKey, {});
|
||||
|
||||
return [
|
||||
{
|
||||
key: "hideSystemMessage",
|
||||
type: "toggle",
|
||||
label: "Rendszerüzenet elrejtése",
|
||||
labelKey: "settings.page_settings.login.hide_system_message",
|
||||
description: "A bejelentkezési oldalon megjelenő rendszerüzenet elrejtése",
|
||||
descriptionKey: "settings.page_settings.login.hide_system_message_desc",
|
||||
value: savedSettings.hideSystemMessage || false
|
||||
},
|
||||
{
|
||||
key: "hideSchoolInfo",
|
||||
type: "toggle",
|
||||
label: "Iskola nevének és azonosítójának elrejtése",
|
||||
labelKey: "settings.page_settings.login.hide_school_info",
|
||||
description: "Az iskola neve és KRÉTA azonosítója nem jelenik meg",
|
||||
descriptionKey: "settings.page_settings.login.hide_school_info_desc",
|
||||
value: savedSettings.hideSchoolInfo || false
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
async function getRoleselectPageSettings() {
|
||||
const settingsKey = "pageSettings_roleselect";
|
||||
const savedSettings = await storageManager.get(settingsKey, {
|
||||
autoRedirect: false,
|
||||
hideSchoolInfo: true
|
||||
});
|
||||
|
||||
return [
|
||||
{
|
||||
key: "autoRedirect",
|
||||
type: "toggle",
|
||||
label: "Automatikus továbblépés",
|
||||
labelKey: "settings.page_settings.roleselect.auto_redirect",
|
||||
description: "Automatikusan átirányít az ellenőrzőkönyvre",
|
||||
descriptionKey: "settings.page_settings.roleselect.auto_redirect_desc",
|
||||
value: savedSettings.autoRedirect || false
|
||||
},
|
||||
{
|
||||
key: "hideSchoolInfo",
|
||||
type: "toggle",
|
||||
label: "Iskola és név elrejtése",
|
||||
labelKey: "settings.page_settings.roleselect.hide_school_info",
|
||||
description: "Az iskola neve és a felhasználó neve nem jelenik meg",
|
||||
descriptionKey: "settings.page_settings.roleselect.hide_school_info_desc",
|
||||
value: savedSettings.hideSchoolInfo !== undefined ? savedSettings.hideSchoolInfo : true
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
async function getBulletinPageSettings() {
|
||||
const settingsKey = "pageSettings_bulletin";
|
||||
const savedSettings = await storageManager.get(settingsKey, {});
|
||||
|
||||
return [
|
||||
{
|
||||
key: "hideGrades",
|
||||
type: "toggle",
|
||||
label: "Értékelések elrejtése",
|
||||
labelKey: "settings.page_settings.bulletin.hide_grades",
|
||||
description: "Az értékeléseid kártya elrejtése",
|
||||
descriptionKey: "settings.page_settings.bulletin.hide_grades_desc",
|
||||
value: savedSettings.hideGrades || false
|
||||
},
|
||||
{
|
||||
key: "hideAbsences",
|
||||
type: "toggle",
|
||||
label: "Mulasztások elrejtése",
|
||||
labelKey: "settings.page_settings.bulletin.hide_absences",
|
||||
description: "A mulasztások kártya elrejtése",
|
||||
descriptionKey: "settings.page_settings.bulletin.hide_absences_desc",
|
||||
value: savedSettings.hideAbsences || false
|
||||
},
|
||||
{
|
||||
key: "hideNotes",
|
||||
type: "toggle",
|
||||
label: "Feljegyzések elrejtése",
|
||||
labelKey: "settings.page_settings.bulletin.hide_notes",
|
||||
description: "A feljegyzések kártya elrejtése",
|
||||
descriptionKey: "settings.page_settings.bulletin.hide_notes_desc",
|
||||
value: savedSettings.hideNotes || false
|
||||
},
|
||||
{
|
||||
key: "hideExams",
|
||||
type: "toggle",
|
||||
label: "Bejelentett dolgozatok elrejtése",
|
||||
labelKey: "settings.page_settings.bulletin.hide_exams",
|
||||
description: "A bejelentett dolgozatok kártya elrejtése",
|
||||
descriptionKey: "settings.page_settings.bulletin.hide_exams_desc",
|
||||
value: savedSettings.hideExams || false
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
async function getGradesPageSettings() {
|
||||
const settingsKey = "pageSettings_grades";
|
||||
const savedSettings = await storageManager.get(settingsKey, {});
|
||||
|
||||
return [
|
||||
{
|
||||
key: "hideChart",
|
||||
type: "toggle",
|
||||
label: "Grafikon elrejtése",
|
||||
labelKey: "settings.page_settings.grades.hide_chart",
|
||||
description: "A jegyek grafikonjának elrejtése",
|
||||
descriptionKey: "settings.page_settings.grades.hide_chart_desc",
|
||||
value: savedSettings.hideChart || false
|
||||
},
|
||||
{
|
||||
key: "hideClassAverage",
|
||||
type: "toggle",
|
||||
label: "Osztályátlag elrejtése",
|
||||
labelKey: "settings.page_settings.grades.hide_class_average",
|
||||
description: "Az osztályátlag értékek elrejtése",
|
||||
descriptionKey: "settings.page_settings.grades.hide_class_average_desc",
|
||||
value: savedSettings.hideClassAverage || false
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
async function loadPageSpecificSettings(pageType) {
|
||||
const container = document.getElementById("pageSpecificSettings");
|
||||
if (!container) return;
|
||||
|
||||
const pageSettings = {
|
||||
"timetable": [],
|
||||
"grades": await getGradesPageSettings(),
|
||||
"absences": [],
|
||||
"messages": [],
|
||||
"profile": [],
|
||||
"search": [],
|
||||
"dashboard": await getBulletinPageSettings(),
|
||||
"roleselect": await getRoleselectPageSettings(),
|
||||
"login": await getLoginPageSettings(),
|
||||
"forgotpassword": [],
|
||||
"unknown": []
|
||||
};
|
||||
|
||||
const settings = pageSettings[pageType] || [];
|
||||
|
||||
if (settings.length === 0) {
|
||||
helper.clearElement(container);
|
||||
|
||||
const placeholder = document.createElement('div');
|
||||
placeholder.className = 'no-settings-placeholder';
|
||||
const icon = document.createElement('span');
|
||||
icon.className = 'material-icons-round';
|
||||
icon.textContent = 'settings_suggest';
|
||||
const text = document.createElement('p');
|
||||
text.setAttribute('data-i18n', 'settings.page_settings.no_settings');
|
||||
text.textContent = 'Ehhez az oldalhoz nincsenek egyéni beállítások.';
|
||||
placeholder.appendChild(icon);
|
||||
placeholder.appendChild(text);
|
||||
container.appendChild(placeholder);
|
||||
|
||||
if (window.LanguageManager) {
|
||||
window.LanguageManager.loadTranslationsForPage();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const template = document.createElement('template');
|
||||
template.innerHTML = settings.map(setting => renderSettingItem(setting)).join("");
|
||||
helper.clearElement(container);
|
||||
container.appendChild(template.content);
|
||||
initSettingItems(container);
|
||||
|
||||
if (window.LanguageManager) {
|
||||
window.LanguageManager.loadTranslationsForPage();
|
||||
}
|
||||
}
|
||||
|
||||
function renderSettingItem(setting) {
|
||||
let control = "";
|
||||
|
||||
switch (setting.type) {
|
||||
case "toggle":
|
||||
control = `
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" data-setting="${setting.key}" ${setting.value ? "checked" : ""}>
|
||||
<span class="toggle-slider"></span>
|
||||
</label>
|
||||
`;
|
||||
break;
|
||||
case "select":
|
||||
control = `
|
||||
<select class="setting-select" data-setting="${setting.key}">
|
||||
${setting.options.map(opt =>
|
||||
`<option value="${opt.value}" ${opt.value === setting.value ? "selected" : ""}>${opt.label}</option>`
|
||||
).join("")}
|
||||
</select>
|
||||
`;
|
||||
break;
|
||||
default:
|
||||
control = "";
|
||||
}
|
||||
|
||||
return `
|
||||
<div class="setting-item" data-setting-key="${setting.key}">
|
||||
<div class="setting-item-info">
|
||||
<div class="setting-item-label" data-i18n="${setting.labelKey || ""}">${setting.label}</div>
|
||||
${setting.description ? `<div class="setting-item-description" data-i18n="${setting.descriptionKey || ""}">${setting.description}</div>` : ""}
|
||||
</div>
|
||||
${control}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
function initSettingItems(container) {
|
||||
container.querySelectorAll('input[type="checkbox"]').forEach(input => {
|
||||
input.addEventListener("change", async () => {
|
||||
const key = input.dataset.setting;
|
||||
await savePageSetting(currentPageType, key, input.checked);
|
||||
});
|
||||
});
|
||||
|
||||
container.querySelectorAll(".setting-select").forEach(select => {
|
||||
select.addEventListener("change", async () => {
|
||||
const key = select.dataset.setting;
|
||||
await savePageSetting(currentPageType, key, select.value);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function savePageSetting(pageType, key, value) {
|
||||
try {
|
||||
const storagePageType = pageType === "dashboard" ? "bulletin" : pageType;
|
||||
const settingsKey = `pageSettings_${storagePageType}`;
|
||||
const existingSettings = await storageManager.get(settingsKey, {});
|
||||
existingSettings[key] = value;
|
||||
await storageManager.set(settingsKey, existingSettings);
|
||||
|
||||
const tabs = await chrome.tabs.query({});
|
||||
tabs.forEach((tab) => {
|
||||
chrome.tabs
|
||||
.sendMessage(tab.id, {
|
||||
action: "pageSettingChanged",
|
||||
pageType: storagePageType,
|
||||
key: key,
|
||||
value: value,
|
||||
})
|
||||
.catch(() => {});
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error saving page setting:", error);
|
||||
}
|
||||
}
|
||||
|
||||
async function loadCustomThemes() {
|
||||
try {
|
||||
@@ -26,75 +369,106 @@ document.addEventListener("DOMContentLoaded", async () => {
|
||||
}
|
||||
|
||||
function renderCustomThemes() {
|
||||
const grid = document.getElementById("customThemesGrid");
|
||||
const grid = document.getElementById("allThemesGrid");
|
||||
if (!grid) return;
|
||||
grid.querySelectorAll(".custom-theme-option").forEach(el => el.remove());
|
||||
document.querySelectorAll(".custom-theme-dropdown").forEach(el => el.remove());
|
||||
|
||||
if (customThemes.length === 0) {
|
||||
grid.innerHTML = `<div class="no-custom-themes" data-i18n="settings.custom_themes.no_themes">Még nincsenek egyéni témák</div>`;
|
||||
if (window.LanguageManager) {
|
||||
window.LanguageManager.updatePageTranslations();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
grid.innerHTML = customThemes.map(theme => `
|
||||
const template = document.createElement('template');
|
||||
template.innerHTML = customThemes.map(theme => `
|
||||
<button class="theme-option custom-theme-option" data-theme="custom-${theme.id}">
|
||||
<div class="theme-preview" style="background: ${theme.colors.background};">
|
||||
<div class="preview-header" style="background: ${theme.colors.card};"></div>
|
||||
<div class="preview-content">
|
||||
<div class="preview-card" style="background: ${theme.colors.accent}20; border: 1px solid ${theme.colors.accent};"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="theme-info">
|
||||
<span class="theme-name">${theme.name}</span>
|
||||
<div class="theme-actions">
|
||||
<span class="theme-action-btn edit-theme" data-id="${theme.id}" title="Szerkesztés">
|
||||
<span class="material-icons-round">edit</span>
|
||||
</span>
|
||||
<span class="theme-action-btn share-theme" data-id="${theme.id}" title="Megosztás">
|
||||
<span class="material-icons-round">share</span>
|
||||
</span>
|
||||
<span class="theme-action-btn delete-theme" data-id="${theme.id}" title="Törlés">
|
||||
<span class="material-icons-round">delete</span>
|
||||
</span>
|
||||
<div class="custom-theme-settings-btn" role="button" tabindex="0" data-id="${theme.id}">
|
||||
<span class="material-icons-round">settings</span>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
`).join("");
|
||||
|
||||
grid.appendChild(template.content);
|
||||
const dropdownTemplate = document.createElement('template');
|
||||
dropdownTemplate.innerHTML = customThemes.map(theme => `
|
||||
<div class="custom-theme-dropdown" data-dropdown-id="${theme.id}">
|
||||
<button class="custom-theme-dropdown-item" data-action="share" data-id="${theme.id}">
|
||||
<span class="material-icons-round">share</span>
|
||||
<span data-i18n="settings.custom_themes.share">Megosztás</span>
|
||||
</button>
|
||||
<button class="custom-theme-dropdown-item" data-action="edit" data-id="${theme.id}">
|
||||
<span class="material-icons-round">edit</span>
|
||||
<span data-i18n="settings.custom_themes.edit">Szerkesztés</span>
|
||||
</button>
|
||||
<button class="custom-theme-dropdown-item delete" data-action="delete" data-id="${theme.id}">
|
||||
<span class="material-icons-round">delete</span>
|
||||
<span data-i18n="settings.custom_themes.delete">Törlés</span>
|
||||
</button>
|
||||
</div>
|
||||
`).join("");
|
||||
|
||||
document.body.appendChild(dropdownTemplate.content);
|
||||
grid.querySelectorAll(".custom-theme-option").forEach(btn => {
|
||||
btn.addEventListener("click", (e) => {
|
||||
if (e.target.closest(".theme-action-btn")) return;
|
||||
if (e.target.closest(".theme-hover-btn")) return;
|
||||
const themeId = btn.dataset.theme;
|
||||
applyTheme(themeId);
|
||||
});
|
||||
});
|
||||
|
||||
grid.querySelectorAll(".edit-theme").forEach(btn => {
|
||||
grid.querySelectorAll(".custom-theme-settings-btn").forEach(btn => {
|
||||
btn.addEventListener("click", (e) => {
|
||||
e.stopPropagation();
|
||||
const id = btn.dataset.id;
|
||||
editTheme(id);
|
||||
const dropdown = document.querySelector(`[data-dropdown-id="${id}"]`);
|
||||
document.querySelectorAll(".custom-theme-dropdown").forEach(d => {
|
||||
if (d !== dropdown) d.classList.remove("active");
|
||||
});
|
||||
|
||||
if (dropdown) {
|
||||
const isActive = dropdown.classList.contains("active");
|
||||
|
||||
if (!isActive) {
|
||||
const btnRect = btn.getBoundingClientRect();
|
||||
dropdown.style.top = `${btnRect.bottom + 4}px`;
|
||||
dropdown.style.left = `${btnRect.right - 160}px`;
|
||||
}
|
||||
|
||||
dropdown.classList.toggle("active");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
grid.querySelectorAll(".share-theme").forEach(btn => {
|
||||
document.querySelectorAll(".custom-theme-dropdown-item").forEach(btn => {
|
||||
btn.addEventListener("click", (e) => {
|
||||
e.stopPropagation();
|
||||
const action = btn.dataset.action;
|
||||
const id = btn.dataset.id;
|
||||
shareTheme(id);
|
||||
const dropdown = btn.closest(".custom-theme-dropdown");
|
||||
dropdown?.classList.remove("active");
|
||||
|
||||
if (action === "share") shareTheme(id);
|
||||
else if (action === "edit") editTheme(id);
|
||||
else if (action === "delete") deleteTheme(id);
|
||||
});
|
||||
});
|
||||
|
||||
grid.querySelectorAll(".delete-theme").forEach(btn => {
|
||||
btn.addEventListener("click", (e) => {
|
||||
e.stopPropagation();
|
||||
const id = btn.dataset.id;
|
||||
deleteTheme(id);
|
||||
});
|
||||
document.addEventListener("click", (e) => {
|
||||
if (!e.target.closest(".custom-theme-settings")) {
|
||||
document.querySelectorAll(".custom-theme-dropdown").forEach(d => d.classList.remove("active"));
|
||||
}
|
||||
});
|
||||
|
||||
updateThemeButtons(getCurrentTheme());
|
||||
|
||||
if (window.LanguageManager) {
|
||||
window.LanguageManager.loadTranslationsForPage();
|
||||
}
|
||||
}
|
||||
|
||||
function openThemeEditor(theme = null) {
|
||||
@@ -224,8 +598,45 @@ document.addEventListener("DOMContentLoaded", async () => {
|
||||
};
|
||||
|
||||
const code = btoa(JSON.stringify(shareData));
|
||||
document.getElementById("shareCode").value = code;
|
||||
document.getElementById("shareThemeModal").classList.add("active");
|
||||
|
||||
if (navigator.clipboard && navigator.clipboard.writeText) {
|
||||
navigator.clipboard.writeText(code).then(() => {
|
||||
showNotification('Téma kód vágólapra másolva!');
|
||||
}).catch(() => {
|
||||
copyToClipboardFallback(code);
|
||||
showNotification('Téma kód vágólapra másolva!');
|
||||
});
|
||||
} else {
|
||||
copyToClipboardFallback(code);
|
||||
showNotification('Téma kód vágólapra másolva!');
|
||||
}
|
||||
}
|
||||
|
||||
function copyToClipboardFallback(text) {
|
||||
const textarea = document.createElement('textarea');
|
||||
textarea.value = text;
|
||||
textarea.style.position = 'fixed';
|
||||
textarea.style.opacity = '0';
|
||||
document.body.appendChild(textarea);
|
||||
textarea.select();
|
||||
document.execCommand('copy');
|
||||
document.body.removeChild(textarea);
|
||||
}
|
||||
|
||||
function showNotification(message) {
|
||||
const notification = document.createElement('div');
|
||||
notification.className = 'notification';
|
||||
notification.textContent = message;
|
||||
document.body.appendChild(notification);
|
||||
setTimeout(() => {
|
||||
notification.classList.add('show');
|
||||
}, 10);
|
||||
setTimeout(() => {
|
||||
notification.classList.remove('show');
|
||||
setTimeout(() => {
|
||||
notification.remove();
|
||||
}, 300);
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
function deleteTheme(id) {
|
||||
@@ -337,19 +748,14 @@ document.addEventListener("DOMContentLoaded", async () => {
|
||||
}
|
||||
|
||||
async function applyLanguage(language) {
|
||||
localStorage.setItem("languagePreference", language);
|
||||
|
||||
updateLanguageButtons(language);
|
||||
|
||||
const tabs = await chrome.tabs.query({});
|
||||
tabs.forEach((tab) => {
|
||||
chrome.tabs
|
||||
.sendMessage(tab.id, {
|
||||
action: "changeLanguage",
|
||||
language: language,
|
||||
})
|
||||
.catch(() => {});
|
||||
});
|
||||
try {
|
||||
if (window.LanguageManager) {
|
||||
await window.LanguageManager.changeLanguage(language);
|
||||
}
|
||||
updateLanguageButtons(language);
|
||||
} catch (error) {
|
||||
console.error("Error applying language:", error);
|
||||
}
|
||||
}
|
||||
|
||||
async function applyTheme(theme) {
|
||||
@@ -384,7 +790,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
||||
function applyCustomThemeColors(theme) {
|
||||
const root = document.documentElement;
|
||||
const isDark = theme.mode === "dark";
|
||||
|
||||
|
||||
root.style.setProperty("--background", theme.colors.background);
|
||||
root.style.setProperty("--card-card", theme.colors.card);
|
||||
root.style.setProperty("--accent-accent", theme.colors.accent);
|
||||
@@ -396,6 +802,15 @@ document.addEventListener("DOMContentLoaded", async () => {
|
||||
root.style.setProperty("--accent-secondary", isDark ? lightenColor(theme.colors.accent, 20) : darkenColor(theme.colors.accent, 20));
|
||||
root.style.setProperty("--shadow-blur", isDark ? "0" : "2px");
|
||||
root.style.setProperty("--accent-shadow", isDark ? "#0000" : theme.colors.accent + "26");
|
||||
root.style.setProperty("--warning-accent", "#FFA046");
|
||||
root.style.setProperty("--warning-text", isDark ? "#f0b37a" : "#8F531B");
|
||||
root.style.setProperty("--warning-15", "#ffa04626");
|
||||
root.style.setProperty("--warning-card", isDark ? "#201203" : "#FAEBDC");
|
||||
root.style.setProperty("--error-accent", "#FF54A1");
|
||||
root.style.setProperty("--error-text", isDark ? "#f59ec5" : "#8F1B4F");
|
||||
root.style.setProperty("--error-15", "#FF54A126");
|
||||
root.style.setProperty("--error-card", isDark ? "#1e030f" : "#FADCE9");
|
||||
|
||||
root.style.setProperty("--icon-filter", hexToFilter(theme.colors.accent));
|
||||
}
|
||||
|
||||
@@ -403,7 +818,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
||||
const root = document.documentElement;
|
||||
const properties = [
|
||||
"--background",
|
||||
"--card-card",
|
||||
"--card-card",
|
||||
"--accent-accent",
|
||||
"--text-primary",
|
||||
"--text-secondary",
|
||||
@@ -413,9 +828,17 @@ document.addEventListener("DOMContentLoaded", async () => {
|
||||
"--accent-secondary",
|
||||
"--shadow-blur",
|
||||
"--accent-shadow",
|
||||
"--icon-filter"
|
||||
"--icon-filter",
|
||||
"--warning-accent",
|
||||
"--warning-text",
|
||||
"--warning-15",
|
||||
"--warning-card",
|
||||
"--error-accent",
|
||||
"--error-text",
|
||||
"--error-15",
|
||||
"--error-card"
|
||||
];
|
||||
|
||||
|
||||
properties.forEach(prop => root.style.removeProperty(prop));
|
||||
}
|
||||
|
||||
@@ -498,12 +921,31 @@ document.addEventListener("DOMContentLoaded", async () => {
|
||||
});
|
||||
});
|
||||
|
||||
document.getElementById("addCustomTheme")?.addEventListener("click", () => openThemeEditor());
|
||||
const themeManageBtn = document.getElementById("themeManageBtn");
|
||||
const themeManageMenu = document.getElementById("themeManageMenu");
|
||||
|
||||
themeManageBtn?.addEventListener("click", (e) => {
|
||||
e.stopPropagation();
|
||||
themeManageMenu.classList.toggle("active");
|
||||
});
|
||||
|
||||
document.addEventListener("click", (e) => {
|
||||
if (!e.target.closest(".theme-manage-dropdown")) {
|
||||
themeManageMenu?.classList.remove("active");
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById("addCustomTheme")?.addEventListener("click", () => {
|
||||
themeManageMenu?.classList.remove("active");
|
||||
openThemeEditor();
|
||||
});
|
||||
|
||||
document.getElementById("closeThemeEditor")?.addEventListener("click", closeThemeEditor);
|
||||
document.getElementById("cancelThemeEditor")?.addEventListener("click", closeThemeEditor);
|
||||
document.getElementById("saveTheme")?.addEventListener("click", saveThemeFromEditor);
|
||||
|
||||
document.getElementById("importCustomTheme")?.addEventListener("click", () => {
|
||||
themeManageMenu?.classList.remove("active");
|
||||
document.getElementById("importThemeModal").classList.add("active");
|
||||
});
|
||||
|
||||
@@ -603,4 +1045,124 @@ document.addEventListener("DOMContentLoaded", async () => {
|
||||
window.addEventListener("languageChanged", (event) => {
|
||||
updateLanguageButtons(event.detail.language);
|
||||
});
|
||||
|
||||
await initTabs();
|
||||
await initErrorReporting();
|
||||
await checkForUpdates();
|
||||
});
|
||||
|
||||
async function initErrorReporting() {
|
||||
const toggle = document.getElementById('errorReportingToggle');
|
||||
if (!toggle) return;
|
||||
const result = await storageManager.get('firka_errorReporting', true);
|
||||
toggle.checked = result !== false;
|
||||
toggle.addEventListener('change', async () => {
|
||||
const enabled = toggle.checked;
|
||||
await storageManager.set('firka_errorReporting', enabled);
|
||||
const tabs = await chrome.tabs.query({});
|
||||
tabs.forEach((tab) => {
|
||||
chrome.tabs.sendMessage(tab.id, {
|
||||
action: 'errorReportingChanged',
|
||||
enabled: enabled
|
||||
}).catch(() => {});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function checkForUpdates() {
|
||||
try {
|
||||
const manifest = chrome.runtime.getManifest();
|
||||
const currentVersion = manifest.version;
|
||||
|
||||
const response = await fetch('https://api.github.com/repos/QwIT-Development/firka-extension/releases/latest');
|
||||
if (!response.ok) {
|
||||
console.error('Failed to fetch latest release');
|
||||
return;
|
||||
}
|
||||
|
||||
const latestRelease = await response.json();
|
||||
const latestVersion = latestRelease.tag_name.replace(/^v/, '');
|
||||
|
||||
if (compareVersions(latestVersion, currentVersion) > 0) {
|
||||
showUpdateModal(currentVersion, latestVersion, latestRelease);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error checking for updates:', error);
|
||||
}
|
||||
}
|
||||
|
||||
function compareVersions(v1, v2) {
|
||||
const parts1 = v1.split('.').map(Number);
|
||||
const parts2 = v2.split('.').map(Number);
|
||||
|
||||
for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
|
||||
const part1 = parts1[i] || 0;
|
||||
const part2 = parts2[i] || 0;
|
||||
|
||||
if (part1 > part2) return 1;
|
||||
if (part1 < part2) return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
function parseMarkdown(markdown) {
|
||||
let html = markdown;
|
||||
|
||||
html = html.replace(/^### (.*$)/gim, '<h3>$1</h3>');
|
||||
html = html.replace(/^## (.*$)/gim, '<h2>$1</h2>');
|
||||
html = html.replace(/^# (.*$)/gim, '<h1>$1</h1>');
|
||||
|
||||
html = html.replace(/\*\*\*(.+?)\*\*\*/g, '<strong><em>$1</em></strong>');
|
||||
html = html.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>');
|
||||
html = html.replace(/\*(.+?)\*/g, '<em>$1</em>');
|
||||
html = html.replace(/__(.+?)__/g, '<strong>$1</strong>');
|
||||
html = html.replace(/_(.+?)_/g, '<em>$1</em>');
|
||||
|
||||
html = html.replace(/`([^`]+)`/g, '<code>$1</code>');
|
||||
|
||||
html = html.replace(/```([^```]+)```/g, '<pre><code>$1</code></pre>');
|
||||
|
||||
html = html.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank">$1</a>');
|
||||
|
||||
html = html.replace(/^> (.+)/gim, '<blockquote>$1</blockquote>');
|
||||
|
||||
html = html.replace(/^\* (.+)$/gim, '<li>$1</li>');
|
||||
html = html.replace(/^- (.+)$/gim, '<li>$1</li>');
|
||||
html = html.replace(/(<li>.*<\/li>)/s, '<ul>$1</ul>');
|
||||
|
||||
html = html.replace(/^\d+\. (.+)$/gim, '<li>$1</li>');
|
||||
|
||||
html = html.replace(/\n\n/g, '</p><p>');
|
||||
html = html.replace(/^(?!<[hou]|<li|<pre|<blockquote)(.+)$/gim, '<p>$1</p>');
|
||||
|
||||
html = html.replace(/<p><\/p>/g, '');
|
||||
html = html.replace(/<p>(<[hou])/g, '$1');
|
||||
html = html.replace(/(<\/[hou]l?>)<\/p>/g, '$1');
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
function showUpdateModal(currentVersion, latestVersion, releaseData) {
|
||||
const modal = document.getElementById('updateModal');
|
||||
const currentVersionEl = document.getElementById('currentVersion');
|
||||
const latestVersionEl = document.getElementById('latestVersion');
|
||||
const changelogEl = document.getElementById('updateChangelog');
|
||||
const updateButton = document.getElementById('updateButton');
|
||||
|
||||
currentVersionEl.textContent = `v${currentVersion}`;
|
||||
latestVersionEl.textContent = `v${latestVersion}`;
|
||||
|
||||
const changelog = releaseData.body || 'Nincs elérhető változásnapló.';
|
||||
changelogEl.innerHTML = parseMarkdown(changelog);
|
||||
|
||||
updateButton.href = releaseData.html_url;
|
||||
|
||||
modal.classList.add('active');
|
||||
document.getElementById('dismissUpdate').addEventListener('click', closeUpdateModal);
|
||||
}
|
||||
|
||||
function closeUpdateModal() {
|
||||
const modal = document.getElementById('updateModal');
|
||||
modal.classList.remove('active');
|
||||
}
|
||||
|
||||
462
setup/setup.css
Normal file
462
setup/setup.css
Normal file
@@ -0,0 +1,462 @@
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Montserrat', sans-serif;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 1rem;
|
||||
background: var(--background);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.setup-container {
|
||||
width: 100%;
|
||||
max-width: 700px;
|
||||
animation: fadeIn 0.5s ease;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.setup-card {
|
||||
background: var(--card-card);
|
||||
border-radius: 20px;
|
||||
padding: 2rem;
|
||||
box-shadow: 0 4px 20px var(--accent-shadow);
|
||||
}
|
||||
|
||||
.setup-header {
|
||||
text-align: center;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.setup-logo {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.setup-title {
|
||||
font-size: 2rem;
|
||||
font-weight: 700;
|
||||
color: var(--text-primary);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.setup-subtitle {
|
||||
font-size: 1rem;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 2rem;
|
||||
padding: 0 1rem;
|
||||
}
|
||||
|
||||
.progress-step {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 0.4rem;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.progress-circle {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
background: var(--button-secondaryFill);
|
||||
border: 2px solid var(--text-teritary);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
color: var(--text-teritary);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.progress-step.active .progress-circle {
|
||||
background: var(--accent-accent);
|
||||
border-color: var(--accent-accent);
|
||||
color: white;
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
.progress-step.completed .progress-circle {
|
||||
background: var(--accent-accent);
|
||||
border-color: var(--accent-accent);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.progress-label {
|
||||
font-size: 0.875rem;
|
||||
color: var(--text-secondary);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.progress-step.active .progress-label {
|
||||
color: var(--accent-accent);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.progress-line {
|
||||
width: 80px;
|
||||
height: 2px;
|
||||
background: var(--text-teritary);
|
||||
margin: 0 0.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.setup-content {
|
||||
min-height: 320px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.setup-step {
|
||||
display: none;
|
||||
animation: slideIn 0.4s ease;
|
||||
}
|
||||
|
||||
.setup-step.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@keyframes slideIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateX(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
.step-icon {
|
||||
text-align: center;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.step-icon .material-icons-round {
|
||||
font-size: 3rem;
|
||||
color: var(--accent-accent);
|
||||
}
|
||||
|
||||
.step-icon.success .material-icons-round {
|
||||
font-size: 3.5rem;
|
||||
color: var(--success);
|
||||
}
|
||||
|
||||
.step-title {
|
||||
text-align: center;
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
color: var(--text-primary);
|
||||
margin-bottom: 0.4rem;
|
||||
}
|
||||
|
||||
.step-description {
|
||||
text-align: center;
|
||||
font-size: 0.9rem;
|
||||
color: var(--text-secondary);
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.theme-options {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
||||
gap: 1rem;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.theme-card {
|
||||
background: var(--button-secondaryFill);
|
||||
border: 3px solid transparent;
|
||||
border-radius: 12px;
|
||||
padding: 1rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 0.8rem;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.theme-card:hover {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 8px 24px var(--accent-shadow);
|
||||
}
|
||||
|
||||
.theme-card.selected {
|
||||
border-color: var(--accent-accent);
|
||||
background: var(--accent-15);
|
||||
}
|
||||
|
||||
.theme-icon {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
right: 1rem;
|
||||
color: var(--text-secondary);
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.theme-card.selected .theme-icon {
|
||||
color: var(--accent-accent);
|
||||
}
|
||||
|
||||
.theme-preview {
|
||||
width: 100%;
|
||||
height: 100px;
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.theme-preview.light-green {
|
||||
background: #FAFFF0;
|
||||
}
|
||||
|
||||
.theme-preview.dark-green {
|
||||
background: #0D1202;
|
||||
}
|
||||
|
||||
.preview-header {
|
||||
height: 30%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.theme-preview.light-green .preview-header {
|
||||
background: #F3FBDE;
|
||||
}
|
||||
|
||||
.theme-preview.dark-green .preview-header {
|
||||
background: #141905;
|
||||
}
|
||||
|
||||
.preview-content {
|
||||
height: 70%;
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.preview-card {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.theme-preview.light-green .preview-card {
|
||||
background: white;
|
||||
}
|
||||
|
||||
.theme-preview.dark-green .preview-card {
|
||||
background: #1a2207;
|
||||
}
|
||||
|
||||
.theme-name {
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.language-options {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
||||
gap: 1rem;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.language-card {
|
||||
background: var(--button-secondaryFill);
|
||||
border: 3px solid transparent;
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 0.8rem;
|
||||
}
|
||||
|
||||
.language-card:hover {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 8px 24px var(--accent-shadow);
|
||||
}
|
||||
|
||||
.language-card.selected {
|
||||
border-color: var(--accent-accent);
|
||||
background: var(--accent-15);
|
||||
}
|
||||
|
||||
.language-flag {
|
||||
font-size: 3rem;
|
||||
}
|
||||
|
||||
.language-name {
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.finish-links {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 0.8rem;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.finish-link {
|
||||
background: var(--button-secondaryFill);
|
||||
border: 2px solid transparent;
|
||||
border-radius: 10px;
|
||||
padding: 1rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.8rem;
|
||||
text-decoration: none;
|
||||
color: var(--text-primary);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.finish-link:hover {
|
||||
border-color: var(--accent-accent);
|
||||
background: var(--accent-15);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.finish-link .material-icons-round {
|
||||
font-size: 2rem;
|
||||
color: var(--accent-accent);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.link-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.link-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.link-description {
|
||||
font-size: 0.8rem;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.setup-actions {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: 2rem;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.actions-spacer {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.btn-primary,
|
||||
.btn-secondary {
|
||||
padding: 1rem 2rem;
|
||||
border-radius: 12px;
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
border: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
font-family: 'Montserrat', sans-serif;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: var(--accent-accent);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background: var(--accent-secondary);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px var(--accent-shadow);
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background: var(--button-secondaryFill);
|
||||
color: var(--text-primary);
|
||||
border: 2px solid var(--text-teritary);
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
border-color: var(--accent-accent);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.btn-primary .material-icons-round,
|
||||
.btn-secondary .material-icons-round {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
body {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.setup-card {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.setup-title {
|
||||
font-size: 1.75rem;
|
||||
}
|
||||
|
||||
.step-title {
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.progress-line {
|
||||
width: 30px;
|
||||
}
|
||||
|
||||
.progress-circle {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
.theme-options,
|
||||
.language-options {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.finish-links {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
159
setup/setup.html
Normal file
159
setup/setup.html
Normal file
@@ -0,0 +1,159 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="hu">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Firxa - Kezdeti beállítások</title>
|
||||
<link rel="icon" type="image/png" href="../images/firka_logo_128.png">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&display=swap" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons+Round" rel="stylesheet">
|
||||
<link rel="stylesheet" href="setup.css">
|
||||
<link rel="stylesheet" href="../global/theme.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="setup-container">
|
||||
<div class="setup-card">
|
||||
<div class="setup-header">
|
||||
<img src="../images/firka_logo.png" alt="Firka" class="setup-logo">
|
||||
<h1 class="setup-title">Üdvözöl a Firxa!</h1>
|
||||
<p class="setup-subtitle" data-i18n="setup.welcome">Állítsd be a bővítményt néhány egyszerű lépésben</p>
|
||||
</div>
|
||||
|
||||
<div class="progress-bar">
|
||||
<div class="progress-step active" data-step="1">
|
||||
<div class="progress-circle">1</div>
|
||||
<span class="progress-label" data-i18n="setup.steps.theme">Téma</span>
|
||||
</div>
|
||||
<div class="progress-line"></div>
|
||||
<div class="progress-step" data-step="2">
|
||||
<div class="progress-circle">2</div>
|
||||
<span class="progress-label" data-i18n="setup.steps.language">Nyelv</span>
|
||||
</div>
|
||||
<div class="progress-line"></div>
|
||||
<div class="progress-step" data-step="3">
|
||||
<div class="progress-circle">3</div>
|
||||
<span class="progress-label" data-i18n="setup.steps.finish">Kész</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="setup-content">
|
||||
<div class="setup-step active" data-step="1">
|
||||
<div class="step-icon">
|
||||
<span class="material-icons-round">palette</span>
|
||||
</div>
|
||||
<h2 class="step-title" data-i18n="setup.theme.title">Válassz témát</h2>
|
||||
<p class="step-description" data-i18n="setup.theme.description">Válaszd ki a számodra legmegfelelőbb megjelenést</p>
|
||||
|
||||
<div class="theme-options">
|
||||
<button class="theme-card" data-theme="light-green">
|
||||
<div class="theme-preview light-green">
|
||||
<div class="preview-header"></div>
|
||||
<div class="preview-content">
|
||||
<div class="preview-card"></div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="theme-name" data-i18n="settings.themes.light_green">Világos Zöld</span>
|
||||
<span class="theme-icon material-icons-round">light_mode</span>
|
||||
</button>
|
||||
|
||||
<button class="theme-card" data-theme="dark-green">
|
||||
<div class="theme-preview dark-green">
|
||||
<div class="preview-header"></div>
|
||||
<div class="preview-content">
|
||||
<div class="preview-card"></div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="theme-name" data-i18n="settings.themes.dark_green">Sötét Zöld</span>
|
||||
<span class="theme-icon material-icons-round">dark_mode</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="setup-step" data-step="2">
|
||||
<div class="step-icon">
|
||||
<span class="material-icons-round">language</span>
|
||||
</div>
|
||||
<h2 class="step-title" data-i18n="setup.language.title">Válassz nyelvet</h2>
|
||||
<p class="step-description" data-i18n="setup.language.description">Válaszd ki a használni kívánt nyelvet</p>
|
||||
|
||||
<div class="language-options">
|
||||
<button class="language-card" data-language="hu">
|
||||
<span class="language-flag">🇭🇺</span>
|
||||
<span class="language-name" data-i18n="settings.languages.hu">Magyar</span>
|
||||
</button>
|
||||
|
||||
<button class="language-card" data-language="en">
|
||||
<span class="language-flag">🇬🇧</span>
|
||||
<span class="language-name" data-i18n="settings.languages.en">English</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="setup-step" data-step="3">
|
||||
<div class="step-icon success">
|
||||
<span class="material-icons-round">check_circle</span>
|
||||
</div>
|
||||
<h2 class="step-title" data-i18n="setup.finish.title">Minden kész!</h2>
|
||||
<p class="step-description" data-i18n="setup.finish.description">A beállítások sikeresen mentve. Indulhat a tanulás!</p>
|
||||
|
||||
<div class="finish-links">
|
||||
<a href="https://firka.app" target="_blank" class="finish-link">
|
||||
<span class="material-icons-round">info</span>
|
||||
<div class="link-content">
|
||||
<span class="link-title" data-i18n="setup.finish.about">Weboldal</span>
|
||||
<span class="link-description" data-i18n="setup.finish.about_desc">Tudj meg többet a projektről</span>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<a href="https://ko-fi.com/zan1456" target="_blank" class="finish-link">
|
||||
<span class="material-icons-round">favorite</span>
|
||||
<div class="link-content">
|
||||
<span class="link-title" data-i18n="setup.finish.support">Támogatás</span>
|
||||
<span class="link-description" data-i18n="setup.finish.support_desc">Támogasd a fejlesztést</span>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<a href="https://github.com/QwIT-Development/" target="_blank" class="finish-link">
|
||||
<span class="material-icons-round">code</span>
|
||||
<div class="link-content">
|
||||
<span class="link-title" data-i18n="setup.finish.github">GitHub</span>
|
||||
<span class="link-description" data-i18n="setup.finish.github_desc">Nézd meg a forráskódot</span>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<a href="https://discord.gg/firka-1111649116020285532" target="_blank" class="finish-link">
|
||||
<span class="material-icons-round">forum</span>
|
||||
<div class="link-content">
|
||||
<span class="link-title" data-i18n="setup.finish.discord">Discord</span>
|
||||
<span class="link-description" data-i18n="setup.finish.discord_desc">Csatlakozz a közösséghez</span>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="setup-actions">
|
||||
<button class="btn-secondary" id="backBtn" style="display: none;">
|
||||
<span class="material-icons-round">arrow_back</span>
|
||||
<span data-i18n="common.back">Vissza</span>
|
||||
</button>
|
||||
<div class="actions-spacer"></div>
|
||||
<button class="btn-primary" id="nextBtn">
|
||||
<span data-i18n="common.next">Tovább</span>
|
||||
<span class="material-icons-round">arrow_forward</span>
|
||||
</button>
|
||||
<button class="btn-primary" id="finishBtn" style="display: none;">
|
||||
<span data-i18n="setup.finish.start">Kezdés</span>
|
||||
<span class="material-icons-round">check</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="../tools/storageManager.js"></script>
|
||||
<script src="../global/language.js"></script>
|
||||
<script src="setup.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
146
setup/setup.js
Normal file
146
setup/setup.js
Normal file
@@ -0,0 +1,146 @@
|
||||
let currentStep = 1;
|
||||
const totalSteps = 3;
|
||||
let selectedTheme = 'light-green';
|
||||
let selectedLanguage = 'hu';
|
||||
|
||||
document.addEventListener('DOMContentLoaded', async () => {
|
||||
selectedTheme = await storageManager.get('themePreference', 'light-green');
|
||||
selectedLanguage = await storageManager.get('languagePreference', 'hu');
|
||||
|
||||
initializeThemeSelection();
|
||||
initializeLanguageSelection();
|
||||
|
||||
document.getElementById('nextBtn').addEventListener('click', nextStep);
|
||||
document.getElementById('backBtn').addEventListener('click', previousStep);
|
||||
document.getElementById('finishBtn').addEventListener('click', finishSetup);
|
||||
|
||||
document.querySelectorAll('.theme-card').forEach(card => {
|
||||
card.addEventListener('click', () => selectTheme(card.dataset.theme));
|
||||
});
|
||||
|
||||
document.querySelectorAll('.language-card').forEach(card => {
|
||||
card.addEventListener('click', () => selectLanguage(card.dataset.language));
|
||||
});
|
||||
|
||||
applyTheme(selectedTheme);
|
||||
});
|
||||
|
||||
function initializeThemeSelection() {
|
||||
const themeCards = document.querySelectorAll('.theme-card');
|
||||
themeCards.forEach(card => {
|
||||
if (card.dataset.theme === selectedTheme) {
|
||||
card.classList.add('selected');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function initializeLanguageSelection() {
|
||||
const languageCards = document.querySelectorAll('.language-card');
|
||||
languageCards.forEach(card => {
|
||||
if (card.dataset.language === selectedLanguage) {
|
||||
card.classList.add('selected');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function selectTheme(theme) {
|
||||
selectedTheme = theme;
|
||||
|
||||
document.querySelectorAll('.theme-card').forEach(card => {
|
||||
card.classList.remove('selected');
|
||||
});
|
||||
|
||||
document.querySelector(`[data-theme="${theme}"]`).classList.add('selected');
|
||||
|
||||
applyTheme(theme);
|
||||
}
|
||||
|
||||
function selectLanguage(language) {
|
||||
selectedLanguage = language;
|
||||
|
||||
document.querySelectorAll('.language-card').forEach(card => {
|
||||
card.classList.remove('selected');
|
||||
});
|
||||
document.querySelector(`[data-language="${language}"]`).classList.add('selected');
|
||||
}
|
||||
|
||||
function applyTheme(theme) {
|
||||
document.documentElement.setAttribute('data-theme', theme);
|
||||
}
|
||||
|
||||
function nextStep() {
|
||||
if (currentStep < totalSteps) {
|
||||
saveCurrentStepSettings();
|
||||
|
||||
currentStep++;
|
||||
updateStepDisplay();
|
||||
}
|
||||
}
|
||||
|
||||
function previousStep() {
|
||||
if (currentStep > 1) {
|
||||
currentStep--;
|
||||
updateStepDisplay();
|
||||
}
|
||||
}
|
||||
|
||||
async function saveCurrentStepSettings() {
|
||||
if (currentStep === 1) {
|
||||
await storageManager.set('themePreference', selectedTheme);
|
||||
} else if (currentStep === 2) {
|
||||
await storageManager.set('language', selectedLanguage);
|
||||
await storageManager.set('languagePreference', selectedLanguage);
|
||||
if (window.LanguageManager) {
|
||||
await window.LanguageManager.changeLanguage(selectedLanguage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updateStepDisplay() {
|
||||
document.querySelectorAll('.progress-step').forEach(step => {
|
||||
const stepNumber = parseInt(step.dataset.step);
|
||||
if (stepNumber < currentStep) {
|
||||
step.classList.add('completed');
|
||||
step.classList.remove('active');
|
||||
} else if (stepNumber === currentStep) {
|
||||
step.classList.add('active');
|
||||
step.classList.remove('completed');
|
||||
} else {
|
||||
step.classList.remove('active', 'completed');
|
||||
}
|
||||
});
|
||||
|
||||
document.querySelectorAll('.setup-step').forEach(step => {
|
||||
if (parseInt(step.dataset.step) === currentStep) {
|
||||
step.classList.add('active');
|
||||
} else {
|
||||
step.classList.remove('active');
|
||||
}
|
||||
});
|
||||
|
||||
const backBtn = document.getElementById('backBtn');
|
||||
const nextBtn = document.getElementById('nextBtn');
|
||||
const finishBtn = document.getElementById('finishBtn');
|
||||
|
||||
if (currentStep === 1) {
|
||||
backBtn.style.display = 'none';
|
||||
} else {
|
||||
backBtn.style.display = 'flex';
|
||||
}
|
||||
|
||||
if (currentStep === totalSteps) {
|
||||
nextBtn.style.display = 'none';
|
||||
finishBtn.style.display = 'flex';
|
||||
} else {
|
||||
nextBtn.style.display = 'flex';
|
||||
finishBtn.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
async function finishSetup() {
|
||||
await saveCurrentStepSettings();
|
||||
|
||||
await storageManager.set('setupCompleted', true);
|
||||
|
||||
window.location.href = 'https://intezmenykereso.e-kreta.hu/';
|
||||
}
|
||||
@@ -147,7 +147,8 @@ body {
|
||||
transition:all 0.3s cubic-bezier(0.4,0,0.2,1);
|
||||
transform:translateY(0);
|
||||
}
|
||||
.lesson-card:hover {
|
||||
.lesson-card:hover,
|
||||
.lesson-card.group-hover {
|
||||
transform:translateY(-4px);
|
||||
box-shadow:0 8px 12px var(--accent-shadow);
|
||||
}
|
||||
@@ -162,6 +163,28 @@ body {
|
||||
opacity:0.5;
|
||||
text-decoration:line-through;
|
||||
}
|
||||
|
||||
.lesson-card.lesson-spans-multiple {
|
||||
border-bottom-left-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
|
||||
.lesson-card.lesson-continuation.continuation-middle {
|
||||
border-radius: 0;
|
||||
}
|
||||
.lesson-card.lesson-continuation.continuation-end {
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
}
|
||||
|
||||
.lesson-slot.has-multi-start {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.lesson-slot.has-continuation {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.lesson-subject {
|
||||
align-self:stretch;
|
||||
color:var(--text-primary);
|
||||
@@ -570,7 +593,10 @@ body {
|
||||
display:flex;
|
||||
}
|
||||
.timetable-container {
|
||||
overflow:hidden;
|
||||
overflow: hidden;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.timetable-grid {
|
||||
grid-template-columns:60px 1fr;
|
||||
@@ -1410,3 +1436,331 @@ to {
|
||||
transition:gap 0.2s ease;
|
||||
font-size:clamp(0.875rem,1.5vw,1rem);
|
||||
}
|
||||
|
||||
.timetable-calendar {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: var(--card-card);
|
||||
border-radius: 16px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0px 1px 2px 0px var(--accent-shadow);
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.calendar-header {
|
||||
display: grid;
|
||||
grid-template-columns: 80px repeat(5, 1fr);
|
||||
background: var(--background-0);
|
||||
border-bottom: 1px solid var(--background-0);
|
||||
padding: 0;
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
.time-column-header {
|
||||
padding: 12px;
|
||||
border-right: 1px solid var(--background-0);
|
||||
}
|
||||
|
||||
.calendar-day-header {
|
||||
padding: 12px;
|
||||
border-right: 1px solid var(--background-0);
|
||||
text-align: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.calendar-day-header:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.calendar-day-header .day-name {
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.calendar-day-header .day-date {
|
||||
color: var(--text-secondary);
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.calendar-notices {
|
||||
display: grid;
|
||||
grid-template-columns: 80px repeat(5, 1fr);
|
||||
border-bottom: 1px solid var(--background-0);
|
||||
padding: 0;
|
||||
gap: 0;
|
||||
background: var(--background);
|
||||
}
|
||||
|
||||
.notices-time-column {
|
||||
border-right: 1px solid var(--background-0);
|
||||
}
|
||||
|
||||
.notice-day-slot {
|
||||
padding: 8px;
|
||||
border-right: 1px solid var(--background-0);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 60px;
|
||||
}
|
||||
|
||||
.notice-day-slot:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.notice-day-slot .special-day-notice {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.calendar-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 80px repeat(5, 1fr);
|
||||
gap: 0;
|
||||
background: var(--background);
|
||||
position: relative;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.time-column {
|
||||
border-right: 1px solid var(--background-0);
|
||||
background: var(--background-0);
|
||||
position: sticky;
|
||||
left: 0;
|
||||
z-index: 10;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.time-marker {
|
||||
padding: 8px;
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
color: var(--text-secondary);
|
||||
border-bottom: 1px solid var(--background);
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.time-marker:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.calendar-day-column {
|
||||
position: relative;
|
||||
border-right: 1px solid var(--background-0);
|
||||
background: var(--background);
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.calendar-day-column:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.day-background {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
pointer-events: none;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.time-slot-bg {
|
||||
border-bottom: 1px solid var(--background-0);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.time-slot-bg:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.lessons-container {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.calendar-lesson-card {
|
||||
position: absolute;
|
||||
left: 4px;
|
||||
right: 4px;
|
||||
width: calc(100% - 8px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 10px;
|
||||
border-radius: 12px;
|
||||
background: var(--card-card);
|
||||
box-shadow: 0px 1px 2px 0px var(--accent-shadow);
|
||||
border: 1px solid var(--background-0);
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.calendar-lesson-card:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 8px var(--accent-shadow);
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
.calendar-lesson-card.substituted {
|
||||
background: var(--warning-card);
|
||||
border-color: var(--warning-15);
|
||||
}
|
||||
|
||||
.calendar-lesson-card.cancelled {
|
||||
background: var(--card-translucent);
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.calendar-lesson-card.cancelled .lesson-subject,
|
||||
.calendar-lesson-card.cancelled .lesson-teacher,
|
||||
.calendar-lesson-card.cancelled .lesson-time-info {
|
||||
opacity: 0.5;
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
.calendar-lesson-card .lesson-subject {
|
||||
font-weight: 600;
|
||||
font-size: 13px;
|
||||
color: var(--text-primary);
|
||||
line-height: 1.2;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.calendar-lesson-card .lesson-teacher {
|
||||
font-size: 11px;
|
||||
color: var(--text-secondary);
|
||||
margin-bottom: 4px;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.calendar-lesson-card .lesson-time-info {
|
||||
font-size: 11px;
|
||||
color: var(--text-secondary);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 4px;
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
.calendar-lesson-card .lesson-room {
|
||||
background: var(--accent-15);
|
||||
padding: 2px 6px;
|
||||
border-radius: 8px;
|
||||
font-size: 10px;
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.calendar-lesson-card.substituted .lesson-room {
|
||||
background: var(--warning-15);
|
||||
}
|
||||
|
||||
.calendar-lesson-card .lesson-time {
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.calendar-lesson-card .lesson-indicators {
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
right: 4px;
|
||||
display: flex;
|
||||
gap: 2px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.calendar-lesson-card .lesson-indicator {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 50%;
|
||||
background: var(--accent-15);
|
||||
color: var(--accent-accent);
|
||||
}
|
||||
|
||||
.calendar-lesson-card .lesson-indicator.test-indicator {
|
||||
background: var(--warning-15);
|
||||
color: var(--warning-accent);
|
||||
}
|
||||
|
||||
.calendar-lesson-card .lesson-indicator.custom-homework-indicator,
|
||||
.calendar-lesson-card .lesson-indicator.custom-test-indicator {
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.calendar-grid {
|
||||
grid-template-columns: 60px repeat(5, 1fr);
|
||||
}
|
||||
|
||||
.time-column-header {
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.calendar-day-header {
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.calendar-day-header .day-name {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.calendar-day-header .day-date {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.time-marker {
|
||||
padding: 4px;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.calendar-lesson-card {
|
||||
padding: 6px;
|
||||
}
|
||||
|
||||
.calendar-lesson-card .lesson-subject {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.calendar-lesson-card .lesson-teacher {
|
||||
font-size: 9px;
|
||||
}
|
||||
|
||||
.calendar-lesson-card .lesson-time-info {
|
||||
font-size: 9px;
|
||||
}
|
||||
|
||||
.calendar-lesson-card .lesson-indicator {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.calendar-lesson-card .lesson-indicator img {
|
||||
width: 14px !important;
|
||||
height: 14px !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -160,7 +160,7 @@
|
||||
}
|
||||
|
||||
function getLessonKey(lesson) {
|
||||
return `${lesson.subject}_${lesson.startTime}_${lesson.day}`;
|
||||
return `${lesson.subject}_${lesson.startTime}_${lesson.date}`;
|
||||
}
|
||||
|
||||
async function updateHomeworkIconsFromCookie() {
|
||||
@@ -197,8 +197,9 @@
|
||||
}
|
||||
|
||||
const htmlText = await response.text();
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(htmlText, 'text/html');
|
||||
const template = document.createElement('template');
|
||||
template.innerHTML = htmlText;
|
||||
const doc = template.content;
|
||||
const panelBody = doc.querySelector('.panel-body');
|
||||
const panelFooter = doc.querySelector('.panel-footer');
|
||||
const teacherInfo = doc.querySelector('.panel-heading');
|
||||
@@ -312,7 +313,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
async function loadTestDetailsFromAPI(testId) {
|
||||
async function loadAllTestDataFromAPI() {
|
||||
const now = Date.now();
|
||||
if (cachedTestData && (now - testDataTimestamp) < TEST_DATA_CACHE_DURATION) {
|
||||
return cachedTestData;
|
||||
}
|
||||
|
||||
try {
|
||||
const timestamp = Date.now();
|
||||
const apiUrl = `https://${window.location.hostname}/api/TanuloBejelentettSzamonkeresekApi/GetBejelentettSzamonkeresekGrid?sort=SzamonkeresDatuma-asc~Oraszam-asc&page=1&pageSize=1000&group=&filter=&data=%7B%22RegiSzamonkeresekElrejtese%22%3Afalse%7D&_=${timestamp}`;
|
||||
@@ -326,15 +332,30 @@
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(
|
||||
`Számonkérés API hiba: ${response.status}`,
|
||||
);
|
||||
throw new Error(`Számonkérés API hiba: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
const testData = data.Data || [];
|
||||
cachedTestData = data.Data || [];
|
||||
testDataTimestamp = now;
|
||||
return cachedTestData;
|
||||
} catch (error) {
|
||||
console.error("Számonkérés adatok betöltési hiba:", error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
async function getTestTypeById(testId) {
|
||||
const testData = await loadAllTestDataFromAPI();
|
||||
const testDetail = testData.find(test => test.ID === testId.toString());
|
||||
return testDetail ? testDetail.ErtekelesModNev : null;
|
||||
}
|
||||
|
||||
async function loadTestDetailsFromAPI(testId) {
|
||||
try {
|
||||
const testData = await loadAllTestDataFromAPI();
|
||||
const testDetail = testData.find(test => test.ID === testId.toString());
|
||||
|
||||
|
||||
if (testDetail) {
|
||||
return {
|
||||
name: testDetail.SzamonkeresMegnevezes || 'Nincs megnevezés',
|
||||
@@ -342,7 +363,7 @@
|
||||
announceDate: testDetail.BejelentesDatuma ? new Date(testDetail.BejelentesDatuma).toLocaleDateString('hu-HU') : 'Nincs dátum'
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
return null;
|
||||
} catch (error) {
|
||||
console.error("Számonkérés adatok betöltési hiba:", error);
|
||||
@@ -409,6 +430,7 @@
|
||||
originalTeacher: "",
|
||||
room: "",
|
||||
day: dayIndex,
|
||||
date: weekDates[dayIndex]?.fullDate || eventDate.toISOString().split('T')[0],
|
||||
isSubstituted: false,
|
||||
isCancelled: false,
|
||||
hasHomework: false,
|
||||
@@ -445,9 +467,9 @@
|
||||
"Ismeretlen tantárgy";
|
||||
|
||||
if (startTimeStr && subject) {
|
||||
const isCancelled = event.isElmaradt || event.Elmaradt || event.cancelled || event.isCancelled ||
|
||||
const isCancelled = event.isElmaradt || event.Elmaradt || event.cancelled || event.isCancelled ||
|
||||
event.oraType === 6 || (event.title && event.title.toLowerCase().includes('elmarad'));
|
||||
|
||||
|
||||
const lesson = {
|
||||
startTime: startTimeStr,
|
||||
endTime: endTimeStr,
|
||||
@@ -456,14 +478,15 @@
|
||||
originalTeacher: event.helyettesitoId ? teacher : "",
|
||||
room: room,
|
||||
day: dayIndex,
|
||||
date: weekDates[dayIndex]?.fullDate || eventDate.toISOString().split('T')[0],
|
||||
isSubstituted: !!event.helyettesitoId,
|
||||
isCancelled: isCancelled,
|
||||
hasHomework: event.hasHaziFeladat || false,
|
||||
testInfo: event.hasBejelentettSzamonkeres
|
||||
? event.Tema || LanguageManager.t("timetable.test_indicator")
|
||||
: "",
|
||||
testId: event.hasBejelentettSzamonkeres && event.BejelentettSzamonkeresIdList && event.BejelentettSzamonkeresIdList.length > 0
|
||||
? event.BejelentettSzamonkeresIdList[0]
|
||||
testId: event.hasBejelentettSzamonkeres && event.BejelentettSzamonkeresIdList && event.BejelentettSzamonkeresIdList.length > 0
|
||||
? event.BejelentettSzamonkeresIdList[0]
|
||||
: null,
|
||||
testDetails: "",
|
||||
homeworkDetails: "",
|
||||
@@ -496,6 +519,15 @@
|
||||
}
|
||||
}
|
||||
|
||||
const testTypeMap = {};
|
||||
const lessonsWithTests = lessons.filter(l => l.testId);
|
||||
if (lessonsWithTests.length > 0) {
|
||||
await loadAllTestDataFromAPI();
|
||||
for (const lesson of lessonsWithTests) {
|
||||
testTypeMap[lesson.testId] = await getTestTypeById(lesson.testId);
|
||||
}
|
||||
}
|
||||
|
||||
const times = [...new Set(regularLessons.map((l) => l.startTime))].sort(
|
||||
(a, b) => {
|
||||
const timeA = helper.convertTimeToMinutes(a);
|
||||
@@ -503,6 +535,26 @@
|
||||
return timeA - timeB;
|
||||
},
|
||||
);
|
||||
|
||||
function getLessonTimeSlots(lesson, allTimes) {
|
||||
const startMinutes = helper.convertTimeToMinutes(lesson.startTime);
|
||||
const endMinutes = helper.convertTimeToMinutes(lesson.endTime);
|
||||
const slots = [];
|
||||
for (let i = 0; i < allTimes.length; i++) {
|
||||
const slotMinutes = helper.convertTimeToMinutes(allTimes[i]);
|
||||
if (slotMinutes >= startMinutes && slotMinutes < endMinutes) {
|
||||
slots.push(i);
|
||||
}
|
||||
}
|
||||
return slots.length > 0 ? slots : [allTimes.indexOf(lesson.startTime)];
|
||||
}
|
||||
|
||||
const lessonSlotMap = new Map();
|
||||
regularLessons.forEach(lesson => {
|
||||
const slots = getLessonTimeSlots(lesson, times);
|
||||
lessonSlotMap.set(lesson, slots);
|
||||
});
|
||||
|
||||
const days = [
|
||||
LanguageManager.t("timetable.monday"),
|
||||
LanguageManager.t("timetable.tuesday"),
|
||||
@@ -568,38 +620,84 @@
|
||||
const dayLessons = regularLessons.filter(
|
||||
(l) => l.startTime === time && l.day === dayIndex,
|
||||
);
|
||||
|
||||
const continuingInSlot = regularLessons.filter(lesson => {
|
||||
if (lesson.day !== dayIndex) return false;
|
||||
const slots = lessonSlotMap.get(lesson);
|
||||
return slots && slots.includes(timeIndex) && slots[0] !== timeIndex;
|
||||
});
|
||||
|
||||
const hasMultiLesson = dayLessons.some(lesson => {
|
||||
const slots = lessonSlotMap.get(lesson);
|
||||
return slots && slots.length > 1;
|
||||
});
|
||||
|
||||
const lastLessonTime = lastLessonTimes[dayIndex];
|
||||
const isAfterLastLesson = lastLessonTime && helper.convertTimeToMinutes(time) > helper.convertTimeToMinutes(lastLessonTime);
|
||||
|
||||
if (dayLessons.length === 0 && isAfterLastLesson) {
|
||||
|
||||
if (dayLessons.length === 0 && continuingInSlot.length === 0 && isAfterLastLesson) {
|
||||
return `<div class="lesson-slot"></div>`;
|
||||
}
|
||||
|
||||
|
||||
if (continuingInSlot.length > 0) {
|
||||
return `
|
||||
<div class="lesson-slot ${dayLessons.length === 0 ? 'empty-slot' : ''}">
|
||||
${dayLessons.length === 0 ? '<div class="empty-lesson-placeholder"></div>' : ''}
|
||||
${dayLessons
|
||||
.map(
|
||||
(lesson) => `
|
||||
<div class="lesson-card ${lesson.isSubstituted ? "substituted" : ""}
|
||||
<div class="lesson-slot has-continuation">
|
||||
${continuingInSlot.map(lesson => {
|
||||
const slots = lessonSlotMap.get(lesson);
|
||||
const isLastSlot = slots[slots.length - 1] === timeIndex;
|
||||
const lessonGroupId = `${lesson.subject}_${lesson.day}_${lesson.date}_${lesson.startTime}`;
|
||||
return `
|
||||
<div class="lesson-card ${lesson.isSubstituted ? "substituted" : ""}
|
||||
${lesson.isCancelled ? "cancelled" : ""}
|
||||
${lesson.hasHomework ? "has-homework" : ""}"
|
||||
lesson-continuation ${isLastSlot ? 'continuation-end' : 'continuation-middle'}"
|
||||
data-lesson='${JSON.stringify(lesson)}'
|
||||
data-lesson-id='${lesson.lessonId || ""}'>
|
||||
<div class="lesson-subject">${lesson.subject}</div>
|
||||
<div class="lesson-teacher">${lesson.teacher}</div>
|
||||
data-lesson-id='${lesson.lessonId || ""}'
|
||||
data-lesson-group='${lessonGroupId}'>
|
||||
<div class="lesson-bottom">
|
||||
<div class="lesson-room">${lesson.room}</div>
|
||||
<div class="lesson-time">${lesson.isCancelled ? LanguageManager.t("timetable.cancelled") : lesson.startTime}</div>
|
||||
</div>
|
||||
</div>
|
||||
`}).join('')}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
return `
|
||||
<div class="lesson-slot ${dayLessons.length === 0 ? 'empty-slot' : ''} ${hasMultiLesson ? 'has-multi-start' : ''}">
|
||||
${dayLessons.length === 0 ? '<div class="empty-lesson-placeholder"></div>' : ''}
|
||||
${dayLessons
|
||||
.map(
|
||||
(lesson) => {
|
||||
const slots = lessonSlotMap.get(lesson);
|
||||
const spansMultiple = slots && slots.length > 1;
|
||||
const lessonGroupId = `${lesson.subject}_${lesson.day}_${lesson.date}_${lesson.startTime}`;
|
||||
return `
|
||||
<div class="lesson-card ${lesson.isSubstituted ? "substituted" : ""}
|
||||
${lesson.isCancelled ? "cancelled" : ""}
|
||||
${lesson.hasHomework ? "has-homework" : ""}
|
||||
${spansMultiple ? "lesson-spans-multiple" : ""}"
|
||||
data-lesson='${JSON.stringify(lesson)}'
|
||||
data-lesson-id='${lesson.lessonId || ""}'
|
||||
data-spans='${slots ? slots.length : 1}'
|
||||
data-lesson-group='${lessonGroupId}'>
|
||||
<div class="lesson-subject">${lesson.subject}</div>
|
||||
<div class="lesson-teacher">${lesson.teacher}</div>
|
||||
${!spansMultiple ? `<div class="lesson-bottom">
|
||||
<div class="lesson-room">${lesson.room}</div>
|
||||
<div class="lesson-time">${lesson.isCancelled ? LanguageManager.t("timetable.cancelled") : lesson.startTime}</div>
|
||||
</div>` : ''}
|
||||
${
|
||||
(() => {
|
||||
const lessonKey = `${lesson.subject}_${lesson.startTime}_${lesson.day}`;
|
||||
const hasCustomHomework = customHomework[lessonKey] && customHomework[lessonKey].length > 0;
|
||||
const hasCustomTests = customTests[lessonKey] && customTests[lessonKey].length > 0;
|
||||
const lessonKey = `${lesson.subject}_${lesson.startTime}_${lesson.date}`;
|
||||
const customHomeworkItems = customHomework[lessonKey] || [];
|
||||
const customTestItems = customTests[lessonKey] || [];
|
||||
const hasCustomHomework = customHomeworkItems.length > 0;
|
||||
const hasCustomTests = customTestItems.length > 0;
|
||||
const allCustomHomeworkCompleted = hasCustomHomework && customHomeworkItems.every(hw => hw.completed);
|
||||
const allCustomTestsCompleted = hasCustomTests && customTestItems.every(test => test.completed);
|
||||
const hasAnyIndicators = lesson.hasHomework || lesson.testInfo || hasCustomHomework || hasCustomTests;
|
||||
|
||||
|
||||
return hasAnyIndicators ? `
|
||||
<div class="lesson-indicators">
|
||||
${
|
||||
@@ -613,18 +711,40 @@
|
||||
}
|
||||
${
|
||||
lesson.testInfo
|
||||
? `
|
||||
<span class="lesson-indicator test-indicator" title="${LanguageManager.t("timetable.test_indicator")}">
|
||||
<img src="${chrome.runtime.getURL("icons/assigment.svg")}" alt="Teszt" style="width: 20px; height: 20px;">
|
||||
? (() => {
|
||||
const testType = lesson.testId ? testTypeMap[lesson.testId] : null;
|
||||
const isKontaktOra = testType === "KONTAKT ÓRA";
|
||||
const isProjektOra = testType === "PROJEKT ÓRA";
|
||||
let indicatorClass = "test-indicator";
|
||||
let iconPath = "icons/assigment.svg";
|
||||
let titleText = LanguageManager.t("timetable.test_indicator");
|
||||
let altText = "Teszt";
|
||||
|
||||
if (isKontaktOra) {
|
||||
indicatorClass = "homework-indicator";
|
||||
iconPath = "icons/contact.svg";
|
||||
titleText = "Kontakt óra";
|
||||
altText = "Kontakt óra";
|
||||
} else if (isProjektOra) {
|
||||
indicatorClass = "homework-indicator";
|
||||
iconPath = "icons/project.svg";
|
||||
titleText = "Projekt óra";
|
||||
altText = "Projekt óra";
|
||||
}
|
||||
|
||||
return `
|
||||
<span class="lesson-indicator ${indicatorClass}" title="${titleText}">
|
||||
<img src="${chrome.runtime.getURL(iconPath)}" alt="${altText}" style="width: 20px; height: 20px;">
|
||||
</span>
|
||||
`
|
||||
`;
|
||||
})()
|
||||
: ""
|
||||
}
|
||||
${
|
||||
hasCustomHomework
|
||||
? `
|
||||
<span class="lesson-indicator custom-homework-indicator" title="Saját házi feladat">
|
||||
<img src="${chrome.runtime.getURL("icons/homework.svg")}" alt="Saját házi feladat" style="width: 20px; height: 20px; opacity: 0.7;">
|
||||
<img src="${chrome.runtime.getURL(allCustomHomeworkCompleted ? "icons/pipa.svg" : "icons/homework.svg")}" alt="${allCustomHomeworkCompleted ? 'Megoldott saját házi feladat' : 'Saját házi feladat'}" style="width: 20px; height: 20px; opacity: 0.7;">
|
||||
</span>
|
||||
`
|
||||
: ""
|
||||
@@ -633,7 +753,7 @@
|
||||
hasCustomTests
|
||||
? `
|
||||
<span class="lesson-indicator custom-test-indicator" title="Saját számonkérés">
|
||||
<img src="${chrome.runtime.getURL("icons/assigment.svg")}" alt="Saját számonkérés" style="width: 20px; height: 20px; opacity: 0.7;">
|
||||
<img src="${chrome.runtime.getURL(allCustomTestsCompleted ? "icons/pipa.svg" : "icons/assigment.svg")}" alt="${allCustomTestsCompleted ? 'Megoldott saját számonkérés' : 'Saját számonkérés'}" style="width: 20px; height: 20px; opacity: 0.7;">
|
||||
</span>
|
||||
`
|
||||
: ""
|
||||
@@ -643,8 +763,8 @@
|
||||
})()
|
||||
}
|
||||
</div>
|
||||
`,
|
||||
)
|
||||
`;
|
||||
})
|
||||
.join("")
|
||||
}
|
||||
</div>
|
||||
@@ -887,18 +1007,27 @@
|
||||
detailsDiv.className = 'homework-details';
|
||||
|
||||
const contentP = document.createElement('p');
|
||||
contentP.innerHTML = `<strong>Feladat:</strong> ${homeworkDetails.content}`;
|
||||
const contentStrong = document.createElement('strong');
|
||||
contentStrong.textContent = 'Feladat: ';
|
||||
contentP.appendChild(contentStrong);
|
||||
contentP.appendChild(document.createTextNode(homeworkDetails.content));
|
||||
detailsDiv.appendChild(contentP);
|
||||
|
||||
if (homeworkDetails.deadline) {
|
||||
const deadlineP = document.createElement('p');
|
||||
deadlineP.innerHTML = `<strong>Határidő:</strong> ${homeworkDetails.deadline}`;
|
||||
const deadlineStrong = document.createElement('strong');
|
||||
deadlineStrong.textContent = 'Határidő: ';
|
||||
deadlineP.appendChild(deadlineStrong);
|
||||
deadlineP.appendChild(document.createTextNode(homeworkDetails.deadline));
|
||||
detailsDiv.appendChild(deadlineP);
|
||||
}
|
||||
|
||||
if (homeworkDetails.teacher) {
|
||||
const teacherP = document.createElement('p');
|
||||
teacherP.innerHTML = `<strong>Tanár:</strong> ${homeworkDetails.teacher}`;
|
||||
const teacherStrong = document.createElement('strong');
|
||||
teacherStrong.textContent = 'Tanár: ';
|
||||
teacherP.appendChild(teacherStrong);
|
||||
teacherP.appendChild(document.createTextNode(homeworkDetails.teacher));
|
||||
detailsDiv.appendChild(teacherP);
|
||||
}
|
||||
|
||||
@@ -908,7 +1037,9 @@
|
||||
attachmentsDiv.style.marginTop = '1rem';
|
||||
|
||||
const attachmentsTitle = document.createElement('p');
|
||||
attachmentsTitle.innerHTML = '<strong>Csatolmányok:</strong>';
|
||||
const attachStrong = document.createElement('strong');
|
||||
attachStrong.textContent = 'Csatolmányok:';
|
||||
attachmentsTitle.appendChild(attachStrong);
|
||||
attachmentsTitle.style.marginBottom = '0.5rem';
|
||||
attachmentsDiv.appendChild(attachmentsTitle);
|
||||
|
||||
@@ -1184,74 +1315,92 @@
|
||||
}
|
||||
|
||||
if (lesson.testInfo) {
|
||||
let testDetails = null;
|
||||
if (lesson.testId) {
|
||||
testDetails = await loadTestDetailsFromAPI(lesson.testId);
|
||||
}
|
||||
|
||||
const isKontaktOra = testDetails && testDetails.type === "KONTAKT ÓRA";
|
||||
const isProjektOra = testDetails && testDetails.type === "PROJEKT ÓRA";
|
||||
const isSpecialType = isKontaktOra || isProjektOra;
|
||||
|
||||
const testSection = document.createElement('div');
|
||||
testSection.className = 'modal-section test-section';
|
||||
|
||||
testSection.className = isSpecialType ? 'modal-section homework-section' : 'modal-section test-section';
|
||||
|
||||
const testH4 = document.createElement('h4');
|
||||
const testIcon = document.createElement('img');
|
||||
testIcon.src = chrome.runtime.getURL('icons/assigment.svg');
|
||||
testIcon.alt = 'Teszt';
|
||||
|
||||
let iconPath = 'icons/assigment.svg';
|
||||
let sectionTitle = LanguageManager.t('timetable.test_indicator');
|
||||
let altText = 'Teszt';
|
||||
|
||||
if (isKontaktOra) {
|
||||
iconPath = 'icons/contact.svg';
|
||||
sectionTitle = 'Kontakt óra';
|
||||
altText = 'Kontakt óra';
|
||||
} else if (isProjektOra) {
|
||||
iconPath = 'icons/project.svg';
|
||||
sectionTitle = 'Projekt óra';
|
||||
altText = 'Projekt óra';
|
||||
}
|
||||
|
||||
testIcon.src = chrome.runtime.getURL(iconPath);
|
||||
testIcon.alt = altText;
|
||||
testIcon.style.width = '20px';
|
||||
testIcon.style.height = '20px';
|
||||
testH4.appendChild(testIcon);
|
||||
testH4.appendChild(document.createTextNode(LanguageManager.t('timetable.test_indicator')));
|
||||
|
||||
testH4.appendChild(document.createTextNode(sectionTitle));
|
||||
if (isSpecialType) {
|
||||
testH4.style.color = 'var(--accent-accent)';
|
||||
}
|
||||
|
||||
const testContent = document.createElement('div');
|
||||
testContent.className = 'test-content';
|
||||
|
||||
if (lesson.testId) {
|
||||
const loadingDiv = document.createElement('div');
|
||||
loadingDiv.className = 'test-details-loading';
|
||||
loadingDiv.textContent = 'Részletek betöltése...';
|
||||
testContent.appendChild(loadingDiv);
|
||||
if (testDetails) {
|
||||
const detailsDiv = document.createElement('div');
|
||||
detailsDiv.className = 'test-details';
|
||||
|
||||
loadTestDetailsFromAPI(lesson.testId).then(testDetails => {
|
||||
loadingDiv.remove();
|
||||
|
||||
if (testDetails) {
|
||||
const detailsDiv = document.createElement('div');
|
||||
detailsDiv.className = 'test-details';
|
||||
|
||||
const nameP = document.createElement('p');
|
||||
nameP.innerHTML = `<strong>Megnevezés:</strong> ${testDetails.name}`;
|
||||
detailsDiv.appendChild(nameP);
|
||||
|
||||
const typeP = document.createElement('p');
|
||||
typeP.innerHTML = `<strong>Típus:</strong> ${testDetails.type}`;
|
||||
detailsDiv.appendChild(typeP);
|
||||
|
||||
const dateP = document.createElement('p');
|
||||
dateP.innerHTML = `<strong>Bejelentés dátuma:</strong> ${testDetails.announceDate}`;
|
||||
detailsDiv.appendChild(dateP);
|
||||
|
||||
testContent.appendChild(detailsDiv);
|
||||
} else {
|
||||
const errorP = document.createElement('p');
|
||||
errorP.className = 'test-details-error';
|
||||
errorP.textContent = 'Nem sikerült betölteni a számonkérés részleteit.';
|
||||
testContent.appendChild(errorP);
|
||||
}
|
||||
}).catch(error => {
|
||||
loadingDiv.remove();
|
||||
const errorP = document.createElement('p');
|
||||
errorP.className = 'test-details-error';
|
||||
errorP.textContent = 'Hiba történt a számonkérés részletek betöltése során.';
|
||||
testContent.appendChild(errorP);
|
||||
});
|
||||
const nameP = document.createElement('p');
|
||||
const nameStrong = document.createElement('strong');
|
||||
nameStrong.textContent = 'Megnevezés: ';
|
||||
nameP.appendChild(nameStrong);
|
||||
nameP.appendChild(document.createTextNode(testDetails.name));
|
||||
detailsDiv.appendChild(nameP);
|
||||
|
||||
const typeP = document.createElement('p');
|
||||
const typeStrong = document.createElement('strong');
|
||||
typeStrong.textContent = 'Típus: ';
|
||||
typeP.appendChild(typeStrong);
|
||||
typeP.appendChild(document.createTextNode(testDetails.type));
|
||||
detailsDiv.appendChild(typeP);
|
||||
|
||||
const dateP = document.createElement('p');
|
||||
const dateStrong = document.createElement('strong');
|
||||
dateStrong.textContent = 'Bejelentés dátuma: ';
|
||||
dateP.appendChild(dateStrong);
|
||||
dateP.appendChild(document.createTextNode(testDetails.announceDate));
|
||||
detailsDiv.appendChild(dateP);
|
||||
|
||||
testContent.appendChild(detailsDiv);
|
||||
} else if (lesson.testId) {
|
||||
const errorP = document.createElement('p');
|
||||
errorP.className = 'test-details-error';
|
||||
errorP.textContent = 'Nem sikerült betölteni a számonkérés részleteit.';
|
||||
testContent.appendChild(errorP);
|
||||
}
|
||||
|
||||
|
||||
|
||||
const lessonKey = getLessonKey(lesson);
|
||||
const customTests = await getCustomTests();
|
||||
const customTestItems = customTests[lessonKey] || [];
|
||||
|
||||
|
||||
if (customTestItems.length > 0) {
|
||||
const customTestsDiv = document.createElement('div');
|
||||
customTestsDiv.className = 'custom-tests-in-section';
|
||||
customTestsDiv.style.marginTop = '1rem';
|
||||
customTestsDiv.style.paddingTop = '1rem';
|
||||
customTestsDiv.style.borderTop = '1px solid var(--background-0)';
|
||||
|
||||
|
||||
const customTestsTitle = document.createElement('h5');
|
||||
customTestsTitle.textContent = 'Saját számonkérések:';
|
||||
customTestsTitle.style.fontSize = '14px';
|
||||
@@ -1259,10 +1408,10 @@
|
||||
customTestsTitle.style.color = 'var(--warning-accent)';
|
||||
customTestsTitle.style.marginBottom = '0.5rem';
|
||||
customTestsDiv.appendChild(customTestsTitle);
|
||||
|
||||
|
||||
const customTestsList = document.createElement('div');
|
||||
customTestsList.className = 'custom-tests-list-integrated';
|
||||
|
||||
|
||||
customTestItems.forEach(test => {
|
||||
const testItem = document.createElement('div');
|
||||
testItem.className = `custom-test-item-integrated ${test.completed ? 'completed' : ''}`;
|
||||
@@ -1274,7 +1423,7 @@
|
||||
testItem.style.background = 'var(--background)';
|
||||
testItem.style.borderRadius = '6px';
|
||||
testItem.style.border = '1px solid var(--background-0)';
|
||||
|
||||
|
||||
const testText = document.createElement('span');
|
||||
testText.className = 'test-text-integrated';
|
||||
testText.textContent = test.text;
|
||||
@@ -1284,12 +1433,12 @@
|
||||
testText.style.textDecoration = 'line-through';
|
||||
testText.style.opacity = '0.6';
|
||||
}
|
||||
|
||||
|
||||
const testActions = document.createElement('div');
|
||||
testActions.className = 'test-actions-integrated';
|
||||
testActions.style.display = 'flex';
|
||||
testActions.style.gap = '0.5rem';
|
||||
|
||||
|
||||
const completeBtn = document.createElement('button');
|
||||
completeBtn.className = 'test-complete-btn-integrated';
|
||||
completeBtn.title = test.completed ? 'Megoldva - kattints a visszavonáshoz' : 'Megoldottként jelöl';
|
||||
@@ -1301,7 +1450,7 @@
|
||||
completeBtn.style.display = 'flex';
|
||||
completeBtn.style.alignItems = 'center';
|
||||
completeBtn.style.justifyContent = 'center';
|
||||
|
||||
|
||||
const completeIcon = document.createElement('img');
|
||||
completeIcon.src = chrome.runtime.getURL('icons/pipa.svg');
|
||||
completeIcon.alt = 'Megoldva';
|
||||
@@ -1314,7 +1463,7 @@
|
||||
completeIcon.style.opacity = '0.5';
|
||||
}
|
||||
completeBtn.appendChild(completeIcon);
|
||||
|
||||
|
||||
const deleteBtn = document.createElement('button');
|
||||
deleteBtn.className = 'test-delete-btn-integrated';
|
||||
deleteBtn.title = 'Törlés';
|
||||
@@ -1326,7 +1475,7 @@
|
||||
deleteBtn.style.display = 'flex';
|
||||
deleteBtn.style.alignItems = 'center';
|
||||
deleteBtn.style.justifyContent = 'center';
|
||||
|
||||
|
||||
const deleteIcon = document.createElement('img');
|
||||
deleteIcon.src = chrome.runtime.getURL('icons/delete.svg');
|
||||
deleteIcon.alt = 'Törlés';
|
||||
@@ -1334,7 +1483,7 @@
|
||||
deleteIcon.style.height = '16px';
|
||||
deleteIcon.style.opacity = '0.5';
|
||||
deleteBtn.appendChild(deleteIcon);
|
||||
|
||||
|
||||
completeBtn.addEventListener('click', async () => {
|
||||
const newCompleted = await toggleCustomTestCompletion(lessonKey, test.id);
|
||||
if (newCompleted) {
|
||||
@@ -1351,30 +1500,30 @@
|
||||
completeBtn.title = 'Megoldottként jelöl';
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
deleteBtn.addEventListener('click', async () => {
|
||||
if (confirm('Biztosan törölni szeretnéd ezt a számonkérést?')) {
|
||||
await removeCustomTest(lessonKey, test.id);
|
||||
testItem.remove();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
testActions.appendChild(completeBtn);
|
||||
testActions.appendChild(deleteBtn);
|
||||
testItem.appendChild(testText);
|
||||
testItem.appendChild(testActions);
|
||||
customTestsList.appendChild(testItem);
|
||||
});
|
||||
|
||||
|
||||
customTestsDiv.appendChild(customTestsList);
|
||||
testContent.appendChild(customTestsDiv);
|
||||
}
|
||||
|
||||
|
||||
testSection.appendChild(testH4);
|
||||
testSection.appendChild(testContent);
|
||||
body.appendChild(testSection);
|
||||
}
|
||||
|
||||
|
||||
|
||||
const lessonKey = getLessonKey(lesson);
|
||||
const customHomework = await getCustomHomework();
|
||||
@@ -1632,7 +1781,7 @@
|
||||
modalContent.appendChild(header);
|
||||
modalContent.appendChild(body);
|
||||
|
||||
modal.innerHTML = '';
|
||||
helper.clearElement(modal);
|
||||
modal.appendChild(modalContent);
|
||||
|
||||
document.body.appendChild(modal);
|
||||
@@ -1703,11 +1852,11 @@
|
||||
if (timetableGrid) {
|
||||
|
||||
const newContent = await generateTimeGrid(allLessons, weekDates);
|
||||
timetableGrid.innerHTML = '';
|
||||
helper.clearElement(timetableGrid);
|
||||
|
||||
const parser1 = new DOMParser();
|
||||
const doc = parser1.parseFromString(`<div>${newContent}</div>`, 'text/html');
|
||||
const tempDiv = doc.querySelector('div');
|
||||
const template = document.createElement('template');
|
||||
template.innerHTML = `<div>${newContent}</div>`;
|
||||
const tempDiv = template.content.querySelector('div');
|
||||
while (tempDiv.firstChild) {
|
||||
timetableGrid.appendChild(tempDiv.firstChild);
|
||||
}
|
||||
@@ -1869,6 +2018,24 @@
|
||||
const lessonData = JSON.parse(card.dataset.lesson);
|
||||
await showLessonModal(lessonData);
|
||||
});
|
||||
|
||||
card.addEventListener("mouseenter", () => {
|
||||
const groupId = card.dataset.lessonGroup;
|
||||
if (groupId) {
|
||||
document.querySelectorAll(`[data-lesson-group="${groupId}"]`).forEach(relatedCard => {
|
||||
relatedCard.classList.add('group-hover');
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
card.addEventListener("mouseleave", () => {
|
||||
const groupId = card.dataset.lessonGroup;
|
||||
if (groupId) {
|
||||
document.querySelectorAll(`[data-lesson-group="${groupId}"]`).forEach(relatedCard => {
|
||||
relatedCard.classList.remove('group-hover');
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2186,7 +2353,7 @@
|
||||
}
|
||||
|
||||
|
||||
modalGrid.innerHTML = '';
|
||||
helper.clearElement(modalGrid);
|
||||
allWeeks.forEach((week) => {
|
||||
const weekCell = document.createElement('div');
|
||||
weekCell.className = `week-cell ${week.selected ? 'selected' : ''} ${week.selected ? 'current-week' : ''}`;
|
||||
@@ -2274,7 +2441,7 @@
|
||||
const weekGrid = document.getElementById("week-grid");
|
||||
if (weekGrid) {
|
||||
|
||||
weekGrid.innerHTML = '';
|
||||
helper.clearElement(weekGrid);
|
||||
newWeekOptions.forEach((opt) => {
|
||||
const weekCell = document.createElement('div');
|
||||
weekCell.className = `week-cell ${opt.selected ? 'selected' : ''}`;
|
||||
@@ -2321,7 +2488,7 @@
|
||||
};
|
||||
|
||||
|
||||
document.body.innerHTML = '';
|
||||
helper.clearElement(document.body);
|
||||
|
||||
|
||||
const kretaContainer = document.createElement('div');
|
||||
@@ -2330,9 +2497,9 @@
|
||||
|
||||
const headerDiv = document.createElement('div');
|
||||
|
||||
const parser2 = new DOMParser();
|
||||
const headerDoc = parser2.parseFromString(await createTemplate.header(), 'text/html');
|
||||
const headerContent = headerDoc.body;
|
||||
const template = document.createElement('template');
|
||||
template.innerHTML = await createTemplate.header();
|
||||
const headerContent = template.content;
|
||||
while (headerContent.firstChild) {
|
||||
headerDiv.appendChild(headerContent.firstChild);
|
||||
}
|
||||
@@ -2480,9 +2647,9 @@
|
||||
|
||||
const gridContent = await generateTimeGrid(data.lessons, data.weekDates);
|
||||
|
||||
const parser3 = new DOMParser();
|
||||
const doc = parser3.parseFromString(`<div>${gridContent}</div>`, 'text/html');
|
||||
const tempDiv = doc.querySelector('div');
|
||||
const template3 = document.createElement('template');
|
||||
template3.innerHTML = `<div>${gridContent}</div>`;
|
||||
const tempDiv = template3.content.querySelector('div');
|
||||
while (tempDiv.firstChild) {
|
||||
timetableGrid.appendChild(tempDiv.firstChild);
|
||||
}
|
||||
@@ -2541,7 +2708,7 @@
|
||||
}
|
||||
|
||||
|
||||
weekDisplay.innerHTML = '';
|
||||
helper.clearElement(weekDisplay);
|
||||
visibleWeeks.forEach((weekNum, index) => {
|
||||
const isSelected = index === 2;
|
||||
const isCurrent = weekNum === currentWeekNumber;
|
||||
@@ -2637,12 +2804,12 @@
|
||||
const timetableContainer = document.querySelector(".timetable-grid");
|
||||
if (timetableContainer) {
|
||||
|
||||
timetableContainer.innerHTML = '';
|
||||
helper.clearElement(timetableContainer);
|
||||
const gridContent = await generateTimeGrid(lessons, weekDates);
|
||||
|
||||
const parser2 = new DOMParser();
|
||||
const doc = parser2.parseFromString(`<div>${gridContent}</div>`, 'text/html');
|
||||
const tempDiv = doc.querySelector('div');
|
||||
const template2 = document.createElement('template');
|
||||
template2.innerHTML = `<div>${gridContent}</div>`;
|
||||
const tempDiv = template2.content.querySelector('div');
|
||||
while (tempDiv.firstChild) {
|
||||
timetableContainer.appendChild(tempDiv.firstChild);
|
||||
}
|
||||
@@ -2670,7 +2837,7 @@
|
||||
}
|
||||
|
||||
|
||||
modalGrid.innerHTML = '';
|
||||
helper.clearElement(modalGrid);
|
||||
allWeeks.forEach((weekNumber) => {
|
||||
const isSelected = weekNumber === selectedWeekNumber;
|
||||
const isCurrent = weekNumber === currentWeekNumber;
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
chrome.runtime.onInstalled.addListener(async (details) => {
|
||||
if (details.reason === 'install') {
|
||||
const setupCompleted = await chrome.storage.sync.get('firka_setupCompleted');
|
||||
|
||||
if (!setupCompleted.firka_setupCompleted) {
|
||||
chrome.tabs.create({
|
||||
url: chrome.runtime.getURL('setup/setup.html')
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
||||
(async () => {
|
||||
try {
|
||||
@@ -22,6 +34,11 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
||||
sendResponse({ success: true });
|
||||
break;
|
||||
|
||||
case 'download_attachment':
|
||||
const downloadResult = await handleDownloadAttachment(request.azonosito, request.fileName);
|
||||
sendResponse(downloadResult);
|
||||
break;
|
||||
|
||||
default:
|
||||
console.warn('[Background] Unknown action:', request.action);
|
||||
sendResponse({ success: false, error: 'Unknown action' });
|
||||
@@ -67,7 +84,7 @@ 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);
|
||||
}
|
||||
@@ -77,6 +94,82 @@ async function handleStorageClear() {
|
||||
}
|
||||
}
|
||||
|
||||
async function handleDownloadAttachment(azonosito, fileName) {
|
||||
try {
|
||||
const apiUrl = `https://eugyintezes.e-kreta.hu/api/v1/dokumentumok/uzenetek/${azonosito}`;
|
||||
|
||||
const redirectResponse = await fetch(apiUrl, {
|
||||
method: 'GET',
|
||||
credentials: 'include',
|
||||
headers: {
|
||||
'Accept': 'application/json, text/plain, */*',
|
||||
'x-csrf': '1',
|
||||
'x-uzenet-json-formatum': 'CamelCase',
|
||||
'x-uzenet-lokalizacio': 'hu-HU',
|
||||
'x-uzenet-verzio-szam': '1.2.3'
|
||||
},
|
||||
redirect: 'manual'
|
||||
});
|
||||
|
||||
let fileUrl;
|
||||
if (redirectResponse.type === 'opaqueredirect' || redirectResponse.status === 0) {
|
||||
const followResponse = await fetch(apiUrl, {
|
||||
method: 'GET',
|
||||
credentials: 'include',
|
||||
headers: {
|
||||
'Accept': 'application/json, text/plain, */*',
|
||||
'x-csrf': '1',
|
||||
'x-uzenet-json-formatum': 'CamelCase',
|
||||
'x-uzenet-lokalizacio': 'hu-HU',
|
||||
'x-uzenet-verzio-szam': '1.2.3'
|
||||
},
|
||||
redirect: 'follow'
|
||||
});
|
||||
|
||||
if (followResponse.ok) {
|
||||
const blob = await followResponse.blob();
|
||||
const base64 = await blobToBase64(blob);
|
||||
return { success: true, data: base64, fileName: fileName };
|
||||
}
|
||||
} else if (redirectResponse.status === 302 || redirectResponse.status === 301) {
|
||||
fileUrl = redirectResponse.headers.get('location');
|
||||
}
|
||||
|
||||
if (fileUrl) {
|
||||
const fileResponse = await fetch(fileUrl, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Accept': 'application/json, text/plain, */*',
|
||||
'x-csrf': '1',
|
||||
'x-uzenet-json-formatum': 'CamelCase',
|
||||
'x-uzenet-lokalizacio': 'hu-HU',
|
||||
'x-uzenet-verzio-szam': '1.2.3'
|
||||
}
|
||||
});
|
||||
|
||||
if (fileResponse.ok) {
|
||||
const blob = await fileResponse.blob();
|
||||
const base64 = await blobToBase64(blob);
|
||||
return { success: true, data: base64, fileName: fileName };
|
||||
}
|
||||
}
|
||||
|
||||
return { success: false, error: 'Nem sikerült letölteni a mellékletet.' };
|
||||
} catch (error) {
|
||||
console.error('[Background] Attachment download error:', error);
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
function blobToBase64(blob) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.onloadend = () => resolve(reader.result);
|
||||
reader.onerror = reject;
|
||||
reader.readAsDataURL(blob);
|
||||
});
|
||||
}
|
||||
|
||||
chrome.storage.onChanged.addListener((changes, namespace) => {
|
||||
if (namespace === 'sync') {
|
||||
const firkaChanges = Object.keys(changes).filter(key => key.startsWith('firka_'));
|
||||
|
||||
@@ -37,4 +37,28 @@ const helper = {
|
||||
const [hours, minutes] = timeStr.split(":").map(Number);
|
||||
return hours * 60 + minutes;
|
||||
},
|
||||
|
||||
createElementFromHTML(htmlString) {
|
||||
const template = document.createElement('template');
|
||||
template.innerHTML = htmlString.trim();
|
||||
return template.content;
|
||||
},
|
||||
|
||||
setTextContent(element, text) {
|
||||
element.textContent = text;
|
||||
},
|
||||
|
||||
clearElement(element) {
|
||||
while (element.firstChild) {
|
||||
element.removeChild(element.firstChild);
|
||||
}
|
||||
},
|
||||
|
||||
appendChildren(parent, children) {
|
||||
if (Array.isArray(children)) {
|
||||
children.forEach(child => parent.appendChild(child));
|
||||
} else {
|
||||
parent.appendChild(children);
|
||||
}
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user