From aa3c1855b28096553dddf7ea6ba59e500259df98 Mon Sep 17 00:00:00 2001 From: esdrasrenan Date: Sat, 13 Dec 2025 21:36:44 -0300 Subject: [PATCH 001/192] fix(desktop): remove instalacao WebView2 para evitar UAC duplo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Windows 10/11 ja possuem WebView2 pre-instalado. Usar "skip" elimina o segundo prompt de UAC durante a instalacao. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- apps/desktop/src-tauri/tauri.conf.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/desktop/src-tauri/tauri.conf.json b/apps/desktop/src-tauri/tauri.conf.json index 6c9fa49..d7672b5 100644 --- a/apps/desktop/src-tauri/tauri.conf.json +++ b/apps/desktop/src-tauri/tauri.conf.json @@ -52,8 +52,7 @@ ], "windows": { "webviewInstallMode": { - "type": "downloadBootstrapper", - "silent": true + "type": "skip" }, "nsis": { "displayLanguageSelector": true, From 7c82ef18b388062028cc65041574223ba845a580 Mon Sep 17 00:00:00 2001 From: esdrasrenan Date: Sat, 13 Dec 2025 22:03:35 -0300 Subject: [PATCH 002/192] =?UTF-8?q?fix(ui):=20ajustes=20em=20automa=C3=A7?= =?UTF-8?q?=C3=B5es=20e=20checklist?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../automations/automation-editor-dialog.tsx | 196 +++++++++++++----- .../automations/automations-manager.tsx | 10 +- src/components/tickets/new-ticket-dialog.tsx | 6 +- .../tickets/ticket-checklist-card.tsx | 14 +- src/components/ui/select.tsx | 77 +++++-- 5 files changed, 218 insertions(+), 85 deletions(-) diff --git a/src/components/automations/automation-editor-dialog.tsx b/src/components/automations/automation-editor-dialog.tsx index 7494697..383f2bf 100644 --- a/src/components/automations/automation-editor-dialog.tsx +++ b/src/components/automations/automation-editor-dialog.tsx @@ -15,7 +15,7 @@ import { Checkbox } from "@/components/ui/checkbox" import { DialogClose, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@/components/ui/dialog" import { Input } from "@/components/ui/input" import { Label } from "@/components/ui/label" -import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" +import { Select, SelectContent, SelectEmptyState, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" import { Separator } from "@/components/ui/separator" import { Switch } from "@/components/ui/switch" import { Textarea } from "@/components/ui/textarea" @@ -689,11 +689,19 @@ export function AutomationEditorDialog({ - {(companies ?? []).map((company) => ( - - {company.name} - - ))} + {(companies ?? []).length === 0 ? ( + + ) : ( + (companies ?? []).map((company) => ( + + {company.name} + + )) + )} ) : c.field === "queueId" ? ( @@ -707,11 +715,19 @@ export function AutomationEditorDialog({ - {(queues ?? []).map((queue) => ( - - {queue.name} - - ))} + {(queues ?? []).length === 0 ? ( + + ) : ( + (queues ?? []).map((queue) => ( + + {queue.name} + + )) + )} ) : c.field === "categoryId" ? ( @@ -725,11 +741,19 @@ export function AutomationEditorDialog({ - {(categories ?? []).map((cat) => ( - - {cat.name} - - ))} + {(categories ?? []).length === 0 ? ( + + ) : ( + (categories ?? []).map((cat) => ( + + {cat.name} + + )) + )} ) : c.field === "subcategoryId" ? ( @@ -743,11 +767,19 @@ export function AutomationEditorDialog({ - {subcategoryOptions.map((sub) => ( - - {sub.name} - - ))} + {subcategoryOptions.length === 0 ? ( + + ) : ( + subcategoryOptions.map((sub) => ( + + {sub.name} + + )) + )} ) : ( @@ -764,12 +796,22 @@ export function AutomationEditorDialog({ - Nenhum - {(templates ?? []).map((tpl) => ( - - {tpl.label} - - ))} + {(templates ?? []).length === 0 ? ( + + ) : ( + <> + Nenhum + {(templates ?? []).map((tpl) => ( + + {tpl.label} + + ))} + + )} )} @@ -890,11 +932,19 @@ export function AutomationEditorDialog({ - {(queues ?? []).map((queue) => ( - - {queue.name} - - ))} + {(queues ?? []).length === 0 ? ( + + ) : ( + (queues ?? []).map((queue) => ( + + {queue.name} + + )) + )} ) : a.type === "ASSIGN_TO" ? ( @@ -908,11 +958,19 @@ export function AutomationEditorDialog({ - {(agents ?? []).map((u) => ( - - {u.name} - - ))} + {(agents ?? []).length === 0 ? ( + + ) : ( + (agents ?? []).map((u) => ( + + {u.name} + + )) + )} ) : a.type === "SET_FORM_TEMPLATE" ? ( @@ -929,12 +987,22 @@ export function AutomationEditorDialog({ - Nenhum - {(templates ?? []).map((tpl) => ( - - {tpl.label} - - ))} + {(templates ?? []).length === 0 ? ( + + ) : ( + <> + Nenhum + {(templates ?? []).map((tpl) => ( + + {tpl.label} + + ))} + + )} ) : a.type === "SET_CHAT_ENABLED" ? ( @@ -959,12 +1027,20 @@ export function AutomationEditorDialog({ - {(checklistTemplates ?? []).map((tpl) => ( - - {tpl.name} - {tpl.company ? ` — ${tpl.company.name}` : ""} - - ))} + {(checklistTemplates ?? []).length === 0 ? ( + + ) : ( + (checklistTemplates ?? []).map((tpl) => ( + + {tpl.name} + {tpl.company ? ` — ${tpl.company.name}` : ""} + + )) + )} ) : a.type === "SEND_EMAIL" ? ( @@ -1040,12 +1116,22 @@ export function AutomationEditorDialog({ - Nenhum - {(agents ?? []).map((u) => ( - - {u.name} - - ))} + {(agents ?? []).length === 0 ? ( + + ) : ( + <> + Nenhum + {(agents ?? []).map((u) => ( + + {u.name} + + ))} + + )} diff --git a/src/components/automations/automations-manager.tsx b/src/components/automations/automations-manager.tsx index 7e2a4d5..0f42781 100644 --- a/src/components/automations/automations-manager.tsx +++ b/src/components/automations/automations-manager.tsx @@ -240,13 +240,13 @@ export function AutomationsManager() {
- - + + - - - + + + diff --git a/src/components/tickets/new-ticket-dialog.tsx b/src/components/tickets/new-ticket-dialog.tsx index a025f79..1da5ddf 100644 --- a/src/components/tickets/new-ticket-dialog.tsx +++ b/src/components/tickets/new-ticket-dialog.tsx @@ -964,8 +964,8 @@ export function NewTicketDialog({ renderValue={(option) => { if (!option) return Selecionar solicitante return ( -
-
+
+
{option.label} {option.description ? ( {option.description} @@ -974,7 +974,7 @@ export function NewTicketDialog({ {selectedCompanyOption && selectedCompanyOption.id !== NO_COMPANY_VALUE ? ( {selectedCompanyOption.name} diff --git a/src/components/tickets/ticket-checklist-card.tsx b/src/components/tickets/ticket-checklist-card.tsx index cf62f04..7dc5cca 100644 --- a/src/components/tickets/ticket-checklist-card.tsx +++ b/src/components/tickets/ticket-checklist-card.tsx @@ -200,6 +200,7 @@ export function TicketChecklistCard({ + +
+ ) : ( +

{ + if (!canEdit || isResolved) return + setEditingId(item.id) + setEditingText(item.text) + }} + > + {item.text} +

+ )} +
+ {required ? ( + + Obrigatório + + ) : ( + + Opcional + + )} + {templateLabel ? ( + + Template: {templateLabel} + + ) : null} +
+
+ + + {canEdit && !isResolved ? ( +
+
- ) : ( -

{ - if (!canEdit || isResolved) return - setEditingId(item.id) - setEditingText(item.text) - }} - > - {item.text} -

- )} -
- {required ? ( - - Obrigatório - - ) : ( - - Opcional - - )} - {templateLabel ? ( - - Template: {templateLabel} - - ) : null} -
+ ) : null}
- - - {canEdit && !isResolved ? ( -
- - -
- ) : null} -
- ) - })} - + ) + }) + )} + + )} {canEdit && !isResolved ? ( From 06388b368870d03082ebcce802dc2d7b398888f2 Mon Sep 17 00:00:00 2001 From: esdrasrenan Date: Sat, 13 Dec 2025 22:22:42 -0300 Subject: [PATCH 004/192] =?UTF-8?q?ui:=20ajustes=20de=20layout=20em=20auto?= =?UTF-8?q?ma=C3=A7=C3=B5es=20e=20checklist?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Reorganiza header da página de automações (título/descrição em cima, filtros embaixo) - Aumenta espaçamento da badge "quando" na tabela - Ajusta largura do input de checklist nos tickets para melhor distribuição 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .../automations/automations-manager.tsx | 60 ++++++++++--------- .../tickets/ticket-checklist-card.tsx | 22 +++---- 2 files changed, 43 insertions(+), 39 deletions(-) diff --git a/src/components/automations/automations-manager.tsx b/src/components/automations/automations-manager.tsx index 0f42781..9806681 100644 --- a/src/components/automations/automations-manager.tsx +++ b/src/components/automations/automations-manager.tsx @@ -11,7 +11,7 @@ import { useAuth } from "@/lib/auth-client" import { DEFAULT_TENANT_ID } from "@/lib/constants" import { Badge } from "@/components/ui/badge" import { Button } from "@/components/ui/button" -import { Card, CardAction, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" import { Dialog, DialogTrigger } from "@/components/ui/dialog" import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu" import { Input } from "@/components/ui/input" @@ -152,21 +152,23 @@ export function AutomationsManager() { return ( - - Automações - - Crie gatilhos para executar ações automáticas. - - + +
+ Automações + + Crie gatilhos para executar ações automáticas nos tickets. + +
+
setQueryText(e.target.value)} placeholder="Buscar automação..." - className="h-8 w-full text-sm sm:w-64" + className="h-8 w-full text-sm sm:w-56" /> - - - - - setEditorOpen(false)} - /> -
- + + + + + setEditorOpen(false)} + /> + +
- - + + - + @@ -285,13 +287,13 @@ export function AutomationsManager() { {row.name} - +
- + {triggerLabel(row.trigger)} {row.timing === "DELAYED" && row.delayMs ? ( - + +{Math.round(row.delayMs / 60000)}m ) : null} diff --git a/src/components/tickets/ticket-checklist-card.tsx b/src/components/tickets/ticket-checklist-card.tsx index 72b3b66..7cf23a4 100644 --- a/src/components/tickets/ticket-checklist-card.tsx +++ b/src/components/tickets/ticket-checklist-card.tsx @@ -428,12 +428,12 @@ export function TicketChecklistCard({ )} {canEdit && !isResolved ? ( -
+
setNewText(e.target.value)} placeholder="Adicionar item do checklist..." - className="h-8 flex-1 bg-white" + className="h-8 w-full bg-white sm:max-w-md" onKeyDown={(e) => { if (e.key === "Enter") { e.preventDefault() @@ -441,14 +441,16 @@ export function TicketChecklistCard({ } }} /> - - +
+ + +
) : null} From 245d5dc15bda2475e90d817366aa74f4d0f5cc33 Mon Sep 17 00:00:00 2001 From: esdrasrenan Date: Sat, 13 Dec 2025 22:42:37 -0300 Subject: [PATCH 005/192] =?UTF-8?q?ui:=20melhorias=20de=20UX=20em=20v?= =?UTF-8?q?=C3=A1rias=20telas?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Truncate com ellipsis na coluna Empresa (tickets-table) - Botão excluir em templates de checklist + mutation remove no backend - Botões Editar/Arquivar com size="sm" em checklist templates - Hover com borda no botão "Tornar opcional" do checklist - Botão Resetar em devices com estilo padrão (remove amarelo) - Botão "Encerrar" no modo apresentação do dashboard - Sidebar abre automaticamente ao sair do fullscreen 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- convex/checklistTemplates.ts | 20 +++++++ .../admin/devices/admin-devices-overview.tsx | 2 +- .../dashboards/dashboard-builder.tsx | 58 ++++++++++++------- .../settings/checklist-templates-manager.tsx | 31 +++++++++- .../tickets/ticket-checklist-card.tsx | 2 +- src/components/tickets/tickets-table.tsx | 6 +- 6 files changed, 91 insertions(+), 28 deletions(-) diff --git a/convex/checklistTemplates.ts b/convex/checklistTemplates.ts index c9c410a..34c75cc 100644 --- a/convex/checklistTemplates.ts +++ b/convex/checklistTemplates.ts @@ -259,3 +259,23 @@ export const update = mutation({ return { ok: true } }, }) + +export const remove = mutation({ + args: { + tenantId: v.string(), + actorId: v.id("users"), + templateId: v.id("ticketChecklistTemplates"), + }, + handler: async (ctx, { tenantId, actorId, templateId }) => { + await requireAdmin(ctx, actorId, tenantId) + + const existing = await ctx.db.get(templateId) + if (!existing || existing.tenantId !== tenantId) { + throw new ConvexError("Template de checklist não encontrado.") + } + + await ctx.db.delete(templateId) + + return { ok: true } + }, +}) diff --git a/src/components/admin/devices/admin-devices-overview.tsx b/src/components/admin/devices/admin-devices-overview.tsx index cdd5d90..70e4916 100644 --- a/src/components/admin/devices/admin-devices-overview.tsx +++ b/src/components/admin/devices/admin-devices-overview.tsx @@ -4000,7 +4000,7 @@ export function DeviceDetails({ device }: DeviceDetailsProps) {
-
- {sections.map((section, index) => ( +
+
+ {sections.map((section, index) => ( +
+ {fullscreen && onExitPresentation ? ( + ) : null}
) diff --git a/src/components/settings/checklist-templates-manager.tsx b/src/components/settings/checklist-templates-manager.tsx index f6f5dea..7ae3c07 100644 --- a/src/components/settings/checklist-templates-manager.tsx +++ b/src/components/settings/checklist-templates-manager.tsx @@ -266,6 +266,7 @@ export function ChecklistTemplatesManager() { ) as Array<{ id: Id<"companies">; name: string }> | undefined const updateTemplate = useMutation(api.checklistTemplates.update) + const removeTemplate = useMutation(api.checklistTemplates.remove) const companyOptions = useMemo( () => (companies ?? []).map((c) => ({ id: c.id, name: c.name })).sort((a, b) => a.name.localeCompare(b.name, "pt-BR")), @@ -303,6 +304,22 @@ export function ChecklistTemplatesManager() { } } + const handleDelete = async (tpl: ChecklistTemplateRow) => { + if (!viewerId) return + const ok = confirm(`Excluir o template "${tpl.name}"? Esta ação não pode ser desfeita.`) + if (!ok) return + try { + await removeTemplate({ + tenantId, + actorId: viewerId, + templateId: tpl.id, + }) + toast.success("Template excluído.") + } catch (error) { + toast.error(error instanceof Error ? error.message : "Falha ao excluir template.") + } + } + return (
@@ -366,12 +383,22 @@ export function ChecklistTemplatesManager() { ) : null}
- - +
diff --git a/src/components/tickets/ticket-checklist-card.tsx b/src/components/tickets/ticket-checklist-card.tsx index 7cf23a4..469ac80 100644 --- a/src/components/tickets/ticket-checklist-card.tsx +++ b/src/components/tickets/ticket-checklist-card.tsx @@ -376,7 +376,7 @@ export function TicketChecklistCard({ + + + + ) } From 3f5062c9b6c9f259fd43c1a5077924f880124070 Mon Sep 17 00:00:00 2001 From: esdrasrenan Date: Sat, 13 Dec 2025 23:10:33 -0300 Subject: [PATCH 009/192] =?UTF-8?q?ui:=20ajusta=20espa=C3=A7amento=20dos?= =?UTF-8?q?=20bot=C3=B5es=20no=20modal=20de=20exclus=C3=A3o?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- src/components/settings/checklist-templates-manager.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/settings/checklist-templates-manager.tsx b/src/components/settings/checklist-templates-manager.tsx index f8d5af4..72f1a6a 100644 --- a/src/components/settings/checklist-templates-manager.tsx +++ b/src/components/settings/checklist-templates-manager.tsx @@ -427,7 +427,7 @@ export function ChecklistTemplatesManager() { Tem certeza que deseja excluir o template "{deleteTarget?.name}"? Esta ação não pode ser desfeita. - + From 10918d08936061513473c4a93aca50743c63c6ff Mon Sep 17 00:00:00 2001 From: esdrasrenan Date: Sat, 13 Dec 2025 23:19:26 -0300 Subject: [PATCH 010/192] =?UTF-8?q?fix:=20corrige=20scroll=20do=20dropdown?= =?UTF-8?q?=20e=20ajusta=20bot=C3=A3o=20Adicionar?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Adiciona collisionPadding e overflow-hidden no SearchableCombobox - Reduz tamanho do botão Adicionar no checklist (size=sm) - Adiciona espaçamento antes do botão Adicionar 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- src/components/tickets/new-ticket-dialog.tsx | 2 +- src/components/ui/searchable-combobox.tsx | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/tickets/new-ticket-dialog.tsx b/src/components/tickets/new-ticket-dialog.tsx index 1da5ddf..a6c0f70 100644 --- a/src/components/tickets/new-ticket-dialog.tsx +++ b/src/components/tickets/new-ticket-dialog.tsx @@ -1514,7 +1514,7 @@ export function NewTicketDialog({ /> Obrigatório - diff --git a/src/components/ui/searchable-combobox.tsx b/src/components/ui/searchable-combobox.tsx index dafdd02..32f08c3 100644 --- a/src/components/ui/searchable-combobox.tsx +++ b/src/components/ui/searchable-combobox.tsx @@ -126,7 +126,8 @@ export function SearchableCombobox({
) : null} {filtered.length === 0 ? ( From e4010536674f5337dea4a59648620ca03afa103f Mon Sep 17 00:00:00 2001 From: esdrasrenan Date: Sat, 13 Dec 2025 23:26:40 -0300 Subject: [PATCH 011/192] fix: remove overflow-hidden que bloqueava scroll do combobox MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit O overflow-hidden no PopoverContent impedia o scroll da ScrollArea filha. Removendo, o scroll funciona corretamente. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- src/components/ui/searchable-combobox.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ui/searchable-combobox.tsx b/src/components/ui/searchable-combobox.tsx index 32f08c3..349a18d 100644 --- a/src/components/ui/searchable-combobox.tsx +++ b/src/components/ui/searchable-combobox.tsx @@ -126,7 +126,7 @@ export function SearchableCombobox({
From a4144dd39e0284d0582100832a074861680f86fe Mon Sep 17 00:00:00 2001 From: esdrasrenan Date: Sat, 13 Dec 2025 23:49:38 -0300 Subject: [PATCH 012/192] feat(automations): redesign da acao de enviar e-mail com UX melhorada MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Cria componente EmailActionConfig dedicado para configuracao de e-mail - Layout expandido (full-width) para melhor aproveitamento do espaco - Variaveis como badges clicaveis que inserem no campo ativo - Editor TipTap para mensagem com suporte a variaveis inline - Autocomplete de variaveis ao digitar {{ - Organizacao visual melhorada com secoes claras 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .../automations/automation-editor-dialog.tsx | 156 +---- .../automations/email-action-config.tsx | 614 ++++++++++++++++++ 2 files changed, 630 insertions(+), 140 deletions(-) create mode 100644 src/components/automations/email-action-config.tsx diff --git a/src/components/automations/automation-editor-dialog.tsx b/src/components/automations/automation-editor-dialog.tsx index 383f2bf..0c03a39 100644 --- a/src/components/automations/automation-editor-dialog.tsx +++ b/src/components/automations/automation-editor-dialog.tsx @@ -19,6 +19,7 @@ import { Select, SelectContent, SelectEmptyState, SelectItem, SelectTrigger, Sel import { Separator } from "@/components/ui/separator" import { Switch } from "@/components/ui/switch" import { Textarea } from "@/components/ui/textarea" +import { EmailActionConfig } from "@/components/automations/email-action-config" type AutomationRow = { id: Id<"ticketAutomations"> @@ -848,7 +849,19 @@ export function AutomationEditorDialog({
- {actions.map((a) => ( + {actions.map((a) => + a.type === "SEND_EMAIL" ? ( +
+ + setActions((prev) => prev.map((item) => (item.id === a.id ? updated : item))) + } + onRemove={() => handleRemoveAction(a.id)} + agents={(agents ?? []).map((u) => ({ _id: String(u._id), name: u.name }))} + /> +
+ ) : (
@@ -1043,144 +1056,6 @@ export function AutomationEditorDialog({ )} - ) : a.type === "SEND_EMAIL" ? ( -
-
- - - setActions((prev) => prev.map((item) => (item.id === a.id ? { ...item, subject: e.target.value } : item))) - } - placeholder="Ex.: Atualização do chamado #{{ticket.reference}}" - /> -
-
- -