import { action, mutation, query } from "./_generated/server" import { api } from "./_generated/api" import { v } from "convex/values" import type { Id } from "./_generated/dataModel" export const log = mutation({ args: { tenantId: v.string(), companyId: v.optional(v.id("companies")), companyName: v.string(), usagePct: v.number(), threshold: v.number(), range: v.string(), recipients: v.array(v.string()), deliveredCount: v.number(), }, handler: async (ctx, args) => { const now = Date.now() await ctx.db.insert("alerts", { tenantId: args.tenantId, companyId: args.companyId, companyName: args.companyName, usagePct: args.usagePct, threshold: args.threshold, range: args.range, recipients: args.recipients, deliveredCount: args.deliveredCount, createdAt: now, }) }, }) export const list = query({ args: { tenantId: v.string(), viewerId: v.id("users"), limit: v.optional(v.number()), companyId: v.optional(v.id("companies")), start: v.optional(v.number()), end: v.optional(v.number()), }, handler: async (ctx, { tenantId, viewerId, limit, companyId, start, end }) => { // Only admins can see the full alerts log const user = await ctx.db.get(viewerId) if (!user || user.tenantId !== tenantId || (user.role ?? "").toUpperCase() !== "ADMIN") { return [] } let items = await ctx.db .query("alerts") .withIndex("by_tenant", (q) => q.eq("tenantId", tenantId)) .collect() if (companyId) items = items.filter((a) => a.companyId === companyId) if (typeof start === "number") items = items.filter((a) => a.createdAt >= start) if (typeof end === "number") items = items.filter((a) => a.createdAt < end) return items .sort((a, b) => b.createdAt - a.createdAt) .slice(0, Math.max(1, Math.min(limit ?? 200, 500))) }, }) export const managersForCompany = query({ args: { tenantId: v.string(), companyId: v.id("companies") }, handler: async (ctx, { tenantId, companyId }) => { const users = await ctx.db .query("users") .withIndex("by_tenant_company", (q) => q.eq("tenantId", tenantId).eq("companyId", companyId)) .collect() return users.filter((u) => (u.role ?? "").toUpperCase() === "MANAGER") }, }) export const lastForCompanyBySlug = query({ args: { tenantId: v.string(), slug: v.string() }, handler: async (ctx, { tenantId, slug }) => { const company = await ctx.db .query("companies") .withIndex("by_tenant_slug", (q) => q.eq("tenantId", tenantId).eq("slug", slug)) .first() if (!company) return null const items = await ctx.db .query("alerts") .withIndex("by_tenant", (q) => q.eq("tenantId", tenantId)) .collect() const matches = items.filter((a) => a.companyId === company._id) if (matches.length === 0) return null const last = matches.sort((a, b) => b.createdAt - a.createdAt)[0] return { createdAt: last.createdAt, usagePct: last.usagePct, threshold: last.threshold } }, }) export const lastForCompaniesBySlugs = query({ args: { tenantId: v.string(), slugs: v.array(v.string()) }, handler: async (ctx, { tenantId, slugs }) => { const result: Record = {} // Fetch all alerts once for the tenant const alerts = await ctx.db .query("alerts") .withIndex("by_tenant", (q) => q.eq("tenantId", tenantId)) .collect() for (const slug of slugs) { const company = await ctx.db .query("companies") .withIndex("by_tenant_slug", (q) => q.eq("tenantId", tenantId).eq("slug", slug)) .first() if (!company) { result[slug] = null continue } const matches = alerts.filter((a) => a.companyId === company._id) if (matches.length === 0) { result[slug] = null continue } const last = matches.sort((a, b) => b.createdAt - a.createdAt)[0] result[slug] = { createdAt: last.createdAt, usagePct: last.usagePct, threshold: last.threshold } } return result }, }) export const tenantIds = query({ args: {}, handler: async (ctx) => { const companies = await ctx.db.query("companies").collect() return Array.from(new Set(companies.map((c) => c.tenantId))) }, }) export const existsForCompanyRange = query({ args: { tenantId: v.string(), companyId: v.id("companies"), start: v.number(), end: v.number() }, handler: async (ctx, { tenantId, companyId, start, end }) => { const items = await ctx.db .query("alerts") .withIndex("by_tenant", (q) => q.eq("tenantId", tenantId)) .collect() return items.some((a) => a.companyId === companyId && a.createdAt >= start && a.createdAt < end) }, })