feat(category-management): show category item counts

This commit is contained in:
4gray committed 2026-01-12 00:52:42 +01:00
1 parent 51ce39e2b4
commit fb1104f6b7
6 files changed
+54 -17

No files matched your search

@@ -59,6 +59,7 @@
>
</mat-checkbox>
<span class="category-name">{{ category.name }}</span>
<span class="item-count">({{ getItemCount(category) }})</span>
</div>
} @empty {
<div class="empty-message">
@@ -79,6 +79,12 @@
text-overflow: ellipsis;
}
.item-count {
opacity: 0.6;
font-size: 12px;
color: var(--mat-sys-on-surface-variant);
}
.empty-message {
padding: 24px;
text-align: center;
@@ -18,6 +18,7 @@ import { DatabaseService, XCategoryFromDb } from 'services';
export interface CategoryManagementDialogData {
playlistId: string;
contentType: 'live' | 'vod' | 'series';
itemCounts: Map<number, number>;
}
interface CategoryWithSelection extends XCategoryFromDb {
@@ -104,6 +105,12 @@ export class CategoryManagementDialogComponent implements OnInit {
this.searchTerm.set('');
}
getItemCount(category: CategoryWithSelection): number {
// content.category_id references categories.id (internal DB id), not xtream_id
const categoryId = Number(category.id);
return this.data.itemCounts.get(categoryId) ?? 0;
}
private getDbType(): 'live' | 'movies' | 'series' {
switch (this.data.contentType) {
case 'live':
@@ -32,7 +32,9 @@ export class CategoryViewComponent {
}
getItemCount(item: XtreamCategory): number {
const itemId = Number((item as any).category_id ?? item.id);
// content.category_id references categories.id (internal DB id)
// For DB format categories, use id; for API format, use category_id
const itemId = Number(item.id ?? (item as any).category_id);
return this.itemCounts().get(itemId) ?? 0;
}
}
@@ -118,6 +118,7 @@ export class LiveStreamLayoutComponent implements OnInit {
data: {
playlistId,
contentType,
itemCounts: this.categoryItemCounts(),
},
width: '500px',
maxHeight: '80vh',
@@ -50,7 +50,9 @@ export function withSelection() {
*/
getSelectedCategory: computed(() => {
const categoryId = store.selectedCategoryId();
if (!categoryId) return null;
if (!categoryId) {
return { id: 0, name: 'All Items', type: store.selectedContentType() };
}
// Access parent store categories (from withContent)
const storeAny = store as any;
@@ -98,9 +100,6 @@ export function withSelection() {
const startIndex = store.page() * store.limit();
const endIndex = startIndex + store.limit();
const categoryId = store.selectedCategoryId();
if (!categoryId) return [];
const categoryType = store.selectedContentType();
// Access parent store content (from withContent)
@@ -112,9 +111,20 @@ export function withSelection() {
? storeAny.vodStreams?.() || []
: storeAny.serialStreams?.() || [];
return content
.filter((item: any) => Number(item.category_id) === categoryId)
.slice(startIndex, endIndex);
let filteredContent = content;
if (categoryId) {
filteredContent = content.filter((item: any) => Number(item.category_id) === categoryId);
} else {
// Filter nothing (all items), sort by added
// Clone to avoid mutation
filteredContent = [...content].sort((a: any, b: any) => {
const dateA = parseInt(categoryType === 'series' ? a.last_modified : a.added) || 0;
const dateB = parseInt(categoryType === 'series' ? b.last_modified : b.added) || 0;
return dateB - dateA;
});
}
return filteredContent.slice(startIndex, endIndex);
}),
/**
@@ -124,8 +134,6 @@ export function withSelection() {
const categoryId = store.selectedCategoryId();
const categoryType = store.selectedContentType();
if (!categoryId) return [];
// Access parent store content (from withContent)
const storeAny = store as any;
const content =
@@ -135,6 +143,15 @@ export function withSelection() {
? storeAny.vodStreams?.() || []
: storeAny.serialStreams?.() || [];
if (!categoryId) {
// Return all items sorted by added
return [...content].sort((a: any, b: any) => {
const dateA = parseInt(categoryType === 'series' ? a.last_modified : a.added) || 0;
const dateB = parseInt(categoryType === 'series' ? b.last_modified : b.added) || 0;
return dateB - dateA;
});
}
return content.filter(
(item: any) => Number(item.category_id) === categoryId
);
@@ -145,8 +162,6 @@ export function withSelection() {
*/
getTotalPages: computed(() => {
const categoryId = store.selectedCategoryId();
if (!categoryId) return 0;
const categoryType = store.selectedContentType();
// Access parent store content (from withContent)
@@ -158,9 +173,14 @@ export function withSelection() {
? storeAny.vodStreams?.() || []
: storeAny.serialStreams?.() || [];
const totalItems = content.filter(
(item: any) => Number(item.category_id) === categoryId
).length;
let totalItems = 0;
if (categoryId) {
totalItems = content.filter(
(item: any) => Number(item.category_id) === categoryId
).length;
} else {
totalItems = content.length;
}
return Math.ceil(totalItems / store.limit());
}),
@@ -230,9 +250,9 @@ export function withSelection() {
/**
* Set the selected category
*/
setSelectedCategory(categoryId: number): void {
setSelectedCategory(categoryId: number | null): void {
patchState(store, {
selectedCategoryId: Number(categoryId),
selectedCategoryId: categoryId !== null ? Number(categoryId) : null,
page: 0,
});
},