From d36b80802675856ad9d8fa84307015aa81ac01d2 Mon Sep 17 00:00:00 2001 From: 4gray Date: Thu, 26 Dec 2024 13:33:59 +0100 Subject: [PATCH] feat(xtream): enhance portal status display with dynamic styling and actions --- .../navigation/navigation.component.html | 57 +++++-- .../navigation/navigation.component.scss | 144 ++++++++++++++++++ .../navigation/navigation.component.ts | 44 +++++- 3 files changed, 229 insertions(+), 16 deletions(-) diff --git a/src/app/xtream-tauri/navigation/navigation.component.html b/src/app/xtream-tauri/navigation/navigation.component.html index d4dc89754..d40a6bbef 100644 --- a/src/app/xtream-tauri/navigation/navigation.component.html +++ b/src/app/xtream-tauri/navigation/navigation.component.html @@ -1,5 +1,48 @@ +
+
+
+
+ {{ getStatusIcon() }} +
+
+
+ {{ + currentPlaylist()?.title || 'Unknown Portal' + }} + {{ xtreamStore.portalStatus() }} +
+
+ + + +
+
+
- home Home - + --> - - account_circle - Account Info - - - - info - Playlist Info - - settings {{ 'MENU.SETTINGS' | translate }} diff --git a/src/app/xtream-tauri/navigation/navigation.component.scss b/src/app/xtream-tauri/navigation/navigation.component.scss index 15099cdb2..c04b8cc0e 100644 --- a/src/app/xtream-tauri/navigation/navigation.component.scss +++ b/src/app/xtream-tauri/navigation/navigation.component.scss @@ -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; } \ No newline at end of file diff --git a/src/app/xtream-tauri/navigation/navigation.component.ts b/src/app/xtream-tauri/navigation/navigation.component.ts index 5683309e3..f912d3245 100644 --- a/src/app/xtream-tauri/navigation/navigation.component.ts +++ b/src/app/xtream-tauri/navigation/navigation.component.ts @@ -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, {