diff --git a/plugins/sync/frontend/src/SyncSettings.svelte b/plugins/sync/frontend/src/SyncSettings.svelte index 0c72417..3634f31 100644 --- a/plugins/sync/frontend/src/SyncSettings.svelte +++ b/plugins/sync/frontend/src/SyncSettings.svelte @@ -6,6 +6,7 @@ let errorMsg = '' let resultMsg = '' let resultKind = '' + let conflictDetails = [] let connectionResult = '' let connectionOk = null @@ -119,14 +120,36 @@ return parts.join(' · ') } + function conflictField(conflict, keys) { + for (const key of keys) { + const value = conflict && conflict[key] + if (value != null && String(value).trim()) return String(value) + } + return '' + } + + function formatSyncConflict(conflict) { + const entityType = conflictField(conflict, ['entity_type', 'entityType']) || 'item' + const entityId = conflictField(conflict, ['entity_id', 'entityId', 'path']) || 'unknown' + const opId = conflictField(conflict, ['op_id', 'opId']) + const reason = conflictField(conflict, ['reason', 'message']) + const parts = [entityType + ': ' + entityId] + if (opId) parts.push('op ' + opId) + if (reason) parts.push(reason) + return parts.join(' · ') + } + async function runSyncNow() { loading = true errorMsg = '' resultMsg = '' + conflictDetails = [] try { const r = await syncAPI().now() const summary = 'Pushed ' + (r?.pushed || 0) + ', pulled ' + (r?.pulled || 0) const warning = syncResultWarning(r) + const conflicts = Array.isArray(r?.conflicts) ? r.conflicts : [] + conflictDetails = conflicts.slice(0, 5).map(formatSyncConflict) resultMsg = warning ? summary + ' · ' + warning : summary resultKind = warning ? 'warning' : '' await load() @@ -193,6 +216,14 @@ {#if resultMsg && !errorMsg}
{resultMsg}
{/if} + {#if conflictDetails.length > 0 && !errorMsg} +
+
Sync conflicts
+ {#each conflictDetails as detail} +
{detail}
+ {/each} +
+ {/if} {#if settings && settings.lastError && !errorMsg}
Last sync error: {sanitizeError(settings.lastError)} diff --git a/scripts/smoke-sync-plugin.js b/scripts/smoke-sync-plugin.js index 543dfb5..6232bb6 100644 --- a/scripts/smoke-sync-plugin.js +++ b/scripts/smoke-sync-plugin.js @@ -15,5 +15,14 @@ if (!source.includes('sanitizeError(settings.lastError)')) { if (!source.includes('Last sync error')) { throw new Error('SyncSettings must label the persisted sync error'); } +if (!source.includes('function formatSyncConflict')) { + throw new Error('SyncSettings must format individual sync conflicts'); +} +if (!source.includes('conflictDetails')) { + throw new Error('SyncSettings must store sync conflict details after Sync Now'); +} +if (!source.includes('Sync conflicts')) { + throw new Error('SyncSettings must label the conflict details section'); +} console.log('sync plugin smoke passed');