refactor(dashboard): improvements for dashboard widgets

- Updated dashboard activity items styles for improved layout and media representation.
- Introduced new styles for movie and series media types.
- Enhanced placeholder styles for live, movie, and series items using oklch color model.
- Added hover effects for activity items and action buttons in grid mode.
- Refactored dashboard widget shell styles for better alignment and padding.
- Created new on-air widget component with category filtering and loading skeletons.
- Implemented responsive design for on-air widget and improved empty state handling.
- Removed deprecated recent sources widget and integrated recent sources functionality into existing components.
- Updated recently watched widget to include action links for adding sources.
- Enhanced source stats widget with new styling for different source types (Xtream, Stalker, M3U).
This commit is contained in:
4gray committed 2026-03-08 11:23:34 +01:00
1 parent a8fd16b16d
commit 268a710bf2
27 files changed
+1487 -352

No files matched your search

@@ -1,4 +1,4 @@
import { Injectable, computed, inject, signal } from '@angular/core';
import { Injectable, NgZone, computed, inject, signal } from '@angular/core';
import { Store } from '@ngrx/store';
import {
PlaylistActions,
@@ -58,6 +58,7 @@ interface DashboardNavigationTarget {
export class DashboardDataService {
private readonly store = inject(Store);
private readonly dbService = inject(DatabaseService);
private readonly ngZone = inject(NgZone);
private readonly xtreamGlobalRecentItems = signal<GlobalRecentItem[]>([]);
private readonly xtreamGlobalFavorites = signal<DashboardFavoriteItem[]>(
@@ -133,9 +134,13 @@ export class DashboardDataService {
const normalized = recentItems.map((item) =>
this.mapGlobalRecent(item)
);
this.xtreamGlobalRecentItems.set(normalized);
} catch {
this.xtreamGlobalRecentItems.set([]);
this.ngZone.run(() => this.xtreamGlobalRecentItems.set(normalized));
} catch (err) {
console.warn(
'[DashboardData] Failed to reload global recent items',
err
);
this.ngZone.run(() => this.xtreamGlobalRecentItems.set([]));
}
}
@@ -150,9 +155,13 @@ export class DashboardDataService {
const normalized = favorites.map((item) =>
this.mapGlobalFavorite(item)
);
this.xtreamGlobalFavorites.set(normalized);
} catch {
this.xtreamGlobalFavorites.set([]);
this.ngZone.run(() => this.xtreamGlobalFavorites.set(normalized));
} catch (err) {
console.warn(
'[DashboardData] Failed to reload global favorites',
err
);
this.ngZone.run(() => this.xtreamGlobalFavorites.set([]));
}
}
@@ -1,7 +1,7 @@
export type DashboardWidgetType =
| 'recent-sources'
| 'source-stats'
| 'continue-watching'
| 'on-air'
| 'recently-watched'
| 'global-favorites';
@@ -33,7 +33,7 @@ export interface DashboardLayoutState {
widgets: DashboardWidgetConfig[];
}
export const DASHBOARD_LAYOUT_VERSION = 6;
export const DASHBOARD_LAYOUT_VERSION = 12;
export const DASHBOARD_WIDGET_SIZE_OPTIONS: DashboardWidgetSize[] = [
'one-third',
'half',
@@ -58,22 +58,13 @@ export function createDefaultWidgetScope(): DashboardWidgetScopeSettings {
}
export const DEFAULT_DASHBOARD_WIDGETS: DashboardWidgetConfig[] = [
{
id: 'recent-sources',
type: 'recent-sources',
title: 'Recent Sources',
description: 'Latest added or refreshed playlists across providers.',
size: 'half',
enabled: true,
order: 0,
},
{
id: 'source-stats',
type: 'source-stats',
title: 'Source Statistics',
description: 'Current source mix and total connected libraries.',
size: 'one-third',
enabled: true,
enabled: false,
order: 1,
},
{
@@ -81,10 +72,19 @@ export const DEFAULT_DASHBOARD_WIDGETS: DashboardWidgetConfig[] = [
type: 'continue-watching',
title: 'Continue Watching',
description: 'Jump back into the last active source.',
size: 'half',
size: 'full',
enabled: true,
order: 2,
},
{
id: 'on-air',
type: 'on-air',
title: 'On Air Now',
description: 'Live programs currently airing across your EPG channels.',
size: 'full',
enabled: true,
order: 3,
},
{
id: 'recently-watched',
type: 'recently-watched',
@@ -92,7 +92,7 @@ export const DEFAULT_DASHBOARD_WIDGETS: DashboardWidgetConfig[] = [
description: 'Global watch history across channels, VOD and series.',
size: 'two-thirds',
enabled: true,
order: 3,
order: 4,
settings: {
scope: createDefaultWidgetScope(),
},
@@ -104,7 +104,7 @@ export const DEFAULT_DASHBOARD_WIDGETS: DashboardWidgetConfig[] = [
description: 'Pinned items across all connected providers.',
size: 'one-third',
enabled: true,
order: 4,
order: 5,
settings: {
scope: createDefaultWidgetScope(),
},