From 9411ab815eeabe1a35056fd188a32e2305dd307f Mon Sep 17 00:00:00 2001 From: Flaminel Date: Sun, 15 Jun 2025 23:51:23 +0300 Subject: [PATCH] fixed lidarr layout --- .../Controllers/ConfigurationController.cs | 48 +- .../settings/lidarr/lidarr-config.store.ts | 44 +- .../lidarr/lidarr-settings.component.html | 314 +++++++---- .../lidarr/lidarr-settings.component.scss | 5 +- .../lidarr/lidarr-settings.component.ts | 491 +++++++++--------- 5 files changed, 520 insertions(+), 382 deletions(-) diff --git a/code/Executable/Controllers/ConfigurationController.cs b/code/Executable/Controllers/ConfigurationController.cs index a507b734..a3cbe92b 100644 --- a/code/Executable/Controllers/ConfigurationController.cs +++ b/code/Executable/Controllers/ConfigurationController.cs @@ -264,9 +264,10 @@ public class ConfigurationController : ControllerBase try { var config = await _dataContext.ArrConfigs + .Include(x => x.Instances) .AsNoTracking() .FirstAsync(x => x.Type == InstanceType.Lidarr); - return Ok(config); + return Ok(config.Adapt()); } finally { @@ -482,29 +483,14 @@ public class ConfigurationController : ControllerBase try { // Get existing config - // var oldConfig = await _dataContext.LidarrConfigs - // .Include(x => x.Instances) - // .FirstAsync(); - // - // // Create new config with updated basic settings only (instances managed separately) - // var updatedConfig = new LidarrConfig - // { - // Id = oldConfig.Id, // Keep the existing ID - // Enabled = newConfigDto.Enabled, - // FailedImportMaxStrikes = newConfigDto.FailedImportMaxStrikes, - // Instances = oldConfig.Instances // Keep existing instances unchanged - // }; - // - // // Validate the configuration - // updatedConfig.Validate(); - // - // // Update the existing entity using Mapster, excluding the ID - // var config = new TypeAdapterConfig(); - // config.NewConfig() - // .Ignore(dest => dest.Id) - // .Ignore(dest => dest.Instances); // Don't update instances here - // - // updatedConfig.Adapt(oldConfig, config); + var config = await _dataContext.ArrConfigs + .FirstAsync(x => x.Type == InstanceType.Lidarr); + + config.Enabled = newConfigDto.Enabled; + config.FailedImportMaxStrikes = newConfigDto.FailedImportMaxStrikes; + + // Validate the configuration + config.Validate(); // Persist the configuration await _dataContext.SaveChangesAsync(); @@ -781,7 +767,6 @@ public class ConfigurationController : ControllerBase { // Get the Lidarr config to add the instance to var config = await _dataContext.ArrConfigs - .Include(c => c.Instances) .FirstAsync(x => x.Type == InstanceType.Lidarr); // Create the new instance @@ -791,14 +776,15 @@ public class ConfigurationController : ControllerBase Url = new Uri(newInstance.Url), ApiKey = newInstance.ApiKey, ArrConfigId = config.Id, - ArrConfig = config // Set the navigation property }; - - // Add to the config - config.Instances.Add(instance); + + // Add to the config's instances collection + // config.Instances.Add(instance); + await _dataContext.ArrInstances.AddAsync(instance); + // Save changes await _dataContext.SaveChangesAsync(); - return CreatedAtAction(nameof(GetLidarrConfig), new { id = instance.Id }, instance); + return CreatedAtAction(nameof(GetLidarrConfig), new { id = instance.Id }, instance.Adapt()); } catch (Exception ex) { @@ -835,7 +821,7 @@ public class ConfigurationController : ControllerBase await _dataContext.SaveChangesAsync(); - return Ok(instance); + return Ok(instance.Adapt()); } catch (Exception ex) { diff --git a/code/UI/src/app/settings/lidarr/lidarr-config.store.ts b/code/UI/src/app/settings/lidarr/lidarr-config.store.ts index aaae0d97..8182003e 100644 --- a/code/UI/src/app/settings/lidarr/lidarr-config.store.ts +++ b/code/UI/src/app/settings/lidarr/lidarr-config.store.ts @@ -48,10 +48,52 @@ export class LidarrConfigStore extends signalStore( ) ), + /** + * Save the Lidarr configuration (basic settings only) + */ + saveConfig: rxMethod>( + (config$: Observable>) => config$.pipe( + tap(() => patchState(store, { saving: true, error: null })), + switchMap(configUpdate => { + const currentConfig = store.config(); + if (!currentConfig) { + patchState(store, { + saving: false, + error: 'No current configuration available' + }); + return EMPTY; + } + + const updatedConfig: LidarrConfig = { + ...currentConfig, + ...configUpdate + }; + + return configService.updateLidarrConfig(updatedConfig).pipe( + tap({ + next: () => { + patchState(store, { + config: updatedConfig, + saving: false + }); + }, + error: (error) => { + patchState(store, { + saving: false, + error: error.message || 'Failed to save Lidarr configuration' + }); + } + }), + catchError(() => EMPTY) + ); + }) + ) + ), + /** * Save the Lidarr configuration */ - saveConfig: rxMethod( + saveFullConfig: rxMethod( (config$: Observable) => config$.pipe( tap(() => patchState(store, { saving: true, error: null })), switchMap(config => configService.updateLidarrConfig(config).pipe( diff --git a/code/UI/src/app/settings/lidarr/lidarr-settings.component.html b/code/UI/src/app/settings/lidarr/lidarr-settings.component.html index 1b6b311d..05b3f976 100644 --- a/code/UI/src/app/settings/lidarr/lidarr-settings.component.html +++ b/code/UI/src/app/settings/lidarr/lidarr-settings.component.html @@ -1,18 +1,10 @@ - - -
-
-

Lidarr Configuration

- Configure Lidarr integration settings -
-
- -
-
-
+
+
+

Lidarr

+
-
- + +
+
- -
- -
- -
- - When enabled, Lidarr API integration will be used + +
+ + + + +
+
+

Global Settings

+ Configure general Lidarr integration settings +
+
+ +
-
+ -
- -
+ +
+
- -
- Maximum number of strikes before removing a failed import (-1 to use global setting; 0 to disable) -
-
- - -
-

Lidarr Instances

- Configure multiple Lidarr server instances -
- - -
- -
-

No Lidarr instances defined. Add an instance to start using Lidarr integration.

-
- - -
-
-
-
- - -
- -
- Name is required - -
-
- -
- - URL is required - URL must be a valid URL - URL must use http or https protocol -
-
- -
- -
- - API key is required -
-
-
+ + When enabled, Lidarr API integration will be used
- -
- + +
+ +
+
+ +
+ Maximum number of strikes before removing a failed import (-1 to use global setting; 0 to disable) +
+
+ + + + + + + + + +
+
+

Instances

+ Manage Lidarr server instances +
+
+ +
+
+
+ + +
+ +

No Lidarr instances configured

+ Add an instance to start using Lidarr integration +
+ + +
+
+
+
+ + {{ instance.name }} +
+
+ + +
+
+ +
+
+ +
+
- +
- +
+ + + +
+
+ + + Name is required +
+ +
+ + + URL is required + URL must be a valid URL + URL must use http or https protocol +
+ +
+ + + API key is required +
+
+ + + + +
+ + + diff --git a/code/UI/src/app/settings/lidarr/lidarr-settings.component.scss b/code/UI/src/app/settings/lidarr/lidarr-settings.component.scss index 486cf16c..b6ebdac6 100644 --- a/code/UI/src/app/settings/lidarr/lidarr-settings.component.scss +++ b/code/UI/src/app/settings/lidarr/lidarr-settings.component.scss @@ -1,4 +1,5 @@ -/* Sonarr Settings Styles */ +/* Lidarr Settings Styles */ @import '../styles/settings-shared.scss'; -@import '../styles/arr-shared.scss'; \ No newline at end of file +@import '../styles/arr-shared.scss'; +@import '../settings-page/settings-page.component.scss'; \ No newline at end of file diff --git a/code/UI/src/app/settings/lidarr/lidarr-settings.component.ts b/code/UI/src/app/settings/lidarr/lidarr-settings.component.ts index c727b941..1d9d5df2 100644 --- a/code/UI/src/app/settings/lidarr/lidarr-settings.component.ts +++ b/code/UI/src/app/settings/lidarr/lidarr-settings.component.ts @@ -1,6 +1,6 @@ import { Component, EventEmitter, OnDestroy, Output, effect, inject } from "@angular/core"; import { CommonModule } from "@angular/common"; -import { FormArray, FormBuilder, FormGroup, ReactiveFormsModule, Validators, AbstractControl, ValidationErrors } from "@angular/forms"; +import { FormBuilder, FormGroup, ReactiveFormsModule, Validators, AbstractControl, ValidationErrors } from "@angular/forms"; import { Subject, takeUntil } from "rxjs"; import { LidarrConfigStore } from "./lidarr-config.store"; import { CanComponentDeactivate } from "../../core/guards"; @@ -13,10 +13,11 @@ import { InputTextModule } from "primeng/inputtext"; import { CheckboxModule } from "primeng/checkbox"; import { ButtonModule } from "primeng/button"; import { InputNumberModule } from "primeng/inputnumber"; -import { SelectButtonModule } from "primeng/selectbutton"; import { ToastModule } from "primeng/toast"; +import { DialogModule } from "primeng/dialog"; +import { ConfirmDialogModule } from "primeng/confirmdialog"; +import { ConfirmationService } from "primeng/api"; import { NotificationService } from "../../core/services/notification.service"; -import { DropdownModule } from "primeng/dropdown"; import { LoadingErrorStateComponent } from "../../shared/components/loading-error-state/loading-error-state.component"; @Component({ @@ -30,12 +31,12 @@ import { LoadingErrorStateComponent } from "../../shared/components/loading-erro CheckboxModule, ButtonModule, InputNumberModule, - SelectButtonModule, ToastModule, - DropdownModule, + DialogModule, + ConfirmDialogModule, LoadingErrorStateComponent, ], - providers: [LidarrConfigStore], + providers: [LidarrConfigStore, ConfirmationService], templateUrl: "./lidarr-settings.component.html", styleUrls: ["./lidarr-settings.component.scss"], }) @@ -43,22 +44,26 @@ export class LidarrSettingsComponent implements OnDestroy, CanComponentDeactivat @Output() saved = new EventEmitter(); @Output() error = new EventEmitter(); - // Lidarr Configuration Form - lidarrForm: FormGroup; + // Forms + globalForm: FormGroup; + instanceForm: FormGroup; + + // Modal state + showInstanceModal = false; + modalMode: 'add' | 'edit' = 'add'; + editingInstance: ArrInstance | null = null; // Original form values for tracking changes - private originalFormValues: any; - - // Track whether the form has actual changes compared to original values - hasActualChanges = false; + private originalGlobalValues: any; + hasGlobalChanges = false; // Clean up subscriptions private destroy$ = new Subject(); - // Inject the necessary services + // Services private formBuilder = inject(FormBuilder); - // Using the notification service for all toast messages private notificationService = inject(NotificationService); + private confirmationService = inject(ConfirmationService); private lidarrStore = inject(LidarrConfigStore); // Signals from store @@ -66,41 +71,46 @@ export class LidarrSettingsComponent implements OnDestroy, CanComponentDeactivat lidarrLoading = this.lidarrStore.loading; lidarrError = this.lidarrStore.error; lidarrSaving = this.lidarrStore.saving; - instanceOperations = this.lidarrStore.instanceOperations; /** * Check if component can be deactivated (navigation guard) */ canDeactivate(): boolean { - return !this.lidarrForm?.dirty || !this.hasActualChanges; + return !this.globalForm?.dirty || !this.hasGlobalChanges; } constructor() { - // Initialize the main form - this.lidarrForm = this.formBuilder.group({ + // Initialize forms + this.globalForm = this.formBuilder.group({ enabled: [false], - failedImportMaxStrikes: [-1], + failedImportMaxStrikes: [{ value: -1, disabled: true }], }); - // Add instances FormArray to main form - this.lidarrForm.addControl('instances', this.formBuilder.array([])); + this.instanceForm = this.formBuilder.group({ + name: ['', Validators.required], + url: ['', [Validators.required, this.uriValidator.bind(this)]], + apiKey: ['', Validators.required], + }); // Load Lidarr config data this.lidarrStore.loadConfig(); + // Setup form value change listeners + this.setupFormValueChangeListeners(); + // Setup effect to update form when config changes effect(() => { const config = this.lidarrConfig(); if (config) { - this.updateFormFromConfig(config); + this.updateGlobalFormFromConfig(config); } }); - // Track form changes for dirty state - this.lidarrForm.valueChanges + // Track global form changes + this.globalForm.valueChanges .pipe(takeUntil(this.destroy$)) .subscribe(() => { - this.hasActualChanges = this.formValuesChanged(); + this.hasGlobalChanges = this.globalFormValuesChanged(); }); } @@ -113,51 +123,74 @@ export class LidarrSettingsComponent implements OnDestroy, CanComponentDeactivat } /** - * Update form with values from the configuration + * Update global form with values from the configuration */ - private updateFormFromConfig(config: LidarrConfig): void { - // Update main form controls - this.lidarrForm.patchValue({ + private updateGlobalFormFromConfig(config: LidarrConfig): void { + this.globalForm.patchValue({ enabled: config.enabled, - failedImportMaxStrikes: config.failedImportMaxStrikes + failedImportMaxStrikes: config.failedImportMaxStrikes, }); - // Clear and rebuild the instances form array - const instancesArray = this.lidarrForm.get('instances') as FormArray; - instancesArray.clear(); + // Update form control disabled states + this.updateFormControlDisabledStates(config); - // Add all instances to the form array - if (config.instances && config.instances.length > 0) { - config.instances.forEach(instance => { - instancesArray.push( - this.formBuilder.group({ - id: [instance.id || ''], - name: [instance.name, Validators.required], - url: [instance.url, [Validators.required, this.uriValidator.bind(this)]], - apiKey: [instance.apiKey, Validators.required], - }) - ); - }); + // Store original values for dirty checking + this.storeOriginalGlobalValues(); + } + + /** + * Set up listeners for form control value changes to manage dependent control states + */ + private setupFormValueChangeListeners(): void { + // Listen for changes to the 'enabled' control + const enabledControl = this.globalForm.get('enabled'); + if (enabledControl) { + enabledControl.valueChanges + .pipe(takeUntil(this.destroy$)) + .subscribe(enabled => { + this.updateMainControlsState(enabled); + }); } - - // Store original form values for dirty checking - this.storeOriginalValues(); } /** - * Store original form values for dirty checking + * Update form control disabled states based on the configuration */ - private storeOriginalValues(): void { - this.originalFormValues = JSON.parse(JSON.stringify(this.lidarrForm.value)); - this.lidarrForm.markAsPristine(); - this.hasActualChanges = false; + private updateFormControlDisabledStates(config: LidarrConfig): void { + const enabled = config.enabled; + this.updateMainControlsState(enabled); } /** - * Check if the current form values are different from the original values + * Update the state of main controls based on the 'enabled' control value */ - private formValuesChanged(): boolean { - return !this.isEqual(this.lidarrForm.value, this.originalFormValues); + private updateMainControlsState(enabled: boolean): void { + const failedImportMaxStrikesControl = this.globalForm.get('failedImportMaxStrikes'); + + // Disable emitting events during state changes to prevent infinite loops + const options = { emitEvent: false }; + + if (enabled) { + failedImportMaxStrikesControl?.enable(options); + } else { + failedImportMaxStrikesControl?.disable(options); + } + } + + /** + * Store original global form values for dirty checking + */ + private storeOriginalGlobalValues(): void { + this.originalGlobalValues = JSON.parse(JSON.stringify(this.globalForm.value)); + this.globalForm.markAsPristine(); + this.hasGlobalChanges = false; + } + + /** + * Check if the current global form values are different from the original values + */ + private globalFormValuesChanged(): boolean { + return !this.isEqual(this.globalForm.value, this.originalGlobalValues); } /** @@ -188,93 +221,6 @@ export class LidarrSettingsComponent implements OnDestroy, CanComponentDeactivat return true; } - /** - * Update form control disabled states based on the configuration - */ - private updateFormControlDisabledStates(config: LidarrConfig): void { - const enabled = config.enabled; - this.updateMainControlsState(enabled); - } - - /** - * Update the state of main controls based on the 'enabled' control value - */ - private updateMainControlsState(enabled: boolean): void { - const failedImportMaxStrikesControl = this.lidarrForm.get('failedImportMaxStrikes'); - const searchTypeControl = this.lidarrForm.get('searchType'); - - if (enabled) { - failedImportMaxStrikesControl?.enable(); - searchTypeControl?.enable(); - } else { - failedImportMaxStrikesControl?.disable(); - searchTypeControl?.disable(); - } - } - - /** - * Add a new instance to the instances form array - * @param instance Optional instance configuration to initialize the form with - */ - addInstance(instance: ArrInstance | null = null): void { - const instanceForm = this.formBuilder.group({ - id: [instance?.id || ''], - name: [instance?.name || '', Validators.required], - url: [instance?.url?.toString() || '', [Validators.required, this.uriValidator.bind(this)]], - apiKey: [instance?.apiKey || '', Validators.required] - }); - - this.instances.push(instanceForm); - - // Mark form as dirty to enable save button - this.lidarrForm.markAsDirty(); - this.hasActualChanges = this.formValuesChanged(); - } - - /** - * Remove an instance at the specified index - */ - removeInstance(index: number): void { - const instanceForm = this.getInstanceAsFormGroup(index); - const instanceId = instanceForm.get('id')?.value; - - // Just remove from the form array - deletion will be handled on save - this.instances.removeAt(index); - - // Mark form as dirty to enable save button - this.lidarrForm.markAsDirty(); - this.hasActualChanges = this.formValuesChanged(); - } - - /** - * Get the instances form array - */ - get instances(): FormArray { - return this.lidarrForm.get('instances') as FormArray; - } - - /** - * Get an instance at the specified index as a FormGroup - */ - getInstanceAsFormGroup(index: number): FormGroup { - return this.instances.at(index) as FormGroup; - } - - /** - * Check if an instance field has an error - * @param instanceIndex The index of the instance in the array - * @param fieldName The name of the field to check - * @param errorName The name of the error to check for - * @returns True if the field has the specified error - */ - hasInstanceFieldError(instanceIndex: number, fieldName: string, errorName: string): boolean { - const instancesArray = this.lidarrForm.get('instances') as FormArray; - if (!instancesArray || !instancesArray.controls[instanceIndex]) return false; - - const control = (instancesArray.controls[instanceIndex] as FormGroup).get(fieldName); - return control !== null && control.hasError(errorName) && control.touched; - } - /** * Custom validator to check if the input is a valid URI */ @@ -311,148 +257,211 @@ export class LidarrSettingsComponent implements OnDestroy, CanComponentDeactivat } /** - * Check if the form control has an error - * @param controlName The name of the control to check - * @param errorName The name of the error to check for - * @returns True if the control has the specified error + * Check if a form control has an error */ - hasError(controlName: string, errorName: string): boolean { - const control = this.lidarrForm.get(controlName); + hasError(form: FormGroup, controlName: string, errorName: string): boolean { + const control = form.get(controlName); return control !== null && control.hasError(errorName) && control.touched; } /** - * Save the Lidarr configuration + * Save the global Lidarr configuration */ - saveLidarrConfig(): void { - // Mark all form controls as touched to trigger validation - this.markFormGroupTouched(this.lidarrForm); + saveGlobalConfig(): void { + this.markFormGroupTouched(this.globalForm); - if (this.lidarrForm.invalid) { + if (this.globalForm.invalid) { this.notificationService.showError('Please fix the validation errors before saving'); return; } - if (!this.hasActualChanges) { + if (!this.hasGlobalChanges) { this.notificationService.showSuccess('No changes detected'); return; } - // Get the current config to preserve existing instances const currentConfig = this.lidarrConfig(); if (!currentConfig) return; - // Create the updated main config - const updatedConfig: LidarrConfig = { - ...currentConfig, - enabled: this.lidarrForm.get('enabled')?.value, - failedImportMaxStrikes: this.lidarrForm.get('failedImportMaxStrikes')?.value + const updatedConfig = { + enabled: this.globalForm.get('enabled')?.value, + failedImportMaxStrikes: this.globalForm.get('failedImportMaxStrikes')?.value }; - // Get the instances from the form - const formInstances = this.instances.getRawValue(); + this.lidarrStore.saveConfig(updatedConfig); - // Separate creates and updates - const creates: CreateArrInstanceDto[] = []; - const updates: Array<{ id: string, instance: ArrInstance }> = []; - - formInstances.forEach((instance: any) => { - if (instance.id) { - // This is an existing instance, prepare for update - const updateInstance: ArrInstance = { - id: instance.id, - name: instance.name, - url: instance.url, - apiKey: instance.apiKey - }; - updates.push({ id: instance.id, instance: updateInstance }); - } else { - // This is a new instance, prepare for creation (don't send ID) - const createInstance: CreateArrInstanceDto = { - name: instance.name, - url: instance.url, - apiKey: instance.apiKey - }; - creates.push(createInstance); - } - }); - - // Use the new method that saves config and processes instances sequentially - this.lidarrStore.saveConfigAndInstances({ - config: updatedConfig, - instanceOperations: { creates, updates, deletes: [] } - }); - - // Monitor the saving state to show completion feedback - this.monitorSavingCompletion(); + // Monitor saving completion + this.monitorGlobalSaving(); } /** - * Monitor saving completion and show appropriate feedback + * Monitor global saving completion */ - private monitorSavingCompletion(): void { - // Use a timeout to check the saving state periodically + private monitorGlobalSaving(): void { const checkSavingStatus = () => { const saving = this.lidarrSaving(); const error = this.lidarrError(); - const pendingOps = this.instanceOperations(); - if (!saving && Object.keys(pendingOps).length === 0) { - // Operations are complete + if (!saving) { if (error) { - this.notificationService.showError(`Save completed with issues: ${error}`); + this.notificationService.showError(`Save failed: ${error}`); this.error.emit(error); - // Don't mark as pristine if there were errors } else { - // Complete success - this.notificationService.showSuccess('Lidarr configuration saved successfully'); + this.notificationService.showSuccess('Global configuration saved successfully'); this.saved.emit(); - // Reload config from backend to ensure UI is in sync - this.lidarrStore.loadConfig(); - - // Reset form state after successful save - setTimeout(() => { - this.lidarrForm.markAsPristine(); - this.hasActualChanges = false; - this.storeOriginalValues(); - }, 100); + // Reset form state without reloading from backend + this.globalForm.markAsPristine(); + this.hasGlobalChanges = false; + this.storeOriginalGlobalValues(); } } else { - // Still saving, check again in a short while setTimeout(checkSavingStatus, 100); } }; - // Start monitoring setTimeout(checkSavingStatus, 100); } /** - * Reset the Lidarr configuration form to default values + * Get instances from current config */ - resetLidarrConfig(): void { - // Clear all instances - const instancesArray = this.lidarrForm.get('instances') as FormArray; - instancesArray.clear(); - - // Reset main config to defaults - this.lidarrForm.patchValue({ - enabled: false, - failedImportMaxStrikes: -1 - }); - - // Check if this reset actually changes anything compared to the original state - const hasChangesAfterReset = this.formValuesChanged(); - - if (hasChangesAfterReset) { - // Only mark as dirty if the reset actually changes something - this.lidarrForm.markAsDirty(); - this.hasActualChanges = true; - } else { - // If reset brings us back to original state, mark as pristine - this.lidarrForm.markAsPristine(); - this.hasActualChanges = false; - } + get instances(): ArrInstance[] { + return this.lidarrConfig()?.instances || []; } + + /** + * Check if instance management should be disabled + */ + get instanceManagementDisabled(): boolean { + return !this.globalForm.get('enabled')?.value; + } + + /** + * Open modal to add new instance + */ + openAddInstanceModal(): void { + this.modalMode = 'add'; + this.editingInstance = null; + this.instanceForm.reset(); + this.showInstanceModal = true; + } + + /** + * Open modal to edit existing instance + */ + openEditInstanceModal(instance: ArrInstance): void { + this.modalMode = 'edit'; + this.editingInstance = instance; + this.instanceForm.patchValue({ + name: instance.name, + url: instance.url, + apiKey: instance.apiKey, + }); + this.showInstanceModal = true; + } + + /** + * Close instance modal + */ + closeInstanceModal(): void { + this.showInstanceModal = false; + this.editingInstance = null; + this.instanceForm.reset(); + } + + /** + * Save instance (add or edit) + */ + saveInstance(): void { + this.markFormGroupTouched(this.instanceForm); + + if (this.instanceForm.invalid) { + this.notificationService.showError('Please fix the validation errors before saving'); + return; + } + + const instanceData: CreateArrInstanceDto = { + name: this.instanceForm.get('name')?.value, + url: this.instanceForm.get('url')?.value, + apiKey: this.instanceForm.get('apiKey')?.value, + }; + + if (this.modalMode === 'add') { + this.lidarrStore.createInstance(instanceData); + } else if (this.editingInstance) { + this.lidarrStore.updateInstance({ + id: this.editingInstance.id!, + instance: instanceData + }); + } + + this.monitorInstanceSaving(); + } + + /** + * Monitor instance saving completion + */ + private monitorInstanceSaving(): void { + const checkSavingStatus = () => { + const saving = this.lidarrSaving(); + const error = this.lidarrError(); + + if (!saving) { + if (error) { + this.notificationService.showError(`Operation failed: ${error}`); + } else { + const action = this.modalMode === 'add' ? 'created' : 'updated'; + this.notificationService.showSuccess(`Instance ${action} successfully`); + this.closeInstanceModal(); + } + } else { + setTimeout(checkSavingStatus, 100); + } + }; + + setTimeout(checkSavingStatus, 100); + } + + /** + * Delete instance with confirmation + */ + deleteInstance(instance: ArrInstance): void { + this.confirmationService.confirm({ + message: `Are you sure you want to delete the instance "${instance.name}"?`, + header: 'Confirm Deletion', + icon: 'pi pi-exclamation-triangle', + acceptButtonStyleClass: 'p-button-danger', + accept: () => { + this.lidarrStore.deleteInstance(instance.id!); + + // Monitor deletion + const checkDeletionStatus = () => { + const saving = this.lidarrSaving(); + const error = this.lidarrError(); + + if (!saving) { + if (error) { + this.notificationService.showError(`Deletion failed: ${error}`); + } else { + this.notificationService.showSuccess('Instance deleted successfully'); + } + } else { + setTimeout(checkDeletionStatus, 100); + } + }; + + setTimeout(checkDeletionStatus, 100); + } + }); + } + + /** + * Get modal title based on mode + */ + get modalTitle(): string { + return this.modalMode === 'add' ? 'Add Lidarr Instance' : 'Edit Lidarr Instance'; + } + + // Add any other necessary methods here }