From 180437d228553542373c45f31f152d6c3e95cccd Mon Sep 17 00:00:00 2001 From: CodeWithMaBot <310216433+CodeWithMaBot@users.noreply.github.com> Date: Sun, 23 Aug 2026 12:48:02 +0200 Subject: [PATCH 1/3] feat(home): add dedicated paused section --- .../components/home/home.component.spec.ts | 79 +++++++++++++++++++ src/app/components/home/home.component.ts | 56 +++++++++++++ .../item-form/item-form.component.spec.ts | 28 +++++++ .../item-form/item-form.component.ts | 18 +---- src/app/domain/item-form.spec.ts | 36 +++++++-- src/app/domain/item-form.ts | 7 +- src/app/services/watch-list.service.spec.ts | 4 +- src/app/services/watch-list.service.ts | 3 +- 8 files changed, 201 insertions(+), 30 deletions(-) create mode 100644 src/app/components/home/home.component.spec.ts diff --git a/src/app/components/home/home.component.spec.ts b/src/app/components/home/home.component.spec.ts new file mode 100644 index 0000000..9695e0b --- /dev/null +++ b/src/app/components/home/home.component.spec.ts @@ -0,0 +1,79 @@ +import { signal } from '@angular/core'; +import { TestBed } from '@angular/core/testing'; +import { provideRouter } from '@angular/router'; +import { vi } from 'vitest'; +import { Item } from '../../models/item.model'; +import { RoundRobinService } from '../../services/round-robin.service'; +import { WatchListService } from '../../services/watch-list.service'; +import { HomeComponent } from './home.component'; + +describe('HomeComponent', () => { + const items: Item[] = [ + { + id: 'paused-movie', + title: 'Paused Movie', + type: 'movie', + groupId: 'ungrouped', + status: 'paused', + watchHistory: [], + createdAt: '2026-05-02T10:00:00.000Z', + }, + { + id: 'backlog-item', + title: 'Backlog Series', + type: 'series', + groupId: 'ungrouped', + status: 'not-started', + watchHistory: [], + createdAt: '2026-05-01T10:00:00.000Z', + }, + ]; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + provideRouter([]), + { + provide: RoundRobinService, + useValue: { nextSeries: signal(null), nextMovie: signal(null) }, + }, + { + provide: WatchListService, + useValue: { + items: signal(items), + inProgressSeries: signal([]), + markStarted: vi.fn(), + markDropped: vi.fn(), + }, + }, + ], + }); + }); + + it('shows paused movies separately from backlog', async () => { + const fixture = TestBed.createComponent(HomeComponent); + fixture.detectChanges(); + await fixture.whenStable(); + fixture.detectChanges(); + + const sections = Array.from( + fixture.nativeElement.querySelectorAll('h2'), + ) as HTMLHeadingElement[]; + const pausedSection = sections.find((section) => section.textContent?.trim() === 'Paused'); + const pausedContainer = pausedSection?.parentElement; + + expect(pausedContainer?.textContent).toContain('Paused Movie'); + expect(pausedContainer?.textContent).not.toContain('Backlog Series'); + }); + + it('resumes and drops paused items', () => { + const fixture = TestBed.createComponent(HomeComponent); + const watchListService = TestBed.inject(WatchListService); + + fixture.componentInstance.resumePausedItem('paused-movie'); + fixture.componentInstance.dropPausedItem('paused-movie'); + + expect(watchListService.markStarted).toHaveBeenCalledWith('paused-movie'); + expect(watchListService.markDropped).toHaveBeenCalledWith('paused-movie'); + }); +}); diff --git a/src/app/components/home/home.component.ts b/src/app/components/home/home.component.ts index ba8cf5e..0471370 100644 --- a/src/app/components/home/home.component.ts +++ b/src/app/components/home/home.component.ts @@ -161,6 +161,47 @@ import { Item, ItemType } from '../../models/item.model';

} + +
+

Paused

