feat(usage): add Today period option to Usage & Analytics (#1063)

Bổ sung lựa chọn Today vào bộ lọc thời gian của trang Usage & Analytics
(trước đây chỉ có 24h, 7D, 30D, 60D).

Khác biệt với 24h:
- 24h: cuộn 24 giờ trước → hiện tại
- Today: cố định từ 00:00 hôm nay (giờ local) → hiện tại

Thay đổi:
- page.js, UsageStats.js: thêm option Today vào danh sách PERIODS,
  đổi grid mobile từ 4 cột sang 5 cột để fit option mới.
- api/usage/stats, api/usage/chart: cho phép giá trị period today.
- usageRepo.js:
  + getUsageStats: dùng nhánh live history khi period = today,
    cutoff lấy từ 00:00 hôm nay theo local time.
  + getChartData: thêm 24 bucket theo giờ từ 00:00 → 23:59 hôm nay.
This commit is contained in:
Dũng A Tô Ri A
2026-05-14 10:03:05 +07:00
committed by GitHub
parent d1613e8ad5
commit d1a8dd4987
5 changed files with 41 additions and 6 deletions

View File

@@ -415,7 +415,7 @@ export async function getUsageStats(period = "all") {
}
}
const useDailySummary = period !== "24h";
const useDailySummary = period !== "24h" && period !== "today";
if (useDailySummary) {
const periodDays = { "7d": 7, "30d": 30, "60d": 60 };
@@ -529,8 +529,15 @@ export async function getUsageStats(period = "all") {
if (stats.byEndpoint[endpointKey] && new Date(ts) > new Date(stats.byEndpoint[endpointKey].lastUsed)) stats.byEndpoint[endpointKey].lastUsed = ts;
}
} else {
// 24h: live history
const cutoff = new Date(Date.now() - PERIOD_MS["24h"]).toISOString();
// 24h / today: live history
let cutoff;
if (period === "today") {
const startOfDay = new Date();
startOfDay.setHours(0, 0, 0, 0);
cutoff = startOfDay.toISOString();
} else {
cutoff = new Date(Date.now() - PERIOD_MS["24h"]).toISOString();
}
const filtered = db.all(
`SELECT timestamp, provider, model, connectionId, apiKey, endpoint, promptTokens, completionTokens, cost, tokens FROM usageHistory WHERE timestamp >= ?`,
[cutoff]
@@ -614,6 +621,32 @@ export async function getChartData(period = "7d") {
const db = await getAdapter();
const now = Date.now();
if (period === "today") {
const bucketCount = 24;
const bucketMs = 3600000;
const startOfDay = new Date();
startOfDay.setHours(0, 0, 0, 0);
const startTime = startOfDay.getTime();
const endTime = startTime + bucketCount * bucketMs;
const labelFn = (ts) => new Date(ts).toLocaleTimeString("en-US", { hour: "2-digit", minute: "2-digit", hour12: false });
const buckets = Array.from({ length: bucketCount }, (_, i) => ({ label: labelFn(startTime + i * bucketMs), tokens: 0, cost: 0 }));
const rows = db.all(
`SELECT timestamp, promptTokens, completionTokens, cost FROM usageHistory WHERE timestamp >= ?`,
[new Date(startTime).toISOString()]
);
for (const r of rows) {
const t = new Date(r.timestamp).getTime();
if (t < startTime || t >= endTime) continue;
const idx = Math.floor((t - startTime) / bucketMs);
if (idx >= 0 && idx < bucketCount) {
buckets[idx].tokens += (r.promptTokens || 0) + (r.completionTokens || 0);
buckets[idx].cost += r.cost || 0;
}
}
return buckets;
}
if (period === "24h") {
const bucketCount = 24;
const bucketMs = 3600000;