130 lines
4.1 KiB
TypeScript
130 lines
4.1 KiB
TypeScript
import { mutation, query } from "./_generated/server";
|
|
import { ConvexError, v } from "convex/values";
|
|
import { requireAdmin } from "./rbac";
|
|
|
|
const STAFF_ROLES = new Set(["ADMIN", "MANAGER", "AGENT", "COLLABORATOR"]);
|
|
|
|
export const ensureUser = mutation({
|
|
args: {
|
|
tenantId: v.string(),
|
|
email: v.string(),
|
|
name: v.string(),
|
|
avatarUrl: v.optional(v.string()),
|
|
role: v.optional(v.string()),
|
|
teams: v.optional(v.array(v.string())),
|
|
companyId: v.optional(v.id("companies")),
|
|
},
|
|
handler: async (ctx, args) => {
|
|
const existing = await ctx.db
|
|
.query("users")
|
|
.withIndex("by_tenant_email", (q) => q.eq("tenantId", args.tenantId).eq("email", args.email))
|
|
.first();
|
|
const reconcile = async (record: typeof existing) => {
|
|
if (!record) return null;
|
|
const shouldPatch =
|
|
record.tenantId !== args.tenantId ||
|
|
(args.role && record.role !== args.role) ||
|
|
(args.avatarUrl && record.avatarUrl !== args.avatarUrl) ||
|
|
record.name !== args.name ||
|
|
(args.teams && JSON.stringify(args.teams) !== JSON.stringify(record.teams ?? [])) ||
|
|
(args.companyId && record.companyId !== args.companyId);
|
|
|
|
if (shouldPatch) {
|
|
await ctx.db.patch(record._id, {
|
|
tenantId: args.tenantId,
|
|
role: args.role ?? record.role,
|
|
avatarUrl: args.avatarUrl ?? record.avatarUrl,
|
|
name: args.name,
|
|
teams: args.teams ?? record.teams,
|
|
companyId: args.companyId ?? record.companyId,
|
|
});
|
|
const updated = await ctx.db.get(record._id);
|
|
if (updated) {
|
|
return updated;
|
|
}
|
|
}
|
|
return record;
|
|
};
|
|
|
|
if (existing) {
|
|
const reconciled = await reconcile(existing);
|
|
if (reconciled) {
|
|
return reconciled;
|
|
}
|
|
} else {
|
|
const anyTenant = (await ctx.db.query("users").collect()).find((user) => user.email === args.email);
|
|
if (anyTenant) {
|
|
const reconciled = await reconcile(anyTenant);
|
|
if (reconciled) {
|
|
return reconciled;
|
|
}
|
|
}
|
|
}
|
|
const id = await ctx.db.insert("users", {
|
|
tenantId: args.tenantId,
|
|
email: args.email,
|
|
name: args.name,
|
|
avatarUrl: args.avatarUrl,
|
|
role: args.role ?? "AGENT",
|
|
teams: args.teams ?? [],
|
|
companyId: args.companyId,
|
|
});
|
|
return await ctx.db.get(id);
|
|
},
|
|
});
|
|
|
|
export const listAgents = query({
|
|
args: { tenantId: v.string() },
|
|
handler: async (ctx, { tenantId }) => {
|
|
const users = await ctx.db
|
|
.query("users")
|
|
.withIndex("by_tenant", (q) => q.eq("tenantId", tenantId))
|
|
.collect();
|
|
|
|
return users
|
|
.filter((user) => {
|
|
const normalizedRole = (user.role ?? "AGENT").toUpperCase();
|
|
return STAFF_ROLES.has(normalizedRole);
|
|
})
|
|
.sort((a, b) => a.name.localeCompare(b.name, "pt-BR"));
|
|
},
|
|
});
|
|
|
|
export const deleteUser = mutation({
|
|
args: { userId: v.id("users"), actorId: v.id("users") },
|
|
handler: async (ctx, { userId, actorId }) => {
|
|
const user = await ctx.db.get(userId);
|
|
if (!user) {
|
|
return { status: "not_found" };
|
|
}
|
|
|
|
await requireAdmin(ctx, actorId, user.tenantId);
|
|
|
|
const assignedTickets = await ctx.db
|
|
.query("tickets")
|
|
.withIndex("by_tenant_assignee", (q) => q.eq("tenantId", user.tenantId).eq("assigneeId", userId))
|
|
.take(1);
|
|
|
|
if (assignedTickets.length > 0) {
|
|
throw new ConvexError("Usuário ainda está atribuído a tickets");
|
|
}
|
|
|
|
await ctx.db.delete(userId);
|
|
return { status: "deleted" };
|
|
},
|
|
});
|
|
|
|
export const assignCompany = mutation({
|
|
args: { tenantId: v.string(), email: v.string(), companyId: v.id("companies"), actorId: v.id("users") },
|
|
handler: async (ctx, { tenantId, email, companyId, actorId }) => {
|
|
await requireAdmin(ctx, actorId, tenantId)
|
|
const user = await ctx.db
|
|
.query("users")
|
|
.withIndex("by_tenant_email", (q) => q.eq("tenantId", tenantId).eq("email", email))
|
|
.first()
|
|
if (!user) throw new ConvexError("Usuário não encontrado no Convex")
|
|
await ctx.db.patch(user._id, { companyId })
|
|
const updated = await ctx.db.get(user._id)
|
|
return updated
|
|
},
|
|
})
|