chore: sync staging
This commit is contained in:
parent
c5ddd54a3e
commit
561b19cf66
610 changed files with 105285 additions and 1206 deletions
178
convex/incidents.ts
Normal file
178
convex/incidents.ts
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
import { ConvexError, v } from "convex/values"
|
||||
|
||||
import { mutation, query } from "./_generated/server"
|
||||
import type { Id } from "./_generated/dataModel"
|
||||
import { requireStaff } from "./rbac"
|
||||
|
||||
const DEFAULT_STATUS = "investigating"
|
||||
|
||||
function timelineId() {
|
||||
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
||||
return crypto.randomUUID()
|
||||
}
|
||||
return `${Date.now()}-${Math.random().toString(36).slice(2, 10)}`
|
||||
}
|
||||
|
||||
export const list = query({
|
||||
args: { tenantId: v.string(), viewerId: v.id("users") },
|
||||
handler: async (ctx, { tenantId, viewerId }) => {
|
||||
await requireStaff(ctx, viewerId, tenantId)
|
||||
const incidents = await ctx.db
|
||||
.query("incidents")
|
||||
.withIndex("by_tenant_updated", (q) => q.eq("tenantId", tenantId))
|
||||
.order("desc")
|
||||
.collect()
|
||||
return incidents
|
||||
},
|
||||
})
|
||||
|
||||
export const createIncident = mutation({
|
||||
args: {
|
||||
tenantId: v.string(),
|
||||
actorId: v.id("users"),
|
||||
title: v.string(),
|
||||
severity: v.string(),
|
||||
impactSummary: v.optional(v.string()),
|
||||
affectedQueues: v.optional(v.array(v.string())),
|
||||
initialUpdate: v.optional(v.string()),
|
||||
},
|
||||
handler: async (ctx, { tenantId, actorId, title, severity, impactSummary, affectedQueues, initialUpdate }) => {
|
||||
const viewer = await requireStaff(ctx, actorId, tenantId)
|
||||
const normalizedTitle = title.trim()
|
||||
if (normalizedTitle.length < 3) {
|
||||
throw new ConvexError("Informe um título válido para o incidente")
|
||||
}
|
||||
const now = Date.now()
|
||||
const timelineEntry = {
|
||||
id: timelineId(),
|
||||
authorId: actorId,
|
||||
authorName: viewer.user.name ?? viewer.user.email ?? "Equipe",
|
||||
message: initialUpdate?.trim().length ? initialUpdate.trim() : "Incidente registrado.",
|
||||
type: "created",
|
||||
createdAt: now,
|
||||
}
|
||||
const id = await ctx.db.insert("incidents", {
|
||||
tenantId,
|
||||
title: normalizedTitle,
|
||||
status: DEFAULT_STATUS,
|
||||
severity,
|
||||
impactSummary: impactSummary?.trim() || undefined,
|
||||
affectedQueues: affectedQueues ?? [],
|
||||
ownerId: actorId,
|
||||
ownerName: viewer.user.name ?? undefined,
|
||||
ownerEmail: viewer.user.email ?? undefined,
|
||||
startedAt: now,
|
||||
updatedAt: now,
|
||||
resolvedAt: undefined,
|
||||
timeline: [timelineEntry],
|
||||
})
|
||||
return id
|
||||
},
|
||||
})
|
||||
|
||||
export const updateIncidentStatus = mutation({
|
||||
args: {
|
||||
tenantId: v.string(),
|
||||
actorId: v.id("users"),
|
||||
incidentId: v.id("incidents"),
|
||||
status: v.string(),
|
||||
},
|
||||
handler: async (ctx, { tenantId, actorId, incidentId, status }) => {
|
||||
const viewer = await requireStaff(ctx, actorId, tenantId)
|
||||
const incident = await ctx.db.get(incidentId)
|
||||
if (!incident || incident.tenantId !== tenantId) {
|
||||
throw new ConvexError("Incidente não encontrado")
|
||||
}
|
||||
const now = Date.now()
|
||||
const timeline = [
|
||||
...(incident.timeline ?? []),
|
||||
{
|
||||
id: timelineId(),
|
||||
authorId: actorId,
|
||||
authorName: viewer.user.name ?? viewer.user.email ?? "Equipe",
|
||||
message: `Status atualizado para ${status}`,
|
||||
type: "status",
|
||||
createdAt: now,
|
||||
},
|
||||
]
|
||||
await ctx.db.patch(incidentId, {
|
||||
status,
|
||||
updatedAt: now,
|
||||
resolvedAt: status === "resolved" ? now : incident.resolvedAt ?? undefined,
|
||||
timeline,
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
export const bulkUpdateIncidentStatus = mutation({
|
||||
args: {
|
||||
tenantId: v.string(),
|
||||
actorId: v.id("users"),
|
||||
incidentIds: v.array(v.id("incidents")),
|
||||
status: v.string(),
|
||||
},
|
||||
handler: async (ctx, { tenantId, actorId, incidentIds, status }) => {
|
||||
const viewer = await requireStaff(ctx, actorId, tenantId)
|
||||
const now = Date.now()
|
||||
for (const incidentId of incidentIds) {
|
||||
const incident = await ctx.db.get(incidentId)
|
||||
if (!incident || incident.tenantId !== tenantId) continue
|
||||
const timeline = [
|
||||
...(incident.timeline ?? []),
|
||||
{
|
||||
id: timelineId(),
|
||||
authorId: actorId,
|
||||
authorName: viewer.user.name ?? viewer.user.email ?? "Equipe",
|
||||
message: `Status atualizado em massa para ${status}`,
|
||||
type: "status",
|
||||
createdAt: now,
|
||||
},
|
||||
]
|
||||
await ctx.db.patch(incidentId, {
|
||||
status,
|
||||
updatedAt: now,
|
||||
resolvedAt: status === "resolved" ? now : incident.resolvedAt ?? undefined,
|
||||
timeline,
|
||||
})
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
export const addIncidentUpdate = mutation({
|
||||
args: {
|
||||
tenantId: v.string(),
|
||||
actorId: v.id("users"),
|
||||
incidentId: v.id("incidents"),
|
||||
message: v.string(),
|
||||
status: v.optional(v.string()),
|
||||
},
|
||||
handler: async (ctx, { tenantId, actorId, incidentId, message, status }) => {
|
||||
const viewer = await requireStaff(ctx, actorId, tenantId)
|
||||
const incident = await ctx.db.get(incidentId)
|
||||
if (!incident || incident.tenantId !== tenantId) {
|
||||
throw new ConvexError("Incidente não encontrado")
|
||||
}
|
||||
const trimmed = message.trim()
|
||||
if (trimmed.length < 3) {
|
||||
throw new ConvexError("Descreva a atualização do incidente")
|
||||
}
|
||||
const now = Date.now()
|
||||
const timeline = [
|
||||
...(incident.timeline ?? []),
|
||||
{
|
||||
id: timelineId(),
|
||||
authorId: actorId,
|
||||
authorName: viewer.user.name ?? viewer.user.email ?? "Equipe",
|
||||
message: trimmed,
|
||||
type: "update",
|
||||
createdAt: now,
|
||||
},
|
||||
]
|
||||
await ctx.db.patch(incidentId, {
|
||||
timeline,
|
||||
status: status ?? incident.status,
|
||||
updatedAt: now,
|
||||
resolvedAt: status === "resolved" ? now : incident.resolvedAt ?? undefined,
|
||||
})
|
||||
},
|
||||
})
|
||||
Loading…
Add table
Add a link
Reference in a new issue