Files
alibb-server/frontend/src/utils/stats.ts
T

56 lines
2.3 KiB
TypeScript
Raw Normal View History

2026-08-27 16:11:49 +08:00
import type { MonthStat } from '@/api/stats'
/**
* Pure derived-stat helpers for the Stats page (Task 6).
*
* Extracted from {@link Stats.vue} so the two pieces of trivia that are easy
* to get wrong — (1) finding the current month's count in a DESC-ordered list
* and (2) flipping that list to chronological ASC for the line chart — are
* unit-testable without mounting the component or stubbing ECharts.
*/
/**
* Look up the inquiry count for a given `YYYY-MM` month in the `monthly` list.
*
* Used by Stats.vue for the "本月新增" card: it passes
* `dayjs().format('YYYY-MM')` and we return the matching count, or `0` when
* there's no data for the current month yet (the backend omits zero-count
* months entirely, and a fresh month may not have appeared in the trailing
* 12-month window at all).
*
* The lookup is by value equality on `ym`, so input order (DESC vs ASC) is
* irrelevant. Returns `0` for an empty list — graceful fallback for an empty
* DB / new install.
*/
export function currentMonthCount(
monthly: readonly MonthStat[],
ym: string,
): number {
// `find` rather than indexing `[0]` — the current month is not guaranteed to
// be the first entry (DESC newest-first only covers months that have data,
// and a brand-new month may be ABSENT from the trailing 12-month window).
const hit = monthly.find((m) => m.ym === ym)
return hit?.count ?? 0
}
/**
* Return a new array of monthly stats sorted **ascending** by `ym`
* (chronological order — what the trend line chart needs).
*
* The backend returns `monthly` **DESC** (newest first). The line chart's X
* axis wants oldest → newest, so this flips the order. A fresh copy is always
* returned (the input is never mutated), even when the input is already ASC —
* callers can safely pass a `readonly` slice from the API response.
*
* String comparison on `YYYY-MM` is lexicographically correct (zero-padded
* fixed-width format), so no Date parsing is needed.
*/
export function sortMonthlyAsc(monthly: readonly MonthStat[]): MonthStat[] {
// Slice first so `.sort` cannot reorder the caller's array — important
// because `monthly` may be the live API response object.
return monthly.slice().sort((a, b) => {
if (a.ym !== b.ym) return a.ym < b.ym ? -1 : 1
return 0
})
}