feat: implement invite onboarding and dynamic ticket fields

This commit is contained in:
esdrasrenan 2025-10-05 21:47:28 -03:00
parent 29a647f6c6
commit f24a7f68ca
34 changed files with 2240 additions and 97 deletions

View file

@ -3,6 +3,8 @@ import type { MutationCtx } from "./_generated/server"
import { ConvexError, v } from "convex/values"
import { Id } from "./_generated/dataModel"
import { requireAdmin } from "./rbac"
type CategorySeed = {
name: string
description?: string
@ -295,10 +297,13 @@ export const ensureDefaults = mutation({
export const createCategory = mutation({
args: {
tenantId: v.string(),
actorId: v.id("users"),
name: v.string(),
description: v.optional(v.string()),
secondary: v.optional(v.array(v.string())),
},
handler: async (ctx, { tenantId, name, description }) => {
handler: async (ctx, { tenantId, actorId, name, description, secondary }) => {
await requireAdmin(ctx, actorId, tenantId)
const trimmed = name.trim()
if (trimmed.length < 2) {
throw new ConvexError("Informe um nome válido para a categoria")
@ -321,6 +326,31 @@ export const createCategory = mutation({
createdAt: now,
updatedAt: now,
})
if (secondary?.length) {
let subOrder = 0
for (const item of secondary) {
const value = item.trim()
if (value.length < 2) continue
const subSlug = await ensureUniqueSlug(
ctx,
"ticketSubcategories",
tenantId,
slugify(value),
{ categoryId: id }
)
await ctx.db.insert("ticketSubcategories", {
tenantId,
categoryId: id,
name: value,
slug: subSlug,
order: subOrder,
createdAt: now,
updatedAt: now,
})
subOrder += 1
}
}
return id
},
})
@ -329,10 +359,12 @@ export const updateCategory = mutation({
args: {
categoryId: v.id("ticketCategories"),
tenantId: v.string(),
actorId: v.id("users"),
name: v.string(),
description: v.optional(v.string()),
},
handler: async (ctx, { categoryId, tenantId, name, description }) => {
handler: async (ctx, { categoryId, tenantId, actorId, name, description }) => {
await requireAdmin(ctx, actorId, tenantId)
const category = await ctx.db.get(categoryId)
if (!category || category.tenantId !== tenantId) {
throw new ConvexError("Categoria não encontrada")
@ -354,9 +386,11 @@ export const deleteCategory = mutation({
args: {
categoryId: v.id("ticketCategories"),
tenantId: v.string(),
actorId: v.id("users"),
transferTo: v.optional(v.id("ticketCategories")),
},
handler: async (ctx, { categoryId, tenantId, transferTo }) => {
handler: async (ctx, { categoryId, tenantId, actorId, transferTo }) => {
await requireAdmin(ctx, actorId, tenantId)
const category = await ctx.db.get(categoryId)
if (!category || category.tenantId !== tenantId) {
throw new ConvexError("Categoria não encontrada")
@ -412,10 +446,12 @@ export const deleteCategory = mutation({
export const createSubcategory = mutation({
args: {
tenantId: v.string(),
actorId: v.id("users"),
categoryId: v.id("ticketCategories"),
name: v.string(),
},
handler: async (ctx, { tenantId, categoryId, name }) => {
handler: async (ctx, { tenantId, actorId, categoryId, name }) => {
await requireAdmin(ctx, actorId, tenantId)
const category = await ctx.db.get(categoryId)
if (!category || category.tenantId !== tenantId) {
throw new ConvexError("Categoria não encontrada")
@ -449,10 +485,12 @@ export const createSubcategory = mutation({
export const updateSubcategory = mutation({
args: {
tenantId: v.string(),
actorId: v.id("users"),
subcategoryId: v.id("ticketSubcategories"),
name: v.string(),
},
handler: async (ctx, { tenantId, subcategoryId, name }) => {
handler: async (ctx, { tenantId, actorId, subcategoryId, name }) => {
await requireAdmin(ctx, actorId, tenantId)
const subcategory = await ctx.db.get(subcategoryId)
if (!subcategory || subcategory.tenantId !== tenantId) {
throw new ConvexError("Subcategoria não encontrada")
@ -471,10 +509,12 @@ export const updateSubcategory = mutation({
export const deleteSubcategory = mutation({
args: {
tenantId: v.string(),
actorId: v.id("users"),
subcategoryId: v.id("ticketSubcategories"),
transferTo: v.optional(v.id("ticketSubcategories")),
},
handler: async (ctx, { tenantId, subcategoryId, transferTo }) => {
handler: async (ctx, { tenantId, actorId, subcategoryId, transferTo }) => {
await requireAdmin(ctx, actorId, tenantId)
const subcategory = await ctx.db.get(subcategoryId)
if (!subcategory || subcategory.tenantId !== tenantId) {
throw new ConvexError("Subcategoria não encontrada")