feat(epg): restructure progress panel template and actions

Refactor the EPG progress panel template to improve DOM structure and
action handling. Move the top-level panel wrapper and header markup so
the "dismiss all" button is rendered consistently when multiple imports
exist. Reorder imports list and per-item blocks so each import item
contains its own header, icon, url and dismiss button. Restore the
per-item dismiss button and adjust classes to distinguish between the
global dismiss-all and item-level dismiss actions.

These changes simplify markup nesting, fix button placement bugs, and
prepare the template for clearer styling and behavior of active and
queued import items.
This commit is contained in:
4gray committed 2026-01-05 21:52:45 +01:00
1 parent 397f57186e
commit 520f00a133
4 files changed
+119 -105

No files matched your search

+2
View File
@@ -1 +1,3 @@
<router-outlet />
<app-epg-progress-panel />
@@ -1,95 +1,117 @@
@if (isVisible()) {
<div class="epg-progress-panel">
<div class="panel-header">
<span class="header-title">
<mat-icon>live_tv</mat-icon>
{{ 'EPG.IMPORT_PROGRESS' | translate }}
</span>
@if (imports().length > 1) {
<button
mat-icon-button
class="dismiss-all-btn"
(click)="dismissAll()"
[attr.aria-label]="'BUTTONS.CLOSE' | translate"
>
<mat-icon>close</mat-icon>
</button>
}
</div>
<div class="imports-list">
<!-- Active/Completed imports -->
@for (item of activeImports; track item.url) {
<div class="import-item" [class]="getStatusClass(item.status)">
<div class="item-header">
<div class="status-icon-wrapper">
<mat-icon [class.spinning]="item.status === 'loading'">
{{ getStatusIcon(item.status) }}
</mat-icon>
</div>
<span class="item-url" [title]="item.url">
{{ getDisplayUrl(item.url) }}
</span>
<div class="epg-progress-panel">
<div class="panel-header">
<span class="header-title">
<mat-icon>live_tv</mat-icon>
{{ 'EPG.IMPORT_PROGRESS' | translate }}
</span>
@if (imports().length > 1) {
<button
mat-icon-button
class="dismiss-btn"
(click)="dismiss(item.url)"
class="dismiss-all-btn"
(click)="dismissAll()"
[attr.aria-label]="'BUTTONS.CLOSE' | translate"
>
<mat-icon>close</mat-icon>
</button>
</div>
}
</div>
@if (item.status === 'loading') {
<mat-progress-bar mode="indeterminate"></mat-progress-bar>
<div class="imports-list">
<!-- Active/Completed imports -->
@for (item of activeImports; track item.url) {
<div class="import-item" [class]="getStatusClass(item.status)">
<div class="item-header">
<div class="status-icon-wrapper">
<mat-icon
[class.spinning]="item.status === 'loading'"
>
{{ getStatusIcon(item.status) }}
</mat-icon>
</div>
<span class="item-url" [title]="item.url">
{{ getDisplayUrl(item.url) }}
</span>
<button
mat-icon-button
class="dismiss-btn"
(click)="dismiss(item.url)"
[attr.aria-label]="'BUTTONS.CLOSE' | translate"
>
<mat-icon>close</mat-icon>
</button>
</div>
@if (item.status === 'loading') {
<mat-progress-bar mode="indeterminate" />
}
<div class="item-stats">
@if (item.stats) {
<span
class="stat-badge"
matTooltip="{{
'EPG.TOTAL_CHANNELS' | translate
}}"
>
<mat-icon>tv</mat-icon>
{{ item.stats.totalChannels }}
</span>
<span
class="stat-badge"
matTooltip="{{
'EPG.TOTAL_PROGRAMS' | translate
}}"
>
<mat-icon>event</mat-icon>
{{ item.stats.totalPrograms }}
</span>
} @else if (item.error) {
<span class="error-text">{{ item.error }}</span>
} @else if (item.status === 'loading') {
<span>{{ 'EPG.LOADING' | translate }}...</span>
}
</div>
</div>
}
<div class="item-stats">
@if (item.stats) {
<span class="stat-badge">
<mat-icon>tv</mat-icon>
{{ item.stats.totalChannels }}
</span>
<span class="stat-badge">
<mat-icon>event</mat-icon>
{{ item.stats.totalPrograms }}
</span>
} @else if (item.error) {
<span class="error-text">{{ item.error }}</span>
} @else if (item.status === 'loading') {
<span>{{ 'EPG.LOADING' | translate }}...</span>
}
</div>
</div>
}
<!-- Queued imports section -->
@if (queuedImports.length > 0) {
<div class="queue-section">
<div class="queue-header">
<mat-icon>queue</mat-icon>
<span>{{ 'EPG.QUEUED' | translate }} ({{ queuedImports.length }})</span>
</div>
<div class="queue-list">
@for (item of queuedImports; track item.url; let i = $index) {
<div class="queue-item">
<span class="queue-position">{{ i + 1 }}</span>
<span class="queue-url" [title]="item.url">
{{ getDisplayUrl(item.url) }}
</span>
<button
mat-icon-button
class="dismiss-btn"
(click)="dismiss(item.url)"
[attr.aria-label]="'BUTTONS.CLOSE' | translate"
>
<mat-icon>close</mat-icon>
</button>
<!-- Queued imports section -->
@if (queuedImports.length > 0) {
<div class="queue-section">
<div class="queue-header">
<mat-icon>queue</mat-icon>
<span
>{{ 'EPG.QUEUED' | translate }} ({{
queuedImports.length
}})</span
>
</div>
<div class="queue-list">
@for (
item of queuedImports;
track item.url;
let i = $index
) {
<div class="queue-item">
<span class="queue-position">{{ i + 1 }}</span>
<span class="queue-url" [title]="item.url">
{{ getDisplayUrl(item.url) }}
</span>
<button
mat-icon-button
class="dismiss-btn"
(click)="dismiss(item.url)"
[attr.aria-label]="
'BUTTONS.CLOSE' | translate
"
>
<mat-icon>close</mat-icon>
</button>
</div>
}
</div>
</div>
}
</div>
}
</div>
}
</div>
</div>
}
@@ -144,16 +144,12 @@
.dismiss-all-btn {
width: 28px;
height: 28px;
padding: 2px;
line-height: 28px;
border-radius: 8px;
transition: all 0.2s ease;
color: var(--epg-text-tertiary);
&:hover {
background: var(--epg-item-hover);
color: var(--epg-text-primary);
}
mat-icon {
font-size: 18px;
width: 18px;
@@ -199,11 +195,11 @@
}
.import-item {
padding: 14px 16px;
margin: 0 8px;
padding: 6px 12px;
border-radius: 10px;
transition: all 0.2s ease;
position: relative;
margin: 0 6px;
&:not(:last-child) {
margin-bottom: 2px;
@@ -252,17 +248,13 @@
.dismiss-btn {
width: 26px;
height: 26px;
padding: 2px;
line-height: 26px;
border-radius: 6px;
opacity: 0;
transition: all 0.2s ease;
color: var(--epg-text-tertiary);
&:hover {
background: var(--epg-error-glow);
color: var(--epg-error);
}
mat-icon {
font-size: 16px;
width: 16px;
@@ -378,7 +370,7 @@
// Queue section styles
.queue-section {
margin: 8px;
//margin: 8px;
padding: 12px;
border-radius: 10px;
animation: fadeIn 0.3s ease;
@@ -455,6 +447,7 @@
.dismiss-btn {
width: 22px;
height: 22px;
padding: 0;
line-height: 22px;
border-radius: 4px;
opacity: 0;
@@ -462,11 +455,6 @@
color: var(--epg-text-tertiary);
flex-shrink: 0;
&:hover {
background: var(--epg-error-glow);
color: var(--epg-error);
}
mat-icon {
font-size: 14px;
width: 14px;
@@ -1,12 +1,13 @@
import { Component, inject } from '@angular/core';
import { CommonModule } from '@angular/common';
import { MatProgressBarModule } from '@angular/material/progress-bar';
import { MatIconModule } from '@angular/material/icon';
import { Component, inject } from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { MatProgressBar } from '@angular/material/progress-bar';
import { MatTooltip } from '@angular/material/tooltip';
import { TranslateModule } from '@ngx-translate/core';
import {
EpgProgressService,
EpgImportProgress,
EpgProgressService,
} from '../../services/epg-progress.service';
@Component({
@@ -14,9 +15,10 @@ import {
standalone: true,
imports: [
CommonModule,
MatProgressBarModule,
MatIconModule,
MatButtonModule,
MatIconModule,
MatTooltip,
MatProgressBar,
TranslateModule,
],
templateUrl: './epg-progress-panel.component.html',