+ {t('Verbindung dieser Feature-Instanz zu einem Redmine-Projekt. Speichern, testen, dann initialen Sync starten.')}
+
+
{t('Verbindung')}
+
+
+
{t('Basis-URL')}
+
setBaseUrl(e.target.value)}
+ placeholder="https://redmine.example.com"
+ spellCheck={false}
+ />
+
{t('Ohne abschliessenden Slash, z.B. https://redmine.logobject.ch')}
+
+
+
+ {t('Projekt-ID oder -Slug')}
+ setProjectId(e.target.value)}
+ placeholder="logobject-mars"
+ spellCheck={false}
+ />
+
+
+
+
{t('Wurzel-Tracker (Name)')}
+
setRootTrackerName(e.target.value)}
+ placeholder="Userstory"
+ spellCheck={false}
+ />
+
+ {t('Tracker, der die Wurzel der Ticket-Hierarchie bildet. Wird beim Sync gegen die Tracker-Liste aufgeloest.')}
+
+
+
+
+
{t('API-Key')}
+
setApiKey(e.target.value)}
+ placeholder={config?.hasApiKey ? t('(gesetzt -- leer lassen, um nicht zu aendern)') : t('Redmine API Access Key')}
+ spellCheck={false}
+ autoComplete="new-password"
+ />
+
+ {t('Wird verschluesselt gespeichert. Status: ')}
+ {config?.hasApiKey ? {t('gesetzt')} : {t('nicht gesetzt')} }
+
+
+
+
+
+ {saving ? t('Speichere ...') : t('Speichern')}
+
+
+ {testing ? t('Teste ...') : t('Verbindung testen')}
+
+ {config?.id && (
+
+ {t('Konfiguration loeschen')}
+
+ )}
+
+
+ {testResult && (
+
+ {testResult.ok ? (
+
+ {t('Verbindung OK')}. {' '}
+ {testResult.user?.name && <>{t('Angemeldet als')} {testResult.user.name} . >}
+ {testResult.project?.name && <>{t('Projekt')}: {testResult.project.name} .>}
+
+ ) : (
+
+ {t('Verbindung fehlgeschlagen')}. {' '}
+ {testResult.message || testResult.reason || ''}
+ {testResult.status ? ` (HTTP ${testResult.status})` : ''}
+
+ )}
+
+ )}
+
+
{t('Mirror-Sync')}
+
+ {t('Tickets werden in die lokale Datenbank gespiegelt, damit Statistik und Browser auch bei 20\u2019000+ Tickets schnell sind. Nach Aenderungen wird das Mirror-Bild automatisch nachgezogen.')}
+
+
+
+
{t('Letzter Sync')}:
+
{_formatTs(config?.lastSyncAt)}
+
{t('Letzter Full-Sync')}:
+
{_formatTs(config?.lastFullSyncAt)}
+
{t('Letzte Sync-Dauer')}:
+
{_formatDuration(syncStatus?.lastSyncDurationMs)}
+
{t('Tickets im Mirror')}:
+
{syncStatus?.mirroredTicketCount ?? '-'}
+
{t('Beziehungen im Mirror')}:
+
{syncStatus?.mirroredRelationCount ?? '-'}
+ {config?.lastSyncErrorMessage && (
+ <>
+
{t('Letzter Fehler')}:
+
{config.lastSyncErrorMessage}
+ >
+ )}
+
+
+
+ _runSync(false)} disabled={!canSync || syncing}>
+ {syncing ? t('Synchronisiere ...') : t('Sync starten (inkrementell)')}
+
+ _runSync(true)} disabled={!canSync || syncing}>
+ {t('Full-Sync (alle Tickets)')}
+
+
+
+ {syncResult && (
+
+ {syncResult.full ? t('Full-Sync') : t('Inkrementeller Sync')}: {' '}
+ {syncResult.ticketsUpserted} {t('Tickets')}, {syncResult.relationsUpserted}{' '}
+ {t('Beziehungen')} in {_formatDuration(syncResult.durationMs)}.
+
+ )}
+
+ {!canSync && (
+
+ {t('Bitte zuerst Basis-URL, Projekt-ID und API-Key speichern.')}
+
+ )}
+