feat: status + queue updates, filters e UI
- Status renomeados e cores (Em andamento azul, Pausado amarelo) - Transições automáticas: iniciar=Em andamento, pausar=Pausado - Fila padrão: Chamados ao criar ticket - Admin/Empresas: renomeia ‘Slug’ → ‘Apelido’ + mensagens - Dashboard: últimos tickets priorizam sem responsável (mais antigos) - Tickets: filtro por responsável + salvar filtro por usuário - Encerrar ticket: adiciona botão ‘Cancelar’ - Strings atualizadas (PDF, relatórios, badges)
This commit is contained in:
parent
e91192a1f6
commit
5535ba81e6
19 changed files with 399 additions and 86 deletions
|
|
@ -104,6 +104,17 @@ export function NewTicketDialog({ triggerClassName }: { triggerClassName?: strin
|
|||
setAssigneeInitialized(true)
|
||||
}, [open, assigneeInitialized, convexUserId, form])
|
||||
|
||||
// Default queue to "Chamados" if available when opening
|
||||
useEffect(() => {
|
||||
if (!open) return
|
||||
const current = form.getValues("queueName")
|
||||
if (current) return
|
||||
const hasChamados = queues.some((q) => q.name === "Chamados")
|
||||
if (hasChamados) {
|
||||
form.setValue("queueName", "Chamados", { shouldDirty: false, shouldTouch: false })
|
||||
}
|
||||
}, [open, queues, form])
|
||||
|
||||
const handleCategoryChange = (value: string) => {
|
||||
const previous = form.getValues("categoryId") ?? ""
|
||||
const next = value ?? ""
|
||||
|
|
@ -166,6 +177,13 @@ export function NewTicketDialog({ triggerClassName }: { triggerClassName?: strin
|
|||
})
|
||||
const summaryFallback = values.summary?.trim() ?? ""
|
||||
const bodyHtml = plainDescription.length > 0 ? sanitizedDescription : summaryFallback
|
||||
const MAX_COMMENT_CHARS = 20000
|
||||
const plainForLimit = (plainDescription.length > 0 ? plainDescription : summaryFallback).trim()
|
||||
if (plainForLimit.length > MAX_COMMENT_CHARS) {
|
||||
toast.error(`Descrição muito longa (máx. ${MAX_COMMENT_CHARS} caracteres)`, { id: "new-ticket" })
|
||||
setLoading(false)
|
||||
return
|
||||
}
|
||||
if (attachments.length > 0 || bodyHtml.trim().length > 0) {
|
||||
const typedAttachments = attachments.map((a) => ({
|
||||
storageId: a.storageId as unknown as Id<"_storage">,
|
||||
|
|
@ -254,9 +272,15 @@ export function NewTicketDialog({ triggerClassName }: { triggerClassName?: strin
|
|||
<FieldLabel htmlFor="summary">Resumo</FieldLabel>
|
||||
<textarea
|
||||
id="summary"
|
||||
className="min-h-[96px] w-full rounded-lg border border-slate-300 bg-background p-3 text-sm shadow-sm focus-visible:border-[#00d6eb] focus-visible:outline-none"
|
||||
className="min-h-[96px] w-full resize-none overflow-hidden rounded-lg border border-slate-300 bg-background p-3 text-sm shadow-sm focus-visible:border-[#00d6eb] focus-visible:outline-none"
|
||||
maxLength={600}
|
||||
{...form.register("summary")}
|
||||
placeholder="Explique em poucas linhas o contexto do chamado."
|
||||
onInput={(e) => {
|
||||
const el = e.currentTarget
|
||||
el.style.height = 'auto'
|
||||
el.style.height = `${el.scrollHeight}px`
|
||||
}}
|
||||
/>
|
||||
</Field>
|
||||
<Field>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue