Merge backfill into Sync now, replace Full backfill with a destructive Reset all

Sync now now runs Backfill (resumes from the watermark, so a widened history
horizon is picked up automatically) then IncrementalSync then detail-fill, in
one click. "Full backfill" is gone -- in its place, "Reset all" (styled as a
destructive action, gated by a confirm dialog) wipes every synced activity
and its laps/kind assignments and rewinds the backfill watermark, so the next
sync performs a genuinely fresh pull instead of trying to patch up existing
rows with newer schema fields.
This commit is contained in:
2026-07-19 12:41:38 +02:00
parent af41aa0f7f
commit 7f7e4b10f0
10 changed files with 235 additions and 12 deletions

View File

@@ -137,6 +137,17 @@ button:disabled {
cursor: default;
}
.button-danger {
border-color: #7f1d1d;
color: #f87171;
}
.button-danger:hover:not(:disabled) {
background: #7f1d1d;
border-color: #f87171;
color: #fff;
}
.filter-pills {
display: flex;
gap: 0.5rem;

View File

@@ -36,7 +36,10 @@ export const api = {
// Sync
syncRun: () => request<{ status: string }>("/api/sync/run", { method: "POST" }),
syncBackfill: () => request<{ status: string }>("/api/sync/backfill", { method: "POST" }),
// Deletes every synced activity (and its laps/samples/kind assignments)
// and rewinds the backfill watermark -- destructive, gated by a
// confirmation in the UI.
resetSync: () => request<{ status: string }>("/api/sync/reset", { method: "POST" }),
syncRuns: () => request<SyncRun[]>("/api/sync/runs"),
syncStatus: () => request<SyncStatus>("/api/sync/status"),

View File

@@ -68,11 +68,27 @@ export function GarminConnection() {
}
}
async function sync(kind: "run" | "backfill") {
async function sync() {
setBusy(true);
setError(null);
try {
await (kind === "run" ? api.syncRun() : api.syncBackfill());
await api.syncRun();
refreshStatus();
} catch (e) {
setError(String(e));
} finally {
setBusy(false);
}
}
async function resetAll() {
if (!window.confirm("This deletes every synced activity, lap, and kind assignment, then starts a fresh pull from Garmin next sync. This cannot be undone. Continue?")) {
return;
}
setBusy(true);
setError(null);
try {
await api.resetSync();
refreshStatus();
} catch (e) {
setError(String(e));
@@ -102,11 +118,11 @@ export function GarminConnection() {
{status === "authenticated" && (
<>
<button disabled={busy} onClick={() => sync("run")}>
<button disabled={busy} onClick={sync}>
Sync now
</button>
<button disabled={busy} onClick={() => sync("backfill")}>
Full backfill
<button className="button-danger" disabled={busy} onClick={resetAll}>
Reset all
</button>
{syncStatus?.in_progress && (
<span className="status-label">{syncProgressLabel(syncStatus)}</span>