diff --git a/absences/absences.css b/absences/absences.css index 747bd52..822942a 100644 --- a/absences/absences.css +++ b/absences/absences.css @@ -1,485 +1,825 @@ * { - box-sizing:border-box; - margin:0; - padding:0; + box-sizing: border-box; + margin: 0; + padding: 0; } + body { - margin:0; - padding:0; - color:var(--text-primary); - background-color:var(--background) !important; - font-family:"Montserrat",serif !important; - min-height:100vh; - font-size:16px; + margin: 0; + padding: 0; + color: var(--text-primary); + background-color: var(--background) !important; + font-family: "Montserrat", sans-serif !important; + min-height: 100vh; + font-size: 16px; } -@media (max-width:768px) { - body { - font-size:14px; -} -}.kreta-container { - min-height:100vh; - display:flex; - flex-direction:column; -} -.kreta-header { - padding:clamp(1rem,3vw,2rem); - display:grid; - grid-template-columns:minmax(300px,400px) 1fr minmax(200px,300px); - align-items:center; - gap:1rem; -} -@media (max-width:1200px) { - .kreta-header { - grid-template-columns:minmax(250px,350px) 1fr minmax(180px,250px); -} -}@media (max-width:768px) { - .kreta-header { - grid-template-columns:1fr auto auto; - grid-template-areas:"school toggle user" - "nav nav nav"; - padding:1rem; - gap:0.5rem; -} -}.school-info { - margin:0; -} -@media (max-width:768px) { - .school-info { - grid-area:school; - max-width:none; - display:flex; - align-items:center; - gap:0.5rem; -} -}.logo-text { - color:var(--text-primary); - font-size:24px; - font-weight:600; - margin:0 0 0.5rem; - display:flex; - align-items:center; -} -@media (max-width:768px) { - .logo-text { - margin:0; - font-size:20px; -} -}.logo { - width:24px; - border-radius:8px; - margin-right:0.5rem; -} -.school-details { - color:var(--text-secondary); - font-size:14px; -} -.school-details span { - display:block; - white-space:nowrap; - overflow:hidden; - text-overflow:ellipsis; - max-width:300px; -} -@media (max-width:768px) { - .school-details span { - max-width:200px; -} -.school-details { - font-size:12px; -} -}.user-profile { - position:relative; - justify-self:flex-end; -} -@media (max-width:768px) { - .user-profile { - grid-area:user; -} -}.user-dropdown-btn { - display:flex; - align-items:center; - gap:1rem; - background:none; - border:none; - cursor:pointer; - padding:0.5rem; - border-radius:8px; - transition:background-color 0.2s; -} -.user-dropdown-btn:hover { - background:var(--card-card); -} -.user-info { - text-align:right; -} -.user-dropdown { - position:absolute; - top:100%; - right:0; - margin-top:0.5rem; - background:var(--card-card); - border-radius:12px; - box-shadow:0 4px 6px -1px rgba(0,0,0,0.1); - width:200px; - display:none; - z-index:1000; -} -.user-dropdown.show { - display:block; - animation:dropdownShow 0.2s ease; -} -.dropdown-item { - display:flex; - align-items:center; - gap:0.75rem; - padding:0.75rem 1rem; - color:var(--text-primary); - text-decoration:none; - transition:background-color 0.2s; -} -.dropdown-item:hover { - background:var(--button-secondaryFill); - text-decoration:none; + +.kreta-container { + min-height: 100vh; + display: flex; + flex-direction: column; } + .kreta-main { - flex:1; - padding:clamp(1rem,3vw,2rem); - max-width:1200px; - margin:0 auto; - width:100%; + flex: 1; + padding: clamp(1rem, 3vw, 2rem); + max-width: 1400px; + margin: 0 auto; + width: 100%; } + +.absences-page { + display: grid; + grid-template-columns: 300px 1fr; + grid-template-rows: 1fr; + grid-template-areas: + "sidebar content"; + gap: 24px; + min-height: calc(100vh - 200px); +} + +.absences-sidebar { + grid-area: sidebar; + display: flex; + flex-direction: column; + gap: 16px; + position: sticky; + top: 24px; + height: fit-content; +} + .filter-card { - background:var(--card-card); - border-radius:24px; - padding:20px; - margin-bottom:24px; - box-shadow:0px 1px var(--shadow-blur) 0px var(--accent-shadow); + background: var(--card-card); + border-radius: 20px; + padding: 24px; + box-shadow: 0px 1px var(--shadow-blur) 0px var(--accent-shadow); } + .filter-header { - margin-bottom:16px; + margin-bottom: 20px; + display: flex; + align-items: center; + gap: 12px; } + .filter-header h2 { - font-size:18px; - font-weight:600; - color:var(--text-primary); - background-color:var(--card-card); + font-size: 18px; + font-weight: 600; + color: var(--text-primary); + margin: 0; + background-color: #00000000 !important; } + .filter-content { - display:grid; - grid-template-columns:repeat(auto-fit,minmax(200px,1fr)); - gap:16px; + display: flex; + flex-direction: column; + gap: 20px; } + .filter-group { - display:flex; - flex-direction:column; - gap:8px; + display: flex; + flex-direction: column; + gap: 8px; } + .filter-group label { - display:flex; - align-items:center; - gap:8px; - color:var(--text-secondary); - font-size:14px; + display: flex; + align-items: center; + gap: 10px; + color: var(--text-secondary); + font-size: 14px; + font-weight: 500; } + +.filter-group label img { + width: 20px; + height: 20px; + opacity: 0.8; +} + .filter-input { - padding:10px; - border:none; - border-radius:8px; - background:var(--button-secondaryFill); - color:var(--text-primary); - font-family:inherit; - font-size:14px; - transition:all 0.2s ease; + padding: 12px 16px; + border: 2px solid transparent; + border-radius: 12px; + background: var(--button-secondaryFill); + color: var(--text-primary); + font-family: inherit; + font-size: 14px; + transition: all 0.2s ease; + cursor: pointer; } + +.filter-input:hover { + background: var(--accent-15); +} + .filter-input:focus { - outline:none; - box-shadow:0 0 0 2px var(--accent-accent); + outline: none; + border-color: var(--accent-accent); + background: var(--button-secondaryFill); } -.stats-overview { - display:grid; - grid-template-columns:repeat(auto-fit,minmax(200px,1fr)); - gap:16px; - margin-bottom:24px; + +.stats-section { + grid-area: stats; } + +.stats-grid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 12px; +} + .stat-card { - background:var(--card-card); - border-radius:16px; - padding:20px; - text-align:center; - box-shadow:0px 1px var(--shadow-blur) 0px var(--accent-shadow); - transition:transform 0.2s ease; + background: var(--card-card); + border-radius: 16px; + padding: 20px; + 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; } + .stat-card:hover { - transform:translateY(-2px); + transform: translateY(-2px); } + +.stat-card.total { + border-left: 4px solid var(--accent-accent); +} + +.stat-card.justified { + border-left: 4px solid var(--grades-4); +} + +.stat-card.unjustified { + border-left: 4px solid var(--grades-1); +} + +.stat-card.pending { + border-left: 4px solid var(--grades-3); +} + +.stat-icon { + width: 24px; + height: 24px; + margin-bottom: 8px; + opacity: 0.8; +} + .stat-number { - font-size:32px; - font-weight:700; - color:var(--accent-accent); - margin-bottom:8px; + font-size: 32px; + font-weight: 700; + color: var(--text-primary); + line-height: 1; + margin-bottom: 6px; } + +.stat-card.total .stat-number { + color: var(--accent-accent); +} + +.stat-card.justified .stat-number { + color: var(--grades-4); +} + +.stat-card.unjustified .stat-number { + color: var(--grades-1); +} + +.stat-card.pending .stat-number { + color: var(--grades-3); +} + .stat-label { - color:var(--text-secondary); - font-size:14px; - font-weight:500; + color: var(--text-secondary); + font-size: 12px; + font-weight: 500; + text-transform: uppercase; + letter-spacing: 0.5px; } -.absences-container { - background:var(--card-card); - border-radius:16px; - overflow:hidden; - box-shadow:0px 1px var(--shadow-blur) 0px var(--accent-shadow); + +.absences-content { + grid-area: content; + display: flex; + flex-direction: column; + gap: 24px; } -.absences-table { - width:100%; - border-collapse:collapse; + +.day-group { + background: var(--card-card); + border-radius: 20px; + overflow: hidden; + box-shadow: 0px 1px var(--shadow-blur) 0px var(--accent-shadow); } -.table-header { - background:var(--accent-15); - border-bottom:1px solid var(--accent-30); + +.day-header { + background: var(--accent-accent); + color: white; + padding: 16px 24px; + display: flex; + align-items: center; + justify-content: space-between; + font-weight: 600; } -.table-header th { - padding:16px; - text-align:left; - font-weight:600; - color:var(--text-primary); - font-size:14px; - text-transform:uppercase; - letter-spacing:0.5px; + +.day-date { + display: flex; + align-items: center; + gap: 12px; + font-size: 16px; } -.table-row { - border-bottom:1px solid var(--accent-15); - transition:background-color 0.2s ease; + +.day-date img { + width: 20px; + height: 20px; + filter: brightness(0) invert(1); } -.table-row:hover { - background:var(--accent-10); + +.day-count { + background: rgba(255, 255, 255, 0.2); + padding: 4px 12px; + border-radius: 20px; + font-size: 13px; } -.table-row:last-child { - border-bottom:none; + +.day-absences { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(340px, 1fr)); + gap: 16px; + padding: 20px; } -.table-cell { - padding:16px; - color:var(--text-primary); - vertical-align:middle; + +.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); } -.date-cell { - font-weight:600; - color:var(--accent-accent); + +.absence-card:hover { + transform: translateY(-2px); + box-shadow: 0 4px 12px var(--accent-shadow); } -.lesson-cell { - text-align:center; - font-weight:500; -} -.subject-cell { - font-weight:600; -} -.topic-cell { - color:var(--text-secondary); - font-size:14px; - max-width:200px; - overflow:hidden; - text-overflow:ellipsis; - white-space:nowrap; -} -.status-cell { - text-align:center; -} -.status-badge { - display:inline-flex; - align-items:center; - gap:4px; - padding:6px 12px; - border-radius:20px; - font-size:12px; - font-weight:600; - text-transform:uppercase; - letter-spacing:0.5px; -} -.status-badge.justified { - background:var(--grades-4-bg); - color:var(--grades-4); -} -.status-badge.unjustified { - background:var(--grades-1-bg); - color:var(--grades-1); -} -.status-badge.pending { - background:var(--grades-3-bg); - color:var(--grades-3); -} -@media (max-width:768px) { - .absences-table { - font-size:14px; -} -.table-header th,.table-cell { - padding:12px 8px; -} -.topic-cell { - max-width:120px; -} -.stats-overview { - grid-template-columns:repeat(2,1fr); -} -}@media (max-width:480px) { - .absences-table,.table-header,.table-row { - display:block; -} -.table-header { - display:none; -} -.date-group { - margin-bottom:24px; -} -.date-group-header { - background:var(--accent-accent); - color:white; - padding:12px 16px; - border-radius:12px 12px 0 0; - font-weight:600; - font-size:16px; - margin-bottom:0; -} -.date-group-content { - background:var(--card-card); - border:1px solid var(--accent-15); - border-radius:0 0 12px 12px; - overflow:hidden; -} -.table-row { - width:100%; - margin-bottom:0; - border:none; - border-bottom:1px solid var(--accent-15); - border-radius:0; - padding:16px; - background:transparent; -} -.table-row:last-child { - border-bottom:none; -} -.table-cell { - display:flex; - justify-content:space-between; - align-items:flex-start; - padding:6px 0; - border-bottom:1px solid var(--accent-15); -} -.table-cell:last-child { - border-bottom:none; -} -.table-cell::before { - content:attr(data-label); - font-weight:600; - color:var(--text-secondary); - font-size:12px; - text-transform:uppercase; - flex-shrink:0; -} -.topic-cell { - max-width:none; - white-space:normal; - text-overflow:initial; - overflow:visible; - text-align:right; - flex:1; -} -.stats-overview { - grid-template-columns:1fr; -} -}.absence-details { - display:flex; - flex-direction:column; - gap:4px; + +.absence-lesson { + grid-area: lesson; + display: flex; + align-items: center; + justify-content: center; + width: 48px; + height: 48px; + background: var(--accent-15); + border-radius: 12px; + font-size: 20px; + font-weight: 700; + color: var(--accent-accent); } + .absence-subject { - font-weight:600; - color:var(--text-primary); + grid-area: subject; + font-size: 16px; + font-weight: 600; + color: var(--text-primary); + align-self: end; } + .absence-topic { - color:var(--text-secondary); - font-size:14px; + 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 { - display:flex; - align-items:center; - gap:4px; - font-size:14px; - font-weight:500; + grid-area: status; + display: flex; + align-items: center; + justify-content: center; } -.absence-status.justified { - color:var(--grades-4); + +.status-badge { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 8px 14px; + border-radius: 24px; + font-size: 11px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.2px; + white-space: nowrap; + flex-shrink: 0; } -.absence-status.unjustified { - color:var(--grades-1); + +.status-badge img { + width: 16px; + height: 16px; } -.absence-status.pending { - color:var(--grades-3); + +.status-badge.justified { + background: var(--grades-background-4); + color: var(--grades-4); } -.loading-overlay { - position:fixed; - top:0; - left:0; - width:100%; - height:100%; - background:var(--background); - display:flex; - flex-direction:column; - align-items:center; - justify-content:center; - z-index:9999; + +.status-badge.justified img { + filter: brightness(0) saturate(100%) invert(76%) sepia(43%) saturate(609%) hue-rotate(53deg) brightness(101%) contrast(92%); } -.loading-container { - display:flex; - flex-direction:column; - align-items:center; - gap:1rem; - padding:2rem; - border-radius:24px; + +.status-badge.unjustified { + background: var(--grades-background-1); + color: var(--grades-1); } -.loading-text { - color:var(--Text-Primary); - text-align:center; - font-family:Montserrat; - font-size:20px; - font-style:normal; - font-weight:700; - line-height:normal; + +.status-badge.unjustified .material-icons-round { + font-size: 16px; } -.loading-text2 { - align-self:stretch; - color:var(--Text-Secondary); - text-align:center; - font-family:Figtree; - font-size:16px; - font-style:normal; - font-weight:500; - line-height:130%; + +.status-badge.pending { + background: var(--grades-background-3); + color: var(--grades-3); } -.loading-logo { - width:48px; - height:48px; - margin-bottom:1rem; - border-radius:8px; + +.status-badge.pending .material-icons-round { + font-size: 16px; } + +.empty-state { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 60px 20px; + text-align: center; + background: var(--card-card); + border-radius: 20px; +} + +.empty-state img { + width: 80px; + height: 80px; + opacity: 0.5; + margin-bottom: 20px; +} + +.empty-state h3 { + font-size: 18px; + font-weight: 600; + color: var(--text-primary); + margin-bottom: 8px; +} + +.empty-state p { + font-size: 14px; + color: var(--text-secondary); +} + +@media (max-width: 1024px) { + .absences-page { + grid-template-columns: 260px 1fr; + gap: 20px; + } + + .day-absences { + grid-template-columns: 1fr; + } +} + +@media (max-width: 768px) { + body { + font-size: 14px; + } + + .kreta-main { + padding: 16px; + } + + .absences-page { + grid-template-columns: 1fr; + grid-template-rows: auto 1fr; + grid-template-areas: + "sidebar" + "content"; + gap: 16px; + } + + .absences-sidebar { + position: static; + } + + .filter-card { + padding: 20px; + border-radius: 16px; + } + + .filter-content { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 12px; + } + + .filter-group label { + font-size: 12px; + } + + .filter-input { + padding: 10px 12px; + font-size: 13px; + } + + .stats-grid { + grid-template-columns: repeat(4, 1fr); + gap: 10px; + } + + .stat-card { + padding: 14px 10px; + min-height: 80px; + border-radius: 12px; + } + + .stat-number { + font-size: 24px; + } + + .stat-label { + font-size: 10px; + } + + .day-group { + border-radius: 16px; + } + + .day-header { + padding: 14px 18px; + font-size: 14px; + } + + .day-absences { + padding: 14px; + gap: 12px; + } + + .absence-card { + padding: 16px; + border-radius: 12px; + } + + .absence-lesson { + width: 44px; + height: 44px; + font-size: 18px; + } + + .absence-subject { + font-size: 15px; + } +} + +@media (max-width: 480px) { + .kreta-main { + padding: 12px; + } + + .absences-page { + gap: 12px; + } + + .filter-card { + padding: 16px; + } + + .filter-header { + margin-bottom: 16px; + } + + .filter-header h2 { + font-size: 16px; + } + + .filter-content { + grid-template-columns: 1fr; + gap: 14px; + } + + .stats-grid { + grid-template-columns: repeat(2, 1fr); + gap: 8px; + } + + .stat-card { + padding: 12px 8px; + min-height: 70px; + } + + .stat-number { + font-size: 22px; + margin-bottom: 4px; + } + + .stat-label { + font-size: 9px; + letter-spacing: 0.3px; + } + + .day-header { + padding: 12px 16px; + flex-wrap: wrap; + gap: 8px; + } + + .day-date { + font-size: 14px; + } + + .day-count { + font-size: 12px; + padding: 3px 10px; + } + + .day-absences { + padding: 12px; + gap: 10px; + } + + .absence-card { + padding: 14px; + grid-template-columns: auto 1fr; + grid-template-rows: auto auto auto; + grid-template-areas: + "lesson subject" + "lesson topic" + "status status"; + gap: 6px 12px; + } + + .absence-lesson { + width: 40px; + height: 40px; + font-size: 16px; + grid-row: span 2; + } + + .absence-status { + justify-content: flex-start; + margin-top: 8px; + padding-top: 10px; + border-top: 1px solid var(--accent-15); + } + + .status-badge { + padding: 6px 12px; + font-size: 11px; + } + + .absence-subject { + font-size: 14px; + } + + .absence-topic { + font-size: 12px; + } +} + @keyframes fadeIn { from { - opacity:0; - transform:translateY(-10px); -} -to { - opacity:1; - transform:translateY(0); -} -}@keyframes dropdownShow { - from { - opacity:0; - transform:translateY(-10px); -} -to { - opacity:1; - transform:translateY(0); -} -}@keyframes spin { + opacity: 0; + transform: translateY(10px); + } to { - transform:rotate(360deg); + opacity: 1; + transform: translateY(0); + } } + +.day-group { + animation: fadeIn 0.3s ease forwards; +} + +.day-group:nth-child(1) { animation-delay: 0.05s; } +.day-group:nth-child(2) { animation-delay: 0.1s; } +.day-group:nth-child(3) { animation-delay: 0.15s; } +.day-group:nth-child(4) { animation-delay: 0.2s; } +.day-group:nth-child(5) { animation-delay: 0.25s; } + +.kreta-header { + padding: clamp(1rem, 3vw, 2rem); + display: grid; + grid-template-columns: minmax(300px, 400px) 1fr minmax(200px, 300px); + align-items: center; + gap: 1rem; +} + +@media (max-width: 1200px) { + .kreta-header { + grid-template-columns: minmax(250px, 350px) 1fr minmax(180px, 250px); + } +} + +@media (max-width: 768px) { + .kreta-header { + grid-template-columns: 1fr auto auto; + grid-template-areas: + "school toggle user" + "nav nav nav"; + padding: 1rem; + gap: 0.5rem; + } +} + +.school-info { + margin: 0; +} + +@media (max-width: 768px) { + .school-info { + grid-area: school; + max-width: none; + display: flex; + align-items: center; + gap: 0.5rem; + } +} + +.logo-text { + color: var(--text-primary); + font-size: 24px; + font-weight: 600; + margin: 0 0 0.5rem; + display: flex; + align-items: center; +} + +@media (max-width: 768px) { + .logo-text { + margin: 0; + font-size: 20px; + } +} + +.logo { + width: 24px; + border-radius: 8px; + margin-right: 0.5rem; +} + +.school-details { + color: var(--text-secondary); + font-size: 14px; +} + +.school-details span { + display: block; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + max-width: 300px; +} + +@media (max-width: 768px) { + .school-details span { + max-width: 200px; + } + .school-details { + font-size: 12px; + } +} + +.user-profile { + position: relative; + justify-self: flex-end; +} + +@media (max-width: 768px) { + .user-profile { + grid-area: user; + } +} + +.user-dropdown-btn { + display: flex; + align-items: center; + gap: 1rem; + background: none; + border: none; + cursor: pointer; + padding: 0.5rem; + border-radius: 8px; + transition: background-color 0.2s; +} + +.user-dropdown-btn:hover { + background: var(--card-card); +} + +.user-info { + text-align: right; +} + +.user-dropdown { + position: absolute; + top: 100%; + right: 0; + margin-top: 0.5rem; + background: var(--card-card); + border-radius: 12px; + box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); + width: 200px; + display: none; + z-index: 1000; +} + +.user-dropdown.show { + display: block; + animation: dropdownShow 0.2s ease; +} + +.dropdown-item { + display: flex; + align-items: center; + gap: 0.75rem; + padding: 0.75rem 1rem; + color: var(--text-primary); + text-decoration: none; + transition: background-color 0.2s; +} + +.dropdown-item:hover { + background: var(--button-secondaryFill); + text-decoration: none; +} + +@keyframes dropdownShow { + from { + opacity: 0; + transform: translateY(-10px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.loading-overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: var(--background); + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + z-index: 9999; +} + +.loading-container { + display: flex; + flex-direction: column; + align-items: center; + gap: 1rem; + padding: 2rem; + border-radius: 24px; +} + +.loading-text { + color: var(--text-primary); + text-align: center; + font-family: Montserrat, sans-serif; + font-size: 20px; + font-weight: 700; +} + +.loading-text2 { + color: var(--text-secondary); + text-align: center; + font-family: Figtree, sans-serif; + font-size: 16px; + font-weight: 500; + line-height: 130%; +} + +.loading-logo { + width: 48px; + height: 48px; + margin-bottom: 1rem; + border-radius: 8px; } \ No newline at end of file diff --git a/absences/absences.js b/absences/absences.js index 0729a96..bc611d3 100644 --- a/absences/absences.js +++ b/absences/absences.js @@ -47,6 +47,7 @@ async function collectAbsencesData() { absences.push({ date: formattedDate, + rawDate: date, lesson: item.Oraszam?.toString() || "", subject: item.Targy || "", topic: item.Tema || "", @@ -59,12 +60,19 @@ async function collectAbsencesData() { }); } - const groupedAbsences = {}; - absences.forEach((absence) => { - if (!groupedAbsences[absence.date]) { - groupedAbsences[absence.date] = []; + + const groupedAbsences = absences.reduce((groups, absence) => { + const date = absence.date; + if (!groups[date]) { + groups[date] = []; } - groupedAbsences[absence.date].push(absence); + groups[date].push(absence); + return groups; + }, {}); + + + Object.keys(groupedAbsences).forEach(date => { + groupedAbsences[date].sort((a, b) => parseInt(a.lesson) - parseInt(b.lesson)); }); return { basicData, absences, groupedAbsences }; @@ -86,20 +94,87 @@ function createFilterCard(absences) { const filterContent = document.createElement('div'); filterContent.className = 'filter-content'; + + const dateGroup = document.createElement('div'); + dateGroup.className = 'filter-group'; - const dateGroup = createFilterGroup( - 'Calendar.svg', - 'Dátum', - LanguageManager.t('absences.date'), - 'input', - { type: 'date', id: 'dateFilter', className: 'filter-input' } - ); + 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 = createSubjectFilterGroup(absences); + 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 = createJustificationFilterGroup(); + 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); @@ -108,118 +183,40 @@ function createFilterCard(absences) { return filterCard; } -function createFilterGroup(iconName, altText, labelText, elementType, attributes) { - const group = document.createElement('div'); - group.className = 'filter-group'; +function createStatsSection(absences) { + const statsSection = document.createElement('div'); + statsSection.className = 'stats-section'; - const label = document.createElement('label'); - const img = document.createElement('img'); - img.src = chrome.runtime.getURL(`icons/${iconName}`); - img.alt = altText; - img.style.width = '24px'; - img.style.height = '24px'; - - label.appendChild(img); - label.appendChild(document.createTextNode(' ' + labelText)); - - const element = document.createElement(elementType); - Object.assign(element, attributes); - - group.appendChild(label); - group.appendChild(element); - - return group; -} - -function createSubjectFilterGroup(absences) { - const group = document.createElement('div'); - group.className = 'filter-group'; - - const label = document.createElement('label'); - const img = document.createElement('img'); - img.src = chrome.runtime.getURL('icons/Subject.svg'); - img.alt = 'Tantárgy'; - img.style.width = '24px'; - img.style.height = '24px'; - - label.appendChild(img); - label.appendChild(document.createTextNode(' ' + LanguageManager.t('absences.subject'))); - - const select = document.createElement('select'); - select.id = 'subjectFilter'; - select.className = 'filter-input'; - - const defaultOption = document.createElement('option'); - defaultOption.value = ''; - defaultOption.textContent = LanguageManager.t('absences.all_subjects'); - select.appendChild(defaultOption); - - const subjects = [...new Set(absences.map(a => a.subject))].sort(); - subjects.forEach(subject => { - const option = document.createElement('option'); - option.value = subject; - option.textContent = subject; - select.appendChild(option); - }); - - group.appendChild(label); - group.appendChild(select); - - return group; -} - -function createJustificationFilterGroup() { - const group = document.createElement('div'); - group.className = 'filter-group'; - - const label = document.createElement('label'); - const img = document.createElement('img'); - img.src = chrome.runtime.getURL('icons/BadgeCheck.svg'); - img.alt = 'Igazolás'; - img.style.width = '24px'; - img.style.height = '24px'; - - label.appendChild(img); - label.appendChild(document.createTextNode(' ' + LanguageManager.t('absences.justification'))); - - const select = document.createElement('select'); - select.id = 'justificationFilter'; - select.className = 'filter-input'; - - const options = [ - { value: '', text: LanguageManager.t('absences.all_types') }, - { value: 'justified', text: LanguageManager.t('absences.justified') }, - { value: 'unjustified', text: LanguageManager.t('absences.unjustified') }, - { value: 'pending', text: LanguageManager.t('absences.pending') } - ]; - - options.forEach(optionData => { - const option = document.createElement('option'); - option.value = optionData.value; - option.textContent = optionData.text; - select.appendChild(option); - }); - - group.appendChild(label); - group.appendChild(select); - - return group; -} - -function createStatsOverview(absences) { - const statsOverview = document.createElement('div'); - statsOverview.className = 'stats-overview'; + const statsGrid = document.createElement('div'); + statsGrid.className = 'stats-grid'; const stats = [ - { number: absences.length, label: LanguageManager.t('absences.total_absences') }, - { number: absences.filter(a => a.justificationStatus === 'justified').length, label: LanguageManager.t('absences.justified') }, - { number: absences.filter(a => a.justificationStatus === 'unjustified').length, label: LanguageManager.t('absences.unjustified') }, - { number: absences.filter(a => a.justificationStatus === 'pending').length, label: LanguageManager.t('absences.pending') } + { + type: 'total', + number: absences.length, + label: LanguageManager.t('absences.total_absences') + }, + { + type: 'justified', + number: absences.filter(a => a.justificationStatus === 'justified').length, + label: LanguageManager.t('absences.justified') + }, + { + type: 'unjustified', + number: absences.filter(a => a.justificationStatus === 'unjustified').length, + label: LanguageManager.t('absences.unjustified') + }, + { + type: 'pending', + number: absences.filter(a => a.justificationStatus === 'pending').length, + label: LanguageManager.t('absences.pending') + } ]; stats.forEach(stat => { const statCard = document.createElement('div'); - statCard.className = 'stat-card'; + statCard.className = `stat-card ${stat.type}`; + statCard.dataset.type = stat.type; const statNumber = document.createElement('div'); statNumber.className = 'stat-number'; @@ -231,54 +228,179 @@ function createStatsOverview(absences) { statCard.appendChild(statNumber); statCard.appendChild(statLabel); - statsOverview.appendChild(statCard); + statsGrid.appendChild(statCard); }); - return statsOverview; + statsSection.appendChild(statsGrid); + return statsSection; } -function createAbsencesContainer(absences) { - const container = document.createElement('div'); - container.className = 'absences-container'; +function createDayGroup(date, dayAbsences) { + const dayGroup = document.createElement('div'); + dayGroup.className = 'day-group'; + dayGroup.dataset.date = date; - const table = document.createElement('table'); - table.className = 'absences-table'; - const thead = document.createElement('thead'); - thead.className = 'table-header'; + const dayHeader = document.createElement('div'); + dayHeader.className = 'day-header'; - const headerRow = document.createElement('tr'); - const headers = [ - LanguageManager.t('absences.date'), - LanguageManager.t('absences.lesson'), - LanguageManager.t('absences.subject'), - LanguageManager.t('absences.topic'), - LanguageManager.t('absences.status') - ]; + const dayDate = document.createElement('div'); + dayDate.className = 'day-date'; - headers.forEach(headerText => { - const th = document.createElement('th'); - th.textContent = headerText; - headerRow.appendChild(th); + 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); }); - thead.appendChild(headerRow); + dayGroup.appendChild(dayHeader); + dayGroup.appendChild(dayAbsencesContainer); - const tbody = document.createElement('tbody'); - generateAbsencesRows(absences, tbody); + return dayGroup; +} + +function formatDateWithDay(dateStr) { + const parts = dateStr.split('.'); + const year = parseInt(parts[0]); + const month = parseInt(parts[1]) - 1; + const day = parseInt(parts[2]); - table.appendChild(thead); - table.appendChild(tbody); - container.appendChild(table); + 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') + ]; - return container; + const dayName = days[date.getDay()]; + return `${dateStr} - ${dayName.charAt(0).toUpperCase() + dayName.slice(1)}`; +} + +function createAbsenceCard(absence) { + const card = document.createElement('div'); + card.className = 'absence-card'; + card.dataset.subject = absence.subject; + card.dataset.status = absence.justificationStatus; + card.dataset.date = absence.date; + + const lessonDiv = document.createElement('div'); + lessonDiv.className = 'absence-lesson'; + lessonDiv.textContent = absence.lesson + '.'; + + const subjectDiv = document.createElement('div'); + subjectDiv.className = 'absence-subject'; + subjectDiv.textContent = absence.subject; + + const topicDiv = document.createElement('div'); + topicDiv.className = 'absence-topic'; + topicDiv.textContent = absence.topic || '-'; + topicDiv.title = absence.topic; + + const statusDiv = document.createElement('div'); + statusDiv.className = 'absence-status'; + + const statusBadge = document.createElement('span'); + statusBadge.className = `status-badge ${absence.justificationStatus}`; + + if (absence.justificationStatus === 'justified') { + const img = document.createElement('img'); + img.src = chrome.runtime.getURL('icons/BadgeCheck.svg'); + img.alt = 'Igazolt'; + statusBadge.appendChild(img); + statusBadge.appendChild(document.createTextNode(' ' + LanguageManager.t('absences.justified'))); + } else if (absence.justificationStatus === 'unjustified') { + const span = document.createElement('span'); + span.className = 'material-icons-round'; + span.textContent = 'cancel'; + statusBadge.appendChild(span); + statusBadge.appendChild(document.createTextNode(' ' + LanguageManager.t('absences.unjustified'))); + } else { + const img = document.createElement('img'); + img.src = chrome.runtime.getURL('icons/pending.svg'); + img.alt = 'Függőben'; + statusBadge.appendChild(img); + statusBadge.appendChild(document.createTextNode(' ' + LanguageManager.t('absences.pending'))); + } + + statusDiv.appendChild(statusBadge); + + card.appendChild(lessonDiv); + card.appendChild(subjectDiv); + card.appendChild(topicDiv); + card.appendChild(statusDiv); + + return card; +} + +function createAbsencesContent(groupedAbsences) { + const content = document.createElement('div'); + content.className = 'absences-content'; + + const sortedDates = Object.keys(groupedAbsences).sort((a, b) => { + const dateA = new Date(a.replace(/\./g, '-').slice(0, -1)); + const dateB = new Date(b.replace(/\./g, '-').slice(0, -1)); + return dateB - dateA; + }); + + if (sortedDates.length === 0) { + const emptyState = document.createElement('div'); + emptyState.className = 'empty-state'; + + const emptyIcon = document.createElement('img'); + emptyIcon.src = chrome.runtime.getURL('icons/BadgeCheck.svg'); + emptyIcon.alt = 'Nincs hiányzás'; + + const emptyTitle = document.createElement('h3'); + emptyTitle.textContent = LanguageManager.t('absences.title'); + + const emptyText = document.createElement('p'); + emptyText.textContent = LanguageManager.t('dashboard.not_supported'); + + emptyState.appendChild(emptyIcon); + emptyState.appendChild(emptyTitle); + emptyState.appendChild(emptyText); + content.appendChild(emptyState); + } else { + sortedDates.forEach(date => { + const dayGroup = createDayGroup(date, groupedAbsences[date]); + content.appendChild(dayGroup); + }); + } + + return content; } async function transformAbsencesPage() { const { basicData, absences, groupedAbsences } = await collectAbsencesData(); + document.body.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'); @@ -287,244 +409,60 @@ async function transformAbsencesPage() { headerDiv.appendChild(tempDiv.firstChild); } container.appendChild(headerDiv); + const main = document.createElement('main'); main.className = 'kreta-main'; - const filterCard = createFilterCard(absences); - main.appendChild(filterCard); - const statsOverview = createStatsOverview(absences); - main.appendChild(statsOverview); - const absencesContainer = createAbsencesContainer(absences); - main.appendChild(absencesContainer); + const pageGrid = document.createElement('div'); + pageGrid.className = 'absences-page'; + + const sidebar = document.createElement('div'); + sidebar.className = 'absences-sidebar'; + const filterCard = createFilterCard(absences); + sidebar.appendChild(filterCard); + + const statsSection = createStatsSection(absences); + sidebar.appendChild(statsSection); + + const absencesContent = createAbsencesContent(groupedAbsences); + + pageGrid.appendChild(sidebar); + pageGrid.appendChild(absencesContent); + + main.appendChild(pageGrid); container.appendChild(main); document.body.appendChild(container); - setupUserDropdown(); setupMobileNavigation(); - - setupEventListeners(); - setupFilters(); + setupFilters(groupedAbsences); loadingScreen.hide(); } -function generateAbsencesRows(absences, tbody) { - const groupedByDate = absences.reduce((groups, absence) => { - const date = absence.date; - if (!groups[date]) { - groups[date] = []; - } - groups[date].push(absence); - return groups; - }, {}); - - const sortedDates = Object.keys(groupedByDate).sort( - (a, b) => new Date(b) - new Date(a), - ); - - sortedDates.forEach((date) => { - const dateAbsences = groupedByDate[date]; - const divider = document.createElement('tr'); - divider.className = 'date-group-divider'; - divider.style.display = 'none'; - tbody.appendChild(divider); - - dateAbsences.forEach((absence) => { - const row = document.createElement('tr'); - row.className = 'table-row'; - row.dataset.subject = absence.subject; - row.dataset.justified = absence.justified; - row.dataset.date = absence.date; - row.dataset.dateGroup = date; - - const dateCell = document.createElement('td'); - dateCell.className = 'table-cell date-cell'; - dateCell.dataset.label = LanguageManager.t('absences.date'); - dateCell.textContent = absence.date; - row.appendChild(dateCell); - - const lessonCell = document.createElement('td'); - lessonCell.className = 'table-cell lesson-cell'; - lessonCell.dataset.label = LanguageManager.t('absences.lesson'); - lessonCell.textContent = absence.lesson + '.'; - row.appendChild(lessonCell); - - const subjectCell = document.createElement('td'); - subjectCell.className = 'table-cell subject-cell'; - subjectCell.dataset.label = LanguageManager.t('absences.subject'); - subjectCell.textContent = absence.subject; - row.appendChild(subjectCell); - - const topicCell = document.createElement('td'); - topicCell.className = 'table-cell topic-cell'; - topicCell.dataset.label = LanguageManager.t('absences.topic'); - topicCell.title = absence.topic; - topicCell.textContent = absence.topic; - row.appendChild(topicCell); - - const statusCell = document.createElement('td'); - statusCell.className = 'table-cell status-cell'; - statusCell.dataset.label = LanguageManager.t('absences.status'); - - const statusBadge = document.createElement('span'); - statusBadge.className = `status-badge ${absence.justificationStatus}`; - - if (absence.justificationStatus === 'justified') { - const img = document.createElement('img'); - img.src = chrome.runtime.getURL('icons/BadgeCheck.svg'); - img.alt = 'Igazolt'; - img.style.width = '16px'; - img.style.height = '16px'; - statusBadge.appendChild(img); - statusBadge.appendChild(document.createTextNode(' ' + LanguageManager.t('absences.justified'))); - } else if (absence.justificationStatus === 'unjustified') { - const span = document.createElement('span'); - span.className = 'material-icons-round'; - span.textContent = 'cancel'; - statusBadge.appendChild(span); - statusBadge.appendChild(document.createTextNode(' ' + LanguageManager.t('absences.unjustified'))); - } else { - const span = document.createElement('span'); - span.className = 'material-icons-round'; - span.textContent = 'pending'; - statusBadge.appendChild(span); - statusBadge.appendChild(document.createTextNode(' ' + LanguageManager.t('absences.pending'))); - } - - statusCell.appendChild(statusBadge); - row.appendChild(statusCell); - - tbody.appendChild(row); - }); - }); -} - -function setupEventListeners() { - function setupMobileGrouping() { - if (window.innerWidth <= 480) { - createMobileGroups(); - } else { - removeMobileGroups(); - } - } - - window.addEventListener("resize", setupMobileGrouping); - - setupMobileGrouping(); -} - -function createMobileGroups() { - const tbody = document.querySelector(".absences-table tbody"); - if (!tbody) return; - - removeMobileGroups(); - - const rows = Array.from(tbody.querySelectorAll(".table-row")); - const groupedRows = {}; - - rows.forEach((row) => { - const date = row.dataset.date; - if (!groupedRows[date]) { - groupedRows[date] = []; - } - groupedRows[date].push(row); - }); - - const sortedDates = Object.keys(groupedRows).sort( - (a, b) => new Date(b) - new Date(a), - ); - - while (tbody.firstChild) { - tbody.removeChild(tbody.firstChild); - } - - sortedDates.forEach((date) => { - const dateRows = groupedRows[date]; - - const dateGroup = document.createElement("div"); - dateGroup.className = "date-group"; - - const dateHeader = document.createElement("div"); - dateHeader.className = "date-group-header"; - dateHeader.textContent = date; - - const dateContent = document.createElement("div"); - dateContent.className = "date-group-content"; - - dateRows.forEach((row) => { - dateContent.appendChild(row); - }); - - dateGroup.appendChild(dateHeader); - dateGroup.appendChild(dateContent); - tbody.appendChild(dateGroup); - }); -} - -function removeMobileGroups() { - const tbody = document.querySelector(".absences-table tbody"); - if (!tbody) return; - - const dateGroups = tbody.querySelectorAll(".date-group"); - if (dateGroups.length === 0) return; - - const allRows = []; - dateGroups.forEach((group) => { - const rows = group.querySelectorAll(".table-row"); - rows.forEach((row) => allRows.push(row)); - }); - - while (tbody.firstChild) { - tbody.removeChild(tbody.firstChild); - } - allRows.forEach((row) => tbody.appendChild(row)); -} - -function updateDateGroupsVisibility() { - if (window.innerWidth > 480) return; - - const dateGroups = document.querySelectorAll(".date-group"); - - dateGroups.forEach((group) => { - const visibleRows = group.querySelectorAll( - '.table-row[style=""], .table-row:not([style])', - ); - - if (visibleRows.length > 0) { - group.style.display = ""; - } else { - group.style.display = "none"; - } - }); -} - -function setupFilters() { +function setupFilters(originalGroupedAbsences) { try { - const filters = { - dateFilter: document.getElementById("dateFilter"), - subject: document.getElementById("subjectFilter"), - justified: document.getElementById("justificationFilter"), - }; + const dateFilter = document.getElementById("dateFilter"); + const subjectFilter = document.getElementById("subjectFilter"); + const justificationFilter = document.getElementById("justificationFilter"); - if (!filters.dateFilter || !filters.subject || !filters.justified) { + if (!dateFilter || !subjectFilter || !justificationFilter) { console.warn("Some filter elements were not found in the DOM"); return; } const filterAbsences = () => { try { - const dateFilterValue = filters.dateFilter.value; - const subject = filters.subject.value; - const justified = filters.justified.value; + const dateFilterValue = dateFilter.value; + const subject = subjectFilter.value; + const justified = justificationFilter.value; const selectedDate = dateFilterValue ? new Date(dateFilterValue) : null; - document.querySelectorAll(".table-row").forEach((row) => { - const dateStr = row.dataset.date; + document.querySelectorAll(".absence-card").forEach((card) => { + const dateStr = card.dataset.date; const dateParts = dateStr.split("."); if (dateParts.length < 3) { - console.error(`Invalid date format: ${dateStr}`); return; } @@ -532,100 +470,101 @@ function setupFilters() { const parsedMonth = parseInt(dateParts[1].trim(), 10) - 1; const parsedDay = parseInt(dateParts[2].trim(), 10); - if (isNaN(parsedDay) || isNaN(parsedMonth) || isNaN(parsedYear)) { - console.error(`Invalid date components: ${dateStr}`); - return; - } + const cardDate = new Date(parsedYear, parsedMonth, parsedDay); - const rowDate = new Date(parsedYear, parsedMonth, parsedDay); - - let showRow = true; + let showCard = true; if (selectedDate) { if ( - rowDate.getFullYear() !== selectedDate.getFullYear() || - rowDate.getMonth() !== selectedDate.getMonth() || - rowDate.getDate() !== selectedDate.getDate() + cardDate.getFullYear() !== selectedDate.getFullYear() || + cardDate.getMonth() !== selectedDate.getMonth() || + cardDate.getDate() !== selectedDate.getDate() ) { - showRow = false; + showCard = false; } } - if (subject && row.dataset.subject !== subject) { - showRow = false; + if (subject && card.dataset.subject !== subject) { + showCard = false; } - if (justified) { - const statusElement = row.querySelector(".status-badge"); - const hasStatus = statusElement.classList.contains(justified); - if (!hasStatus) showRow = false; + if (justified && card.dataset.status !== justified) { + showCard = false; } - row.style.display = showRow ? "" : "none"; + card.style.display = showCard ? "" : "none"; }); - updateDateGroupsVisibility(); + updateDayGroupsVisibility(); + updateStatistics(); } catch (err) { console.error("Error during filtering absences:", err); } }; - Object.values(filters).forEach((filter) => { - try { - if (filter) { - filter.addEventListener("change", filterAbsences); - } - } catch (err) { - if ( - err.message && - err.message.includes("Extension context invalidated") - ) { - console.warn( - "Extension context invalidated during event listener setup", - ); - } else { - console.error("Error setting up filter event listener:", err); - } + [dateFilter, subjectFilter, justificationFilter].forEach((filter) => { + if (filter) { + filter.addEventListener("change", filterAbsences); } }); - filterAbsences(); } catch (err) { - if (err.message && err.message.includes("Extension context invalidated")) { - console.warn("Extension context invalidated during filter setup"); - } else { - console.error("Error setting up filters:", err); - } + console.error("Error setting up filters:", err); } } +function updateDayGroupsVisibility() { + document.querySelectorAll(".day-group").forEach((group) => { + const visibleCards = group.querySelectorAll('.absence-card:not([style*="display: none"])'); + const dayCount = group.querySelector('.day-count'); + + if (visibleCards.length > 0) { + group.style.display = ""; + if (dayCount) { + dayCount.textContent = `${visibleCards.length} ${LanguageManager.t('absences.hours')}`; + } + } else { + group.style.display = "none"; + } + }); +} + function updateStatistics() { try { - const visibleRows = document.querySelectorAll( - '.table-row:not([style*="display: none"])', - ); - const totalVisible = visibleRows.length; - const justifiedVisible = Array.from(visibleRows).filter((row) => - row.querySelector(".status-badge.justified"), + 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(visibleRows).filter((row) => - row.querySelector(".status-badge.unjustified"), + const unjustifiedVisible = Array.from(visibleCards).filter( + card => card.dataset.status === 'unjustified' ).length; - const pendingVisible = Array.from(visibleRows).filter((row) => - row.querySelector(".status-badge.pending"), + const pendingVisible = Array.from(visibleCards).filter( + card => card.dataset.status === 'pending' ).length; const statCards = document.querySelectorAll(".stat-card"); - if (statCards[0]) - statCards[0].querySelector(".stat-number").textContent = totalVisible; - if (statCards[1]) - statCards[1].querySelector(".stat-number").textContent = justifiedVisible; - if (statCards[2]) - statCards[2].querySelector(".stat-number").textContent = - unjustifiedVisible; - if (statCards[3]) - statCards[3].querySelector(".stat-number").textContent = pendingVisible; + 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); } diff --git a/icons/pending.svg b/icons/pending.svg new file mode 100644 index 0000000..cf99da5 --- /dev/null +++ b/icons/pending.svg @@ -0,0 +1 @@ + \ No newline at end of file