72 lines
3.0 KiB
TypeScript
72 lines
3.0 KiB
TypeScript
import type { DashboardRange } from "@/lib/analytics/dashboardQueries";
|
|
|
|
export type RangeKey = "today" | "7d" | "30d" | "90d" | "custom";
|
|
|
|
export const RANGE_OPTIONS: { key: RangeKey; label: string; days: number | "today" }[] = [
|
|
{ key: "today", label: "Heute", days: "today" },
|
|
{ key: "7d", label: "7 Tage", days: 7 },
|
|
{ key: "30d", label: "30 Tage", days: 30 },
|
|
{ key: "90d", label: "90 Tage", days: 90 },
|
|
];
|
|
|
|
const DAY_MS = 24 * 60 * 60 * 1000;
|
|
|
|
function startOfUTCDay(date: Date): Date {
|
|
return new Date(`${date.toISOString().slice(0, 10)}T00:00:00.000Z`);
|
|
}
|
|
|
|
function endOfUTCDay(date: Date): Date {
|
|
return new Date(`${date.toISOString().slice(0, 10)}T23:59:59.999Z`);
|
|
}
|
|
|
|
function parseDateParam(value: string | null): Date | null {
|
|
if (!value || !/^\d{4}-\d{2}-\d{2}$/.test(value)) return null;
|
|
const date = new Date(`${value}T00:00:00.000Z`);
|
|
return Number.isNaN(date.getTime()) ? null : date;
|
|
}
|
|
|
|
/**
|
|
* Resolves the dashboard's period filter (?range=&from=&to=) into an actual
|
|
* date range, clamped to `maxDaysBack` (the configured raw-event retention -
|
|
* see globals/AnalyticsSettings.ts) so the UI never claims to show data that
|
|
* has already been purged. Also computes the matching "previous period" for
|
|
* comparison (same length, immediately preceding).
|
|
*/
|
|
export function resolveRange(
|
|
searchParams: URLSearchParams,
|
|
maxDaysBack: number,
|
|
): { key: RangeKey; range: DashboardRange; compareRange: DashboardRange } {
|
|
const now = new Date();
|
|
const earliestAllowed = startOfUTCDay(new Date(now.getTime() - maxDaysBack * DAY_MS));
|
|
const rawKey = searchParams.get("range");
|
|
const key: RangeKey = (["today", "7d", "30d", "90d", "custom"] as const).includes(rawKey as RangeKey)
|
|
? (rawKey as RangeKey)
|
|
: "7d";
|
|
|
|
if (key === "custom") {
|
|
const fromParam = parseDateParam(searchParams.get("from"));
|
|
const toParam = parseDateParam(searchParams.get("to"));
|
|
let from = fromParam ? startOfUTCDay(fromParam) : startOfUTCDay(new Date(now.getTime() - 7 * DAY_MS));
|
|
let to = toParam ? endOfUTCDay(toParam) : now;
|
|
if (from < earliestAllowed) from = earliestAllowed;
|
|
if (to > now) to = now;
|
|
if (from > to) from = to;
|
|
|
|
const lengthMs = to.getTime() - from.getTime();
|
|
const compareRange: DashboardRange = { from: new Date(from.getTime() - lengthMs), to: new Date(from.getTime() - 1) };
|
|
return { key, range: { from, to }, compareRange };
|
|
}
|
|
|
|
if (key === "today") {
|
|
const from = startOfUTCDay(now);
|
|
const yesterday = new Date(from.getTime() - DAY_MS);
|
|
return { key, range: { from, to: now }, compareRange: { from: yesterday, to: new Date(from.getTime() - 1) } };
|
|
}
|
|
|
|
const days = RANGE_OPTIONS.find((o) => o.key === key)?.days;
|
|
const windowDays = typeof days === "number" ? Math.min(days, maxDaysBack) : 7;
|
|
const from = new Date(Math.max(now.getTime() - windowDays * DAY_MS, earliestAllowed.getTime()));
|
|
const compareRange: DashboardRange = { from: new Date(from.getTime() - windowDays * DAY_MS), to: new Date(from.getTime() - 1) };
|
|
return { key, range: { from, to: now }, compareRange };
|
|
}
|