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:
codex-bot 2025-10-20 14:57:22 -03:00
parent e91192a1f6
commit 5535ba81e6
19 changed files with 399 additions and 86 deletions

View file

@ -14,7 +14,7 @@ import { cn } from "@/lib/utils"
const statusLabel: Record<Ticket["status"], string> = {
PENDING: "Pendente",
AWAITING_ATTENDANCE: "Aguardando atendimento",
AWAITING_ATTENDANCE: "Em andamento",
PAUSED: "Pausado",
RESOLVED: "Resolvido",
}

View file

@ -239,6 +239,11 @@ export function PortalTicketDetail({ ticketId }: PortalTicketDetailProps) {
const plainText = typeof window !== "undefined"
? new DOMParser().parseFromString(sanitizedHtml, "text/html").body.textContent?.replace(/\u00a0/g, " ").trim() ?? ""
: sanitizedHtml.replace(/<[^>]*>/g, "").replace(/&nbsp;/g, " ").trim()
const MAX_COMMENT_CHARS = 20000
if (plainText.length > MAX_COMMENT_CHARS) {
toast.error(`Comentário muito longo (máx. ${MAX_COMMENT_CHARS} caracteres)`)
return
}
const hasMeaningfulText = plainText.length > 0
if (!hasMeaningfulText && attachments.length === 0) {
toast.error("Adicione uma mensagem ou anexe ao menos um arquivo antes de enviar.")

View file

@ -102,6 +102,12 @@ export function PortalTicketForm() {
})
if (plainDescription.length > 0) {
const MAX_COMMENT_CHARS = 20000
if (plainDescription.length > MAX_COMMENT_CHARS) {
toast.error(`Descrição muito longa (máx. ${MAX_COMMENT_CHARS} caracteres)`, { id: "portal-new-ticket" })
setIsSubmitting(false)
return
}
const htmlBody = sanitizedDescription || toHtml(trimmedSummary || trimmedSubject)
const typedAttachments = attachments.map((file) => ({
@ -178,6 +184,7 @@ export function PortalTicketForm() {
value={summary}
onChange={(event) => setSummary(event.target.value)}
placeholder="Descreva rapidamente o que está acontecendo"
maxLength={600}
disabled={machineInactive || isSubmitting}
/>
</div>