This commit is contained in:
tidusjar 2025-08-24 21:52:47 +01:00
commit 7b418673c8
7 changed files with 131 additions and 68 deletions

View file

@ -76,3 +76,16 @@
<button *ngIf="!request.denied" mat-raised-button color="danger" (click)="deny(request);">{{ 'Requests.Deny' | translate }}</button> <button *ngIf="!request.denied" mat-raised-button color="danger" (click)="deny(request);">{{ 'Requests.Deny' | translate }}</button>
</div> </div>
--> -->
<button *ngIf="!tv.fullyAvailable && requestable" mat-fab color="accent" id="addFabBtn" class="floating-fab" [matMenuTriggerFor]="aboveMenu">
<i class="fas fa-plus fa-lg"></i>
<mat-menu #aboveMenu="matMenu" yPosition="above">
<button id="requestAll" mat-menu-item (click)="requestAllSeasons()">{{ 'Search.TvShows.AllSeasons' | translate }}</button>
<button id="requestFirst" mat-menu-item (click)="requestFirstSeason()">{{ 'Search.TvShows.FirstSeason' | translate }}</button>
<button id="requestLatest" mat-menu-item (click)="requestLatestSeason()">{{ 'Search.TvShows.LatestSeason' | translate }}</button>
<button id="requestSelected" mat-menu-item (click)="submitRequests()">{{ 'Common.Request' | translate }}</button>
</mat-menu>
</button>

View file

@ -192,11 +192,13 @@
</div> </div>
<div class="col-12 col-md-10"> <div class="col-12 col-md-10">
@defer (on viewport; prefetch on idle) { <tv-request-grid
<tv-request-grid id="requests-grid" [tvRequest]="tvRequest" [isAdmin]="isAdmin" [tv]="tv"></tv-request-grid> id="requests-grid"
} @placeholder { [tvRequest]="tvRequest"
<p-skeleton height="2rem" styleClass="mb-2"></p-skeleton> [isAdmin]="isAdmin"
} [tv]="tv">
</tv-request-grid>
</div> </div>
<div class="col-12 col-md-2"> <div class="col-12 col-md-2">
@ -229,17 +231,6 @@
</mat-accordion> </mat-accordion>
</div> </div>
</div> </div>
<!-- FAB Button moved outside of deferred components so it's always visible -->
<button *ngIf="!tv.fullyAvailable && (!tv.partlyAvailable || tv.partlyAvailable)" mat-fab color="accent" id="addFabBtn" class="floating-fab" [matMenuTriggerFor]="aboveMenu">
<i class="fas fa-plus fa-lg"></i>
<mat-menu #aboveMenu="matMenu" yPosition="above">
<button id="requestAll" mat-menu-item (click)="requestAllSeasons()">{{ 'Search.TvShows.AllSeasons' | translate }}</button>
<button id="requestFirst" mat-menu-item (click)="requestFirstSeason()">{{ 'Search.TvShows.FirstSeason' | translate }}</button>
<button id="requestLatest" mat-menu-item (click)="requestLatestSeason()">{{ 'Search.TvShows.LatestSeason' | translate }}</button>
<button id="requestSelected" mat-menu-item (click)="submitRequests()">{{ 'Common.Request' | translate }}</button>
</mat-menu>
</button>
</div> </div>
<div class="bottom-page-gap"></div> <div class="bottom-page-gap"></div>

View file

