diff --git a/src/lib/server/db/queries/arrSync.ts b/src/lib/server/db/queries/arrSync.ts index 2e57706..ed2bf48 100644 --- a/src/lib/server/db/queries/arrSync.ts +++ b/src/lib/server/db/queries/arrSync.ts @@ -1,7 +1,7 @@ import { db } from '../db.ts'; // Types -export type SyncTrigger = 'none' | 'manual' | 'on_pull' | 'on_change' | 'schedule'; +export type SyncTrigger = 'manual' | 'on_pull' | 'on_change' | 'schedule'; export interface ProfileSelection { databaseId: number; @@ -75,7 +75,7 @@ export const arrSyncQueries = { profileId: row.profile_id })), config: { - trigger: (configRow?.trigger as SyncTrigger) ?? 'none', + trigger: (configRow?.trigger as SyncTrigger) ?? 'manual', cron: configRow?.cron ?? null } }; @@ -133,7 +133,7 @@ export const arrSyncQueries = { profileId: row.profile_id })), config: { - trigger: (configRow?.trigger as SyncTrigger) ?? 'none', + trigger: (configRow?.trigger as SyncTrigger) ?? 'manual', cron: configRow?.cron ?? null } }; @@ -184,7 +184,7 @@ export const arrSyncQueries = { namingDatabaseId: row?.naming_database_id ?? null, qualityDefinitionsDatabaseId: row?.quality_definitions_database_id ?? null, mediaSettingsDatabaseId: row?.media_settings_database_id ?? null, - trigger: (row?.trigger as SyncTrigger) ?? 'none', + trigger: (row?.trigger as SyncTrigger) ?? 'manual', cron: row?.cron ?? null }; }, diff --git a/src/lib/server/sync/processor.ts b/src/lib/server/sync/processor.ts index 1286d20..cc23aa0 100644 --- a/src/lib/server/sync/processor.ts +++ b/src/lib/server/sync/processor.ts @@ -240,22 +240,19 @@ export async function syncInstance(instanceId: number): Promise 0) { + if (qpConfig.selections.length > 0) { const syncer = new QualityProfileSyncer(client, instanceId, instance.name, instance.type as SyncArrType); result.qualityProfiles = await syncer.sync(); } // Sync delay profiles if configured - if (dpConfig.config.trigger !== 'none' && dpConfig.selections.length > 0) { + if (dpConfig.selections.length > 0) { const syncer = new DelayProfileSyncer(client, instanceId, instance.name); result.delayProfiles = await syncer.sync(); } // Sync media management if configured - if ( - mmConfig.trigger !== 'none' && - (mmConfig.namingDatabaseId || mmConfig.qualityDefinitionsDatabaseId || mmConfig.mediaSettingsDatabaseId) - ) { + if (mmConfig.namingDatabaseId || mmConfig.qualityDefinitionsDatabaseId || mmConfig.mediaSettingsDatabaseId) { const syncer = new MediaManagementSyncer(client, instanceId, instance.name, instance.type as ArrType); result.mediaManagement = await syncer.sync(); } diff --git a/src/routes/arr/[id]/sync/+page.server.ts b/src/routes/arr/[id]/sync/+page.server.ts index 525e2cc..2c7e51c 100644 --- a/src/routes/arr/[id]/sync/+page.server.ts +++ b/src/routes/arr/[id]/sync/+page.server.ts @@ -76,7 +76,7 @@ export const actions: Actions = { try { const selections: ProfileSelection[] = JSON.parse(selectionsJson || '[]'); - const effectiveTrigger = trigger || 'none'; + const effectiveTrigger = trigger || 'manual'; const effectiveCron = cron || null; arrSyncQueries.saveQualityProfilesSync(id, selections, { trigger: effectiveTrigger, @@ -113,7 +113,7 @@ export const actions: Actions = { try { const selections: ProfileSelection[] = JSON.parse(selectionsJson || '[]'); - const effectiveTrigger = trigger || 'none'; + const effectiveTrigger = trigger || 'manual'; const effectiveCron = cron || null; arrSyncQueries.saveDelayProfilesSync(id, selections, { trigger: effectiveTrigger, @@ -151,7 +151,7 @@ export const actions: Actions = { const cron = formData.get('cron') as string | null; try { - const effectiveTrigger = trigger || 'none'; + const effectiveTrigger = trigger || 'manual'; const effectiveCron = cron || null; arrSyncQueries.saveMediaManagementSync(id, { namingDatabaseId: namingDatabaseId ? parseInt(namingDatabaseId, 10) : null, diff --git a/src/routes/arr/[id]/sync/+page.svelte b/src/routes/arr/[id]/sync/+page.svelte index 46751ce..ee7799e 100644 --- a/src/routes/arr/[id]/sync/+page.svelte +++ b/src/routes/arr/[id]/sync/+page.svelte @@ -2,10 +2,12 @@ import type { PageData } from './$types'; import { Info } from 'lucide-svelte'; import InfoModal from '$ui/modal/InfoModal.svelte'; + import DirtyModal from '$ui/modal/DirtyModal.svelte'; import QualityProfiles from './components/QualityProfiles.svelte'; import DelayProfiles from './components/DelayProfiles.svelte'; import MediaManagement from './components/MediaManagement.svelte'; import type { SyncTrigger } from '$db/queries/arrSync.ts'; + import { initEdit, initCreate } from '$lib/client/stores/dirty'; export let data: PageData; @@ -40,6 +42,19 @@ }; let mediaManagementTrigger: SyncTrigger = data.syncData.mediaManagement.trigger; let mediaManagementCron: string = data.syncData.mediaManagement.cron || '0 * * * *'; + + // Track dirty state from each component + let qualityProfilesDirty = false; + let delayProfilesDirty = false; + let mediaManagementDirty = false; + + // Sync combined dirty state to global dirty store for DirtyModal + $: anyDirty = qualityProfilesDirty || delayProfilesDirty || mediaManagementDirty; + $: if (anyDirty) { + initCreate({}); + } else { + initEdit({}); + } @@ -72,18 +87,21 @@ bind:state={qualityProfileState} bind:syncTrigger={qualityProfileTrigger} bind:cronExpression={qualityProfileCron} + bind:isDirty={qualityProfilesDirty} /> @@ -153,3 +171,5 @@ + + diff --git a/src/routes/arr/[id]/sync/components/DelayProfiles.svelte b/src/routes/arr/[id]/sync/components/DelayProfiles.svelte index 701cdf3..6eea9a2 100644 --- a/src/routes/arr/[id]/sync/components/DelayProfiles.svelte +++ b/src/routes/arr/[id]/sync/components/DelayProfiles.svelte @@ -13,12 +13,18 @@ export let databases: DatabaseWithProfiles[]; export let state: Record> = {}; - export let syncTrigger: 'none' | 'manual' | 'on_pull' | 'on_change' | 'schedule' = 'none'; + export let syncTrigger: 'manual' | 'on_pull' | 'on_change' | 'schedule' = 'manual'; export let cronExpression: string = '0 * * * *'; let saving = false; let syncing = false; + // Track saved state for dirty detection + let savedState = JSON.stringify({ state, syncTrigger, cronExpression }); + $: currentState = JSON.stringify({ state, syncTrigger, cronExpression }); + export let isDirty = false; + $: isDirty = currentState !== savedState; + // Initialize state for all databases/profiles $: { for (const db of databases) { @@ -60,6 +66,8 @@ if (response.ok) { alertStore.add('success', 'Delay profiles sync config saved'); + // Update saved state to current + savedState = JSON.stringify({ state, syncTrigger, cronExpression }); } else { alertStore.add('error', 'Failed to save delay profiles sync config'); } @@ -139,5 +147,5 @@ {/if} - + diff --git a/src/routes/arr/[id]/sync/components/MediaManagement.svelte b/src/routes/arr/[id]/sync/components/MediaManagement.svelte index c6426aa..ca6fd78 100644 --- a/src/routes/arr/[id]/sync/components/MediaManagement.svelte +++ b/src/routes/arr/[id]/sync/components/MediaManagement.svelte @@ -43,12 +43,18 @@ showMediaDropdown = false; } - export let syncTrigger: 'none' | 'manual' | 'on_pull' | 'on_change' | 'schedule' = 'none'; + export let syncTrigger: 'manual' | 'on_pull' | 'on_change' | 'schedule' = 'manual'; export let cronExpression: string = '0 * * * *'; let saving = false; let syncing = false; + // Track saved state for dirty detection + let savedState = JSON.stringify({ state, syncTrigger, cronExpression }); + $: currentState = JSON.stringify({ state, syncTrigger, cronExpression }); + export let isDirty = false; + $: isDirty = currentState !== savedState; + async function handleSave() { saving = true; try { @@ -66,6 +72,8 @@ if (response.ok) { alertStore.add('success', 'Media management sync config saved'); + // Update saved state to current + savedState = JSON.stringify({ state, syncTrigger, cronExpression }); } else { alertStore.add('error', 'Failed to save media management sync config'); } @@ -246,5 +254,5 @@ - + diff --git a/src/routes/arr/[id]/sync/components/QualityProfiles.svelte b/src/routes/arr/[id]/sync/components/QualityProfiles.svelte index c9520e3..5bfb934 100644 --- a/src/routes/arr/[id]/sync/components/QualityProfiles.svelte +++ b/src/routes/arr/[id]/sync/components/QualityProfiles.svelte @@ -13,12 +13,18 @@ export let databases: DatabaseWithProfiles[]; export let state: Record> = {}; - export let syncTrigger: 'none' | 'manual' | 'on_pull' | 'on_change' | 'schedule' = 'none'; + export let syncTrigger: 'manual' | 'on_pull' | 'on_change' | 'schedule' = 'manual'; export let cronExpression: string = '0 * * * *'; let saving = false; let syncing = false; + // Track saved state for dirty detection + let savedState = JSON.stringify({ state, syncTrigger, cronExpression }); + $: currentState = JSON.stringify({ state, syncTrigger, cronExpression }); + export let isDirty = false; + $: isDirty = currentState !== savedState; + // Initialize state for all databases/profiles $: { for (const db of databases) { @@ -60,6 +66,8 @@ if (response.ok) { alertStore.add('success', 'Quality profiles sync config saved'); + // Update saved state to current + savedState = JSON.stringify({ state, syncTrigger, cronExpression }); } else { alertStore.add('error', 'Failed to save quality profiles sync config'); } @@ -139,5 +147,5 @@ {/if} - + diff --git a/src/routes/arr/[id]/sync/components/SyncFooter.svelte b/src/routes/arr/[id]/sync/components/SyncFooter.svelte index 4eee87d..b988cb7 100644 --- a/src/routes/arr/[id]/sync/components/SyncFooter.svelte +++ b/src/routes/arr/[id]/sync/components/SyncFooter.svelte @@ -3,20 +3,24 @@ import { Check, RefreshCw, Save, Loader2 } from 'lucide-svelte'; import { createEventDispatcher } from 'svelte'; - export let syncTrigger: 'none' | 'manual' | 'on_pull' | 'on_change' | 'schedule' = 'none'; + export let syncTrigger: 'manual' | 'on_pull' | 'on_change' | 'schedule' = 'manual'; export let cronExpression: string = '0 * * * *'; export let saving: boolean = false; export let syncing: boolean = false; + export let isDirty: boolean = false; const dispatch = createEventDispatcher<{ save: void; sync: void }>(); const triggerOptions = [ - { value: 'none', label: 'None' }, { value: 'manual', label: 'Manual' }, { value: 'on_pull', label: 'On Pull' }, { value: 'on_change', label: 'On Change' }, { value: 'schedule', label: 'Schedule' } ] as const; + + // Save disabled when not dirty, Sync disabled when dirty (unsaved changes) + $: saveDisabled = saving || !isDirty; + $: syncDisabled = syncing || isDirty;
@@ -48,8 +52,9 @@