feat(rich-text, types): Tiptap editor, SSR-safe, comments + description; stricter typing (no any) across app

- Add Tiptap editor + toolbar and rich content rendering with sanitize-html
- Fix SSR hydration (immediatelyRender: false) and setContent options
- Comments: rich text + visibility selector, typed attachments (Id<_storage>)
- New Ticket: description rich text; attachments typed; queues typed
- Convex: server-side filters using indexes; priority order rename; stronger Doc/Id typing; remove helper with any
- Schemas/Mappers: zod v4 record typing; event payload record typing; customFields typed
- UI: replace any in header/play/list/timeline/fields; improve select typings
- Build passes; only non-blocking lint warnings remain
This commit is contained in:
esdrasrenan 2025-10-04 14:25:10 -03:00
parent 9b0c0bd80a
commit ea60c3b841
26 changed files with 1390 additions and 245 deletions

View file

@ -56,12 +56,12 @@ export function TicketTimeline({ ticket }: TicketTimelineProps) {
{entry.payload?.actorName ? (
<span className="flex items-center gap-1 text-xs text-muted-foreground">
<Avatar className="size-5">
<AvatarImage src={entry.payload.actorAvatar} alt={entry.payload.actorName} />
<AvatarImage src={entry.payload?.actorAvatar as string | undefined} alt={String(entry.payload?.actorName ?? "")} />
<AvatarFallback>
{entry.payload.actorName.split(' ').slice(0,2).map((p:string)=>p[0]).join('').toUpperCase()}
{String(entry.payload?.actorName ?? '').split(' ').slice(0,2).map((p:string)=>p[0]).join('').toUpperCase()}
</AvatarFallback>
</Avatar>
por {entry.payload.actorName}
por {String(entry.payload?.actorName ?? '')}
</span>
) : null}
<span className="text-xs text-muted-foreground">
@ -69,7 +69,7 @@ export function TicketTimeline({ ticket }: TicketTimelineProps) {
</span>
</div>
{(() => {
const p: any = entry.payload || {}
const p = (entry.payload || {}) as { toLabel?: string; to?: string; assigneeName?: string; assigneeId?: string; queueName?: string; queueId?: string; requesterName?: string; authorName?: string; authorId?: string }
let message: string | null = null
if (entry.type === "STATUS_CHANGED" && (p.toLabel || p.to)) message = `Status alterado para ${p.toLabel || p.to}`
if (entry.type === "ASSIGNEE_CHANGED" && (p.assigneeName || p.assigneeId)) message = `Responsável alterado${p.assigneeName ? ` para ${p.assigneeName}` : ""}`