@ -13,7 +13,6 @@ import { TvAdvancedOptionsComponent } from "./panels/tv-advanced-options/tv-adva
import { RequestServiceV2 } from "../../../services/requestV2.service"; import { RequestServiceV2 } from "../../../services/requestV2.service";
import { forkJoin } from "rxjs"; import { forkJoin } from "rxjs";
import { SonarrFacade } from "app/state/sonarr"; import { SonarrFacade } from "app/state/sonarr";
import { ITvRequestViewModelV2 } from "../../../interfaces/ISearchTvResult";
@Component({ @Component({
standalone: false, standalone: false,
@ -136,57 +135,6 @@ export class TvDetailsComponent implements OnInit {
return this.tv.seasonRequests.every(e => e.episodes.every(x => x.available || x.approved || x.requested)); return this.tv.seasonRequests.every(e => e.episodes.every(x => x.available || x.approved || x.requested));
} }
// FAB Button methods moved from tv-request-grid component
public requestAllSeasons() {
this.tv.requestAll = true;
this.tv.firstSeason = false;
this.tv.latestSeason = false;
this.submitRequests();
}
public requestFirstSeason() {
this.tv.requestAll = false;
this.tv.firstSeason = true;
this.tv.latestSeason = false;
this.submitRequests();
}
public requestLatestSeason() {
this.tv.requestAll = false;
this.tv.firstSeason = false;
this.tv.latestSeason = true;
this.submitRequests();
}
public async submitRequests() {
// Make sure something has been selected
if (!this.tv.requestAll && !this.tv.firstSeason && !this.tv.latestSeason) {
this.messageService.send('You need to select some episodes!');
return;
}
this.tv.requested = true;
const viewModel = <ITvRequestViewModelV2>{
firstSeason: this.tv.firstSeason,
latestSeason: this.tv.latestSeason,
requestAll: this.tv.requestAll,
theMovieDbId: this.tv.id,
requestOnBehalf: null,
languageCode: 'en' // Default language, can be enhanced later
};
viewModel.seasons = [];
try {
await this.requestService2.requestTv(viewModel).toPromise();
this.messageService.send(`Request for ${this.tv.title} has been added successfully`);
// Refresh the data
await this.load();
} catch (error) {
this.messageService.send('Failed to submit request');
}
}
private checkPoster() { private checkPoster() {
if (this.tv.images.original == null) { if (this.tv.images.original == null) {
this.tv.images.original = "../../../images/default_movie_poster.png"; this.tv.images.original = "../../../images/default_movie_poster.png";

View file

@ -35,6 +35,8 @@ export default defineConfig({
// Project configuration // Project configuration
projectId: 'o5451s', projectId: 'o5451s',
e2e: { e2e: {
// Setup node events // Setup node events
async setupNodeEvents( async setupNodeEvents(

View file

@ -0,0 +1,22 @@
{
"database": {
"type": "sqlite",
"connectionString": "Data Source=Ombi.db"
},
"mediaServer": {
"type": "plex",
"host": "http://localhost:32400",
"token": "test-token"
},
"user": {
"username": "admin",
"password": "admin123",
"claims": ["Admin"]
},
"ombiConfig": {
"landingPage": "discover",
"autoApprove": false
}
}

View file

@ -21,5 +21,8 @@ import './mock-data.commands';
import "cypress-real-events/support"; import "cypress-real-events/support";
import '@bahmutov/cy-api/support'; import '@bahmutov/cy-api/support';
// Import our setup utilities
import './setup';
// Alternatively you can use CommonJS syntax: // Alternatively you can use CommonJS syntax:
// require('./commands') // require('./commands')

View file

@ -0,0 +1,84 @@
// Test setup and initialization utilities
import './commands';
// Global setup that runs before all tests
beforeEach(() => {
// Check if application is already set up
cy.checkApplicationSetup();
});
// Check if the application is already configured
Cypress.Commands.add('checkApplicationSetup', () => {
cy.log('Checking application setup status...');
// Try to access the application to see if it's already configured
cy.request({
method: 'GET',
url: '/api/v1/status',
failOnStatusCode: false,
timeout: 10000
}).then((response) => {
if (response.status === 200) {
// Application is running and configured
cy.log('Application is already configured, skipping wizard');
return;
}
// Check if we're on the wizard page
cy.visit('/').then(() => {
cy.url().then((url) => {
if (url.includes('/wizard') || url.includes('/setup')) {
cy.log('Wizard detected, running setup...');
cy.runWizardSetup();
} else {
cy.log('Application appears to be configured');
}
});
});
});
});
// Run the wizard setup
Cypress.Commands.add('runWizardSetup', () => {
cy.log('Running wizard setup...');
// Import wizard page object
cy.fixture('wizard-config').then((config) => {
// Run through wizard steps
cy.get('[data-test="wizard-welcome-next"]').click();
cy.get('[data-test="wizard-database-next"]').click();
cy.get('[data-test="wizard-media-server-next"]').click();
// Create local user
cy.get('[data-test="wizard-username"]').type(Cypress.env('username'));
cy.get('[data-test="wizard-password"]').type(Cypress.env('password'));
cy.get('[data-test="wizard-user-next"]').click();
// Complete configuration
cy.get('[data-test="wizard-config-next"]').click();
cy.get('[data-test="wizard-finish"]').click();
// Wait for setup to complete
cy.url().should('not.include', '/wizard');
cy.log('Wizard setup completed successfully');
});
});
// Check if we need to run setup for this test run
Cypress.Commands.add('ensureApplicationSetup', () => {
// This command can be called explicitly in tests that need setup
cy.checkApplicationSetup();
});
// Add to global commands interface
declare global {
namespace Cypress {
interface Chainable {
checkApplicationSetup(): Chainable<void>;
runWizardSetup(): Chainable<void>;
ensureApplicationSetup(): Chainable<void>;
}
}
}