fix: show manual sync conflicts in gui
This commit is contained in:
parent
e2aad19cc4
commit
3c6bc097e1
|
|
@ -148,6 +148,8 @@
|
||||||
// ===== Sync state =====
|
// ===== Sync state =====
|
||||||
let syncStatus = null
|
let syncStatus = null
|
||||||
let syncLoading = false
|
let syncLoading = false
|
||||||
|
let syncMessage = ''
|
||||||
|
let syncMessageKind = ''
|
||||||
|
|
||||||
const tabs = [
|
const tabs = [
|
||||||
{ id: 'overview', label: t('tab.overview') },
|
{ id: 'overview', label: t('tab.overview') },
|
||||||
|
|
@ -1431,16 +1433,36 @@
|
||||||
showSettings = false
|
showSettings = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function syncResultMessage(result) {
|
||||||
|
const conflicts = Array.isArray(result?.conflicts) ? result.conflicts : []
|
||||||
|
const applyErrors = Array.isArray(result?.applyErrors) ? result.applyErrors : []
|
||||||
|
const parts = []
|
||||||
|
if (conflicts.length > 0) {
|
||||||
|
parts.push(t('sync.conflictsCount', { count: conflicts.length }))
|
||||||
|
}
|
||||||
|
if (applyErrors.length > 0) {
|
||||||
|
parts.push(t('sync.applyErrorsCount', { count: applyErrors.length }))
|
||||||
|
}
|
||||||
|
return parts.join(' · ')
|
||||||
|
}
|
||||||
|
|
||||||
async function runSyncNow() {
|
async function runSyncNow() {
|
||||||
syncLoading = true
|
syncLoading = true
|
||||||
|
syncMessage = ''
|
||||||
|
syncMessageKind = ''
|
||||||
try {
|
try {
|
||||||
await wailsCall('SyncNow')
|
const result = await wailsCall('SyncNow')
|
||||||
await loadSyncStatus()
|
await loadSyncStatus()
|
||||||
|
syncMessage = syncResultMessage(result)
|
||||||
|
syncMessageKind = syncMessage ? 'warning' : ''
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('sync error:', e)
|
console.error('sync error:', e)
|
||||||
}
|
syncMessage = `${t('sync.status.error')}: ${e?.message || e}`
|
||||||
|
syncMessageKind = 'warning'
|
||||||
|
} finally {
|
||||||
syncLoading = false
|
syncLoading = false
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// First run / recovery handlers
|
// First run / recovery handlers
|
||||||
function onFirstRunComplete(status) {
|
function onFirstRunComplete(status) {
|
||||||
|
|
@ -1521,7 +1543,7 @@
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
<div class="sidebar-footer">
|
<div class="sidebar-footer">
|
||||||
<SyncStatus {syncStatus} {syncLoading} onSync={runSyncNow} onOpenSettings={() => openSettings('sync')} />
|
<SyncStatus {syncStatus} {syncLoading} {syncMessage} {syncMessageKind} onSync={runSyncNow} onOpenSettings={() => openSettings('sync')} />
|
||||||
<div class="sidebar-footer-row">
|
<div class="sidebar-footer-row">
|
||||||
<button class="sidebar-settings-btn" on:click={() => openSettings()} title={t('common.settings')}>
|
<button class="sidebar-settings-btn" on:click={() => openSettings()} title={t('common.settings')}>
|
||||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@
|
||||||
|
|
||||||
export let syncStatus = null
|
export let syncStatus = null
|
||||||
export let syncLoading = false
|
export let syncLoading = false
|
||||||
|
export let syncMessage = ''
|
||||||
|
export let syncMessageKind = ''
|
||||||
export let onSync = null
|
export let onSync = null
|
||||||
export let onOpenSettings = null
|
export let onOpenSettings = null
|
||||||
|
|
||||||
|
|
@ -41,6 +43,11 @@
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{#if syncMessage}
|
||||||
|
<div class={syncMessageKind === 'warning' ? 'sync-message sync-message-warning' : 'sync-message'}>
|
||||||
|
{syncMessage}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
{:else}
|
{:else}
|
||||||
<div class="sync-status-row">
|
<div class="sync-status-row">
|
||||||
<span class="sync-dot dot-disabled"></span>
|
<span class="sync-dot dot-disabled"></span>
|
||||||
|
|
@ -88,6 +95,14 @@
|
||||||
gap: 0.3rem;
|
gap: 0.3rem;
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
}
|
}
|
||||||
|
.sync-message {
|
||||||
|
margin-top: 0.25rem;
|
||||||
|
color: var(--text-dim, #888);
|
||||||
|
line-height: 1.25;
|
||||||
|
}
|
||||||
|
.sync-message-warning {
|
||||||
|
color: #f59e0b;
|
||||||
|
}
|
||||||
.btn-xs {
|
.btn-xs {
|
||||||
padding: 0.2rem 0.5rem;
|
padding: 0.2rem 0.5rem;
|
||||||
font-size: 0.75rem;
|
font-size: 0.75rem;
|
||||||
|
|
|
||||||
|
|
@ -87,6 +87,9 @@ export default {
|
||||||
'sync.connected': 'Connected',
|
'sync.connected': 'Connected',
|
||||||
'sync.notConnected': 'Not connected',
|
'sync.notConnected': 'Not connected',
|
||||||
'sync.disabled': 'Disabled',
|
'sync.disabled': 'Disabled',
|
||||||
|
'sync.status.error': 'Sync error',
|
||||||
|
'sync.conflictsCount': 'Conflicts: {count}',
|
||||||
|
'sync.applyErrorsCount': 'Apply errors: {count}',
|
||||||
|
|
||||||
'kind.project': 'Project',
|
'kind.project': 'Project',
|
||||||
'kind.client': 'Client',
|
'kind.client': 'Client',
|
||||||
|
|
|
||||||
|
|
@ -431,6 +431,8 @@ export default {
|
||||||
'sync.retry': 'Повторить',
|
'sync.retry': 'Повторить',
|
||||||
'sync.run': 'Синхронизировать',
|
'sync.run': 'Синхронизировать',
|
||||||
'sync.running': 'Синхронизация...',
|
'sync.running': 'Синхронизация...',
|
||||||
|
'sync.conflictsCount': 'Конфликты: {count}',
|
||||||
|
'sync.applyErrorsCount': 'Ошибки применения: {count}',
|
||||||
|
|
||||||
'error.generic': 'Произошла ошибка',
|
'error.generic': 'Произошла ошибка',
|
||||||
'error.invalidCredentials': 'Неверный логин или пароль',
|
'error.invalidCredentials': 'Неверный логин или пароль',
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue