feat(costs): add agent model breakdown, harden date validation, sync CostByProject type, fix quota threshold and tab-gated queries

- add byAgentModel endpoint and expandable per-agent model sub-rows in the spend tab
- validate date range inputs with isNaN + badRequest to return HTTP 400 on bad input
- move CostByProject from a local api/costs.ts definition into packages/shared types
- gate providerData query on mainTab === providers, consistent with weekData/windowData/quotaData
- fix byProject range filter from finishedAt to startedAt, consistent with byProvider runs query
- fix WHAM used_percent threshold from <= 1 to < 1 to avoid misclassifying 1% usage as 100%
- replace inline opacity style with tailwind bg-primary/85 class in ProviderQuotaCard
- reset expandedAgents set when company or date range changes
- sort agent model sub-rows by cost descending in ui memo
This commit is contained in:
Sai Shankar
2026-03-08 20:56:13 +05:30
committed by Dotta
parent db20f4f46e
commit 9d21380699
9 changed files with 192 additions and 56 deletions

View File

@@ -5,6 +5,7 @@ import { validate } from "../middleware/validate.js";
import { costService, companyService, agentService, logActivity } from "../services/index.js";
import { assertBoard, assertCompanyAccess, getActorInfo } from "./authz.js";
import { fetchAllQuotaWindows } from "../services/quota-windows.js";
import { badRequest } from "../errors.js";
export function costRoutes(db: Db) {
const router = Router();
@@ -42,8 +43,12 @@ export function costRoutes(db: Db) {
});
function parseDateRange(query: Record<string, unknown>) {
const from = query.from ? new Date(query.from as string) : undefined;
const to = query.to ? new Date(query.to as string) : undefined;
const fromRaw = query.from as string | undefined;
const toRaw = query.to as string | undefined;
const from = fromRaw ? new Date(fromRaw) : undefined;
const to = toRaw ? new Date(toRaw) : undefined;
if (from && isNaN(from.getTime())) throw badRequest("invalid 'from' date");
if (to && isNaN(to.getTime())) throw badRequest("invalid 'to' date");
return (from || to) ? { from, to } : undefined;
}
@@ -63,6 +68,14 @@ export function costRoutes(db: Db) {
res.json(rows);
});
router.get("/companies/:companyId/costs/by-agent-model", async (req, res) => {
const companyId = req.params.companyId as string;
assertCompanyAccess(req, companyId);
const range = parseDateRange(req.query);
const rows = await costs.byAgentModel(companyId, range);
res.json(rows);
});
router.get("/companies/:companyId/costs/by-provider", async (req, res) => {
const companyId = req.params.companyId as string;
assertCompanyAccess(req, companyId);