+ @if (pausedItems().length > 0) { +
+ @for (item of pausedItems(); track item.id) { +
+
+ {{ item.title }} + {{ item.type }} +
+
+ + +
+
+ } +
+ } +
`, }) @@ -177,6 +218,13 @@ export class HomeComponent { .sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()), ); + pausedItems = computed(() => + this.watchListService + .items() + .filter((item) => item.status === 'paused') + .sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()), + ); + protected hasSeries = computed(() => this.hasItemType('series')); protected hasMovies = computed(() => this.hasItemType('movie')); protected hasInProgressSeries = computed( @@ -203,6 +251,14 @@ export class HomeComponent { this.watchListService.markDropped(itemId); } + resumePausedItem(itemId: string): void { + this.watchListService.markStarted(itemId); + } + + dropPausedItem(itemId: string): void { + this.watchListService.markDropped(itemId); + } + private hasItemType(type: ItemType): boolean { return this.watchListService.items().some((item) => item.type === type); } diff --git a/src/app/components/item-form/item-form.component.spec.ts b/src/app/components/item-form/item-form.component.spec.ts index e24023d..d1d0dad 100644 --- a/src/app/components/item-form/item-form.component.spec.ts +++ b/src/app/components/item-form/item-form.component.spec.ts @@ -186,6 +186,34 @@ describe('ItemFormComponent', () => { expect(fixture.componentInstance.formValue().seasons[0].firstEpisodeAirDate).toBe('2026-05-01'); }); + it('allows a movie to remain paused', async () => { + const fixture = TestBed.createComponent(ItemFormComponent); + fixture.componentRef.setInput('groups', groups); + fixture.componentRef.setInput('initialValue', { + title: 'Paused Movie', + type: 'series', + groupId: 'ungrouped', + status: 'paused', + season: 2, + episode: 3, + seasons: [{ seasonNumber: 2, totalEpisodes: 10 }], + startImmediately: false, + }); + + fixture.detectChanges(); + await fixture.whenStable(); + + fixture.componentInstance.setType('movie'); + await fixture.whenStable(); + + expect(fixture.componentInstance.formValue()).toMatchObject({ + title: 'Paused Movie', + type: 'movie', + status: 'paused', + }); + expect(fixture.componentInstance.itemStatuses).toContain('paused'); + }); + it('applies an autofill patch once when the patch id changes', async () => { const fixture = TestBed.createComponent(ItemFormComponent); fixture.componentRef.setInput('groups', groups); diff --git a/src/app/components/item-form/item-form.component.ts b/src/app/components/item-form/item-form.component.ts index 4cfbfe3..96338dc 100644 --- a/src/app/components/item-form/item-form.component.ts +++ b/src/app/components/item-form/item-form.component.ts @@ -11,7 +11,7 @@ import { } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { Group } from '../../models/group.model'; -import { SeasonInfo } from '../../models/item.model'; +import { ItemStatus, SeasonInfo } from '../../models/item.model'; import { TmdbSuggestion } from '../../models/tmdb-suggestion.model'; import { createDefaultItemFormValue, @@ -156,7 +156,7 @@ export interface ItemFormAutofillPatch {
Status:
- @for (status of itemStatuses(); track status) { + @for (status of itemStatuses; track status) {
}
+ } @else { +

+ No items paused +

} From 3e5a4024a3c2a102b18e3de7b015fafd9efce374 Mon Sep 17 00:00:00 2001 From: CodeWithMaBot <310216433+CodeWithMaBot@users.noreply.github.com> Date: Sun, 23 Aug 2026 12:54:16 +0200 Subject: [PATCH 3/3] fix(home): correct movie pause action label --- src/app/components/home/home.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/components/home/home.component.ts b/src/app/components/home/home.component.ts index 76bb3eb..f94279f 100644 --- a/src/app/components/home/home.component.ts +++ b/src/app/components/home/home.component.ts @@ -89,7 +89,7 @@ import { Item, ItemType } from '../../models/item.model'; (click)="markItem(nextMovie, 'paused')" class="px-3 py-1.5 border border-light-border dark:border-dark-border rounded bg-light-bg-secondary dark:bg-dark-bg-secondary text-light-font dark:text-dark-font cursor-pointer text-sm hover:bg-light-bg-tertiary dark:hover:bg-dark-bg-tertiary" > - Move to backlog + Pause