feat(devices,custom-fields,csat,portal):\n- Editor de campos personalizados (inclui multiselect) e exibição no detalhe\n- Campos personalizados disponíveis nas colunas/templates de exportação\n- Move cópia de e-mail para ícone inline abaixo do nome do dispositivo\n- Portal: banner para avaliar último chamado e CSAT no detalhe\n- Tickets list inclui campos de CSAT para detectar pendências
This commit is contained in:
parent
06deb99bcd
commit
c2c5707a97
7 changed files with 299 additions and 14 deletions
|
|
@ -5,7 +5,7 @@ import type { Id } from "./_generated/dataModel"
|
|||
|
||||
import { requireAdmin, requireUser } from "./rbac"
|
||||
|
||||
const FIELD_TYPES = ["text", "number", "select", "date", "boolean"] as const
|
||||
const FIELD_TYPES = ["text", "number", "select", "multiselect", "date", "boolean"] as const
|
||||
type FieldType = (typeof FIELD_TYPES)[number]
|
||||
|
||||
type AnyCtx = MutationCtx | QueryCtx
|
||||
|
|
@ -31,7 +31,7 @@ async function ensureUniqueKey(ctx: AnyCtx, tenantId: string, key: string, exclu
|
|||
}
|
||||
|
||||
function validateOptions(type: FieldType, options: { value: string; label: string }[] | undefined) {
|
||||
if (type === "select" && (!options || options.length === 0)) {
|
||||
if ((type === "select" || type === "multiselect") && (!options || options.length === 0)) {
|
||||
throw new ConvexError("Campos de seleção precisam de pelo menos uma opção")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -138,6 +138,19 @@ function formatDeviceCustomFieldDisplay(
|
|||
const option = options?.find((opt) => opt.value === raw || opt.label === raw)
|
||||
return option?.label ?? raw
|
||||
}
|
||||
case "multiselect": {
|
||||
const arr = Array.isArray(value)
|
||||
? value
|
||||
: typeof value === "string"
|
||||
? value.split(",").map((s) => s.trim()).filter(Boolean)
|
||||
: []
|
||||
if (arr.length === 0) return null
|
||||
const labels = arr.map((raw) => {
|
||||
const opt = options?.find((o) => o.value === raw || o.label === raw)
|
||||
return opt?.label ?? String(raw)
|
||||
})
|
||||
return labels.join(", ")
|
||||
}
|
||||
default:
|
||||
try {
|
||||
return JSON.stringify(value)
|
||||
|
|
|
|||
|
|
@ -1236,6 +1236,11 @@ export const list = query({
|
|||
priority: t.priority,
|
||||
channel: t.channel,
|
||||
queue: queueName,
|
||||
csatScore: typeof t.csatScore === "number" ? t.csatScore : null,
|
||||
csatMaxScore: typeof t.csatMaxScore === "number" ? t.csatMaxScore : null,
|
||||
csatComment: typeof t.csatComment === "string" && t.csatComment.trim().length > 0 ? t.csatComment.trim() : null,
|
||||
csatRatedAt: t.csatRatedAt ?? null,
|
||||
csatRatedBy: t.csatRatedBy ? String(t.csatRatedBy) : null,
|
||||
formTemplate: t.formTemplate ?? null,
|
||||
company: company
|
||||
? { id: company._id, name: company.name, isAvulso: company.isAvulso ?? false }
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue