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:
@@ -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;
|
||||
|
||||
@@ -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"),
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user