fix: remove duplicacao de comentario na troca de responsavel e corrige avatar

- Remove criacao automatica de comentario ao trocar responsavel (ja aparece na timeline)
- Adiciona migration removeAssigneeChangeComments para limpar comentarios antigos
- Adiciona campos description, type, options, answer ao schema de checklist no mapper
- Cria mutation updateAvatar no Convex para sincronizar avatar com snapshots
- Atualiza rota /api/profile/avatar para sincronizar com Convex ao adicionar/remover foto

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
esdrasrenan 2025-12-15 18:53:49 -03:00
parent 59e9298d61
commit 9d1908a5aa
6 changed files with 193 additions and 78 deletions

View file

@ -872,23 +872,6 @@ async function ensureTicketFormDefaultsForTenant(ctx: MutationCtx, tenantId: str
}
}
export function buildAssigneeChangeComment(
reason: string,
context: { previousName: string; nextName: string },
): string {
const normalized = reason.replace(/\r\n/g, "\n").trim();
const lines = normalized
.split("\n")
.map((line) => line.trim())
.filter((line) => line.length > 0);
const previous = escapeHtml(context.previousName || "Não atribuído");
const next = escapeHtml(context.nextName || "Não atribuído");
const reasonHtml = lines.length
? lines.map((line) => `<p>${escapeHtml(line)}</p>`).join("")
: `<p>—</p>`;
return `<p><strong>Responsável atualizado:</strong> ${previous}${next}</p><p><strong>Motivo da troca:</strong></p>${reasonHtml}`;
}
function truncateSubject(subject: string) {
if (subject.length <= 60) return subject
return `${subject.slice(0, 57)}`
@ -3475,38 +3458,6 @@ export const changeAssignee = mutation({
createdAt: now,
});
if (normalizedReason.length > 0) {
const commentBody = buildAssigneeChangeComment(normalizedReason, {
previousName: previousAssigneeName,
nextName: nextAssigneeName,
})
const commentPlainLength = plainTextLength(commentBody)
if (commentPlainLength > MAX_COMMENT_CHARS) {
throw new ConvexError(`Comentário muito longo (máx. ${MAX_COMMENT_CHARS} caracteres)`)
}
const authorSnapshot: CommentAuthorSnapshot = {
name: viewerUser.name,
email: viewerUser.email,
avatarUrl: viewerUser.avatarUrl ?? undefined,
teams: viewerUser.teams ?? undefined,
}
await ctx.db.insert("ticketComments", {
ticketId,
authorId: actorId,
visibility: "INTERNAL",
body: commentBody,
authorSnapshot,
attachments: [],
createdAt: now,
updatedAt: now,
})
await ctx.db.insert("ticketEvents", {
ticketId,
type: "COMMENT_ADDED",
payload: { authorId: actorId, authorName: viewerUser.name, authorAvatar: viewerUser.avatarUrl },
createdAt: now,
})
}
},
});