mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-11 11:06:16 -08:00
feat(xtream): enhance portal status display with dynamic styling and actions
This commit is contained in:
1 parent
96f61bc859
commit
d36b808026
3 files changed
+229
-16
No files matched your search
@@ -1,5 +1,48 @@
|
||||
<div class="portal-status-container" [class]="getStatusColor()">
|
||||
<div class="portal-status-wrapper">
|
||||
<div class="portal-header">
|
||||
<div class="portal-icon" [class]="getStatusColor()">
|
||||
<mat-icon>{{ getStatusIcon() }}</mat-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="portal-info">
|
||||
<span class="portal-name">{{
|
||||
currentPlaylist()?.title || 'Unknown Portal'
|
||||
}}</span>
|
||||
<span class="portal-label">{{ xtreamStore.portalStatus() }}</span>
|
||||
</div>
|
||||
<div class="portal-actions">
|
||||
<button
|
||||
mat-icon-button
|
||||
class="action-button"
|
||||
routerLink="/"
|
||||
routerLinkActive="active"
|
||||
[routerLinkActiveOptions]="{ exact: true }"
|
||||
matTooltip="Home"
|
||||
>
|
||||
<mat-icon>home</mat-icon>
|
||||
</button>
|
||||
<button
|
||||
mat-icon-button
|
||||
class="action-button"
|
||||
(click)="openAccountInfo()"
|
||||
matTooltip="Account Info"
|
||||
>
|
||||
<mat-icon>person</mat-icon>
|
||||
</button>
|
||||
<button
|
||||
mat-icon-button
|
||||
class="action-button"
|
||||
(click)="openPlaylistInfo()"
|
||||
matTooltip="Playlist Info"
|
||||
>
|
||||
<mat-icon>playlist_play</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<mat-nav-list>
|
||||
<a
|
||||
<!-- <a
|
||||
mat-list-item
|
||||
routerLink="/"
|
||||
routerLinkActive="active"
|
||||
@@ -7,7 +50,7 @@
|
||||
>
|
||||
<mat-icon matListItemIcon>home</mat-icon>
|
||||
<span matListItemTitle>Home</span>
|
||||
</a>
|
||||
</a> -->
|
||||
|
||||
<a
|
||||
mat-list-item
|
||||
@@ -73,16 +116,6 @@
|
||||
</mat-nav-list>
|
||||
|
||||
<mat-nav-list class="navigation-bottom">
|
||||
<a mat-list-item (click)="openAccountInfo()">
|
||||
<mat-icon matListItemIcon>account_circle</mat-icon>
|
||||
<span matListItemTitle>Account Info</span>
|
||||
</a>
|
||||
|
||||
<a mat-list-item (click)="openPlaylistInfo()">
|
||||
<mat-icon matListItemIcon>info</mat-icon>
|
||||
<span matListItemTitle>Playlist Info</span>
|
||||
</a>
|
||||
|
||||
<a mat-list-item (click)="openSettings()">
|
||||
<mat-icon matListItemIcon>settings</mat-icon>
|
||||
<span matListItemTitle>{{ 'MENU.SETTINGS' | translate }}</span>
|
||||
|
||||
@@ -31,4 +31,148 @@ mat-divider {
|
||||
|
||||
mat-icon {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.portal-status-container {
|
||||
margin: -10px -10px 0px;
|
||||
padding: 16px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
background: linear-gradient(to bottom, rgba(18, 18, 18, 0.8) 0%, transparent 100%);
|
||||
backdrop-filter: blur(20px);
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||
min-height: 120px;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: -1;
|
||||
opacity: 0.8;
|
||||
transition: background 0.3s ease;
|
||||
}
|
||||
|
||||
&.status-active::before {
|
||||
background: linear-gradient(135deg, rgba(76, 175, 80, 0.3) 0%, transparent 100%);
|
||||
}
|
||||
&.status-inactive::before, &.status-unavailable::before {
|
||||
background: linear-gradient(135deg, rgba(244, 67, 54, 0.3) 0%, transparent 100%);
|
||||
}
|
||||
&.status-expired::before {
|
||||
background: linear-gradient(135deg, rgba(255, 152, 0, 0.3) 0%, transparent 100%);
|
||||
}
|
||||
|
||||
.portal-status-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
height: 100%;
|
||||
min-width: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.portal-header {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.portal-icon {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
|
||||
mat-icon {
|
||||
font-size: 28px;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&.status-active { color: #4caf50; }
|
||||
&.status-inactive, &.status-unavailable { color: #f44336; }
|
||||
&.status-expired { color: #ff9800; }
|
||||
}
|
||||
|
||||
.portal-info {
|
||||
text-align: center;
|
||||
|
||||
.portal-name {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
display: block;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
color: rgba(255, 255, 255, 0.95);
|
||||
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.portal-label {
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
white-space: nowrap;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
}
|
||||
|
||||
.portal-actions {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
margin-top: 4px;
|
||||
|
||||
.action-button {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 8px;
|
||||
|
||||
mat-icon {
|
||||
font-size: 20px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin: 0;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
|
||||
mat-icon {
|
||||
color: rgba(255, 255, 255, 0.95);
|
||||
transform: scale(1.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.info-button {
|
||||
opacity: 0.7;
|
||||
margin-left: 8px;
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.navigation-bottom {
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
import { Component, inject } from '@angular/core';
|
||||
import { MatIconButton } from '@angular/material/button';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatIcon } from '@angular/material/icon';
|
||||
import { MatListModule } from '@angular/material/list';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { RouterLink, RouterLinkActive } from '@angular/router';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
@@ -16,10 +18,12 @@ import { XtreamStore } from '../xtream.store';
|
||||
standalone: true,
|
||||
imports: [
|
||||
MatListModule,
|
||||
MatIconModule,
|
||||
MatIcon,
|
||||
MatIconButton,
|
||||
RouterLink,
|
||||
RouterLinkActive,
|
||||
TranslateModule,
|
||||
MatTooltipModule,
|
||||
],
|
||||
templateUrl: './navigation.component.html',
|
||||
styleUrl: './navigation.component.scss',
|
||||
@@ -29,8 +33,40 @@ export class NavigationComponent {
|
||||
private readonly store = inject(Store);
|
||||
readonly xtreamStore = inject(XtreamStore);
|
||||
|
||||
private readonly currentPlaylist =
|
||||
this.store.selectSignal(selectActivePlaylist);
|
||||
// Make currentPlaylist public so template can access it
|
||||
readonly currentPlaylist = this.store.selectSignal(selectActivePlaylist);
|
||||
|
||||
ngOnInit() {
|
||||
this.xtreamStore.checkPortalStatus();
|
||||
}
|
||||
|
||||
getStatusColor(): string {
|
||||
const status = this.xtreamStore.portalStatus();
|
||||
switch (status) {
|
||||
case 'active':
|
||||
return 'status-active';
|
||||
case 'inactive':
|
||||
return 'status-inactive';
|
||||
case 'expired':
|
||||
return 'status-expired';
|
||||
default:
|
||||
return 'status-unavailable';
|
||||
}
|
||||
}
|
||||
|
||||
getStatusIcon(): string {
|
||||
const status = this.xtreamStore.portalStatus();
|
||||
switch (status) {
|
||||
case 'active':
|
||||
return 'check_circle';
|
||||
case 'inactive':
|
||||
return 'cancel';
|
||||
case 'expired':
|
||||
return 'warning';
|
||||
default:
|
||||
return 'error';
|
||||
}
|
||||
}
|
||||
|
||||
openAccountInfo() {
|
||||
this.dialog.open(AccountInfoComponent, {
|
||||
|
||||
Reference in new issue
Block a user