refactor: one relative-time formatter, not three
CI / lint (push) Successful in 2s
CI / extension-version (push) Successful in 2s
Build images / sign-extension (push) Successful in 4s
Build images / build-agent (push) Successful in 6s
Build images / build-ml (push) Successful in 6s
CI / frontend-build (push) Successful in 23s
CI / backend-lint-and-test (push) Successful in 31s
Build images / build-web (push) Successful in 1m26s
Build images / smoke-web (push) Skipped
Build images / promote (push) Skipped
CI / integration (push) Successful in 2m11s

MembershipRosterCard had grown its own ago(iso) helper, a near-copy of utils/date.js::formatRelative. C4's card was about to become a third copy before a hook caught it. Both now use the shared helper.

The hand-rolled copy was also slightly wrong in ways the shared one is not: it floored everything under a minute to '1m ago', and would have rendered NaNm ago for a null timestamp had a caller ever reached it without a v-if guard. Only sub-minute output changes, which no spec exercises - membershipRosterCard.spec.js seeds its rows at exactly 1h and 9d, where both helpers agree, and asserts on literal phrases rather than time strings.

Recorded formatRelative as snippet 3959 so the next component is offered it instead of deriving a fourth copy.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LNXXULQDjVZmbuNa2G9mD9
This commit is contained in:
2026-09-11 22:48:24 -04:00
co-authored by Claude Opus 5
parent 005680f234
commit de11c14448
@@ -33,10 +33,10 @@
never synced
</span>
<span v-else-if="!p.fresh" class="fc-roster__stale">
last synced {{ ago(p.last_success_at) }} too old to rely on
last synced {{ formatRelative(p.last_success_at) }} too old to rely on
</span>
<span v-else class="fc-roster__ok">
synced {{ ago(p.last_success_at) }}
synced {{ formatRelative(p.last_success_at) }}
</span>
</div>
@@ -47,7 +47,7 @@
{{ p.last_count }} membership{{ p.last_count === 1 ? '' : 's' }} found
</template>
<template v-else-if="p.last_attempt_at">
tried {{ ago(p.last_attempt_at) }}, no successful sync yet
tried {{ formatRelative(p.last_attempt_at) }}, no successful sync yet
</template>
</div>
@@ -68,18 +68,12 @@
<script setup>
import { onMounted } from 'vue'
import { formatRelative } from '../../utils/date.js'
import { useMembershipSyncStore } from '../../stores/membershipSync.js'
import MaintenanceTile from '../common/MaintenanceTile.vue'
const store = useMembershipSyncStore()
function ago (iso) {
const diff = (Date.now() - new Date(iso).getTime()) / 1000
if (diff < 3600) return `${Math.max(1, Math.floor(diff / 60))}m ago`
if (diff < 86400) return `${Math.floor(diff / 3600)}h ago`
return `${Math.floor(diff / 86400)}d ago`
}
async function refresh () {
await store.syncNow()
await store.load()