feat: dispositivos e ajustes de csat e relatórios

This commit is contained in:
codex-bot 2025-11-03 19:29:50 -03:00
parent 25d2a9b062
commit e0ef66555d
86 changed files with 5811 additions and 992 deletions

View file

@ -38,8 +38,8 @@ function validateOptions(type: FieldType, options: { value: string; label: strin
}
export const list = query({
args: { tenantId: v.string(), viewerId: v.id("users") },
handler: async (ctx, { tenantId, viewerId }) => {
args: { tenantId: v.string(), viewerId: v.id("users"), scope: v.optional(v.string()) },
handler: async (ctx, { tenantId, viewerId, scope }) => {
await requireAdmin(ctx, viewerId, tenantId);
const fields = await ctx.db
.query("ticketFields")
@ -47,6 +47,12 @@ export const list = query({
.collect();
return fields
.filter((field) => {
if (!scope) return true;
const fieldScope = (field.scope ?? "all").trim();
if (fieldScope === "all" || fieldScope.length === 0) return true;
return fieldScope === scope;
})
.sort((a, b) => a.order - b.order)
.map((field) => ({
id: field._id,
@ -57,6 +63,7 @@ export const list = query({
required: field.required,
options: field.options ?? [],
order: field.order,
scope: field.scope ?? "all",
createdAt: field.createdAt,
updatedAt: field.updatedAt,
}));
@ -64,8 +71,8 @@ export const list = query({
});
export const listForTenant = query({
args: { tenantId: v.string(), viewerId: v.id("users") },
handler: async (ctx, { tenantId, viewerId }) => {
args: { tenantId: v.string(), viewerId: v.id("users"), scope: v.optional(v.string()) },
handler: async (ctx, { tenantId, viewerId, scope }) => {
await requireUser(ctx, viewerId, tenantId);
const fields = await ctx.db
.query("ticketFields")
@ -73,6 +80,12 @@ export const listForTenant = query({
.collect();
return fields
.filter((field) => {
if (!scope) return true;
const fieldScope = (field.scope ?? "all").trim();
if (fieldScope === "all" || fieldScope.length === 0) return true;
return fieldScope === scope;
})
.sort((a, b) => a.order - b.order)
.map((field) => ({
id: field._id,
@ -83,6 +96,7 @@ export const listForTenant = query({
required: field.required,
options: field.options ?? [],
order: field.order,
scope: field.scope ?? "all",
}));
},
});
@ -103,8 +117,9 @@ export const create = mutation({
})
)
),
scope: v.optional(v.string()),
},
handler: async (ctx, { tenantId, actorId, label, description, type, required, options }) => {
handler: async (ctx, { tenantId, actorId, label, description, type, required, options, scope }) => {
await requireAdmin(ctx, actorId, tenantId);
const normalizedLabel = label.trim();
if (normalizedLabel.length < 2) {
@ -116,6 +131,15 @@ export const create = mutation({
validateOptions(type as FieldType, options ?? undefined);
const key = normalizeKey(normalizedLabel);
await ensureUniqueKey(ctx, tenantId, key);
const normalizedScope = (() => {
const raw = scope?.trim();
if (!raw || raw.length === 0) return "all";
const safe = raw.toLowerCase();
if (!/^[a-z0-9_\-]+$/i.test(safe)) {
throw new ConvexError("Escopo inválido para o campo");
}
return safe;
})();
const existing = await ctx.db
.query("ticketFields")
@ -133,6 +157,7 @@ export const create = mutation({
required,
options,
order: maxOrder + 1,
scope: normalizedScope,
createdAt: now,
updatedAt: now,
});
@ -157,8 +182,9 @@ export const update = mutation({
})
)
),
scope: v.optional(v.string()),
},
handler: async (ctx, { tenantId, fieldId, actorId, label, description, type, required, options }) => {
handler: async (ctx, { tenantId, fieldId, actorId, label, description, type, required, options, scope }) => {
await requireAdmin(ctx, actorId, tenantId);
const field = await ctx.db.get(fieldId);
if (!field || field.tenantId !== tenantId) {
@ -173,6 +199,16 @@ export const update = mutation({
}
validateOptions(type as FieldType, options ?? undefined);
const normalizedScope = (() => {
const raw = scope?.trim();
if (!raw || raw.length === 0) return "all";
const safe = raw.toLowerCase();
if (!/^[a-z0-9_\-]+$/i.test(safe)) {
throw new ConvexError("Escopo inválido para o campo");
}
return safe;
})();
let key = field.key;
if (field.label !== normalizedLabel) {
key = normalizeKey(normalizedLabel);
@ -186,6 +222,7 @@ export const update = mutation({
type,
required,
options,
scope: normalizedScope,
updatedAt: Date.now(),
});
},