diff --git a/convex/commentTemplates.ts b/convex/commentTemplates.ts
index 59baa5e..713f5a0 100644
--- a/convex/commentTemplates.ts
+++ b/convex/commentTemplates.ts
@@ -50,20 +50,24 @@ export const list = query({
args: {
tenantId: v.string(),
viewerId: v.id("users"),
+ kind: v.optional(v.string()),
},
- handler: async (ctx, { tenantId, viewerId }) => {
+ handler: async (ctx, { tenantId, viewerId, kind }) => {
await requireStaff(ctx, viewerId, tenantId)
+ const normalizedKind = (kind ?? "comment").toLowerCase()
const templates = await ctx.db
.query("commentTemplates")
.withIndex("by_tenant", (q) => q.eq("tenantId", tenantId))
.collect()
return templates
+ .filter((template) => (template.kind ?? "comment") === normalizedKind)
.sort((a, b) => a.title.localeCompare(b.title, "pt-BR", { sensitivity: "base" }))
.map((template) => ({
id: template._id,
title: template.title,
body: template.body,
+ kind: template.kind ?? "comment",
createdAt: template.createdAt,
updatedAt: template.updatedAt,
createdBy: template.createdBy,
@@ -78,8 +82,9 @@ export const create = mutation({
actorId: v.id("users"),
title: v.string(),
body: v.string(),
+ kind: v.optional(v.string()),
},
- handler: async (ctx, { tenantId, actorId, title, body }) => {
+ handler: async (ctx, { tenantId, actorId, title, body, kind }) => {
await requireStaff(ctx, actorId, tenantId)
const normalizedTitle = normalizeTitle(title)
if (!normalizedTitle || normalizedTitle.length < 3) {
@@ -89,19 +94,21 @@ export const create = mutation({
if (!sanitizedBody) {
throw new ConvexError("Informe o conteúdo do template")
}
+ const normalizedKind = (kind ?? "comment").toLowerCase()
const existing = await ctx.db
.query("commentTemplates")
.withIndex("by_tenant_title", (q) => q.eq("tenantId", tenantId).eq("title", normalizedTitle))
.first()
- if (existing) {
+ if (existing && (existing.kind ?? "comment") === normalizedKind) {
throw new ConvexError("Já existe um template com este título")
}
const now = Date.now()
const id = await ctx.db.insert("commentTemplates", {
tenantId,
+ kind: normalizedKind,
title: normalizedTitle,
body: sanitizedBody,
createdBy: actorId,
@@ -121,8 +128,9 @@ export const update = mutation({
actorId: v.id("users"),
title: v.string(),
body: v.string(),
+ kind: v.optional(v.string()),
},
- handler: async (ctx, { templateId, tenantId, actorId, title, body }) => {
+ handler: async (ctx, { templateId, tenantId, actorId, title, body, kind }) => {
await requireStaff(ctx, actorId, tenantId)
const template = await ctx.db.get(templateId)
if (!template || template.tenantId !== tenantId) {
@@ -137,17 +145,19 @@ export const update = mutation({
if (!sanitizedBody) {
throw new ConvexError("Informe o conteúdo do template")
}
+ const normalizedKind = (kind ?? "comment").toLowerCase()
const duplicate = await ctx.db
.query("commentTemplates")
.withIndex("by_tenant_title", (q) => q.eq("tenantId", tenantId).eq("title", normalizedTitle))
.first()
- if (duplicate && duplicate._id !== templateId) {
+ if (duplicate && duplicate._id !== templateId && (duplicate.kind ?? "comment") === normalizedKind) {
throw new ConvexError("Já existe um template com este título")
}
const now = Date.now()
await ctx.db.patch(templateId, {
+ kind: normalizedKind,
title: normalizedTitle,
body: sanitizedBody,
updatedBy: actorId,
diff --git a/convex/machines.ts b/convex/machines.ts
index db67369..12c3e51 100644
--- a/convex/machines.ts
+++ b/convex/machines.ts
@@ -65,6 +65,24 @@ function computeFingerprint(tenantId: string, companySlug: string | undefined, h
return toHex(sha256(payload))
}
+function extractCollaboratorEmail(metadata: unknown): string | null {
+ if (!metadata || typeof metadata !== "object") return null
+ const record = metadata as Record
+ const collaborator = record["collaborator"]
+ if (!collaborator || typeof collaborator !== "object") return null
+ const email = (collaborator as { email?: unknown }).email
+ if (typeof email !== "string") return null
+ const trimmed = email.trim().toLowerCase()
+ return trimmed || null
+}
+
+function matchesExistingHardware(existing: Doc<"machines">, identifiers: NormalizedIdentifiers, hostname: string): boolean {
+ const intersectsMac = existing.macAddresses.some((mac) => identifiers.macs.includes(mac))
+ const intersectsSerial = existing.serialNumbers.some((serial) => identifiers.serials.includes(serial))
+ const sameHostname = existing.hostname.trim().toLowerCase() === hostname.trim().toLowerCase()
+ return intersectsMac || intersectsSerial || sameHostname
+}
+
function hashToken(token: string) {
return toHex(sha256(token))
}
@@ -305,11 +323,24 @@ export const register = mutation({
const now = Date.now()
const metadataPatch = args.metadata && typeof args.metadata === "object" ? (args.metadata as Record) : undefined
- const existing = await ctx.db
+ let existing = await ctx.db
.query("machines")
.withIndex("by_tenant_fingerprint", (q) => q.eq("tenantId", tenantId).eq("fingerprint", fingerprint))
.first()
+ if (!existing) {
+ const collaboratorEmail = extractCollaboratorEmail(metadataPatch ?? args.metadata)
+ if (collaboratorEmail) {
+ const candidate = await ctx.db
+ .query("machines")
+ .withIndex("by_tenant_assigned_email", (q) => q.eq("tenantId", tenantId).eq("assignedUserEmail", collaboratorEmail))
+ .first()
+ if (candidate && matchesExistingHardware(candidate, identifiers, args.hostname)) {
+ existing = candidate
+ }
+ }
+ }
+
let machineId: Id<"machines">
if (existing) {
@@ -323,6 +354,7 @@ export const register = mutation({
architecture: args.os.architecture,
macAddresses: identifiers.macs,
serialNumbers: identifiers.serials,
+ fingerprint,
metadata: metadataPatch ? mergeMetadata(existing.metadata, metadataPatch) : existing.metadata,
lastHeartbeatAt: now,
updatedAt: now,
@@ -834,6 +866,28 @@ export const getContext = query({
},
})
+export const findByAuthEmail = query({
+ args: {
+ authEmail: v.string(),
+ },
+ handler: async (ctx, args) => {
+ const normalizedEmail = args.authEmail.trim().toLowerCase()
+
+ const machine = await ctx.db
+ .query("machines")
+ .withIndex("by_auth_email", (q) => q.eq("authEmail", normalizedEmail))
+ .first()
+
+ if (!machine) {
+ return null
+ }
+
+ return {
+ id: machine._id,
+ }
+ },
+})
+
export const linkAuthAccount = mutation({
args: {
machineId: v.id("machines"),
diff --git a/convex/schema.ts b/convex/schema.ts
index 7968186..70dd1ba 100644
--- a/convex/schema.ts
+++ b/convex/schema.ts
@@ -146,6 +146,7 @@ export default defineSchema({
commentTemplates: defineTable({
tenantId: v.string(),
+ kind: v.optional(v.string()),
title: v.string(),
body: v.string(),
createdBy: v.id("users"),
@@ -154,7 +155,8 @@ export default defineSchema({
updatedAt: v.number(),
})
.index("by_tenant", ["tenantId"])
- .index("by_tenant_title", ["tenantId", "title"]),
+ .index("by_tenant_title", ["tenantId", "title"])
+ .index("by_tenant_kind", ["tenantId", "kind"]),
ticketWorkSessions: defineTable({
ticketId: v.id("tickets"),
@@ -267,6 +269,7 @@ export default defineSchema({
.index("by_tenant", ["tenantId"])
.index("by_tenant_company", ["tenantId", "companyId"])
.index("by_tenant_fingerprint", ["tenantId", "fingerprint"])
+ .index("by_tenant_assigned_email", ["tenantId", "assignedUserEmail"])
.index("by_auth_email", ["authEmail"]),
machineTokens: defineTable({
diff --git a/convex/tickets.ts b/convex/tickets.ts
index 35ea9d2..cd5e12b 100644
--- a/convex/tickets.ts
+++ b/convex/tickets.ts
@@ -3,7 +3,7 @@ import type { MutationCtx, QueryCtx } from "./_generated/server";
import { ConvexError, v } from "convex/values";
import { Id, type Doc } from "./_generated/dataModel";
-import { requireStaff, requireUser } from "./rbac";
+import { requireAdmin, requireStaff, requireUser } from "./rbac";
const STAFF_ROLES = new Set(["ADMIN", "MANAGER", "AGENT"]);
const INTERNAL_STAFF_ROLES = new Set(["ADMIN", "AGENT"]);
@@ -1343,8 +1343,13 @@ export const playNext = mutation({
});
export const remove = mutation({
- args: { ticketId: v.id("tickets") },
- handler: async (ctx, { ticketId }) => {
+ args: { ticketId: v.id("tickets"), actorId: v.id("users") },
+ handler: async (ctx, { ticketId, actorId }) => {
+ const ticket = await ctx.db.get(ticketId)
+ if (!ticket) {
+ throw new ConvexError("Ticket não encontrado")
+ }
+ await requireAdmin(ctx, actorId, ticket.tenantId)
// delete comments (and attachments)
const comments = await ctx.db
.query("ticketComments")
diff --git a/package.json b/package.json
index 761be3a..903ad33 100644
--- a/package.json
+++ b/package.json
@@ -54,6 +54,7 @@
"next": "15.5.4",
"next-themes": "^0.4.6",
"pdfkit": "^0.17.2",
+ "@react-pdf/renderer": "^4.1.5",
"postcss": "^8.5.6",
"react": "19.2.0",
"react-dom": "19.2.0",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 9f57c85..b7dd1c6 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -71,6 +71,9 @@ importers:
'@radix-ui/react-tooltip':
specifier: ^1.2.8
version: 1.2.8(@types/react-dom@19.2.0(@types/react@19.2.0))(@types/react@19.2.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@react-pdf/renderer':
+ specifier: ^4.1.5
+ version: 4.3.1(react@19.2.0)
'@react-three/fiber':
specifier: ^9.3.0
version: 9.3.0(@types/react@19.2.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(three@0.180.0)
@@ -1519,6 +1522,49 @@ packages:
'@radix-ui/rect@1.1.1':
resolution: {integrity: sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==}
+ '@react-pdf/fns@3.1.2':
+ resolution: {integrity: sha512-qTKGUf0iAMGg2+OsUcp9ffKnKi41RukM/zYIWMDJ4hRVYSr89Q7e3wSDW/Koqx3ea3Uy/z3h2y3wPX6Bdfxk6g==}
+
+ '@react-pdf/font@4.0.3':
+ resolution: {integrity: sha512-N1qQDZr6phXYQOp033Hvm2nkUkx2LkszjGPbmRavs9VOYzi4sp31MaccMKptL24ii6UhBh/z9yPUhnuNe/qHwA==}
+
+ '@react-pdf/image@3.0.3':
+ resolution: {integrity: sha512-lvP5ryzYM3wpbO9bvqLZYwEr5XBDX9jcaRICvtnoRqdJOo7PRrMnmB4MMScyb+Xw10mGeIubZAAomNAG5ONQZQ==}
+
+ '@react-pdf/layout@4.4.1':
+ resolution: {integrity: sha512-GVzdlWoZWldRDzlWj3SttRXmVDxg7YfraAohwy+o9gb9hrbDJaaAV6jV3pc630Evd3K46OAzk8EFu8EgPDuVuA==}
+
+ '@react-pdf/pdfkit@4.0.4':
+ resolution: {integrity: sha512-/nITLggsPlB66bVLnm0X7MNdKQxXelLGZG6zB5acF5cCgkFwmXHnLNyxYOUD4GMOMg1HOPShXDKWrwk2ZeHsvw==}
+
+ '@react-pdf/png-js@3.0.0':
+ resolution: {integrity: sha512-eSJnEItZ37WPt6Qv5pncQDxLJRK15eaRwPT+gZoujP548CodenOVp49GST8XJvKMFt9YqIBzGBV/j9AgrOQzVA==}
+
+ '@react-pdf/primitives@4.1.1':
+ resolution: {integrity: sha512-IuhxYls1luJb7NUWy6q5avb1XrNaVj9bTNI40U9qGRuS6n7Hje/8H8Qi99Z9UKFV74bBP3DOf3L1wV2qZVgVrQ==}
+
+ '@react-pdf/reconciler@1.1.4':
+ resolution: {integrity: sha512-oTQDiR/t4Z/Guxac88IavpU2UgN7eR0RMI9DRKvKnvPz2DUasGjXfChAdMqDNmJJxxV26mMy9xQOUV2UU5/okg==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+
+ '@react-pdf/render@4.3.1':
+ resolution: {integrity: sha512-v1WAaAhQShQZGcBxfjkEThGCHVH9CSuitrZ1bIOLvB5iBKM14abYK5D6djKhWCwF6FTzYeT2WRjRMVgze/ND2A==}
+
+ '@react-pdf/renderer@4.3.1':
+ resolution: {integrity: sha512-dPKHiwGTaOsKqNWCHPYYrx8CDfAGsUnV4tvRsEu0VPGxuot1AOq/M+YgfN/Pb+MeXCTe2/lv6NvA8haUtj3tsA==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+
+ '@react-pdf/stylesheet@6.1.1':
+ resolution: {integrity: sha512-Iyw0A3wRIeQLN4EkaKf8yF9MvdMxiZ8JjoyzLzDHSxnKYoOA4UGu84veCb8dT9N8MxY5x7a0BUv/avTe586Plg==}
+
+ '@react-pdf/textkit@6.0.0':
+ resolution: {integrity: sha512-fDt19KWaJRK/n2AaFoVm31hgGmpygmTV7LsHGJNGZkgzXcFyLsx+XUl63DTDPH3iqxj3xUX128t104GtOz8tTw==}
+
+ '@react-pdf/types@2.9.1':
+ resolution: {integrity: sha512-5GoCgG0G5NMgpPuHbKG2xcVRQt7+E5pg3IyzVIIozKG3nLcnsXW4zy25vG1ZBQA0jmo39q34au/sOnL/0d1A4w==}
+
'@react-three/fiber@9.3.0':
resolution: {integrity: sha512-myPe3YL/C8+Eq939/4qIVEPBW/uxV0iiUbmjfwrs9sGKYDG8ib8Dz3Okq7BQt8P+0k4igedONbjXMQy84aDFmQ==}
peerDependencies:
@@ -2320,6 +2366,9 @@ packages:
'@webgpu/types@0.1.65':
resolution: {integrity: sha512-cYrHab4d6wuVvDW5tdsfI6/o6vcLMDe6w2Citd1oS51Xxu2ycLCnVo4fqwujfKWijrZMInTJIKcXxteoy21nVA==}
+ abs-svg-path@0.1.1:
+ resolution: {integrity: sha512-d8XPSGjfyzlXC3Xx891DJRyZfqk5JU0BJrDQcsWomFIV1/BIzPW5HDH5iDdWpqWaav0YVIEzT1RHTwWr0FFshA==}
+
acorn-jsx@5.3.2:
resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
peerDependencies:
@@ -2453,6 +2502,9 @@ packages:
better-call@1.0.19:
resolution: {integrity: sha512-sI3GcA1SCVa3H+CDHl8W8qzhlrckwXOTKhqq3OOPXjgn5aTOMIqGY34zLY/pHA6tRRMjTUC3lz5Mi7EbDA24Kw==}
+ bidi-js@1.0.3:
+ resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==}
+
brace-expansion@1.1.12:
resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==}
@@ -2466,6 +2518,9 @@ packages:
brotli@1.3.3:
resolution: {integrity: sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==}
+ browserify-zlib@0.2.0:
+ resolution: {integrity: sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==}
+
browserslist@4.26.3:
resolution: {integrity: sha512-lAUU+02RFBuCKQPj/P6NgjlbCnLBMp4UtgTx7vNHd3XSIJF87s9a5rA3aH2yw3GS9DqZAUbOtZdCCiZeVRqt0w==}
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
@@ -2549,6 +2604,9 @@ packages:
color-name@1.1.4:
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
+ color-string@1.9.1:
+ resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==}
+
concat-map@0.0.1:
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
@@ -2746,6 +2804,9 @@ packages:
electron-to-chromium@1.5.234:
resolution: {integrity: sha512-RXfEp2x+VRYn8jbKfQlRImzoJU01kyDvVPBmG39eU2iuRVhuS6vQNocB8J0/8GrIMLnPzgz4eW6WiRnJkTuNWg==}
+ emoji-regex-xs@1.0.0:
+ resolution: {integrity: sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg==}
+
emoji-regex@9.2.2:
resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
@@ -2936,6 +2997,10 @@ packages:
eventemitter3@4.0.7:
resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==}
+ events@3.3.0:
+ resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==}
+ engines: {node: '>=0.8.x'}
+
expect-type@1.2.2:
resolution: {integrity: sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==}
engines: {node: '>=12.0.0'}
@@ -3108,9 +3173,18 @@ packages:
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
engines: {node: '>= 0.4'}
+ hsl-to-hex@1.0.0:
+ resolution: {integrity: sha512-K6GVpucS5wFf44X0h2bLVRDsycgJmf9FF2elg+CrqD8GcFU8c6vYhgXn8NjUkFCwj+xDFb70qgLbTUm6sxwPmA==}
+
+ hsl-to-rgb-for-reals@1.1.1:
+ resolution: {integrity: sha512-LgOWAkrN0rFaQpfdWBQlv/VhkOxb5AsBjk6NQVx4yEzWS923T07X0M1Y0VNko2H52HeSpZrZNNMJ0aFqsdVzQg==}
+
htmlparser2@8.0.2:
resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==}
+ hyphen@1.10.6:
+ resolution: {integrity: sha512-fXHXcGFTXOvZTSkPJuGOQf5Lv5T/R2itiiCVPg9LxAje5D00O0pP83yJShFq5V89Ly//Gt6acj7z8pbBr34stw==}
+
ieee754@1.2.1:
resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
@@ -3130,6 +3204,9 @@ packages:
resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
engines: {node: '>=0.8.19'}
+ inherits@2.0.4:
+ resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
+
internal-slot@1.1.0:
resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==}
engines: {node: '>= 0.4'}
@@ -3142,6 +3219,9 @@ packages:
resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==}
engines: {node: '>= 0.4'}
+ is-arrayish@0.3.4:
+ resolution: {integrity: sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==}
+
is-async-function@2.1.1:
resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==}
engines: {node: '>= 0.4'}
@@ -3233,6 +3313,9 @@ packages:
resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==}
engines: {node: '>= 0.4'}
+ is-url@1.2.4:
+ resolution: {integrity: sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==}
+
is-weakmap@2.0.2:
resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==}
engines: {node: '>= 0.4'}
@@ -3260,6 +3343,9 @@ packages:
peerDependencies:
react: ^19.0.0
+ jay-peg@1.1.1:
+ resolution: {integrity: sha512-D62KEuBxz/ip2gQKOEhk/mx14o7eiFRaU+VNNSP4MOiIkwb/D6B3G1Mfas7C/Fit8EsSV2/IWjZElx/Gs6A4ww==}
+
jiti@2.6.1:
resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==}
hasBin: true
@@ -3438,6 +3524,9 @@ packages:
mdurl@2.0.0:
resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==}
+ media-engine@1.0.3:
+ resolution: {integrity: sha512-aa5tG6sDoK+k70B9iEX1NeyfT8ObCKhNDs6lJVpwF6r8vhUfuKMslIcirq6HIUYuuUYLefcEQOn9bSBOvawtwg==}
+
merge2@1.4.1:
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
engines: {node: '>= 8'}
@@ -3520,6 +3609,9 @@ packages:
node-releases@2.0.23:
resolution: {integrity: sha512-cCmFDMSm26S6tQSDpBCg/NR8NENrVPhAJSf+XbxBG4rPFaaonlEoE9wHQmun+cls499TQGSb7ZyPBRlzgKfpeg==}
+ normalize-svg-path@1.1.0:
+ resolution: {integrity: sha512-r9KHKG2UUeB5LoTouwDzBy2VxXlHsiM6fyLQvnJa0S5hrhzqElH/CH7TUGhT1fVvIYBIKf3OpY4YJ4CK+iaqHg==}
+
nypm@0.6.2:
resolution: {integrity: sha512-7eM+hpOtrKrBDCh7Ypu2lJ9Z7PNZBdi/8AT3AX8xoCj43BBVHD0hPSTEvMtkMpfs8FCqBGhxB+uToIQimA111g==}
engines: {node: ^14.16.0 || >=16.10.0}
@@ -3582,6 +3674,9 @@ packages:
pako@0.2.9:
resolution: {integrity: sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==}
+ pako@1.0.11:
+ resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==}
+
parent-module@1.0.1:
resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
engines: {node: '>=6'}
@@ -3589,6 +3684,9 @@ packages:
parse-srcset@1.0.2:
resolution: {integrity: sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q==}
+ parse-svg-path@0.1.2:
+ resolution: {integrity: sha512-JyPSBnkTJ0AI8GGJLfMXvKq42cj5c006fnLz6fXy6zfoVjJizi8BNTpu8on8ziI1cKy9d9DGNuY17Ce7wuejpQ==}
+
path-exists@4.0.0:
resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
engines: {node: '>=8'}
@@ -3637,6 +3735,9 @@ packages:
resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==}
engines: {node: '>= 0.4'}
+ postcss-value-parser@4.2.0:
+ resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
+
postcss@8.4.31:
resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==}
engines: {node: ^10 || ^12 || >=14}
@@ -3746,6 +3847,9 @@ packages:
queue-microtask@1.2.3:
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
+ queue@6.0.2:
+ resolution: {integrity: sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==}
+
rc9@2.1.2:
resolution: {integrity: sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==}
@@ -3856,6 +3960,10 @@ packages:
resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==}
engines: {node: '>= 0.4'}
+ require-from-string@2.0.2:
+ resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
+ engines: {node: '>=0.10.0'}
+
resolve-from@4.0.0:
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
engines: {node: '>=4'}
@@ -3897,6 +4005,9 @@ packages:
resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==}
engines: {node: '>=0.4'}
+ safe-buffer@5.2.1:
+ resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
+
safe-push-apply@1.0.0:
resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==}
engines: {node: '>= 0.4'}
@@ -3911,6 +4022,9 @@ packages:
scheduler@0.25.0:
resolution: {integrity: sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==}
+ scheduler@0.25.0-rc-603e6108-20241029:
+ resolution: {integrity: sha512-pFwF6H1XrSdYYNLfOcGlM28/j8CGLu8IvdrxqhjWULe2bPcKiKW4CV+OWqR/9fT52mywx65l7ysNkjLKBda7eA==}
+
scheduler@0.27.0:
resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==}
@@ -3969,6 +4083,9 @@ packages:
siginfo@2.0.0:
resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==}
+ simple-swizzle@0.2.4:
+ resolution: {integrity: sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==}
+
sonner@2.0.7:
resolution: {integrity: sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w==}
peerDependencies:
@@ -4015,6 +4132,9 @@ packages:
resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==}
engines: {node: '>= 0.4'}
+ string_decoder@1.3.0:
+ resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
+
strip-bom@3.0.0:
resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==}
engines: {node: '>=4'}
@@ -4049,6 +4169,9 @@ packages:
peerDependencies:
react: '>=17.0'
+ svg-arc-to-cubic-bezier@3.2.0:
+ resolution: {integrity: sha512-djbJ/vZKZO+gPoSDThGNpKDO+o+bAeA4XQKovvkNCqnIS2t+S4qnLAGQhyyrulhCFRl1WWzAp0wUDV8PpTVU3g==}
+
tailwind-merge@3.3.1:
resolution: {integrity: sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g==}
@@ -4220,6 +4343,9 @@ packages:
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+ util-deprecate@1.0.2:
+ resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
+
vaul@1.1.2:
resolution: {integrity: sha512-ZFkClGpWyI2WUQjdLJ/BaGuV6AVQiJ3uELGk3OYtP+B6yCO7Cmn9vPFXVJkRaGkOJu3m8bQMgtyzNHixULceQA==}
peerDependencies:
@@ -4229,6 +4355,10 @@ packages:
victory-vendor@36.9.2:
resolution: {integrity: sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==}
+ vite-compatible-readable-stream@3.6.1:
+ resolution: {integrity: sha512-t20zYkrSf868+j/p31cRIGN28Phrjm3nRSLR2fyc2tiWi4cZGVdv68yNlwnIINTkMTmPoMiSlc0OadaO7DXZaQ==}
+ engines: {node: '>= 6'}
+
vite-node@2.1.9:
resolution: {integrity: sha512-AM9aQ/IPrW/6ENLQg3AGY4K1N2TGZdR5e4gu/MmmR2xR3Ll1+dib+nook92g4TV3PXVyeyxdWwtaCAiUL0hMxA==}
engines: {node: ^18.0.0 || >=20.0.0}
@@ -4374,6 +4504,9 @@ packages:
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
engines: {node: '>=10'}
+ yoga-layout@3.2.1:
+ resolution: {integrity: sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ==}
+
zod@4.1.11:
resolution: {integrity: sha512-WPsqwxITS2tzx1bzhIKsEs19ABD5vmCVa4xBo2tq/SrV4RNZtfws1EnCWQXM6yh8bD08a1idvkB5MZSBiZsjwg==}
@@ -5554,6 +5687,107 @@ snapshots:
'@radix-ui/rect@1.1.1': {}
+ '@react-pdf/fns@3.1.2': {}
+
+ '@react-pdf/font@4.0.3':
+ dependencies:
+ '@react-pdf/pdfkit': 4.0.4
+ '@react-pdf/types': 2.9.1
+ fontkit: 2.0.4
+ is-url: 1.2.4
+
+ '@react-pdf/image@3.0.3':
+ dependencies:
+ '@react-pdf/png-js': 3.0.0
+ jay-peg: 1.1.1
+
+ '@react-pdf/layout@4.4.1':
+ dependencies:
+ '@react-pdf/fns': 3.1.2
+ '@react-pdf/image': 3.0.3
+ '@react-pdf/primitives': 4.1.1
+ '@react-pdf/stylesheet': 6.1.1
+ '@react-pdf/textkit': 6.0.0
+ '@react-pdf/types': 2.9.1
+ emoji-regex-xs: 1.0.0
+ queue: 6.0.2
+ yoga-layout: 3.2.1
+
+ '@react-pdf/pdfkit@4.0.4':
+ dependencies:
+ '@babel/runtime': 7.28.4
+ '@react-pdf/png-js': 3.0.0
+ browserify-zlib: 0.2.0
+ crypto-js: 4.2.0
+ fontkit: 2.0.4
+ jay-peg: 1.1.1
+ linebreak: 1.1.0
+ vite-compatible-readable-stream: 3.6.1
+
+ '@react-pdf/png-js@3.0.0':
+ dependencies:
+ browserify-zlib: 0.2.0
+
+ '@react-pdf/primitives@4.1.1': {}
+
+ '@react-pdf/reconciler@1.1.4(react@19.2.0)':
+ dependencies:
+ object-assign: 4.1.1
+ react: 19.2.0
+ scheduler: 0.25.0-rc-603e6108-20241029
+
+ '@react-pdf/render@4.3.1':
+ dependencies:
+ '@babel/runtime': 7.28.4
+ '@react-pdf/fns': 3.1.2
+ '@react-pdf/primitives': 4.1.1
+ '@react-pdf/textkit': 6.0.0
+ '@react-pdf/types': 2.9.1
+ abs-svg-path: 0.1.1
+ color-string: 1.9.1
+ normalize-svg-path: 1.1.0
+ parse-svg-path: 0.1.2
+ svg-arc-to-cubic-bezier: 3.2.0
+
+ '@react-pdf/renderer@4.3.1(react@19.2.0)':
+ dependencies:
+ '@babel/runtime': 7.28.4
+ '@react-pdf/fns': 3.1.2
+ '@react-pdf/font': 4.0.3
+ '@react-pdf/layout': 4.4.1
+ '@react-pdf/pdfkit': 4.0.4
+ '@react-pdf/primitives': 4.1.1
+ '@react-pdf/reconciler': 1.1.4(react@19.2.0)
+ '@react-pdf/render': 4.3.1
+ '@react-pdf/types': 2.9.1
+ events: 3.3.0
+ object-assign: 4.1.1
+ prop-types: 15.8.1
+ queue: 6.0.2
+ react: 19.2.0
+
+ '@react-pdf/stylesheet@6.1.1':
+ dependencies:
+ '@react-pdf/fns': 3.1.2
+ '@react-pdf/types': 2.9.1
+ color-string: 1.9.1
+ hsl-to-hex: 1.0.0
+ media-engine: 1.0.3
+ postcss-value-parser: 4.2.0
+
+ '@react-pdf/textkit@6.0.0':
+ dependencies:
+ '@react-pdf/fns': 3.1.2
+ bidi-js: 1.0.3
+ hyphen: 1.10.6
+ unicode-properties: 1.4.1
+
+ '@react-pdf/types@2.9.1':
+ dependencies:
+ '@react-pdf/font': 4.0.3
+ '@react-pdf/primitives': 4.1.1
+ '@react-pdf/stylesheet': 6.1.1
+
'@react-three/fiber@9.3.0(@types/react@19.2.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(three@0.180.0)':
dependencies:
'@babel/runtime': 7.28.4
@@ -6319,6 +6553,8 @@ snapshots:
'@webgpu/types@0.1.65': {}
+ abs-svg-path@0.1.1: {}
+
acorn-jsx@5.3.2(acorn@8.15.0):
dependencies:
acorn: 8.15.0
@@ -6467,6 +6703,10 @@ snapshots:
set-cookie-parser: 2.7.1
uncrypto: 0.1.3
+ bidi-js@1.0.3:
+ dependencies:
+ require-from-string: 2.0.2
+
brace-expansion@1.1.12:
dependencies:
balanced-match: 1.0.2
@@ -6484,6 +6724,10 @@ snapshots:
dependencies:
base64-js: 1.5.1
+ browserify-zlib@0.2.0:
+ dependencies:
+ pako: 1.0.11
+
browserslist@4.26.3:
dependencies:
baseline-browser-mapping: 2.8.16
@@ -6576,6 +6820,11 @@ snapshots:
color-name@1.1.4: {}
+ color-string@1.9.1:
+ dependencies:
+ color-name: 1.1.4
+ simple-swizzle: 0.2.4
+
concat-map@0.0.1: {}
confbox@0.2.2: {}
@@ -6746,6 +6995,8 @@ snapshots:
electron-to-chromium@1.5.234: {}
+ emoji-regex-xs@1.0.0: {}
+
emoji-regex@9.2.2: {}
empathic@2.0.0: {}
@@ -7121,6 +7372,8 @@ snapshots:
eventemitter3@4.0.7: {}
+ events@3.3.0: {}
+
expect-type@1.2.2: {}
exsolve@1.0.7: {}
@@ -7301,6 +7554,12 @@ snapshots:
dependencies:
function-bind: 1.1.2
+ hsl-to-hex@1.0.0:
+ dependencies:
+ hsl-to-rgb-for-reals: 1.1.1
+
+ hsl-to-rgb-for-reals@1.1.1: {}
+
htmlparser2@8.0.2:
dependencies:
domelementtype: 2.3.0
@@ -7308,6 +7567,8 @@ snapshots:
domutils: 3.2.2
entities: 4.5.0
+ hyphen@1.10.6: {}
+
ieee754@1.2.1: {}
ignore@5.3.2: {}
@@ -7321,6 +7582,8 @@ snapshots:
imurmurhash@0.1.4: {}
+ inherits@2.0.4: {}
+
internal-slot@1.1.0:
dependencies:
es-errors: 1.3.0
@@ -7335,6 +7598,8 @@ snapshots:
call-bound: 1.0.4
get-intrinsic: 1.3.0
+ is-arrayish@0.3.4: {}
+
is-async-function@2.1.1:
dependencies:
async-function: 1.0.0
@@ -7432,6 +7697,8 @@ snapshots:
dependencies:
which-typed-array: 1.1.19
+ is-url@1.2.4: {}
+
is-weakmap@2.0.2: {}
is-weakref@1.1.1:
@@ -7463,6 +7730,10 @@ snapshots:
transitivePeerDependencies:
- '@types/react'
+ jay-peg@1.1.1:
+ dependencies:
+ restructure: 3.0.2
+
jiti@2.6.1: {}
jose@6.1.0: {}
@@ -7610,6 +7881,8 @@ snapshots:
mdurl@2.0.0: {}
+ media-engine@1.0.3: {}
+
merge2@1.4.1: {}
meshoptimizer@0.22.0: {}
@@ -7677,6 +7950,10 @@ snapshots:
node-releases@2.0.23: {}
+ normalize-svg-path@1.1.0:
+ dependencies:
+ svg-arc-to-cubic-bezier: 3.2.0
+
nypm@0.6.2:
dependencies:
citty: 0.1.6
@@ -7756,12 +8033,16 @@ snapshots:
pako@0.2.9: {}
+ pako@1.0.11: {}
+
parent-module@1.0.1:
dependencies:
callsites: 3.1.0
parse-srcset@1.0.2: {}
+ parse-svg-path@0.1.2: {}
+
path-exists@4.0.0: {}
path-key@3.1.1: {}
@@ -7800,6 +8081,8 @@ snapshots:
possible-typed-array-names@1.1.0: {}
+ postcss-value-parser@4.2.0: {}
+
postcss@8.4.31:
dependencies:
nanoid: 3.3.11
@@ -7948,6 +8231,10 @@ snapshots:
queue-microtask@1.2.3: {}
+ queue@6.0.2:
+ dependencies:
+ inherits: 2.0.4
+
rc9@2.1.2:
dependencies:
defu: 6.1.4
@@ -8066,6 +8353,8 @@ snapshots:
gopd: 1.2.0
set-function-name: 2.0.2
+ require-from-string@2.0.2: {}
+
resolve-from@4.0.0: {}
resolve-pkg-maps@1.0.0: {}
@@ -8130,6 +8419,8 @@ snapshots:
has-symbols: 1.1.0
isarray: 2.0.5
+ safe-buffer@5.2.1: {}
+
safe-push-apply@1.0.0:
dependencies:
es-errors: 1.3.0
@@ -8152,6 +8443,8 @@ snapshots:
scheduler@0.25.0: {}
+ scheduler@0.25.0-rc-603e6108-20241029: {}
+
scheduler@0.27.0: {}
semver@6.3.1: {}
@@ -8248,6 +8541,10 @@ snapshots:
siginfo@2.0.0: {}
+ simple-swizzle@0.2.4:
+ dependencies:
+ is-arrayish: 0.3.4
+
sonner@2.0.7(react-dom@19.2.0(react@19.2.0))(react@19.2.0):
dependencies:
react: 19.2.0
@@ -8316,6 +8613,10 @@ snapshots:
define-properties: 1.2.1
es-object-atoms: 1.1.1
+ string_decoder@1.3.0:
+ dependencies:
+ safe-buffer: 5.2.1
+
strip-bom@3.0.0: {}
strip-json-comments@3.1.1: {}
@@ -8335,6 +8636,8 @@ snapshots:
dependencies:
react: 19.2.0
+ svg-arc-to-cubic-bezier@3.2.0: {}
+
tailwind-merge@3.3.1: {}
tailwindcss@4.1.14: {}
@@ -8521,6 +8824,8 @@ snapshots:
dependencies:
react: 19.2.0
+ util-deprecate@1.0.2: {}
+
vaul@1.1.2(@types/react-dom@19.2.0(@types/react@19.2.0))(@types/react@19.2.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0):
dependencies:
'@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.2.0(@types/react@19.2.0))(@types/react@19.2.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
@@ -8547,6 +8852,12 @@ snapshots:
d3-time: 3.1.0
d3-timer: 3.0.1
+ vite-compatible-readable-stream@3.6.1:
+ dependencies:
+ inherits: 2.0.4
+ string_decoder: 1.3.0
+ util-deprecate: 1.0.2
+
vite-node@2.1.9(@types/node@20.19.19)(lightningcss@1.30.1):
dependencies:
cac: 6.7.14
@@ -8684,6 +8995,8 @@ snapshots:
yocto-queue@0.1.0: {}
+ yoga-layout@3.2.1: {}
+
zod@4.1.11: {}
zustand@5.0.8(@types/react@19.2.0)(react@19.2.0)(use-sync-external-store@1.6.0(react@19.2.0)):
diff --git a/src/app/api/machines/session/route.ts b/src/app/api/machines/session/route.ts
index 11f54a0..a6412d0 100644
--- a/src/app/api/machines/session/route.ts
+++ b/src/app/api/machines/session/route.ts
@@ -1,4 +1,4 @@
-import { NextResponse } from "next/server"
+import { NextRequest, NextResponse } from "next/server"
import { cookies } from "next/headers"
import { ConvexHttpClient } from "convex/browser"
@@ -25,33 +25,55 @@ function decodeMachineCookie(value: string) {
}
}
-export async function GET() {
+function encodeMachineCookie(payload: {
+ machineId: string
+ persona: string | null
+ assignedUserId: string | null
+ assignedUserEmail: string | null
+ assignedUserName: string | null
+ assignedUserRole: string | null
+}) {
+ return Buffer.from(JSON.stringify(payload)).toString("base64url")
+}
+
+export async function GET(request: NextRequest) {
const session = await assertAuthenticatedSession()
if (!session || session.user?.role !== "machine") {
return NextResponse.json({ error: "Sessão de máquina não encontrada." }, { status: 403 })
}
- const cookieStore = await cookies()
- const cookieValue = cookieStore.get(MACHINE_CTX_COOKIE)?.value
- if (!cookieValue) {
- return NextResponse.json({ error: "Contexto da máquina ausente." }, { status: 404 })
- }
-
- const decoded = decodeMachineCookie(cookieValue)
- if (!decoded?.machineId) {
- return NextResponse.json({ error: "Contexto da máquina inválido." }, { status: 400 })
- }
-
const convexUrl = env.NEXT_PUBLIC_CONVEX_URL
if (!convexUrl) {
return NextResponse.json({ error: "Convex não configurado." }, { status: 500 })
}
const client = new ConvexHttpClient(convexUrl)
+ const cookieStore = await cookies()
+ const cookieValue = cookieStore.get(MACHINE_CTX_COOKIE)?.value ?? null
+
+ const decoded = cookieValue ? decodeMachineCookie(cookieValue) : null
+ let machineId: Id<"machines"> | null = decoded?.machineId ? (decoded.machineId as Id<"machines">) : null
+
+ if (!machineId) {
+ try {
+ const lookup = (await client.query(api.machines.findByAuthEmail, {
+ authEmail: session.user.email.toLowerCase(),
+ })) as { id: string } | null
+
+ if (!lookup?.id) {
+ return NextResponse.json({ error: "Máquina não vinculada à sessão atual." }, { status: 404 })
+ }
+
+ machineId = lookup.id as Id<"machines">
+ } catch (error) {
+ console.error("[machines.session] Falha ao localizar máquina por e-mail", error)
+ return NextResponse.json({ error: "Não foi possível localizar a máquina." }, { status: 500 })
+ }
+ }
try {
const context = (await client.query(api.machines.getContext, {
- machineId: decoded.machineId as Id<"machines">,
+ machineId,
})) as {
id: string
tenantId: string
@@ -66,10 +88,32 @@ export async function GET() {
authEmail: string | null
}
- return NextResponse.json({
+ const responsePayload = {
+ machineId: context.id,
+ persona: context.persona,
+ assignedUserId: context.assignedUserId,
+ assignedUserEmail: context.assignedUserEmail,
+ assignedUserName: context.assignedUserName,
+ assignedUserRole: context.assignedUserRole,
+ }
+
+ const response = NextResponse.json({
machine: context,
- cookie: decoded,
+ cookie: responsePayload,
})
+
+ const isSecure = request.nextUrl.protocol === "https:"
+ response.cookies.set({
+ name: MACHINE_CTX_COOKIE,
+ value: encodeMachineCookie(responsePayload),
+ httpOnly: true,
+ sameSite: "lax",
+ secure: isSecure,
+ path: "/",
+ maxAge: 60 * 60 * 24 * 30,
+ })
+
+ return response
} catch (error) {
console.error("[machines.session] Falha ao obter contexto da máquina", error)
return NextResponse.json({ error: "Falha ao obter contexto da máquina." }, { status: 500 })
diff --git a/src/app/api/machines/sessions/route.ts b/src/app/api/machines/sessions/route.ts
index 756d184..de17f21 100644
--- a/src/app/api/machines/sessions/route.ts
+++ b/src/app/api/machines/sessions/route.ts
@@ -55,12 +55,13 @@ export async function POST(request: Request) {
assignedUserName: session.machine.assignedUserName,
assignedUserRole: session.machine.assignedUserRole,
}
+ const isSecure = new URL(request.url).protocol === "https:"
response.cookies.set({
name: "machine_ctx",
value: Buffer.from(JSON.stringify(machineCookiePayload)).toString("base64url"),
httpOnly: true,
sameSite: "lax",
- secure: true,
+ secure: isSecure,
path: "/",
maxAge: 60 * 60 * 24 * 30,
})
diff --git a/src/app/api/tickets/[id]/export/pdf/route.ts b/src/app/api/tickets/[id]/export/pdf/route.ts
index a27665a..0373455 100644
--- a/src/app/api/tickets/[id]/export/pdf/route.ts
+++ b/src/app/api/tickets/[id]/export/pdf/route.ts
@@ -1,13 +1,6 @@
-import { NextResponse } from "next/server"
-// Use the standalone build to avoid AFM filesystem lookups
-// and ensure compatibility in serverless/traced environments.
-// eslint-disable-next-line @typescript-eslint/ban-ts-comment
-// @ts-ignore – no ambient types for this path; declared in types/
-import PDFDocument from "pdfkit/js/pdfkit.standalone.js"
-import { format } from "date-fns"
-import fs from "fs"
import path from "path"
-import { ptBR } from "date-fns/locale"
+import fs from "fs"
+import { NextResponse } from "next/server"
import { ConvexHttpClient } from "convex/browser"
import { api } from "@/convex/_generated/api"
@@ -16,161 +9,19 @@ import { env } from "@/lib/env"
import { assertAuthenticatedSession } from "@/lib/auth-server"
import { mapTicketWithDetailsFromServer } from "@/lib/mappers/ticket"
import { DEFAULT_TENANT_ID } from "@/lib/constants"
-import { TICKET_TIMELINE_LABELS } from "@/lib/ticket-timeline-labels"
+import { renderTicketPdfBuffer } from "@/server/pdf/ticket-pdf-template"
-// Force Node.js runtime for pdfkit compatibility
export const runtime = "nodejs"
-const statusLabel: Record = {
- PENDING: "Pendente",
- AWAITING_ATTENDANCE: "Aguardando atendimento",
- PAUSED: "Pausado",
- RESOLVED: "Resolvido",
-}
-
-const statusColors: Record = {
- PENDING: "#64748B", // slate-500
- AWAITING_ATTENDANCE: "#0EA5E9", // sky-500
- PAUSED: "#F59E0B", // amber-500
- RESOLVED: "#10B981", // emerald-500
-}
-
-const priorityLabel: Record = {
- LOW: "Baixa",
- MEDIUM: "Média",
- HIGH: "Alta",
- URGENT: "Urgente",
- CRITICAL: "Crítica",
-}
-
-const channelLabel: Record = {
- EMAIL: "E-mail",
- PHONE: "Telefone",
- CHAT: "Chat",
- PORTAL: "Portal",
- WEB: "Portal",
- API: "API",
- SOCIAL: "Redes sociais",
- OTHER: "Outro",
-}
-
-
-function formatDateTime(date: Date | null | undefined) {
- if (!date) return "—"
- return format(date, "dd/MM/yyyy HH:mm", { locale: ptBR })
-}
-
-function htmlToPlainText(html?: string | null) {
- if (!html) return ""
- const withBreaks = html
- .replace(/<\s*br\s*\/?>/gi, "\n")
- .replace(/<\/p>/gi, "\n\n")
- const stripped = withBreaks.replace(/<[^>]+>/g, "")
- return decodeHtmlEntities(stripped).replace(/\u00A0/g, " ").trim()
-}
-
-function decodeHtmlEntities(input: string) {
- return input
- .replace(/&/g, "&")
- .replace(/</g, "<")
- .replace(/>/g, ">")
- .replace(/"/g, '"')
- .replace(/'/g, "'")
- .replace(/ /g, " ")
-}
-
-function stringifyPayload(payload: unknown): string | null {
- if (!payload) return null
- if (typeof payload === "object") {
- if (Array.isArray(payload)) {
- if (payload.length === 0) return null
- } else if (payload) {
- if (Object.keys(payload as Record).length === 0) return null
- }
- }
- if (typeof payload === "string" && payload.trim() === "") return null
+async function readLogoAsDataUrl() {
+ const logoPath = path.join(process.cwd(), "public", "raven.png")
try {
- return JSON.stringify(payload, null, 2)
- } catch {
- return String(payload)
- }
-}
-
-function formatDurationMs(ms: number | null | undefined) {
- if (!ms || ms <= 0) return null
- const totalSeconds = Math.floor(ms / 1000)
- const hours = Math.floor(totalSeconds / 3600)
- const minutes = Math.floor((totalSeconds % 3600) / 60)
- const seconds = totalSeconds % 60
- if (hours > 0) return `${hours}h ${String(minutes).padStart(2, "0")}m`
- if (minutes > 0) return `${minutes}m ${String(seconds).padStart(2, "0")}s`
- return `${seconds}s`
-}
-
-function buildTimelineMessage(type: string, payload: Record | null | undefined): string | null {
- const p = payload ?? {}
- const to = (p.toLabel as string | undefined) ?? (p.to as string | undefined)
- const assignee = (p.assigneeName as string | undefined) ?? (p.assigneeId as string | undefined)
- const queue = (p.queueName as string | undefined) ?? (p.queueId as string | undefined)
- const requester = p.requesterName as string | undefined
- const author = (p.authorName as string | undefined) ?? (p.authorId as string | undefined)
- const actor = (p.actorName as string | undefined) ?? (p.actorId as string | undefined)
- const attachmentName = p.attachmentName as string | undefined
- const subjectTo = p.to as string | undefined
- const pauseReason = (p.pauseReasonLabel as string | undefined) ?? (p.pauseReason as string | undefined)
- const pauseNote = p.pauseNote as string | undefined
- const sessionDuration = formatDurationMs((p.sessionDurationMs as number | undefined) ?? null)
- const categoryName = p.categoryName as string | undefined
- const subcategoryName = p.subcategoryName as string | undefined
-
- switch (type) {
- case "STATUS_CHANGED":
- return to ? `Status alterado para ${to}` : "Status alterado"
- case "ASSIGNEE_CHANGED":
- return assignee ? `Responsável alterado para ${assignee}` : "Responsável alterado"
- case "QUEUE_CHANGED":
- return queue ? `Fila alterada para ${queue}` : "Fila alterada"
- case "PRIORITY_CHANGED":
- return to ? `Prioridade alterada para ${to}` : "Prioridade alterada"
- case "CREATED":
- return requester ? `Criado por ${requester}` : "Criado"
- case "COMMENT_ADDED":
- return author ? `Comentário adicionado por ${author}` : "Comentário adicionado"
- case "COMMENT_EDITED": {
- const who = actor ?? author
- return who ? `Comentário editado por ${who}` : "Comentário editado"
- }
- case "SUBJECT_CHANGED":
- return subjectTo ? `Assunto alterado para "${subjectTo}"` : "Assunto alterado"
- case "SUMMARY_CHANGED":
- return "Resumo atualizado"
- case "ATTACHMENT_REMOVED":
- return attachmentName ? `Anexo removido: ${attachmentName}` : "Anexo removido"
- case "WORK_PAUSED": {
- const parts: string[] = []
- if (pauseReason) parts.push(`Motivo: ${pauseReason}`)
- if (sessionDuration) parts.push(`Tempo registrado: ${sessionDuration}`)
- if (pauseNote) parts.push(`Observação: ${pauseNote}`)
- return parts.length > 0 ? parts.join(" • ") : "Atendimento pausado"
- }
- case "WORK_STARTED":
- return "Atendimento iniciado"
- case "CATEGORY_CHANGED": {
- if (categoryName || subcategoryName) {
- return `Categoria alterada para ${categoryName ?? ""}${subcategoryName ? ` • ${subcategoryName}` : ""}`.trim()
- }
- return "Categoria removida"
- }
- case "MANAGER_NOTIFIED":
- return "Gestor notificado"
- case "VISIT_SCHEDULED":
- return "Visita agendada"
- case "CSAT_RECEIVED":
- return "CSAT recebido"
- case "CSAT_RATED":
- return "CSAT avaliado"
- default:
- return null
+ const buffer = await fs.promises.readFile(logoPath)
+ const base64 = buffer.toString("base64")
+ return `data:image/png;base64,${base64}`
+ } catch (error) {
+ console.warn("[tickets.export.pdf] Logo não encontrado, seguindo sem imagem", error)
+ return null
}
}
@@ -188,6 +39,7 @@ export async function GET(_request: Request, context: { params: Promise<{ id: st
const client = new ConvexHttpClient(convexUrl)
const tenantId = session.user.tenantId ?? DEFAULT_TENANT_ID
+
let viewerId: string | null = null
try {
const ensuredUser = await client.mutation(api.users.ensureUser, {
@@ -199,8 +51,8 @@ export async function GET(_request: Request, context: { params: Promise<{ id: st
})
viewerId = ensuredUser?._id ?? null
} catch (error) {
- console.error("Failed to synchronize user with Convex for PDF export", error)
- return NextResponse.json({ error: "Falha ao sincronizar usuário com Convex" }, { status: 500 })
+ console.error("[tickets.export.pdf] Falha ao sincronizar usuário no Convex", error)
+ return NextResponse.json({ error: "Não foi possível preparar a exportação" }, { status: 500 })
}
if (!viewerId) {
@@ -215,12 +67,8 @@ export async function GET(_request: Request, context: { params: Promise<{ id: st
viewerId: viewerId as unknown as Id<"users">,
})
} catch (error) {
- console.error("Failed to load ticket from Convex for PDF export", error, {
- tenantId,
- ticketId,
- viewerId,
- })
- return NextResponse.json({ error: "Falha ao carregar ticket no Convex" }, { status: 500 })
+ console.error("[tickets.export.pdf] Falha ao carregar ticket", error, { tenantId, ticketId, viewerId })
+ return NextResponse.json({ error: "Não foi possível carregar o ticket" }, { status: 500 })
}
if (!ticketRaw) {
@@ -228,284 +76,20 @@ export async function GET(_request: Request, context: { params: Promise<{ id: st
}
const ticket = mapTicketWithDetailsFromServer(ticketRaw)
- const doc = new PDFDocument({ size: "A4", margin: 56 })
- const chunks: Buffer[] = []
- doc.on("data", (chunk: unknown) => {
- const buf = typeof chunk === "string" ? Buffer.from(chunk) : (chunk as Buffer)
- chunks.push(buf)
- })
-
- const pdfBufferPromise = new Promise((resolve, reject) => {
- doc.on("end", () => resolve(Buffer.concat(chunks)))
- doc.on("error", reject)
- })
-
- // Register custom fonts (Inter) if available
+ const logoDataUrl = await readLogoAsDataUrl()
try {
- const pubRegular = path.join(process.cwd(), "public", "fonts", "Inter-Regular.ttf")
- const pubBold = path.join(process.cwd(), "public", "fonts", "Inter-Bold.ttf")
- const fontRegular = fs.existsSync(pubRegular)
- ? pubRegular
- : path.join(process.cwd(), "Inter,Manrope", "Inter", "static", "Inter_24pt-Regular.ttf")
- const fontBold = fs.existsSync(pubBold)
- ? pubBold
- : path.join(process.cwd(), "Inter,Manrope", "Inter", "static", "Inter_24pt-Bold.ttf")
- const D = doc as unknown as {
- registerFont?: (name: string, src: string) => void
- _fontFamilies?: Record
- roundedRect?: (x: number, y: number, w: number, h: number, r: number) => void
- }
- if (fs.existsSync(fontRegular)) {
- D.registerFont?.("Inter", fontRegular)
- }
- if (fs.existsSync(fontBold)) {
- D.registerFont?.("Inter-Bold", fontBold)
- }
- } catch {}
-
- const D = doc as unknown as { _fontFamilies?: Record; roundedRect?: (x:number,y:number,w:number,h:number,r:number)=>void }
- const hasInter = Boolean(D._fontFamilies && (D._fontFamilies as Record)["Inter-Bold"])
-
- // Header with logo and brand bar
- try {
- const logoPath = path.join(process.cwd(), "public", "raven.png")
- if (fs.existsSync(logoPath)) {
- doc.image(logoPath, doc.page.margins.left, doc.y, { width: 120 })
- }
- } catch {}
- doc.moveDown(0.5)
- doc
- .fillColor("#00e8ff")
- .rect(doc.page.margins.left, doc.y, doc.page.width - doc.page.margins.left - doc.page.margins.right, 3)
- .fill()
- doc.moveDown(0.5)
-
- // Título
- doc.fillColor("#0F172A").font(hasInter ? "Inter-Bold" : "Helvetica-Bold").fontSize(18).text(`Ticket #${ticket.reference} — ${ticket.subject}`)
- doc.moveDown(0.25)
- // Linha abaixo do título
- doc
- .strokeColor("#E2E8F0")
- .moveTo(doc.page.margins.left, doc.y)
- .lineTo(doc.page.width - doc.page.margins.right, doc.y)
- .stroke()
-
- // Badge de status
- doc.moveDown(0.5)
- const statusText = statusLabel[ticket.status] ?? ticket.status
- const badgeColor = statusColors[ticket.status] ?? "#475569"
- const badgeFontSize = 10
- const badgePaddingX = 6
- const badgePaddingY = 3
- const badgeX = doc.page.margins.left
- const badgeY = doc.y
- doc.save()
- doc.font(hasInter ? "Inter-Bold" : "Helvetica-Bold").fontSize(badgeFontSize)
- const badgeTextWidth = doc.widthOfString(statusText)
- const badgeHeight = badgeFontSize + badgePaddingY * 2
- const badgeWidth = badgeTextWidth + badgePaddingX * 2
- if (typeof D.roundedRect === "function") {
- D.roundedRect(badgeX, badgeY, badgeWidth, badgeHeight, 4)
- } else {
- doc.rect(badgeX, badgeY, badgeWidth, badgeHeight)
- }
- doc.fill(badgeColor)
- doc.fillColor("#FFFFFF").text(statusText, badgeX + badgePaddingX, badgeY + badgePaddingY)
- doc.restore()
- doc.y = badgeY + badgeHeight + 8
-
- // Metadados em duas colunas
- const leftX = doc.page.margins.left
- const colGap = 24
- const colWidth = (doc.page.width - doc.page.margins.left - doc.page.margins.right - colGap) / 2
- const rightX = leftX + colWidth + colGap
- // const startY = doc.y
- const drawMeta = (x: number, lines: string[]) => {
- doc.save()
- doc.x = x
- doc.fillColor("#0F172A").font(hasInter ? "Inter" : "Helvetica").fontSize(11)
- for (const line of lines) {
- doc.text(line, { width: colWidth, lineGap: 2 })
- }
- const currY = doc.y
- doc.restore()
- return currY
- }
- const leftLines = [
- `Status: ${statusText}`,
- `Prioridade: ${priorityLabel[ticket.priority] ?? ticket.priority}`,
- `Canal: ${channelLabel[ticket.channel] ?? ticket.channel ?? "—"}`,
- `Fila: ${ticket.queue ?? "—"}`,
- ]
- const rightLines = [
- `Solicitante: ${ticket.requester.name} (${ticket.requester.email})`,
- `Responsável: ${ticket.assignee ? `${ticket.assignee.name} (${ticket.assignee.email})` : "Não atribuído"}`,
- `Criado em: ${formatDateTime(ticket.createdAt)}`,
- `Atualizado em: ${formatDateTime(ticket.updatedAt)}`,
- ]
- const leftY = drawMeta(leftX, leftLines)
- const rightY = drawMeta(rightX, rightLines)
- doc.y = Math.max(leftY, rightY)
- doc.moveDown(0.5)
-
- doc.moveDown(0.75)
- doc
- .font(hasInter ? "Inter-Bold" : "Helvetica-Bold")
- .fontSize(12)
- .text("Solicitante")
- doc
- .strokeColor("#E2E8F0")
- .moveTo(doc.page.margins.left, doc.y)
- .lineTo(doc.page.width - doc.page.margins.right, doc.y)
- .stroke()
- doc.moveDown(0.3)
- doc
- .font(hasInter ? "Inter" : "Helvetica")
- .fontSize(11)
- .text(`${ticket.requester.name} (${ticket.requester.email})`)
-
- doc.moveDown(0.5)
- doc.font(hasInter ? "Inter-Bold" : "Helvetica-Bold").fontSize(12).text("Responsável")
- doc
- .strokeColor("#E2E8F0")
- .moveTo(doc.page.margins.left, doc.y)
- .lineTo(doc.page.width - doc.page.margins.right, doc.y)
- .stroke()
- doc.moveDown(0.3)
- doc
- .font(hasInter ? "Inter" : "Helvetica")
- .fontSize(11)
- .text(ticket.assignee ? `${ticket.assignee.name} (${ticket.assignee.email})` : "Não atribuído")
-
- doc.moveDown(0.75)
- doc.font("Helvetica-Bold").fontSize(12).text("Datas")
- doc
- .strokeColor("#E2E8F0")
- .moveTo(doc.page.margins.left, doc.y)
- .lineTo(doc.page.width - doc.page.margins.right, doc.y)
- .stroke()
- doc.moveDown(0.3)
- doc
- .font("Helvetica")
- .fontSize(11)
- .text(`Criado em: ${formatDateTime(ticket.createdAt)}`)
- .moveDown(0.15)
- .text(`Atualizado em: ${formatDateTime(ticket.updatedAt)}`)
- .moveDown(0.15)
- .text(`Resolvido em: ${formatDateTime(ticket.resolvedAt ?? null)}`)
-
- if (ticket.summary) {
- doc.moveDown(0.75)
- doc.font(hasInter ? "Inter-Bold" : "Helvetica-Bold").fontSize(12).text("Resumo")
- doc
- .strokeColor("#E2E8F0")
- .moveTo(doc.page.margins.left, doc.y)
- .lineTo(doc.page.width - doc.page.margins.right, doc.y)
- .stroke()
- doc
- .font(hasInter ? "Inter" : "Helvetica")
- .fontSize(11)
- .text(ticket.summary, { align: "justify", lineGap: 2 })
- }
-
- if (ticket.description) {
- doc.moveDown(0.75)
- doc.font(hasInter ? "Inter-Bold" : "Helvetica-Bold").fontSize(12).text("Descrição")
- doc
- .strokeColor("#E2E8F0")
- .moveTo(doc.page.margins.left, doc.y)
- .lineTo(doc.page.width - doc.page.margins.right, doc.y)
- .stroke()
- doc
- .font(hasInter ? "Inter" : "Helvetica")
- .fontSize(11)
- .text(htmlToPlainText(ticket.description), { align: "justify", lineGap: 2 })
- }
-
- if (ticket.comments.length > 0) {
- doc.addPage()
- doc.font(hasInter ? "Inter-Bold" : "Helvetica-Bold").fontSize(14).text("Comentários")
- doc.moveDown(0.6)
- const commentsSorted = [...ticket.comments].sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime())
- commentsSorted.forEach((comment, index) => {
- const visibility =
- comment.visibility === "PUBLIC" ? "Público" : "Interno"
- doc
- .font(hasInter ? "Inter-Bold" : "Helvetica-Bold")
- .fontSize(11)
- .text(`${comment.author.name} • ${visibility} • ${formatDateTime(comment.createdAt)}`)
- doc.moveDown(0.15)
- const body = htmlToPlainText(comment.body)
- if (body) {
- doc
- .font(hasInter ? "Inter" : "Helvetica")
- .fontSize(11)
- .text(body, { align: "justify", lineGap: 2, indent: 6 })
- }
- if (comment.attachments.length > 0) {
- doc.moveDown(0.25)
- doc.font(hasInter ? "Inter" : "Helvetica").fontSize(10).text("Anexos:")
- comment.attachments.forEach((attachment) => {
- doc
- .font(hasInter ? "Inter" : "Helvetica")
- .fontSize(10)
- .text(`• ${attachment.name ?? attachment.id}`, { indent: 16, lineGap: 1 })
- })
- }
- if (index < commentsSorted.length - 1) {
- doc.moveDown(1)
- doc
- .strokeColor("#E2E8F0")
- .moveTo(doc.x, doc.y)
- .lineTo(doc.page.width - doc.page.margins.right, doc.y)
- .stroke()
- doc.moveDown(0.9)
- }
+ const pdfBuffer = await renderTicketPdfBuffer({ ticket, logoDataUrl })
+ const payload = pdfBuffer instanceof Uint8Array ? pdfBuffer : new Uint8Array(pdfBuffer)
+ return new NextResponse(payload as unknown as BodyInit, {
+ headers: {
+ "Content-Type": "application/pdf",
+ "Content-Disposition": `attachment; filename="ticket-${ticket.reference}.pdf"`,
+ "Cache-Control": "no-store",
+ },
})
+ } catch (error) {
+ console.error("[tickets.export.pdf] Falha ao renderizar PDF", error)
+ return NextResponse.json({ error: "Não foi possível gerar o PDF" }, { status: 500 })
}
-
- if (ticket.timeline.length > 0) {
- doc.addPage()
- doc.font(hasInter ? "Inter-Bold" : "Helvetica-Bold").fontSize(14).text("Linha do tempo")
- doc.moveDown(0.6)
- const timelineSorted = [...ticket.timeline].sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime())
- timelineSorted.forEach((event) => {
- const label = TICKET_TIMELINE_LABELS[event.type] ?? event.type
- doc
- .font(hasInter ? "Inter-Bold" : "Helvetica-Bold")
- .fontSize(11)
- .text(`${label} • ${formatDateTime(event.createdAt)}`)
- doc.moveDown(0.15)
-
- const friendly = buildTimelineMessage(event.type, event.payload)
- if (friendly) {
- doc
- .font(hasInter ? "Inter" : "Helvetica")
- .fontSize(10)
- .text(friendly, { indent: 16, lineGap: 1 })
- } else {
- const payloadText = stringifyPayload(event.payload)
- if (payloadText) {
- doc
- .font(hasInter ? "Inter" : "Helvetica")
- .fontSize(10)
- .text(payloadText, { indent: 16, lineGap: 1 })
- }
- }
- doc.moveDown(0.7)
- })
- }
-
- doc.end()
- const pdfBuffer = await pdfBufferPromise
- const pdfBytes = new Uint8Array(pdfBuffer)
-
- return new NextResponse(pdfBytes, {
- headers: {
- "Content-Type": "application/pdf",
- "Content-Disposition": `attachment; filename="ticket-${ticket.reference}.pdf"`,
- "Cache-Control": "no-store",
- },
- })
}
diff --git a/src/app/icon.png b/src/app/icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..b1c7cf7ec36e98c6fa1fd0faddcd7b66f8a2a561
GIT binary patch
literal 50823
zcmce7^;cBi_x?~KjUXW1QqtWZ4br8wBHi5#Ap%m00@6r#NP|O32uOE#4lvX(FfcP;
z@bw3Le|mq`nsx6zYu&l~+`XT(<2n1r>S(DD;8NiN0006tRYg4j0R8?F9q{3z9suCZ4q?2-T}`*B4;PEC}hg#C~9Ao^$
zy2TzW9v)KgT}RgpQmwySQ*oK$YfO7*dg6qAGG_zYg_=w|C0X!-p}#D6TP!JH9`u+v
zXn+T(s_cd^5w=u77q=r2xS-iXpO5V#ElEpb>5~=IjWn!n<{oNtCnVt#X#UDIB`Y?l
zUm7(OD^Sm^P`+{uzymCPbwDU8%ygPdZsRTkBP}TQ8+B_^c;;aT>%&*jpvgbkci7bu
zr&$(jliZpkRasbi4tCL{z1a321UzU^>>7r(gKZW&gv4n;EqL@azAOX3~f=xg?vrdABJE++YVSiQl
zu0=cNk}DodZIq)EwU&2TS37onXZYts*0HgXO%{7#p9BlhR?B8Z=@9XctCH^{Y`dD!
znhu~gX^*8U=L!rQ9y5?*K0wO~-KY-OXyG;scJ)ST7Shs%rP=3&f_n_RY`-x;K5#AZXnoZs8pqR9J4(v>EM8U-W
zHav|k@8Q+oj08JtqeMWfYAJ1~rxa4!g)l7YeOTM4u$jkqQ5Hb8nQgfZ6YNq-BR8of5PuS(r|GT{>2M(D87X+?or+gvGea~j)
zk&@`+y15}`Ot3F**Nr6+Y^(eaBs9<|h(jRAuqHRZ9I_^^hGG7~u|XbX0OIi*lsmaZ
z@B4tI`0vK?v)PMg5XA2Q$_U~qa9l;bm)+=30)a`wD@p~V1x9W7{00Gv5&y)u;JIg-
zuLD1qhXN^zj$_HfQ>QTIH9wj{+onOX9qaKQ@(BJxd_YO3zGT+RY#o?nvDfyslL77e
z$II>+o+tB1JieqDaiv)gZZyoAw!$0b0b0G6h2Nb^*lSq3rTBHPm1X^@F_wQ$PrtkY
z7U01F(rQSf2&(r`GUX!Iv_!6<1No}sEp>z3TI?-fIJfdlUWhFZ%%MFjcK6=MISD)P
zt&CluHSssXIGtJuNw|}FMB{!n{%p^`cd*AUH*_6EF`x7<%AW$wKSb<_3$k)O&B|A2
z5%Y$WP$od0cR2xS@c@9+HiV?BxW*c)8>@sviu*Fq3Co#!{WdT}yT-0;k@qy-Lc0Z=
zg7EL`i(!0NA$gI3CEv+)*JKA*U?`?0ipv%C7@)dO67R7ZeQE4k+h+d7$
z$;DOUpw8!K|I~o7@v8h6LiR)pxr>QYI{*(#y$ib)_9lzScJ*BM0MU7+pl7oDI^@LU
z1vfV2CXuS4JxDu6g_c0!-|>(oTWgp6X)V1ax8WL(b9}fX-1Hms{WD3Q@`y=yOI608
z-K(&RsE2b3;@gd~#YJz}688VZRl?g&>TTvIsx>_y`<(h(EI4?x|FIWLY|mWp^yd^w
z@Bk~8b{lsTGt4YLf6aRH0V?Y({hyGdvtZ-0!&`0oJycGb8DFqT4R5*~QJrU;t9ZcWMZ#m^DeN0Md&U8LoN_M~ALfuv{v8lXrg
zSJv>Fra}4)O^w^#W<)M`94g%c)rSSj*8MEVvtAQR65hVtpuihQFPS4nRLGy0-8%3y
znUi|6N1FHI9#A{p7`}(=tmV%Modr%yHHe?%;9=qXQ&6@}h;7hfr*2J9naM?v6c-ZD
z@31i(1uQAY?BH!`wM1FexarLxYsb0+e`+N9_vkZtN*P+(^jJhxFKNmVcx-U-;Q0TQ
zwyTk-+xW_(_6PPu0RT{z1JDd`x8r3UqD=f8rE-=6m?{1OO>1hDA!>IR)vR+&wcT#1
zoVmz@Ed%OWUh2(|1Z*l;Mvc!cUA1Ql@h$kqpi0b
ze$rf+u`#!E?5*}CoVwh07q(Yu0z9tP(;WMi7ziSS!ts`mx!p?y|F%gF6_=Qppji!G
zkUGf93-Yg*s=GI7;JSHr=8!j{iILEw%h{s=-0z^snptqDAG*|&riqdENu(3DZgHxf
z^qM0uqq^ma%F{s`JSuB7#%wA?}KQY;f&*$;Wz*%y=iLl5mERR4>_-~cpvR+VMwndiH
zL1M>;s}kaQe7VEYVEl_$k!!y{ZJGr_S(mWa@-4**D3y3?BCM!o1zGi7e|A}<=@wD6
zEK)u}h=S<4F9Grx_a56;+xv?djnwuSBQ6?a*
z87GsPCJS!RGnaz8DUb`1^Q04(Avk|d5+OIHNrGS3NBrQU=MT<&SAL-+OEbb{zpQ30
zjC2s0VXdcpbVOj6&5d~m#z>koIAt@m#V~)tZ^+VG$A#554j%B&gUE;dhaOVl#87lA
zjO}P>TXs1IK&G3{dbfYhsFk6CO#r}YABBmxdcEc%O;$U%u+kxy(r)g8
zx9l6+^QIcsNf`ZoEqIhjA=9Pw0s}a>L`Yt;`8m0!(
z7(RhGjs;Kg^`Og+%TG?BEq@v<
zMcNgu5}d|fw0r<0Mqn~C0J=7I@SFE-aM@PB{>0}cm7%kg-S^4>8Y{Xyjg4qZug$Kr
zzZ3VPivOX3>FNiMlg%6IIxzh8{vrR0rl|NJOO)u#=KXnXPEE#dF?tzm!)7ii*H8Ld
zy83CS>dbP7N9TzL+eMUpYA3R#<;l>O+hr#uOVln-K(Gvry1rGSPw{|PX=q&eP
z?DsR8N;nYj+X>rpY^WYy0pWO!I5Ue=%w?9vSc{bgLZbOK6MgAe`ZSmgIIck1z=yC=ncl%GI7tbFQ$ii|8TvqBV
zTR0d*MC|==-~M(ZQhMzM8M&n~-h8AE1FEEWj542Wch0e5DoB|>1XU|boGzm@%=cng
zbfO`l)bpJcb8JvJx?vHLt&9^fCG$7#bn!t|fVJCZX;fV4IiTwZRR`&?8@|!j5hg;B
zQQUQ^lx12=VTI)pJ*=4L0y~w)#`!4VUp9btdk1n43J)n?KFjzN6hDskpmW5zx$N)=
za%*k3j{CfR2Z<`2gXqp&SSFT>iS1xKh?4kx4$Lh_qA2%K^!4~F&(=DB4B`682+W{8
z%s7m&=?;ix$P>2ydU)dX@D0;d0}29#3zp-D?E5VCVgqPneG2X$V!Ml~!dS=03>(lp
z;@!H{i9}5`oc$de|1^V2sSKj856_^_yGObh&ER!lIUGg%7?AX1-RNo0z+GFwMzmLV6T?|$!=?AOTNxCEZ0
z2f+ON696Xn(^Rk>y+PG<>3bYyAvHT?9YN5QmDwdaAtBjdi>aS`rOybV5S?taf4GiH
z;X&KqC=(+OM8Y*HG`;AN{)Ig(218j9Q)tRHcrbzhFidg345N-
z4Rxev7cDd|Yk-zQ4P@t6d3b+aYjSRzW&rRRZ!sSJ;Qc+`CY}^k<#~ZxXU|1yHmlUb
zmf3zE%v@-UNi`=Z-;iG3SqJT>s|ZVur)gN7Vn3LXP#SGsZsw|!GVAVM3NP+vXl|hK
znw7up;RU}b-uuk2KpOE7N=1}nY843i>*cTumsB;XWPU?sLKGHZYnM}E
zbVc(4_)qQb@3ax7ml6aer~=El!O`~b`yWLoBC4-A9u4>OS>-t_Xi@Q;6a}mDAu;~>
zm$#eFb10RY+{E$?qKU)CZ|2^V=?T;vYjs~+o_)GP`i)O+^1xx8>J345&{izWFaX*8
zjDFw&cxS-xjFBqn?oS;oLkVos%x##Zd_J-MeB1HaIq3v$sv~Tb!qSDpen3s00|wTyf6J#U3ez@eg0YgD`Txk?+wee@>LY8D;@(Jr9w(@T}6;tDqa85@-fE}+SQB|_ig=;ruUB^
zU;pXkEeGm83E>WO>CyLm@ryHXGdE{O9her+N7f{e7mA{!wzY1{U})fQit>>ES6LG#
z>qFbRs{qP3sMqZ1_S9YFNIdHGXfcu#RWk<;
zEI$HD`l}}<^x)fN0N&sKTxTsSspY!cu-sENX7AVaZ$%1=*XaO88~`T4y{qjSLT&aV
zPbQs4Jc7=XM%=YNB)7<9>DFkPoR-YG7s|^tKaw*f3wzJnT)(iPBBP!^g?4%D_8b54
zKZ96RUPGinJSC3!pA$`&mf=6UPyLBveDU0kvf&H7ccf&fdPl%}Wj{crlWaX-$}!a@
z+p~YFHn>5VfvRV~-?nbnEFkgR4V%THx{xEhauvhp=({bUHuIw>4Pi8$Z-ilv*v^HQ
z7}H5PIX)%Yds;*)z%b1F8mDUq7-U{B$9-}+HsEy7=?8z)aCHp%Aq+t{99jm@dZ-lP
z>(Q*mg{3~4sYxHf_B6*d*w3_PeQXfY_2w9P8D-1&InWUhMvC^#48#3BGTUVw913zR
z8N%FeIY#ly!k#8P8gAIkCenk!O&zWgB^P+Uzo`h4WSR3^)8M8x1y^tOlI^>A
zQ++6E>#7nWR9bN{`x>BF!A6S<6c;5O`s0blT#O9L=>HWT-Q@^~2Ts?U0qDME*=GtW
z7ht;bMNpX!f_u7i*EXZ$jsR}_aySVniCY~U%mO$TfsS>4i*FJ-4^6@)yRAz^n&L2I
zzJj6s$+xXG%Q)u`$CWhQm*&@>sRC+fPwJ4}WH>V7oZZB)L4tV+Yl<_Q2g_rdmVysnd@9FCM#o_V|;Ykg;p
z(GDG5Yf1n6P2xavOm}Z`c(7DC5A=>T!QxJaEXX$@iI6UiNJ$%U9Opyd)h}53`g=)!
zN*TM3wrXG}c0l)V@(=ss#LcwZc{N;|eQH
zr=zye5a0qR<#r#|dgPcjmXN+V;m%;nbEC1)8j{l_-M=#mVEGpqt+vA@a6H82<=oTT#53a
zijUKHrdSwzd9eK*_RRG1-R)uW)<@$zXath*I%?MvL)4|H_wvc2=g3K2DIUA?@Joo7PjewY
z#EAHhFI||Rru*L^t!6J{5$XEf7Ge>=zY>%au4YPgSY3<-5s;}ycyT|jQx)*kU)D~G$#oSY_<^e&Np5Z0+^uX
zO$5MKmD0XczE-j=L^IHyHjy>v&wiv`g*ople|zLr=5>PgO%qx+-xzsrY>6X@l_-#{
z(Nz9uodk+Co}a~r
zH}2#6DMYQNF%VH`A?w+bhRiesWCQ-Bk(U8ed}VEo%SyE>woKHU&n9^0>vX4imN^=z
zbV{cz!148^LA!5+f)CZIFn@VpT1wGc9%cFA*nnq^i{-BVSnl#FwLUGSL5QZ4iz-(@
znJo*<=)OQgGF>t;9uz#TRt0a~LBhb$FpCn})^0yS(B-v6a;u6HB7-I9=(p
zQ&!>S9YFs?E~T)E%`SHliVNEMoqUxzZ3NIUlOW|!
z)>N|zO1>WXM|~V~mXuh!szg9mSEd&|2^(jK+1Dg3$xyB8s;?nl0kNlYymj=Mz~l5y
zJm48E)r)ST|7ac=#L^@x`k@qm(iNFYnuH;v|J1Ay9^&0-yITr+%M6|F@m1#zSaO;J
ztND^OPEU0Rw*Qp!*lWt--m=UL)rNrxSs80BGxv(Xmd0Fw1&2(v2H4o|A@Sy5%)hP;
zTC(P(7=zJ8XIf5Q7qQ(YS*+%Ksbml=v}YWWd(QJR5cI=Ucdo&lD9=`1b5#zANBZ}t
zv-7I&ztv1gt>2s^Nu9D%mA*OMN@~H9SH%Rpr6dNhJ#q1a1G#9O4>}2b)!eIXCj`xZ
zT1PodXEGUmp{@97fju?M?VBw)T0B}jGHnh@T|`il6%jQAgW9EHzvk3p9}bLZ%P2=Z
z+7`{6cv>$==LMx4%u>=dD@LM5+}6d`Al)rVEHZFa>zLU
zslYejiBsNK{nZh>letl)nMauxrqs3o2Er{7Nbumi*e3p>`m~_HwoG)f)2U88&t*qa
znD994)`nQXX;v3iEb3@!5_!Nu&0$*iQ2-%%=r^<~u`uT6XOp+(`>xYR*MNQsb$RLA
zPW4CmO2|p_amM4*{O8!S1*jbg)trEgt577by~C1U`Rn$E!lF}Y8{(44`1UInX;jph
z4y@R;)NeEZ9uj0o2@;yE^Dv0)^ucRT@^xx(qmPWYZ}7sl$s8>fmPM^iRiK7zrY4y!
zdAV>^NG8^6Qaj%t3FJL#r)VMJBfmJynkq-zD
zYSLzSO^SbCDaf&DQXdO|PVzC{T#=?USIt)%WHn1p&rAjHEt;0LLkUb?${MxRmkLI%NMvU(*geEFt2KG-qk7Marvy?9@U(+kUCxR@NCHYq9(
zY=2u*nctqJw)_s$w>3GZ+%!(3D0Y1R=)(Sl9)*{@c9+(02jw-U`p8_=Fo%olfNrf$
z%B*RiSROyRx|3g;j7F#1JT)m*+!@f~G)E9TlYnZLx|_k$1bosf2xofOQt84;O0e+L
z+ktg%L_b2dl+a>L<+$c$wu0$g=(4na84V+xM07;jBLp-bH;h$ATM?W~NTltgv?jIy
zhGz4M*@5~wS}vuYQ@%E>Cd*G&7nh70(KsS&gqCvO>zoZIguefHD7u?BhsK<(q
zK2bZ^?hPAAK{2^Qk@XFyh#yJcnYk{tzD8}dd`Vq`7siLxD
z&^tF{8I)$B);I0Uk?}P=C&y6Q@
zd*;DM)GFIb+y)f7M=Kf&8682vO^$kLfbw?pLqWWtdC}=ZU+t8rzGiYa(4N-~l;XJ9
z65(@h9Zrn-KzoeG&cmATN-di84B=@Zaqx1>Dx%Qazg`A3%~8c}L8LZqflWGn9X@}#
zyib13x3;U3ILrMtGwtFWZk6b~oqvPvC#_B~bJnU?mPY?G)JMr6FYk}dn8A?m7P5ae
zwDC+CBg2Jl|G>|J+a0EAsH{^Ze|wl&7PR9d6ntm;&1GKkx0|e4`K5|vGsIX)uBr3o
zji9#Pj|jPpxyiQUBs*99@$B+GEDatX^b|z3W`yUAn0Iw&!fs{vcw(AEX*281K9u{w
zev=iC)5OefdDuvd2!kV+%NNJ6dMC{&u4p(ry=dbUcgoa9IS3wa7ySlLm;NEe%OhPp
z@r$#N)tH&g4QV@hW_V)fi#EzZZNEDLoBTu+n$_mlk@9{;d8l!X+bF(d=`z0jN@I)C
zDdVfntjB0d=5K?r1h650c!BqCL{12<4s=1B^Nm;4`x-X61)sud
zD;-0O(&xB7u{!*s8Viet-wt3Vftz*erl*1N*lkNk_QUx>eZq?$#g(|842v9}!)mDB
zdUq~1bFi|!9g{ZyEsuZs1Vbr|FKIXdwp&_M*F
z4#L!zN$zUrP(rP=$0)=uA#<2wkGpr{!i;I1SCYJh4%vz{*;jq%1{N+_>pHh40{=0S
zSuoCP!k*<0_iY_*$l1ozbY<~dj9T2wYrv~c_~g9v<1{u-^QrRT6RYW|Jdh(9cj!)B
zo7{fmoG_Ywc3Z4O+QFkef|5g-u<)9kYu&Ck%Ep4`Q|syW3l|BzngVx_!_Y@1pGm)x^+}TEJ3Zr1
zcCnjJZoBlc*?o;~ZUxrKpHj~lowXm9WU2z!@gCVg7NM)dZhJDO9CN=sw#vEYYLDhKgq8s|_$49Ig6Xe+`pB8(@q1IFod85FkKbkn#3N{W*OxQV@3JZjS_b_(GWmo7Gn(x<)~oxev&;(
z+hd`iY9TpL2JjQlTnsHPAxqf!ERB;|5^QjC+mYfYt^;I
zO8KNu77px)!Mj~|w`1R$VQnk`E+6_ZEt~gtWwvh@Ko%eM?XU@Jl8pq^t$**BN!(an
zSH0WgS}W2}z(l0zN^&0**?B*zMMVV^auYvouBZMQyE1drdkDb7_(BNne
zHtbsdRl?-9T7IU&7q-N8%%O@UTwT|^m(U?}xt(nZ6fUL+sS>9DIfLf%7GId}qK#m>
z^)@skdqQzf7Gc?NeX39vmj|UCMTFpJASH@>z)n6#koK=a7e9~r8Lt1$>)|wa^i0}Hla_o`s`OIkDv8H
zmowqXuWJ0GlqD~V1{oh?mfjOC%a2f;ub1r0w4^*Ukw^s9G>=1WfRXXcom5e`+le%N
z5t5q|`!%p!E38a?C9(Q|qd7iO#L7h1($?;zVn<+S#P;Zw$6*USW{^S91@)D5@D=MV
z!^;ZUB*dZ#;c=ur`b0C)9rhtaWhTZnWHPZ4Koj>!^W6^DRj9+Sda6fY8nMYIoufAt
zx++k#-!9sv?@%-L=Ut$Q{g$n1YFC}%O@f_X`L~_34MQT226jGr9uP*AVR?>4hV+W$
z<5Mn&H$7N!obTr{G)FBr{8?@b}#O=s7crIx97en}q%AUuSA`($VC
zYr47l;>@l|#;lC{0tjv5bVhy0v$;)YR>B(6x+AdtHYf9>dm&fMKrGBB=p2
zN{B96@DYAf(@E$|D?#gaL_rrXWA-UI=0?*BOxVy$NJ7+&0{-53O}PT-faQXsM~M?u
zy_4mPjLRvHUb_}(9Fs7Pe(qBfesz{!{^`+<9xfZ$W8h~6)6AcDE?w57;V&^9s)I1#
z&A(z*H9uVi?~WZnPtpGP9MwLR@hnBJtK5pJa4X~7PsXnNu!XgH1hmfC|}2g
z;Kk0Xpe%oPcFgM!FPEicW@aAMFg~sh`Qh4~Laa+lSK(G$6e@AcWP%tAjci*aj|~F1
zKDt{0!m)*dk(7-x&cWuf=ow?Mgu57GT`_|EFD~`
z3bd}nP>H?1@*q+?w3jo*E}T_i<;+?xQ;C`%(Xv}jea=8SW>r`t`%Mfb#{H)eGtBd-KcOAQ#yF54-72=@<
zjQyd*clX4hjUYq$Xpb*@FD0Zu;ISerS~(YC9h^78{O=c3SK(*daP%-ldY)-Wjbw8xd)r@<-Z5b?@xsh-nyS4p6Q
zqW#n(m~QVgOG|h=u}(Ihc79-|yLo&K1p<>fuYpDy8KUrw$OIh+5ivdWIf9s%n?-T$
z58kE1kdAbs`Vkx4x5shXItUs(Xee~Mk;@%b|Y~(Q-i`33Xf1uIbm#2UKbj)pk^t}v-kW8<9<0rrB0_p?~
z6<1v-tGtxS(PPwM-zz;`2dF2akS^ibzv4(*7!ba3ub+tZe@r_=|Puo$Ta#_<_
zadIspGqNd7jL;wgYBIxgsy=k8(SjRFtbL;lm(o)3%1St>)l`-X*g-fxj(W0wd8Qb9
z-bct^ab@)z-c(ZURc6VG(cOE4O=|CjPHU>%Vp+CGNH9}*BnM}hf4B%ZP}f^<$BTY(
z#QC!%@Qw;VTzn7U;vi&75H;*1geaY>T2dnr0aInW&3hD{qW>G~;E$Zr>hUC}@iMv0
zRw|?ybkkqjf=7N71gD_|z1bZ5!cJ-b95+jg^*(=EoR{s$-e&o`MU5Y9=JIFp{y75a
z#2=_*)!iv8Iq{2*Mjq+3PY-Y+D5CF)l@44~)A;(AXz!I;s%!hD!jK8pX+4dNm(sA|
zp3XAz6Za~=7*8~yY}_Hyj`WmbPlKrqS+^1Cqa`|Hw{TVoZ|Sh-&oW+Ldc)-R7Be+E
zBRvCjjhc;L>BwQSb$NdMp^N=HUXVjMz_giCW|SLwu~|0Y^^^38vknem{NBzluu-qH
zzpY@)_8()*{8BbS1eq)&$6vVxHWj6Y1jM!4`>3b2>7Ck%k*&9S@=4->*4;>Hw#5V>
zWf&nY9Mlm_A2b6QYWhw@li9+8PfTR)vLm<=|-&ZEPOK2|KGBb>!AZSZ=o)ldJs!4}~-
zT^(SN=vzRm>XyQTo@PSc$R%DyiHEx`*a@F4$(EC))9v4$Y?ZC
z%2ZCD7X<=6Vx2dqTX&zE1b}I$b4=Cx;?~$gHT%LGlgy|{cKTNatdz{ycu0t}C2p<2
zO!gFH1WLK!gc7ij@@=gq=4;&_6DwGT9l8d_jInb18KY5G+DbQO9&g#if6j9Jj_T9!
z-cw*ZJb2gz2(!km&@t{q9hpP&%0yAG{UG&Yp|y>kJ1UZb`V{Y1ME26(ax`D0Gbl5c
zpxbRt!y5NGZbQsz*9#4E;#
$$Q|WO|IY>G6ANzN@jqZfpv*eEJ
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