@fayz-ai/plugin-support 0.0.0-stage → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/LICENSE +21 -0
  2. package/dist/HelpdeskConsole-7W5IAQIU.js +355 -0
  3. package/dist/HelpdeskConsole-7W5IAQIU.js.map +1 -0
  4. package/dist/SupportCenter-Z2HEPJGD.js +1090 -0
  5. package/dist/SupportCenter-Z2HEPJGD.js.map +1 -0
  6. package/dist/chunk-5K7VA5VP.js +106 -0
  7. package/dist/chunk-5K7VA5VP.js.map +1 -0
  8. package/dist/chunk-7NVBUXTX.js +300 -0
  9. package/dist/chunk-7NVBUXTX.js.map +1 -0
  10. package/dist/chunk-JJPB2T3Z.js +131 -0
  11. package/dist/chunk-JJPB2T3Z.js.map +1 -0
  12. package/dist/components/HelpdeskStatusPanel.d.ts +12 -0
  13. package/dist/components/HelpdeskStatusPanel.d.ts.map +1 -0
  14. package/dist/data/api.d.ts +15 -0
  15. package/dist/data/api.d.ts.map +1 -0
  16. package/dist/data/console.d.ts +37 -0
  17. package/dist/data/console.d.ts.map +1 -0
  18. package/dist/index.d.ts +19 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +901 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/lib/content.d.ts +24 -0
  23. package/dist/lib/content.d.ts.map +1 -0
  24. package/dist/lib/fix-prompt.d.ts +30 -0
  25. package/dist/lib/fix-prompt.d.ts.map +1 -0
  26. package/dist/lib/report-tool.d.ts +25 -0
  27. package/dist/lib/report-tool.d.ts.map +1 -0
  28. package/dist/lib/vocabulary.d.ts +34 -0
  29. package/dist/lib/vocabulary.d.ts.map +1 -0
  30. package/dist/locales/en.d.ts +2 -0
  31. package/dist/locales/en.d.ts.map +1 -0
  32. package/dist/locales/index.d.ts +2 -0
  33. package/dist/locales/index.d.ts.map +1 -0
  34. package/dist/locales/pt-BR.d.ts +2 -0
  35. package/dist/locales/pt-BR.d.ts.map +1 -0
  36. package/dist/migrations/index.d.ts +9 -0
  37. package/dist/migrations/index.d.ts.map +1 -0
  38. package/dist/store.d.ts +27 -0
  39. package/dist/store.d.ts.map +1 -0
  40. package/dist/types.d.ts +80 -0
  41. package/dist/types.d.ts.map +1 -0
  42. package/dist/views/HelpdeskConsole.d.ts +10 -0
  43. package/dist/views/HelpdeskConsole.d.ts.map +1 -0
  44. package/dist/views/ScreenCaptureEditor.d.ts +12 -0
  45. package/dist/views/ScreenCaptureEditor.d.ts.map +1 -0
  46. package/dist/views/SupportCenter.d.ts +14 -0
  47. package/dist/views/SupportCenter.d.ts.map +1 -0
  48. package/dist/views/SupportMenu.d.ts +12 -0
  49. package/dist/views/SupportMenu.d.ts.map +1 -0
  50. package/dist/views/SupportRequestDetail.d.ts +6 -0
  51. package/dist/views/SupportRequestDetail.d.ts.map +1 -0
  52. package/dist/views/SupportRequestForm.d.ts +12 -0
  53. package/dist/views/SupportRequestForm.d.ts.map +1 -0
  54. package/dist/views/SupportRequestsPanel.d.ts +11 -0
  55. package/dist/views/SupportRequestsPanel.d.ts.map +1 -0
  56. package/dist/views/TicketDetailPage.d.ts +12 -0
  57. package/dist/views/TicketDetailPage.d.ts.map +1 -0
  58. package/package.json +56 -3
  59. package/README.md +0 -3
@@ -0,0 +1,300 @@
1
+ import { getActiveTenantId, getActiveUnitId, getSupabaseClientOptional } from '@fayz-ai/core';
2
+
3
+ // src/types.ts
4
+ var MAX_ATTACHMENTS = 8;
5
+ var MAX_ATTACHMENT_BYTES = 20 * 1024 * 1024;
6
+ var SUPPORT_BUCKET = "support-attachments";
7
+
8
+ // src/lib/vocabulary.ts
9
+ var KIND_LABEL = {
10
+ problem: "Problema",
11
+ improvement: "Sugest\xE3o de melhoria",
12
+ other: "Outra solicita\xE7\xE3o"
13
+ };
14
+ var STATUS_LABEL = {
15
+ received: "Recebida",
16
+ in_service: "Em atendimento",
17
+ waiting_you: "Aguardando voc\xEA",
18
+ in_development: "Em desenvolvimento",
19
+ resolved_check: "Resolvida: confira",
20
+ closed: "Encerrada",
21
+ cancelled: "Cancelada"
22
+ };
23
+ var PRIORITY_LABEL = {
24
+ urgent: "Urgente",
25
+ high: "Alta",
26
+ normal: "Normal",
27
+ low: "Baixa"
28
+ };
29
+ var PRIORITY_TONE = {
30
+ urgent: "destructive",
31
+ high: "warning",
32
+ normal: "secondary",
33
+ low: "outline"
34
+ };
35
+ var PRIORITY_ORDER = ["urgent", "high", "normal", "low"];
36
+ var STATUS_TONE = {
37
+ received: "secondary",
38
+ in_service: "default",
39
+ waiting_you: "warning",
40
+ in_development: "default",
41
+ resolved_check: "success",
42
+ closed: "outline",
43
+ cancelled: "outline"
44
+ };
45
+ var FILTER_STATUSES = {
46
+ waiting: ["waiting_you", "resolved_check"],
47
+ in_progress: ["received", "in_service", "in_development"],
48
+ closed: ["closed", "cancelled"]
49
+ };
50
+ var FILTER_LABEL = {
51
+ waiting: "Esperando voc\xEA",
52
+ in_progress: "Em andamento",
53
+ closed: "Encerradas",
54
+ all: "Todas"
55
+ };
56
+ function canMessage(status) {
57
+ return !["closed", "cancelled"].includes(status);
58
+ }
59
+ function canCancel(status) {
60
+ return status === "received" || status === "waiting_you";
61
+ }
62
+ function canValidate(status) {
63
+ return status === "resolved_check";
64
+ }
65
+ var DOORS = [
66
+ { kind: "problem", title: "Reportar um problema", hint: "Algo n\xE3o funcionou como deveria", icon: "Wrench" },
67
+ { kind: "improvement", title: "Sugerir uma melhoria", hint: "Conte o que faria seu dia mais f\xE1cil", icon: "Lightbulb" },
68
+ { kind: "other", title: "Outras solicita\xE7\xF5es", hint: "Treinamento, acesso, dados, comercial", icon: "MessageSquare" }
69
+ ];
70
+ var FORM_COPY = {
71
+ problem: {
72
+ title: "Reportar um problema",
73
+ intro: "Conte o que aconteceu. Uma captura da tela ajuda muito a gente a encontrar o problema.",
74
+ subjectPlaceholder: "Ex.: a comanda n\xE3o imprime o nome da mesa"
75
+ },
76
+ improvement: {
77
+ title: "Sugerir uma melhoria",
78
+ intro: "O que faria o seu dia mais f\xE1cil no sistema? Toda sugest\xE3o \xE9 lida pela equipe.",
79
+ subjectPlaceholder: "Ex.: filtrar a agenda por sala"
80
+ },
81
+ other: {
82
+ title: "Outra solicita\xE7\xE3o",
83
+ intro: "Treinamento, acesso de usu\xE1rios, importa\xE7\xE3o de dados, assunto comercial: \xE9 por aqui.",
84
+ subjectPlaceholder: "Ex.: treinamento para a equipe nova"
85
+ }
86
+ };
87
+ var EVENT_LABEL = {
88
+ opened: "Solicita\xE7\xE3o aberta",
89
+ status_changed: "Andamento",
90
+ validated_ok: "Voc\xEA confirmou que ficou bom",
91
+ reopened: "Reaberta: ainda n\xE3o resolveu",
92
+ cancelled: "Andamento: Cancelada",
93
+ closed_no_reply: "Encerrada: sem resposta no prazo",
94
+ merged: "Unida a outra solicita\xE7\xE3o igual"
95
+ };
96
+ function eventLine(type, note) {
97
+ if (type === "status_changed" && note) {
98
+ const label = STATUS_LABEL[note] ?? note;
99
+ return `Andamento: ${label}`;
100
+ }
101
+ if (type === "reopened" && note) return `Reaberta: ${note}`;
102
+ return EVENT_LABEL[type] ?? type;
103
+ }
104
+ function deriveSubject(description) {
105
+ const firstLine = description.trim().split("\n")[0] ?? "";
106
+ const firstSentence = firstLine.split(/(?<=[.!?])\s/)[0] ?? firstLine;
107
+ return firstSentence.slice(0, 200);
108
+ }
109
+ function client() {
110
+ const c = getSupabaseClientOptional();
111
+ if (!c) throw new Error("Supabase client not available");
112
+ return c;
113
+ }
114
+ var cachedUserId = null;
115
+ async function userId() {
116
+ if (cachedUserId) return cachedUserId;
117
+ const { data } = await client().auth.getUser();
118
+ cachedUserId = data?.user?.id ?? null;
119
+ return cachedUserId;
120
+ }
121
+ function toRequest(r) {
122
+ return {
123
+ id: r.id,
124
+ tenantId: r.tenant_id,
125
+ number: r.number,
126
+ kind: r.kind,
127
+ subject: r.subject ?? null,
128
+ description: r.description,
129
+ status: r.status,
130
+ route: r.route ?? null,
131
+ unitId: r.unit_id ?? null,
132
+ screenSnapshot: r.screen_snapshot ?? {},
133
+ originConversationId: r.origin_conversation_id ?? null,
134
+ area: r.area ?? null,
135
+ createdBy: r.created_by,
136
+ createdAt: r.created_at,
137
+ updatedAt: r.updated_at,
138
+ resolvedAt: r.resolved_at ?? null,
139
+ metadata: r.metadata ?? {}
140
+ };
141
+ }
142
+ var toMessage = (r) => ({
143
+ id: r.id,
144
+ requestId: r.request_id,
145
+ authorKind: r.author_kind,
146
+ authorId: r.author_id ?? null,
147
+ body: r.body,
148
+ createdAt: r.created_at
149
+ });
150
+ var toEvent = (r) => ({
151
+ id: r.id,
152
+ requestId: r.request_id,
153
+ type: r.type,
154
+ note: r.note ?? null,
155
+ createdAt: r.created_at
156
+ });
157
+ var toAttachment = (r) => ({
158
+ id: r.id,
159
+ requestId: r.request_id,
160
+ storagePath: r.storage_path,
161
+ name: r.name,
162
+ sizeBytes: r.size_bytes ?? null,
163
+ contentType: r.content_type ?? null,
164
+ createdAt: r.created_at
165
+ });
166
+ async function listRequests(scope, filter) {
167
+ let q = client().from("plg_support_requests").select("*").order("number", { ascending: false });
168
+ if (scope === "mine") {
169
+ const uid = await userId();
170
+ if (uid) q = q.eq("created_by", uid);
171
+ }
172
+ if (filter !== "all") {
173
+ const statuses = FILTER_STATUSES[filter];
174
+ q = q.in("status", statuses);
175
+ }
176
+ const { data, error } = await q;
177
+ if (error) throw error;
178
+ return (data ?? []).map(toRequest);
179
+ }
180
+ async function getRequestDetail(id) {
181
+ const { data: req, error } = await client().from("plg_support_requests").select("*").eq("id", id).maybeSingle();
182
+ if (error) throw error;
183
+ if (!req) return null;
184
+ const [messages, events, attachments] = await Promise.all([
185
+ client().from("plg_support_request_messages").select("*").eq("request_id", id).order("created_at", { ascending: true }),
186
+ client().from("plg_support_request_events").select("*").eq("request_id", id).order("created_at", { ascending: true }),
187
+ client().from("plg_support_request_attachments").select("*").eq("request_id", id).order("created_at", { ascending: true })
188
+ ]);
189
+ return {
190
+ ...toRequest(req),
191
+ messages: (messages.data ?? []).map(toMessage),
192
+ events: (events.data ?? []).map(toEvent),
193
+ attachments: (attachments.data ?? []).map(toAttachment)
194
+ };
195
+ }
196
+ async function unreadCount() {
197
+ const uid = await userId();
198
+ if (!uid) return 0;
199
+ const { count, error } = await client().from("plg_support_notifications").select("id", { count: "exact", head: true }).eq("user_id", uid).is("read_at", null);
200
+ if (error) return 0;
201
+ return count ?? 0;
202
+ }
203
+ async function createRequest(input) {
204
+ const tenantId = getActiveTenantId();
205
+ if (!tenantId) throw new Error("Sem conta ativa");
206
+ const subject = input.subject && input.subject.trim() || deriveSubject(input.description);
207
+ const row = {
208
+ tenant_id: tenantId,
209
+ kind: input.kind,
210
+ subject,
211
+ description: input.description.trim(),
212
+ route: input.route ?? null,
213
+ unit_id: input.unitId ?? getActiveUnitId() ?? null,
214
+ screen_snapshot: input.screenSnapshot ?? {},
215
+ origin_conversation_id: input.originConversationId ?? null,
216
+ area: input.area ?? null,
217
+ status: "received"
218
+ };
219
+ const { data, error } = await client().from("plg_support_requests").insert(row).select("id, number").single();
220
+ if (error) throw error;
221
+ await client().from("plg_support_request_events").insert({
222
+ tenant_id: tenantId,
223
+ request_id: data.id,
224
+ type: "opened",
225
+ note: null
226
+ });
227
+ return { id: data.id, number: data.number };
228
+ }
229
+ async function uploadAttachment(requestId, pending) {
230
+ const tenantId = getActiveTenantId();
231
+ if (!tenantId) throw new Error("Sem conta ativa");
232
+ const safeName = pending.name.replace(/[^\w.\-]+/g, "_");
233
+ const path = `${tenantId}/${requestId}/${crypto.randomUUID()}/${safeName}`;
234
+ const up = await client().storage.from(SUPPORT_BUCKET).upload(path, pending.file, {
235
+ contentType: pending.contentType,
236
+ upsert: false
237
+ });
238
+ if (up.error) throw up.error;
239
+ const { data, error } = await client().from("plg_support_request_attachments").insert({
240
+ tenant_id: tenantId,
241
+ request_id: requestId,
242
+ storage_path: path,
243
+ name: pending.name,
244
+ size_bytes: pending.sizeBytes,
245
+ content_type: pending.contentType
246
+ }).select("*").single();
247
+ if (error) throw error;
248
+ return toAttachment(data);
249
+ }
250
+ async function addMessage(requestId, body) {
251
+ const tenantId = getActiveTenantId();
252
+ if (!tenantId) throw new Error("Sem conta ativa");
253
+ const { data, error } = await client().from("plg_support_request_messages").insert({
254
+ tenant_id: tenantId,
255
+ request_id: requestId,
256
+ author_kind: "user",
257
+ body: body.trim()
258
+ }).select("*").single();
259
+ if (error) throw error;
260
+ return toMessage(data);
261
+ }
262
+ async function cancelRequest(requestId) {
263
+ const tenantId = getActiveTenantId();
264
+ if (!tenantId) throw new Error("Sem conta ativa");
265
+ const { error } = await client().from("plg_support_requests").update({ status: "cancelled" }).eq("id", requestId);
266
+ if (error) throw error;
267
+ await client().from("plg_support_request_events").insert({
268
+ tenant_id: tenantId,
269
+ request_id: requestId,
270
+ type: "cancelled",
271
+ note: null
272
+ });
273
+ }
274
+ async function validateRequest(requestId, ok, reason) {
275
+ const tenantId = getActiveTenantId();
276
+ if (!tenantId) throw new Error("Sem conta ativa");
277
+ const nextStatus = ok ? "closed" : "received";
278
+ const { error } = await client().from("plg_support_requests").update({ status: nextStatus }).eq("id", requestId);
279
+ if (error) throw error;
280
+ await client().from("plg_support_request_events").insert({
281
+ tenant_id: tenantId,
282
+ request_id: requestId,
283
+ type: ok ? "validated_ok" : "reopened",
284
+ note: ok ? null : reason ?? null
285
+ });
286
+ }
287
+ async function markNotificationsRead(requestId) {
288
+ const uid = await userId();
289
+ if (!uid) return;
290
+ await client().from("plg_support_notifications").update({ read_at: (/* @__PURE__ */ new Date()).toISOString() }).eq("request_id", requestId).eq("user_id", uid).is("read_at", null);
291
+ }
292
+ async function signedUrl(storagePath, expiresIn = 300) {
293
+ const { data, error } = await client().storage.from(SUPPORT_BUCKET).createSignedUrl(storagePath, expiresIn);
294
+ if (error) return null;
295
+ return data?.signedUrl ?? null;
296
+ }
297
+
298
+ export { DOORS, FILTER_LABEL, FORM_COPY, KIND_LABEL, MAX_ATTACHMENTS, MAX_ATTACHMENT_BYTES, PRIORITY_LABEL, PRIORITY_ORDER, PRIORITY_TONE, STATUS_LABEL, STATUS_TONE, SUPPORT_BUCKET, addMessage, canCancel, canMessage, canValidate, cancelRequest, createRequest, eventLine, getRequestDetail, listRequests, markNotificationsRead, signedUrl, unreadCount, uploadAttachment, validateRequest };
299
+ //# sourceMappingURL=chunk-7NVBUXTX.js.map
300
+ //# sourceMappingURL=chunk-7NVBUXTX.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/types.ts","../src/lib/vocabulary.ts","../src/data/api.ts"],"names":[],"mappings":";;;AA2GO,IAAM,eAAA,GAAkB;AACxB,IAAM,oBAAA,GAAuB,KAAK,IAAA,GAAO;AACzC,IAAM,cAAA,GAAiB;;;ACxGvB,IAAM,UAAA,GAA0C;AAAA,EACrD,OAAA,EAAS,UAAA;AAAA,EACT,WAAA,EAAa,yBAAA;AAAA,EACb,KAAA,EAAO;AACT;AAEO,IAAM,YAAA,GAA8C;AAAA,EACzD,QAAA,EAAU,UAAA;AAAA,EACV,UAAA,EAAY,gBAAA;AAAA,EACZ,WAAA,EAAa,oBAAA;AAAA,EACb,cAAA,EAAgB,oBAAA;AAAA,EAChB,cAAA,EAAgB,oBAAA;AAAA,EAChB,MAAA,EAAQ,WAAA;AAAA,EACR,SAAA,EAAW;AACb;AAMO,IAAM,cAAA,GAAkD;AAAA,EAC7D,MAAA,EAAQ,SAAA;AAAA,EACR,IAAA,EAAM,MAAA;AAAA,EACN,MAAA,EAAQ,QAAA;AAAA,EACR,GAAA,EAAK;AACP;AAEO,IAAM,aAAA,GAAoD;AAAA,EAC/D,MAAA,EAAQ,aAAA;AAAA,EACR,IAAA,EAAM,SAAA;AAAA,EACN,MAAA,EAAQ,WAAA;AAAA,EACR,GAAA,EAAK;AACP;AAGO,IAAM,cAAA,GAAoC,CAAC,QAAA,EAAU,MAAA,EAAQ,UAAU,KAAK;AAE5E,IAAM,WAAA,GAAgD;AAAA,EAC3D,QAAA,EAAU,WAAA;AAAA,EACV,UAAA,EAAY,SAAA;AAAA,EACZ,WAAA,EAAa,SAAA;AAAA,EACb,cAAA,EAAgB,SAAA;AAAA,EAChB,cAAA,EAAgB,SAAA;AAAA,EAChB,MAAA,EAAQ,SAAA;AAAA,EACR,SAAA,EAAW;AACb;AAGO,IAAM,eAAA,GAA0E;AAAA,EACrF,OAAA,EAAS,CAAC,aAAA,EAAe,gBAAgB,CAAA;AAAA,EACzC,WAAA,EAAa,CAAC,UAAA,EAAY,YAAA,EAAc,gBAAgB,CAAA;AAAA,EACxD,MAAA,EAAQ,CAAC,QAAA,EAAU,WAAW;AAChC,CAAA;AAEO,IAAM,YAAA,GAA8C;AAAA,EACzD,OAAA,EAAS,mBAAA;AAAA,EACT,WAAA,EAAa,cAAA;AAAA,EACb,MAAA,EAAQ,YAAA;AAAA,EACR,GAAA,EAAK;AACP;AAGO,SAAS,WAAW,MAAA,EAAgC;AACzD,EAAA,OAAO,CAAC,CAAC,QAAA,EAAU,WAAW,CAAA,CAAE,SAAS,MAAM,CAAA;AACjD;AAGO,SAAS,UAAU,MAAA,EAAgC;AACxD,EAAA,OAAO,MAAA,KAAW,cAAc,MAAA,KAAW,aAAA;AAC7C;AAGO,SAAS,YAAY,MAAA,EAAgC;AAC1D,EAAA,OAAO,MAAA,KAAW,gBAAA;AACpB;AAGO,IAAM,KAAA,GAKR;AAAA,EACH,EAAE,MAAM,SAAA,EAAW,KAAA,EAAO,wBAAwB,IAAA,EAAM,oCAAA,EAAmC,MAAM,QAAA,EAAS;AAAA,EAC1G,EAAE,MAAM,aAAA,EAAe,KAAA,EAAO,wBAAwB,IAAA,EAAM,yCAAA,EAAwC,MAAM,WAAA,EAAY;AAAA,EACtH,EAAE,MAAM,OAAA,EAAS,KAAA,EAAO,6BAAuB,IAAA,EAAM,uCAAA,EAAyC,MAAM,eAAA;AACtG;AAGO,IAAM,SAAA,GAA+F;AAAA,EAC1G,OAAA,EAAS;AAAA,IACP,KAAA,EAAO,sBAAA;AAAA,IACP,KAAA,EAAO,wFAAA;AAAA,IACP,kBAAA,EAAoB;AAAA,GACtB;AAAA,EACA,WAAA,EAAa;AAAA,IACX,KAAA,EAAO,sBAAA;AAAA,IACP,KAAA,EAAO,yFAAA;AAAA,IACP,kBAAA,EAAoB;AAAA,GACtB;AAAA,EACA,KAAA,EAAO;AAAA,IACL,KAAA,EAAO,yBAAA;AAAA,IACP,KAAA,EAAO,kGAAA;AAAA,IACP,kBAAA,EAAoB;AAAA;AAExB;AAEO,IAAM,WAAA,GAAgD;AAAA,EAC3D,MAAA,EAAQ,0BAAA;AAAA,EACR,cAAA,EAAgB,WAAA;AAAA,EAChB,YAAA,EAAc,iCAAA;AAAA,EACd,QAAA,EAAU,iCAAA;AAAA,EACV,SAAA,EAAW,sBAAA;AAAA,EACX,eAAA,EAAiB,kCAAA;AAAA,EACjB,MAAA,EAAQ;AACV,CAAA;AAGO,SAAS,SAAA,CAAU,MAAwB,IAAA,EAA6B;AAC7E,EAAA,IAAI,IAAA,KAAS,oBAAoB,IAAA,EAAM;AACrC,IAAA,MAAM,KAAA,GAAQ,YAAA,CAAa,IAAqB,CAAA,IAAK,IAAA;AACrD,IAAA,OAAO,cAAc,KAAK,CAAA,CAAA;AAAA,EAC5B;AACA,EAAA,IAAI,IAAA,KAAS,UAAA,IAAc,IAAA,EAAM,OAAO,aAAa,IAAI,CAAA,CAAA;AACzD,EAAA,OAAO,WAAA,CAAY,IAAI,CAAA,IAAK,IAAA;AAC9B;AAGO,SAAS,cAAc,WAAA,EAA6B;AACzD,EAAA,MAAM,SAAA,GAAY,YAAY,IAAA,EAAK,CAAE,MAAM,IAAI,CAAA,CAAE,CAAC,CAAA,IAAK,EAAA;AACvD,EAAA,MAAM,gBAAgB,SAAA,CAAU,KAAA,CAAM,cAAc,CAAA,CAAE,CAAC,CAAA,IAAK,SAAA;AAC5D,EAAA,OAAO,aAAA,CAAc,KAAA,CAAM,CAAA,EAAG,GAAG,CAAA;AACnC;ACzHA,SAAS,MAAA,GAAoB;AAC3B,EAAA,MAAM,IAAI,yBAAA,EAA0B;AACpC,EAAA,IAAI,CAAC,CAAA,EAAG,MAAM,IAAI,MAAM,+BAA+B,CAAA;AACvD,EAAA,OAAO,CAAA;AACT;AAEA,IAAI,YAAA,GAA8B,IAAA;AAClC,eAAe,MAAA,GAAiC;AAC9C,EAAA,IAAI,cAAc,OAAO,YAAA;AACzB,EAAA,MAAM,EAAE,IAAA,EAAK,GAAI,MAAM,MAAA,EAAO,CAAE,KAAK,OAAA,EAAQ;AAC7C,EAAA,YAAA,GAAe,IAAA,EAAM,MAAM,EAAA,IAAM,IAAA;AACjC,EAAA,OAAO,YAAA;AACT;AAIA,SAAS,UAAU,CAAA,EAAwB;AACzC,EAAA,OAAO;AAAA,IACL,IAAI,CAAA,CAAE,EAAA;AAAA,IACN,UAAU,CAAA,CAAE,SAAA;AAAA,IACZ,QAAQ,CAAA,CAAE,MAAA;AAAA,IACV,MAAM,CAAA,CAAE,IAAA;AAAA,IACR,OAAA,EAAS,EAAE,OAAA,IAAW,IAAA;AAAA,IACtB,aAAa,CAAA,CAAE,WAAA;AAAA,IACf,QAAQ,CAAA,CAAE,MAAA;AAAA,IACV,KAAA,EAAO,EAAE,KAAA,IAAS,IAAA;AAAA,IAClB,MAAA,EAAQ,EAAE,OAAA,IAAW,IAAA;AAAA,IACrB,cAAA,EAAgB,CAAA,CAAE,eAAA,IAAmB,EAAC;AAAA,IACtC,oBAAA,EAAsB,EAAE,sBAAA,IAA0B,IAAA;AAAA,IAClD,IAAA,EAAM,EAAE,IAAA,IAAQ,IAAA;AAAA,IAChB,WAAW,CAAA,CAAE,UAAA;AAAA,IACb,WAAW,CAAA,CAAE,UAAA;AAAA,IACb,WAAW,CAAA,CAAE,UAAA;AAAA,IACb,UAAA,EAAY,EAAE,WAAA,IAAe,IAAA;AAAA,IAC7B,QAAA,EAAU,CAAA,CAAE,QAAA,IAAY;AAAC,GAC3B;AACF;AAEA,IAAM,SAAA,GAAY,CAAC,CAAA,MAA4B;AAAA,EAC7C,IAAI,CAAA,CAAE,EAAA;AAAA,EAAI,WAAW,CAAA,CAAE,UAAA;AAAA,EAAY,YAAY,CAAA,CAAE,WAAA;AAAA,EACjD,QAAA,EAAU,EAAE,SAAA,IAAa,IAAA;AAAA,EAAM,MAAM,CAAA,CAAE,IAAA;AAAA,EAAM,WAAW,CAAA,CAAE;AAC5D,CAAA,CAAA;AAEA,IAAM,OAAA,GAAU,CAAC,CAAA,MAA0B;AAAA,EACzC,IAAI,CAAA,CAAE,EAAA;AAAA,EAAI,WAAW,CAAA,CAAE,UAAA;AAAA,EAAY,MAAM,CAAA,CAAE,IAAA;AAAA,EAAM,IAAA,EAAM,EAAE,IAAA,IAAQ,IAAA;AAAA,EAAM,WAAW,CAAA,CAAE;AACtF,CAAA,CAAA;AAEA,IAAM,YAAA,GAAe,CAAC,CAAA,MAA+B;AAAA,EACnD,IAAI,CAAA,CAAE,EAAA;AAAA,EAAI,WAAW,CAAA,CAAE,UAAA;AAAA,EAAY,aAAa,CAAA,CAAE,YAAA;AAAA,EAAc,MAAM,CAAA,CAAE,IAAA;AAAA,EACxE,SAAA,EAAW,EAAE,UAAA,IAAc,IAAA;AAAA,EAAM,WAAA,EAAa,EAAE,YAAA,IAAgB,IAAA;AAAA,EAAM,WAAW,CAAA,CAAE;AACrF,CAAA,CAAA;AAIA,eAAsB,YAAA,CAAa,OAAqB,MAAA,EAAkD;AACxG,EAAA,IAAI,CAAA,GAAI,MAAA,EAAO,CAAE,IAAA,CAAK,sBAAsB,CAAA,CAAE,MAAA,CAAO,GAAG,CAAA,CAAE,KAAA,CAAM,QAAA,EAAU,EAAE,SAAA,EAAW,OAAO,CAAA;AAC9F,EAAA,IAAI,UAAU,MAAA,EAAQ;AACpB,IAAA,MAAM,GAAA,GAAM,MAAM,MAAA,EAAO;AACzB,IAAA,IAAI,GAAA,EAAK,CAAA,GAAI,CAAA,CAAE,EAAA,CAAG,cAAc,GAAG,CAAA;AAAA,EACrC;AACA,EAAA,IAAI,WAAW,KAAA,EAAO;AACpB,IAAA,MAAM,QAAA,GAAW,gBAAgB,MAAM,CAAA;AACvC,IAAA,CAAA,GAAI,CAAA,CAAE,EAAA,CAAG,QAAA,EAAU,QAAQ,CAAA;AAAA,EAC7B;AACA,EAAA,MAAM,EAAE,IAAA,EAAM,KAAA,EAAM,GAAI,MAAM,CAAA;AAC9B,EAAA,IAAI,OAAO,MAAM,KAAA;AACjB,EAAA,OAAA,CAAQ,IAAA,IAAQ,EAAC,EAAG,GAAA,CAAI,SAAS,CAAA;AACnC;AAEA,eAAsB,iBAAiB,EAAA,EAAkD;AACvF,EAAA,MAAM,EAAE,IAAA,EAAM,GAAA,EAAK,OAAM,GAAI,MAAM,QAAO,CAAE,IAAA,CAAK,sBAAsB,CAAA,CAAE,OAAO,GAAG,CAAA,CAAE,GAAG,IAAA,EAAM,EAAE,EAAE,WAAA,EAAY;AAC9G,EAAA,IAAI,OAAO,MAAM,KAAA;AACjB,EAAA,IAAI,CAAC,KAAK,OAAO,IAAA;AACjB,EAAA,MAAM,CAAC,QAAA,EAAU,MAAA,EAAQ,WAAW,CAAA,GAAI,MAAM,QAAQ,GAAA,CAAI;AAAA,IACxD,QAAO,CAAE,IAAA,CAAK,8BAA8B,CAAA,CAAE,OAAO,GAAG,CAAA,CAAE,EAAA,CAAG,YAAA,EAAc,EAAE,CAAA,CAAE,KAAA,CAAM,cAAc,EAAE,SAAA,EAAW,MAAM,CAAA;AAAA,IACtH,QAAO,CAAE,IAAA,CAAK,4BAA4B,CAAA,CAAE,OAAO,GAAG,CAAA,CAAE,EAAA,CAAG,YAAA,EAAc,EAAE,CAAA,CAAE,KAAA,CAAM,cAAc,EAAE,SAAA,EAAW,MAAM,CAAA;AAAA,IACpH,QAAO,CAAE,IAAA,CAAK,iCAAiC,CAAA,CAAE,OAAO,GAAG,CAAA,CAAE,EAAA,CAAG,YAAA,EAAc,EAAE,CAAA,CAAE,KAAA,CAAM,cAAc,EAAE,SAAA,EAAW,MAAM;AAAA,GAC1H,CAAA;AACD,EAAA,OAAO;AAAA,IACL,GAAG,UAAU,GAAG,CAAA;AAAA,IAChB,WAAW,QAAA,CAAS,IAAA,IAAQ,EAAC,EAAG,IAAI,SAAS,CAAA;AAAA,IAC7C,SAAS,MAAA,CAAO,IAAA,IAAQ,EAAC,EAAG,IAAI,OAAO,CAAA;AAAA,IACvC,cAAc,WAAA,CAAY,IAAA,IAAQ,EAAC,EAAG,IAAI,YAAY;AAAA,GACxD;AACF;AAEA,eAAsB,WAAA,GAA+B;AACnD,EAAA,MAAM,GAAA,GAAM,MAAM,MAAA,EAAO;AACzB,EAAA,IAAI,CAAC,KAAK,OAAO,CAAA;AACjB,EAAA,MAAM,EAAE,KAAA,EAAO,KAAA,EAAM,GAAI,MAAM,QAAO,CACnC,IAAA,CAAK,2BAA2B,CAAA,CAChC,MAAA,CAAO,IAAA,EAAM,EAAE,KAAA,EAAO,OAAA,EAAS,IAAA,EAAM,IAAA,EAAM,CAAA,CAC3C,EAAA,CAAG,SAAA,EAAW,GAAG,CAAA,CACjB,EAAA,CAAG,SAAA,EAAW,IAAI,CAAA;AACrB,EAAA,IAAI,OAAO,OAAO,CAAA;AAClB,EAAA,OAAO,KAAA,IAAS,CAAA;AAClB;AAIA,eAAsB,cAAc,KAAA,EAA2E;AAC7G,EAAA,MAAM,WAAW,iBAAA,EAAkB;AACnC,EAAA,IAAI,CAAC,QAAA,EAAU,MAAM,IAAI,MAAM,iBAAiB,CAAA;AAChD,EAAA,MAAM,OAAA,GAAW,MAAM,OAAA,IAAW,KAAA,CAAM,QAAQ,IAAA,EAAK,IAAM,aAAA,CAAc,KAAA,CAAM,WAAW,CAAA;AAC1F,EAAA,MAAM,GAAA,GAAM;AAAA,IACV,SAAA,EAAW,QAAA;AAAA,IACX,MAAM,KAAA,CAAM,IAAA;AAAA,IACZ,OAAA;AAAA,IACA,WAAA,EAAa,KAAA,CAAM,WAAA,CAAY,IAAA,EAAK;AAAA,IACpC,KAAA,EAAO,MAAM,KAAA,IAAS,IAAA;AAAA,IACtB,OAAA,EAAS,KAAA,CAAM,MAAA,IAAU,eAAA,EAAgB,IAAK,IAAA;AAAA,IAC9C,eAAA,EAAiB,KAAA,CAAM,cAAA,IAAkB,EAAC;AAAA,IAC1C,sBAAA,EAAwB,MAAM,oBAAA,IAAwB,IAAA;AAAA,IACtD,IAAA,EAAM,MAAM,IAAA,IAAQ,IAAA;AAAA,IACpB,MAAA,EAAQ;AAAA,GACV;AACA,EAAA,MAAM,EAAE,IAAA,EAAM,KAAA,EAAM,GAAI,MAAM,QAAO,CAAE,IAAA,CAAK,sBAAsB,CAAA,CAAE,OAAO,GAAG,CAAA,CAAE,MAAA,CAAO,YAAY,EAAE,MAAA,EAAO;AAC5G,EAAA,IAAI,OAAO,MAAM,KAAA;AACjB,EAAA,MAAM,MAAA,EAAO,CAAE,IAAA,CAAK,4BAA4B,EAAE,MAAA,CAAO;AAAA,IACvD,SAAA,EAAW,QAAA;AAAA,IAAU,YAAY,IAAA,CAAK,EAAA;AAAA,IAAI,IAAA,EAAM,QAAA;AAAA,IAAU,IAAA,EAAM;AAAA,GACjE,CAAA;AACD,EAAA,OAAO,EAAE,EAAA,EAAI,IAAA,CAAK,EAAA,EAAI,MAAA,EAAQ,KAAK,MAAA,EAAO;AAC5C;AAEA,eAAsB,gBAAA,CAAiB,WAAmB,OAAA,EAAwD;AAChH,EAAA,MAAM,WAAW,iBAAA,EAAkB;AACnC,EAAA,IAAI,CAAC,QAAA,EAAU,MAAM,IAAI,MAAM,iBAAiB,CAAA;AAChD,EAAA,MAAM,QAAA,GAAW,OAAA,CAAQ,IAAA,CAAK,OAAA,CAAQ,cAAc,GAAG,CAAA;AACvD,EAAA,MAAM,IAAA,GAAO,CAAA,EAAG,QAAQ,CAAA,CAAA,EAAI,SAAS,IAAI,MAAA,CAAO,UAAA,EAAY,CAAA,CAAA,EAAI,QAAQ,CAAA,CAAA;AACxE,EAAA,MAAM,EAAA,GAAK,MAAM,MAAA,EAAO,CAAE,OAAA,CAAQ,IAAA,CAAK,cAAc,CAAA,CAAE,MAAA,CAAO,IAAA,EAAM,OAAA,CAAQ,IAAA,EAAM;AAAA,IAChF,aAAa,OAAA,CAAQ,WAAA;AAAA,IAAa,MAAA,EAAQ;AAAA,GAC3C,CAAA;AACD,EAAA,IAAI,EAAA,CAAG,KAAA,EAAO,MAAM,EAAA,CAAG,KAAA;AACvB,EAAA,MAAM,EAAE,IAAA,EAAM,KAAA,EAAM,GAAI,MAAM,QAAO,CAAE,IAAA,CAAK,iCAAiC,CAAA,CAAE,MAAA,CAAO;AAAA,IACpF,SAAA,EAAW,QAAA;AAAA,IAAU,UAAA,EAAY,SAAA;AAAA,IAAW,YAAA,EAAc,IAAA;AAAA,IAC1D,MAAM,OAAA,CAAQ,IAAA;AAAA,IAAM,YAAY,OAAA,CAAQ,SAAA;AAAA,IAAW,cAAc,OAAA,CAAQ;AAAA,GAC1E,CAAA,CAAE,MAAA,CAAO,GAAG,EAAE,MAAA,EAAO;AACtB,EAAA,IAAI,OAAO,MAAM,KAAA;AACjB,EAAA,OAAO,aAAa,IAAI,CAAA;AAC1B;AAEA,eAAsB,UAAA,CAAW,WAAmB,IAAA,EAAuC;AACzF,EAAA,MAAM,WAAW,iBAAA,EAAkB;AACnC,EAAA,IAAI,CAAC,QAAA,EAAU,MAAM,IAAI,MAAM,iBAAiB,CAAA;AAChD,EAAA,MAAM,EAAE,IAAA,EAAM,KAAA,EAAM,GAAI,MAAM,QAAO,CAAE,IAAA,CAAK,8BAA8B,CAAA,CAAE,MAAA,CAAO;AAAA,IACjF,SAAA,EAAW,QAAA;AAAA,IAAU,UAAA,EAAY,SAAA;AAAA,IAAW,WAAA,EAAa,MAAA;AAAA,IAAQ,IAAA,EAAM,KAAK,IAAA;AAAK,GAClF,CAAA,CAAE,MAAA,CAAO,GAAG,EAAE,MAAA,EAAO;AACtB,EAAA,IAAI,OAAO,MAAM,KAAA;AACjB,EAAA,OAAO,UAAU,IAAI,CAAA;AACvB;AAEA,eAAsB,cAAc,SAAA,EAAkC;AACpE,EAAA,MAAM,WAAW,iBAAA,EAAkB;AACnC,EAAA,IAAI,CAAC,QAAA,EAAU,MAAM,IAAI,MAAM,iBAAiB,CAAA;AAChD,EAAA,MAAM,EAAE,KAAA,EAAM,GAAI,MAAM,MAAA,GAAS,IAAA,CAAK,sBAAsB,CAAA,CACzD,MAAA,CAAO,EAAE,MAAA,EAAQ,WAAA,EAA8B,CAAA,CAAE,EAAA,CAAG,MAAM,SAAS,CAAA;AACtE,EAAA,IAAI,OAAO,MAAM,KAAA;AACjB,EAAA,MAAM,MAAA,EAAO,CAAE,IAAA,CAAK,4BAA4B,EAAE,MAAA,CAAO;AAAA,IACvD,SAAA,EAAW,QAAA;AAAA,IAAU,UAAA,EAAY,SAAA;AAAA,IAAW,IAAA,EAAM,WAAA;AAAA,IAAa,IAAA,EAAM;AAAA,GACtE,CAAA;AACH;AAEA,eAAsB,eAAA,CAAgB,SAAA,EAAmB,EAAA,EAAa,MAAA,EAAgC;AACpG,EAAA,MAAM,WAAW,iBAAA,EAAkB;AACnC,EAAA,IAAI,CAAC,QAAA,EAAU,MAAM,IAAI,MAAM,iBAAiB,CAAA;AAChD,EAAA,MAAM,UAAA,GAA4B,KAAK,QAAA,GAAW,UAAA;AAClD,EAAA,MAAM,EAAE,KAAA,EAAM,GAAI,MAAM,MAAA,GAAS,IAAA,CAAK,sBAAsB,CAAA,CACzD,MAAA,CAAO,EAAE,MAAA,EAAQ,UAAA,EAAY,CAAA,CAAE,EAAA,CAAG,MAAM,SAAS,CAAA;AACpD,EAAA,IAAI,OAAO,MAAM,KAAA;AACjB,EAAA,MAAM,MAAA,EAAO,CAAE,IAAA,CAAK,4BAA4B,EAAE,MAAA,CAAO;AAAA,IACvD,SAAA,EAAW,QAAA;AAAA,IAAU,UAAA,EAAY,SAAA;AAAA,IACjC,IAAA,EAAM,KAAK,cAAA,GAAiB,UAAA;AAAA,IAAY,IAAA,EAAM,EAAA,GAAK,IAAA,GAAQ,MAAA,IAAU;AAAA,GACtE,CAAA;AACH;AAEA,eAAsB,sBAAsB,SAAA,EAAkC;AAC5E,EAAA,MAAM,GAAA,GAAM,MAAM,MAAA,EAAO;AACzB,EAAA,IAAI,CAAC,GAAA,EAAK;AACV,EAAA,MAAM,MAAA,EAAO,CAAE,IAAA,CAAK,2BAA2B,CAAA,CAC5C,OAAO,EAAE,OAAA,EAAA,iBAAS,IAAI,IAAA,EAAK,EAAE,WAAA,IAAe,CAAA,CAC5C,EAAA,CAAG,YAAA,EAAc,SAAS,CAAA,CAAE,EAAA,CAAG,SAAA,EAAW,GAAG,CAAA,CAAE,EAAA,CAAG,SAAA,EAAW,IAAI,CAAA;AACtE;AAEA,eAAsB,SAAA,CAAU,WAAA,EAAqB,SAAA,GAAY,GAAA,EAA6B;AAC5F,EAAA,MAAM,EAAE,IAAA,EAAM,KAAA,EAAM,GAAI,MAAM,MAAA,EAAO,CAAE,OAAA,CAAQ,IAAA,CAAK,cAAc,CAAA,CAAE,eAAA,CAAgB,aAAa,SAAS,CAAA;AAC1G,EAAA,IAAI,OAAO,OAAO,IAAA;AAClB,EAAA,OAAO,MAAM,SAAA,IAAa,IAAA;AAC5B","file":"chunk-7NVBUXTX.js","sourcesContent":["// Domain types for the support/solicitações plugin. Mirrors the V1 support_*\n// family, narrowed to what this wave ships (the client/requester side).\n\nexport type SupportKind = 'problem' | 'improvement' | 'other'\n\nexport type SupportPriority = 'low' | 'normal' | 'high' | 'urgent'\n\nexport type SupportStatus =\n | 'received'\n | 'in_service'\n | 'waiting_you'\n | 'in_development'\n | 'resolved_check'\n | 'closed'\n | 'cancelled'\n\nexport type SupportEventType =\n | 'opened'\n | 'status_changed'\n | 'validated_ok'\n | 'reopened'\n | 'cancelled'\n | 'closed_no_reply'\n | 'merged'\n\nexport type SupportMessageAuthor = 'user' | 'team'\n\nexport interface SupportRequest {\n id: string\n tenantId: string\n number: number\n kind: SupportKind\n subject: string | null\n description: string\n status: SupportStatus\n route: string | null\n unitId: string | null\n screenSnapshot: Record<string, unknown>\n originConversationId: string | null\n area: string | null\n createdBy: string\n createdAt: string\n updatedAt: string\n resolvedAt: string | null\n metadata: Record<string, unknown>\n}\n\nexport interface SupportMessage {\n id: string\n requestId: string\n authorKind: SupportMessageAuthor\n authorId: string | null\n body: string\n createdAt: string\n}\n\nexport interface SupportEvent {\n id: string\n requestId: string\n type: SupportEventType\n note: string | null\n createdAt: string\n}\n\nexport interface SupportAttachment {\n id: string\n requestId: string\n storagePath: string\n name: string\n sizeBytes: number | null\n contentType: string | null\n createdAt: string\n}\n\nexport interface SupportRequestDetail extends SupportRequest {\n messages: SupportMessage[]\n events: SupportEvent[]\n attachments: SupportAttachment[]\n}\n\n/** What the form collects before an attachment upload. */\nexport interface CreateSupportRequestInput {\n kind: SupportKind\n description: string\n subject?: string | null\n route?: string | null\n unitId?: string | null\n screenSnapshot?: Record<string, unknown>\n originConversationId?: string | null\n area?: string | null\n}\n\n/** A file staged in the form, before it is uploaded to the bucket. */\nexport interface PendingAttachment {\n id: string\n file: Blob\n name: string\n contentType: string\n sizeBytes: number\n /** Object URL for a local thumbnail; revoke on unmount. */\n previewUrl?: string\n}\n\nexport type SupportScope = 'mine' | 'all'\n\nexport type SupportFilter = 'waiting' | 'in_progress' | 'closed' | 'all'\n\nexport const MAX_ATTACHMENTS = 8\nexport const MAX_ATTACHMENT_BYTES = 20 * 1024 * 1024\nexport const SUPPORT_BUCKET = 'support-attachments'\n","import type { SupportKind, SupportStatus, SupportFilter, SupportEventType, SupportPriority } from '../types'\n\n// End-user text is pt-BR (AGENTS.md: only the end user's strings are pt-BR).\n// Never let a raw enum reach the screen — port-v2 régua de UX.\n\nexport const KIND_LABEL: Record<SupportKind, string> = {\n problem: 'Problema',\n improvement: 'Sugestão de melhoria',\n other: 'Outra solicitação',\n}\n\nexport const STATUS_LABEL: Record<SupportStatus, string> = {\n received: 'Recebida',\n in_service: 'Em atendimento',\n waiting_you: 'Aguardando você',\n in_development: 'Em desenvolvimento',\n resolved_check: 'Resolvida: confira',\n closed: 'Encerrada',\n cancelled: 'Cancelada',\n}\n\n// Badge tone per status, using the design system's Badge variants.\nexport type BadgeTone = 'default' | 'secondary' | 'success' | 'warning' | 'destructive' | 'outline'\n\n// Operator-side: priority (the helpdesk triages by it).\nexport const PRIORITY_LABEL: Record<SupportPriority, string> = {\n urgent: 'Urgente',\n high: 'Alta',\n normal: 'Normal',\n low: 'Baixa',\n}\n\nexport const PRIORITY_TONE: Record<SupportPriority, BadgeTone> = {\n urgent: 'destructive',\n high: 'warning',\n normal: 'secondary',\n low: 'outline',\n}\n\n/** Triage order: urgent first. */\nexport const PRIORITY_ORDER: SupportPriority[] = ['urgent', 'high', 'normal', 'low']\n\nexport const STATUS_TONE: Record<SupportStatus, BadgeTone> = {\n received: 'secondary',\n in_service: 'default',\n waiting_you: 'warning',\n in_development: 'default',\n resolved_check: 'success',\n closed: 'outline',\n cancelled: 'outline',\n}\n\n// Which statuses each filter chip collects (central-de-suporte FLOW.md).\nexport const FILTER_STATUSES: Record<Exclude<SupportFilter, 'all'>, SupportStatus[]> = {\n waiting: ['waiting_you', 'resolved_check'],\n in_progress: ['received', 'in_service', 'in_development'],\n closed: ['closed', 'cancelled'],\n}\n\nexport const FILTER_LABEL: Record<SupportFilter, string> = {\n waiting: 'Esperando você',\n in_progress: 'Em andamento',\n closed: 'Encerradas',\n all: 'Todas',\n}\n\n/** The statuses that still let the person write / attach. */\nexport function canMessage(status: SupportStatus): boolean {\n return !['closed', 'cancelled'].includes(status)\n}\n\n/** Cancel is only offered before the team picks it up. */\nexport function canCancel(status: SupportStatus): boolean {\n return status === 'received' || status === 'waiting_you'\n}\n\n/** \"Ficou bom / Ainda não resolveu\" replaces the message box here. */\nexport function canValidate(status: SupportStatus): boolean {\n return status === 'resolved_check'\n}\n\n// The four doors, in panel order. 'question' is the Dico chat, not a ticket.\nexport const DOORS: Array<{\n kind: SupportKind\n title: string\n hint: string\n icon: string\n}> = [\n { kind: 'problem', title: 'Reportar um problema', hint: 'Algo não funcionou como deveria', icon: 'Wrench' },\n { kind: 'improvement', title: 'Sugerir uma melhoria', hint: 'Conte o que faria seu dia mais fácil', icon: 'Lightbulb' },\n { kind: 'other', title: 'Outras solicitações', hint: 'Treinamento, acesso, dados, comercial', icon: 'MessageSquare' },\n]\n\n// The opening copy per door, matching the V1 form (central-de-suporte FLOW.md).\nexport const FORM_COPY: Record<SupportKind, { title: string; intro: string; subjectPlaceholder: string }> = {\n problem: {\n title: 'Reportar um problema',\n intro: 'Conte o que aconteceu. Uma captura da tela ajuda muito a gente a encontrar o problema.',\n subjectPlaceholder: 'Ex.: a comanda não imprime o nome da mesa',\n },\n improvement: {\n title: 'Sugerir uma melhoria',\n intro: 'O que faria o seu dia mais fácil no sistema? Toda sugestão é lida pela equipe.',\n subjectPlaceholder: 'Ex.: filtrar a agenda por sala',\n },\n other: {\n title: 'Outra solicitação',\n intro: 'Treinamento, acesso de usuários, importação de dados, assunto comercial: é por aqui.',\n subjectPlaceholder: 'Ex.: treinamento para a equipe nova',\n },\n}\n\nexport const EVENT_LABEL: Record<SupportEventType, string> = {\n opened: 'Solicitação aberta',\n status_changed: 'Andamento',\n validated_ok: 'Você confirmou que ficou bom',\n reopened: 'Reaberta: ainda não resolveu',\n cancelled: 'Andamento: Cancelada',\n closed_no_reply: 'Encerrada: sem resposta no prazo',\n merged: 'Unida a outra solicitação igual',\n}\n\n/** The history line as shown: \"Andamento: Em atendimento\" etc. */\nexport function eventLine(type: SupportEventType, note: string | null): string {\n if (type === 'status_changed' && note) {\n const label = STATUS_LABEL[note as SupportStatus] ?? note\n return `Andamento: ${label}`\n }\n if (type === 'reopened' && note) return `Reaberta: ${note}`\n return EVENT_LABEL[type] ?? type\n}\n\n/** Subject fallback: first line/sentence of the description, capped at 200. */\nexport function deriveSubject(description: string): string {\n const firstLine = description.trim().split('\\n')[0] ?? ''\n const firstSentence = firstLine.split(/(?<=[.!?])\\s/)[0] ?? firstLine\n return firstSentence.slice(0, 200)\n}\n","import { getSupabaseClientOptional, getActiveTenantId, getActiveUnitId } from '@fayz-ai/core'\nimport {\n type SupportRequest, type SupportRequestDetail, type SupportMessage,\n type SupportEvent, type SupportAttachment, type CreateSupportRequestInput,\n type PendingAttachment, type SupportScope, type SupportFilter, type SupportStatus,\n SUPPORT_BUCKET,\n} from '../types'\nimport { FILTER_STATUSES, deriveSubject } from '../lib/vocabulary'\n\n// The data provider is PostgREST + RLS, not an RPC layer: a user only reads and\n// writes rows their tenant policy allows, and `created_by` defaults to auth.uid()\n// in the schema so it cannot be spoofed from here. The \"Minhas vs Toda a empresa\"\n// split is a query filter (scope), gated in the UI by the support.all feature.\n\ntype AnyClient = any // supabase-js client; typed loosely as the other plugins do\n\nfunction client(): AnyClient {\n const c = getSupabaseClientOptional()\n if (!c) throw new Error('Supabase client not available')\n return c as AnyClient\n}\n\nlet cachedUserId: string | null = null\nasync function userId(): Promise<string | null> {\n if (cachedUserId) return cachedUserId\n const { data } = await client().auth.getUser()\n cachedUserId = data?.user?.id ?? null\n return cachedUserId\n}\n\n// ── row mappers ───────────────────────────────────────────────────────────\n\nfunction toRequest(r: any): SupportRequest {\n return {\n id: r.id,\n tenantId: r.tenant_id,\n number: r.number,\n kind: r.kind,\n subject: r.subject ?? null,\n description: r.description,\n status: r.status,\n route: r.route ?? null,\n unitId: r.unit_id ?? null,\n screenSnapshot: r.screen_snapshot ?? {},\n originConversationId: r.origin_conversation_id ?? null,\n area: r.area ?? null,\n createdBy: r.created_by,\n createdAt: r.created_at,\n updatedAt: r.updated_at,\n resolvedAt: r.resolved_at ?? null,\n metadata: r.metadata ?? {},\n }\n}\n\nconst toMessage = (r: any): SupportMessage => ({\n id: r.id, requestId: r.request_id, authorKind: r.author_kind,\n authorId: r.author_id ?? null, body: r.body, createdAt: r.created_at,\n})\n\nconst toEvent = (r: any): SupportEvent => ({\n id: r.id, requestId: r.request_id, type: r.type, note: r.note ?? null, createdAt: r.created_at,\n})\n\nconst toAttachment = (r: any): SupportAttachment => ({\n id: r.id, requestId: r.request_id, storagePath: r.storage_path, name: r.name,\n sizeBytes: r.size_bytes ?? null, contentType: r.content_type ?? null, createdAt: r.created_at,\n})\n\n// ── reads ─────────────────────────────────────────────────────────────────\n\nexport async function listRequests(scope: SupportScope, filter: SupportFilter): Promise<SupportRequest[]> {\n let q = client().from('plg_support_requests').select('*').order('number', { ascending: false })\n if (scope === 'mine') {\n const uid = await userId()\n if (uid) q = q.eq('created_by', uid)\n }\n if (filter !== 'all') {\n const statuses = FILTER_STATUSES[filter]\n q = q.in('status', statuses)\n }\n const { data, error } = await q\n if (error) throw error\n return (data ?? []).map(toRequest)\n}\n\nexport async function getRequestDetail(id: string): Promise<SupportRequestDetail | null> {\n const { data: req, error } = await client().from('plg_support_requests').select('*').eq('id', id).maybeSingle()\n if (error) throw error\n if (!req) return null\n const [messages, events, attachments] = await Promise.all([\n client().from('plg_support_request_messages').select('*').eq('request_id', id).order('created_at', { ascending: true }),\n client().from('plg_support_request_events').select('*').eq('request_id', id).order('created_at', { ascending: true }),\n client().from('plg_support_request_attachments').select('*').eq('request_id', id).order('created_at', { ascending: true }),\n ])\n return {\n ...toRequest(req),\n messages: (messages.data ?? []).map(toMessage),\n events: (events.data ?? []).map(toEvent),\n attachments: (attachments.data ?? []).map(toAttachment),\n }\n}\n\nexport async function unreadCount(): Promise<number> {\n const uid = await userId()\n if (!uid) return 0\n const { count, error } = await client()\n .from('plg_support_notifications')\n .select('id', { count: 'exact', head: true })\n .eq('user_id', uid)\n .is('read_at', null)\n if (error) return 0\n return count ?? 0\n}\n\n// ── writes ──────────────────────────────────────────────────────────────\n\nexport async function createRequest(input: CreateSupportRequestInput): Promise<{ id: string; number: number }> {\n const tenantId = getActiveTenantId()\n if (!tenantId) throw new Error('Sem conta ativa')\n const subject = (input.subject && input.subject.trim()) || deriveSubject(input.description)\n const row = {\n tenant_id: tenantId,\n kind: input.kind,\n subject,\n description: input.description.trim(),\n route: input.route ?? null,\n unit_id: input.unitId ?? getActiveUnitId() ?? null,\n screen_snapshot: input.screenSnapshot ?? {},\n origin_conversation_id: input.originConversationId ?? null,\n area: input.area ?? null,\n status: 'received' as SupportStatus,\n }\n const { data, error } = await client().from('plg_support_requests').insert(row).select('id, number').single()\n if (error) throw error\n await client().from('plg_support_request_events').insert({\n tenant_id: tenantId, request_id: data.id, type: 'opened', note: null,\n })\n return { id: data.id, number: data.number }\n}\n\nexport async function uploadAttachment(requestId: string, pending: PendingAttachment): Promise<SupportAttachment> {\n const tenantId = getActiveTenantId()\n if (!tenantId) throw new Error('Sem conta ativa')\n const safeName = pending.name.replace(/[^\\w.\\-]+/g, '_')\n const path = `${tenantId}/${requestId}/${crypto.randomUUID()}/${safeName}`\n const up = await client().storage.from(SUPPORT_BUCKET).upload(path, pending.file, {\n contentType: pending.contentType, upsert: false,\n })\n if (up.error) throw up.error\n const { data, error } = await client().from('plg_support_request_attachments').insert({\n tenant_id: tenantId, request_id: requestId, storage_path: path,\n name: pending.name, size_bytes: pending.sizeBytes, content_type: pending.contentType,\n }).select('*').single()\n if (error) throw error\n return toAttachment(data)\n}\n\nexport async function addMessage(requestId: string, body: string): Promise<SupportMessage> {\n const tenantId = getActiveTenantId()\n if (!tenantId) throw new Error('Sem conta ativa')\n const { data, error } = await client().from('plg_support_request_messages').insert({\n tenant_id: tenantId, request_id: requestId, author_kind: 'user', body: body.trim(),\n }).select('*').single()\n if (error) throw error\n return toMessage(data)\n}\n\nexport async function cancelRequest(requestId: string): Promise<void> {\n const tenantId = getActiveTenantId()\n if (!tenantId) throw new Error('Sem conta ativa')\n const { error } = await client().from('plg_support_requests')\n .update({ status: 'cancelled' as SupportStatus }).eq('id', requestId)\n if (error) throw error\n await client().from('plg_support_request_events').insert({\n tenant_id: tenantId, request_id: requestId, type: 'cancelled', note: null,\n })\n}\n\nexport async function validateRequest(requestId: string, ok: boolean, reason?: string): Promise<void> {\n const tenantId = getActiveTenantId()\n if (!tenantId) throw new Error('Sem conta ativa')\n const nextStatus: SupportStatus = ok ? 'closed' : 'received'\n const { error } = await client().from('plg_support_requests')\n .update({ status: nextStatus }).eq('id', requestId)\n if (error) throw error\n await client().from('plg_support_request_events').insert({\n tenant_id: tenantId, request_id: requestId,\n type: ok ? 'validated_ok' : 'reopened', note: ok ? null : (reason ?? null),\n })\n}\n\nexport async function markNotificationsRead(requestId: string): Promise<void> {\n const uid = await userId()\n if (!uid) return\n await client().from('plg_support_notifications')\n .update({ read_at: new Date().toISOString() })\n .eq('request_id', requestId).eq('user_id', uid).is('read_at', null)\n}\n\nexport async function signedUrl(storagePath: string, expiresIn = 300): Promise<string | null> {\n const { data, error } = await client().storage.from(SUPPORT_BUCKET).createSignedUrl(storagePath, expiresIn)\n if (error) return null\n return data?.signedUrl ?? null\n}\n"]}
@@ -0,0 +1,131 @@
1
+ import { validateRequest, cancelRequest, addMessage, createRequest, uploadAttachment, unreadCount, getRequestDetail, markNotificationsRead, listRequests } from './chunk-7NVBUXTX.js';
2
+ import { create } from 'zustand';
3
+
4
+ // src/lib/content.ts
5
+ var defaultFaq = [
6
+ {
7
+ id: "faq-primeiros-passos",
8
+ category: "Primeiros passos",
9
+ question: "Por onde come\xE7o a configurar minha conta?",
10
+ answer: "Abra Configura\xE7\xF5es e siga o assistente de setup. Ele te leva pelos cadastros essenciais \u2014 formas de pagamento, equipe e dados da empresa \u2014 na ordem certa. O Dico (o bot\xE3o flutuante) pode te guiar passo a passo em cada tela."
11
+ },
12
+ {
13
+ id: "faq-equipe",
14
+ category: "Equipe e acesso",
15
+ question: "Como adiciono algu\xE9m da minha equipe e defino o que ela pode ver?",
16
+ answer: "Em Configura\xE7\xF5es \u2192 Equipe voc\xEA convida a pessoa por e-mail e escolhe o papel dela. O papel define quais telas e a\xE7\xF5es ficam liberadas. Voc\xEA pode ajustar permiss\xE3o a permiss\xE3o quando precisar."
17
+ },
18
+ {
19
+ id: "faq-pagamento",
20
+ category: "Financeiro",
21
+ question: "Como recebo pagamentos pelos pedidos?",
22
+ answer: "Cadastre suas formas de pagamento em Financeiro e, para Pix e cart\xE3o, conecte o provedor de pagamento. Depois disso a cobran\xE7a nasce junto do pedido e o recebimento aparece no extrato."
23
+ },
24
+ {
25
+ id: "faq-dico",
26
+ category: "Dico (assistente)",
27
+ question: "O que o Dico pode fazer por mim?",
28
+ answer: "O Dico responde d\xFAvidas sobre qualquer tela, abre a tela certa e mostra onde clicar \u2014 sempre dentro do seu acesso. Ele nunca salva nada sozinho: prop\xF5e, e quem confirma \xE9 voc\xEA."
29
+ }
30
+ ];
31
+ var defaultWhatsNew = [
32
+ {
33
+ id: "wn-suporte",
34
+ date: "2026-10-16",
35
+ title: "Central de Suporte no ar",
36
+ note: "Agora voc\xEA abre e acompanha suas solicita\xE7\xF5es por aqui, sem sair do sistema."
37
+ }
38
+ ];
39
+ var defaultStatusComponents = [
40
+ { id: "app", label: "Aplicativo" },
41
+ { id: "data", label: "Dados e sincroniza\xE7\xE3o" }
42
+ ];
43
+ function toEmbedUrl(url) {
44
+ const yt = url.match(/(?:youtu\.be\/|youtube\.com\/(?:watch\?v=|embed\/))([\w-]{6,})/);
45
+ if (yt) return `https://www.youtube.com/embed/${yt[1]}`;
46
+ const vimeo = url.match(/vimeo\.com\/(\d+)/);
47
+ if (vimeo) return `https://player.vimeo.com/video/${vimeo[1]}`;
48
+ return url;
49
+ }
50
+ var useSupportStore = create((set, get) => ({
51
+ requests: [],
52
+ loadingList: false,
53
+ scope: "mine",
54
+ filter: "all",
55
+ selectedId: null,
56
+ detail: null,
57
+ loadingDetail: false,
58
+ unread: 0,
59
+ setScope: (scope) => {
60
+ set({ scope });
61
+ void get().loadList();
62
+ },
63
+ setFilter: (filter) => {
64
+ set({ filter });
65
+ void get().loadList();
66
+ },
67
+ loadList: async () => {
68
+ set({ loadingList: true });
69
+ try {
70
+ const requests = await listRequests(get().scope, get().filter);
71
+ set({ requests });
72
+ } finally {
73
+ set({ loadingList: false });
74
+ }
75
+ },
76
+ select: async (id) => {
77
+ set({ selectedId: id, detail: null });
78
+ if (!id) return;
79
+ set({ loadingDetail: true });
80
+ try {
81
+ const detail = await getRequestDetail(id);
82
+ set({ detail });
83
+ await markNotificationsRead(id);
84
+ void get().refreshUnread();
85
+ } finally {
86
+ set({ loadingDetail: false });
87
+ }
88
+ },
89
+ refreshDetail: async () => {
90
+ const id = get().selectedId;
91
+ if (!id) return;
92
+ const detail = await getRequestDetail(id);
93
+ set({ detail });
94
+ },
95
+ refreshUnread: async () => {
96
+ set({ unread: await unreadCount() });
97
+ },
98
+ create: async (input, attachments) => {
99
+ const res = await createRequest(input);
100
+ for (const pending of attachments.slice(0, 8)) {
101
+ try {
102
+ await uploadAttachment(res.id, pending);
103
+ } catch {
104
+ }
105
+ }
106
+ await get().loadList();
107
+ return res;
108
+ },
109
+ sendMessage: async (body) => {
110
+ const id = get().selectedId;
111
+ if (!id || !body.trim()) return;
112
+ await addMessage(id, body);
113
+ await get().refreshDetail();
114
+ },
115
+ cancel: async () => {
116
+ const id = get().selectedId;
117
+ if (!id) return;
118
+ await cancelRequest(id);
119
+ await Promise.all([get().refreshDetail(), get().loadList()]);
120
+ },
121
+ validate: async (ok, reason) => {
122
+ const id = get().selectedId;
123
+ if (!id) return;
124
+ await validateRequest(id, ok, reason);
125
+ await Promise.all([get().refreshDetail(), get().loadList()]);
126
+ }
127
+ }));
128
+
129
+ export { defaultFaq, defaultStatusComponents, defaultWhatsNew, toEmbedUrl, useSupportStore };
130
+ //# sourceMappingURL=chunk-JJPB2T3Z.js.map
131
+ //# sourceMappingURL=chunk-JJPB2T3Z.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/content.ts","../src/store.ts"],"names":[],"mappings":"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","file":"chunk-JJPB2T3Z.js","sourcesContent":["// Configurable content for the Support Center. The app overrides these via\n// createSupportPlugin({ faq, whatsNew, ... }); the defaults below are a minimal\n// sample so a fresh install is not empty. End-user text is pt-BR.\n//\n// This is deliberately NOT a database table: it is editorial content that ships\n// with the app (like a help article), not per-tenant data. A real knowledge base\n// backed by a table/CMS can replace `defaultFaq` later without touching the UI.\n\nexport interface FaqItem {\n id: string\n question: string\n answer: string\n /** YouTube/Vimeo watch or embed URL, or a direct mp4 — optional explainer video. */\n videoUrl?: string\n category?: string\n}\n\nexport interface WhatsNewItem {\n id: string\n date: string // ISO; rendered pt-BR\n title: string\n note?: string\n}\n\nexport const defaultFaq: FaqItem[] = [\n {\n id: 'faq-primeiros-passos',\n category: 'Primeiros passos',\n question: 'Por onde começo a configurar minha conta?',\n answer:\n 'Abra Configurações e siga o assistente de setup. Ele te leva pelos cadastros essenciais — formas de pagamento, equipe e dados da empresa — na ordem certa. O Dico (o botão flutuante) pode te guiar passo a passo em cada tela.',\n },\n {\n id: 'faq-equipe',\n category: 'Equipe e acesso',\n question: 'Como adiciono alguém da minha equipe e defino o que ela pode ver?',\n answer:\n 'Em Configurações → Equipe você convida a pessoa por e-mail e escolhe o papel dela. O papel define quais telas e ações ficam liberadas. Você pode ajustar permissão a permissão quando precisar.',\n },\n {\n id: 'faq-pagamento',\n category: 'Financeiro',\n question: 'Como recebo pagamentos pelos pedidos?',\n answer:\n 'Cadastre suas formas de pagamento em Financeiro e, para Pix e cartão, conecte o provedor de pagamento. Depois disso a cobrança nasce junto do pedido e o recebimento aparece no extrato.',\n },\n {\n id: 'faq-dico',\n category: 'Dico (assistente)',\n question: 'O que o Dico pode fazer por mim?',\n answer:\n 'O Dico responde dúvidas sobre qualquer tela, abre a tela certa e mostra onde clicar — sempre dentro do seu acesso. Ele nunca salva nada sozinho: propõe, e quem confirma é você.',\n },\n]\n\nexport const defaultWhatsNew: WhatsNewItem[] = [\n {\n id: 'wn-suporte',\n date: '2026-10-16',\n title: 'Central de Suporte no ar',\n note: 'Agora você abre e acompanha suas solicitações por aqui, sem sair do sistema.',\n },\n]\n\n// A status component the app can declare; its live state is resolved by the UI\n// (connection) or left as a static label until a real status source is wired.\nexport interface StatusComponent {\n id: string\n label: string\n}\n\nexport const defaultStatusComponents: StatusComponent[] = [\n { id: 'app', label: 'Aplicativo' },\n { id: 'data', label: 'Dados e sincronização' },\n]\n\n/** Turn a watch/share URL into an embeddable one; pass through direct files. */\nexport function toEmbedUrl(url: string): string {\n const yt = url.match(/(?:youtu\\.be\\/|youtube\\.com\\/(?:watch\\?v=|embed\\/))([\\w-]{6,})/)\n if (yt) return `https://www.youtube.com/embed/${yt[1]}`\n const vimeo = url.match(/vimeo\\.com\\/(\\d+)/)\n if (vimeo) return `https://player.vimeo.com/video/${vimeo[1]}`\n return url\n}\n","import { create } from 'zustand'\nimport type {\n SupportRequest, SupportRequestDetail, SupportScope, SupportFilter,\n CreateSupportRequestInput, PendingAttachment,\n} from './types'\nimport * as api from './data/api'\n\ninterface SupportState {\n requests: SupportRequest[]\n loadingList: boolean\n scope: SupportScope\n filter: SupportFilter\n\n selectedId: string | null\n detail: SupportRequestDetail | null\n loadingDetail: boolean\n\n unread: number\n\n setScope: (scope: SupportScope) => void\n setFilter: (filter: SupportFilter) => void\n loadList: () => Promise<void>\n select: (id: string | null) => Promise<void>\n refreshDetail: () => Promise<void>\n refreshUnread: () => Promise<void>\n\n create: (input: CreateSupportRequestInput, attachments: PendingAttachment[]) => Promise<{ id: string; number: number }>\n sendMessage: (body: string) => Promise<void>\n cancel: () => Promise<void>\n validate: (ok: boolean, reason?: string) => Promise<void>\n}\n\nexport const useSupportStore = create<SupportState>((set, get) => ({\n requests: [],\n loadingList: false,\n scope: 'mine',\n filter: 'all',\n selectedId: null,\n detail: null,\n loadingDetail: false,\n unread: 0,\n\n setScope: (scope) => { set({ scope }); void get().loadList() },\n setFilter: (filter) => { set({ filter }); void get().loadList() },\n\n loadList: async () => {\n set({ loadingList: true })\n try {\n const requests = await api.listRequests(get().scope, get().filter)\n set({ requests })\n } finally {\n set({ loadingList: false })\n }\n },\n\n select: async (id) => {\n set({ selectedId: id, detail: null })\n if (!id) return\n set({ loadingDetail: true })\n try {\n const detail = await api.getRequestDetail(id)\n set({ detail })\n // Opening a request clears its \"esperando você\" notices.\n await api.markNotificationsRead(id)\n void get().refreshUnread()\n } finally {\n set({ loadingDetail: false })\n }\n },\n\n refreshDetail: async () => {\n const id = get().selectedId\n if (!id) return\n const detail = await api.getRequestDetail(id)\n set({ detail })\n },\n\n refreshUnread: async () => {\n set({ unread: await api.unreadCount() })\n },\n\n create: async (input, attachments) => {\n const res = await api.createRequest(input)\n // Attachments are a second step: the file can only land once the request exists.\n for (const pending of attachments.slice(0, 8)) {\n try {\n await api.uploadAttachment(res.id, pending)\n } catch {\n // One failed upload does not fail the request — V1 opens it anyway and\n // lets the file be re-sent inside the request.\n }\n }\n await get().loadList()\n return res\n },\n\n sendMessage: async (body) => {\n const id = get().selectedId\n if (!id || !body.trim()) return\n await api.addMessage(id, body)\n await get().refreshDetail()\n },\n\n cancel: async () => {\n const id = get().selectedId\n if (!id) return\n await api.cancelRequest(id)\n await Promise.all([get().refreshDetail(), get().loadList()])\n },\n\n validate: async (ok, reason) => {\n const id = get().selectedId\n if (!id) return\n await api.validateRequest(id, ok, reason)\n await Promise.all([get().refreshDetail(), get().loadList()])\n },\n}))\n"]}
@@ -0,0 +1,12 @@
1
+ import React from 'react';
2
+ /**
3
+ * The helpdesk's chamados grouped by status. With `tenantId` it is ONE store's
4
+ * tickets — drawn inside that store's CRM card (the store is resolved from the
5
+ * CRM company's metadata.tenant_id_v2). Without it, every store's. Reads the same
6
+ * cross-tenant console RPC (operator-gated); each row deep-links to the ticket.
7
+ */
8
+ export declare function HelpdeskStatusPanel({ tenantId }?: {
9
+ tenantId?: string;
10
+ }): React.ReactElement;
11
+ export default HelpdeskStatusPanel;
12
+ //# sourceMappingURL=HelpdeskStatusPanel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HelpdeskStatusPanel.d.ts","sourceRoot":"","sources":["../../src/components/HelpdeskStatusPanel.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAczB;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,EAAE,QAAQ,EAAE,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,KAAK,CAAC,YAAY,CAmDhG;AAED,eAAe,mBAAmB,CAAA"}
@@ -0,0 +1,15 @@
1
+ import { type SupportRequest, type SupportRequestDetail, type SupportMessage, type SupportAttachment, type CreateSupportRequestInput, type PendingAttachment, type SupportScope, type SupportFilter } from '../types';
2
+ export declare function listRequests(scope: SupportScope, filter: SupportFilter): Promise<SupportRequest[]>;
3
+ export declare function getRequestDetail(id: string): Promise<SupportRequestDetail | null>;
4
+ export declare function unreadCount(): Promise<number>;
5
+ export declare function createRequest(input: CreateSupportRequestInput): Promise<{
6
+ id: string;
7
+ number: number;
8
+ }>;
9
+ export declare function uploadAttachment(requestId: string, pending: PendingAttachment): Promise<SupportAttachment>;
10
+ export declare function addMessage(requestId: string, body: string): Promise<SupportMessage>;
11
+ export declare function cancelRequest(requestId: string): Promise<void>;
12
+ export declare function validateRequest(requestId: string, ok: boolean, reason?: string): Promise<void>;
13
+ export declare function markNotificationsRead(requestId: string): Promise<void>;
14
+ export declare function signedUrl(storagePath: string, expiresIn?: number): Promise<string | null>;
15
+ //# sourceMappingURL=api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/data/api.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,cAAc,EAAE,KAAK,oBAAoB,EAAE,KAAK,cAAc,EAChD,KAAK,iBAAiB,EAAE,KAAK,yBAAyB,EACzE,KAAK,iBAAiB,EAAE,KAAK,YAAY,EAAE,KAAK,aAAa,EAE9D,MAAM,UAAU,CAAA;AAgEjB,wBAAsB,YAAY,CAAC,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAaxG;AAED,wBAAsB,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAevF;AAED,wBAAsB,WAAW,IAAI,OAAO,CAAC,MAAM,CAAC,CAUnD;AAID,wBAAsB,aAAa,CAAC,KAAK,EAAE,yBAAyB,GAAG,OAAO,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAsB7G;AAED,wBAAsB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAehH;AAED,wBAAsB,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAQzF;AAED,wBAAsB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CASpE;AAED,wBAAsB,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAWpG;AAED,wBAAsB,qBAAqB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAM5E;AAED,wBAAsB,SAAS,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,SAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAI5F"}
@@ -0,0 +1,37 @@
1
+ import type { SupportKind, SupportStatus, SupportPriority, SupportMessage, SupportEvent, SupportAttachment } from '../types';
2
+ /** A request as the operator sees it — with the originating tenant and a count. */
3
+ export interface ConsoleRequest {
4
+ id: string;
5
+ number: number;
6
+ kind: SupportKind;
7
+ subject: string | null;
8
+ description: string;
9
+ status: SupportStatus;
10
+ priority: SupportPriority;
11
+ route: string | null;
12
+ createdBy: string;
13
+ createdByName: string | null;
14
+ createdAt: string;
15
+ updatedAt: string;
16
+ tenantId: string;
17
+ tenantName: string | null;
18
+ tenantSlug: string | null;
19
+ linkedLeadId: string | null;
20
+ linkedLeadName: string | null;
21
+ messageCount: number;
22
+ }
23
+ export interface ConsoleDetail extends ConsoleRequest {
24
+ screenSnapshot: Record<string, unknown>;
25
+ messages: SupportMessage[];
26
+ events: SupportEvent[];
27
+ attachments: SupportAttachment[];
28
+ }
29
+ /** Is the signed-in user a helpdesk operator? Gates the console menu entry. */
30
+ export declare function isOperator(): Promise<boolean>;
31
+ export declare function consoleListRequests(statuses?: SupportStatus[], search?: string): Promise<ConsoleRequest[]>;
32
+ export declare function consoleDetail(requestId: string): Promise<ConsoleDetail | null>;
33
+ export declare function consoleSetStatus(requestId: string, status: SupportStatus, note?: string): Promise<ConsoleDetail | null>;
34
+ export declare function consoleSetPriority(requestId: string, priority: SupportPriority): Promise<ConsoleDetail | null>;
35
+ export declare function consoleReply(requestId: string, body: string): Promise<ConsoleDetail | null>;
36
+ export declare function consoleLinkLead(requestId: string, leadId: string, leadName?: string): Promise<ConsoleDetail | null>;
37
+ //# sourceMappingURL=console.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"console.d.ts","sourceRoot":"","sources":["../../src/data/console.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,WAAW,EAAE,aAAa,EAAE,eAAe,EAC3C,cAAc,EAAE,YAAY,EAAE,iBAAiB,EAChD,MAAM,UAAU,CAAA;AAejB,mFAAmF;AACnF,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAA;IACV,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,WAAW,CAAA;IACjB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,WAAW,EAAE,MAAM,CAAA;IACnB,MAAM,EAAE,aAAa,CAAA;IACrB,QAAQ,EAAE,eAAe,CAAA;IACzB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,SAAS,EAAE,MAAM,CAAA;IACjB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,YAAY,EAAE,MAAM,CAAA;CACrB;AAED,MAAM,WAAW,aAAc,SAAQ,cAAc;IACnD,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACvC,QAAQ,EAAE,cAAc,EAAE,CAAA;IAC1B,MAAM,EAAE,YAAY,EAAE,CAAA;IACtB,WAAW,EAAE,iBAAiB,EAAE,CAAA;CACjC;AAsCD,+EAA+E;AAC/E,wBAAsB,UAAU,IAAI,OAAO,CAAC,OAAO,CAAC,CAQnD;AAED,wBAAsB,mBAAmB,CAAC,QAAQ,CAAC,EAAE,aAAa,EAAE,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAOhH;AAED,wBAAsB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAIpF;AAED,wBAAsB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAI7H;AAED,wBAAsB,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,eAAe,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAIpH;AAED,wBAAsB,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAIjG;AAED,wBAAsB,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAIzH"}
@@ -0,0 +1,19 @@
1
+ import type { PluginManifest, PluginScope, VerticalId } from '@fayz-ai/core';
2
+ import { type FaqItem, type WhatsNewItem, type StatusComponent } from './lib/content';
3
+ export interface SupportPluginOptions {
4
+ /** Label for the plugin and the user-menu entry (default "Suporte"). */
5
+ label?: string;
6
+ scope?: PluginScope;
7
+ verticalId?: VerticalId;
8
+ /** Knowledge-base content — overrides the sample FAQ that ships by default. */
9
+ faq?: FaqItem[];
10
+ /** Release notes shown in the "Novidades" card. */
11
+ whatsNew?: WhatsNewItem[];
12
+ /** Status components listed under the Status card. */
13
+ statusComponents?: StatusComponent[];
14
+ }
15
+ export declare function createSupportPlugin(options?: SupportPluginOptions): PluginManifest;
16
+ export { useSupportStore } from './store';
17
+ export { HelpdeskStatusPanel } from './components/HelpdeskStatusPanel';
18
+ export * from './types';
19
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAI5E,OAAO,EAEL,KAAK,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,eAAe,EACtD,MAAM,eAAe,CAAA;AAmBtB,MAAM,WAAW,oBAAoB;IACnC,wEAAwE;IACxE,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,KAAK,CAAC,EAAE,WAAW,CAAA;IACnB,UAAU,CAAC,EAAE,UAAU,CAAA;IACvB,+EAA+E;IAC/E,GAAG,CAAC,EAAE,OAAO,EAAE,CAAA;IACf,mDAAmD;IACnD,QAAQ,CAAC,EAAE,YAAY,EAAE,CAAA;IACzB,sDAAsD;IACtD,gBAAgB,CAAC,EAAE,eAAe,EAAE,CAAA;CACrC;AAED,wBAAgB,mBAAmB,CAAC,OAAO,CAAC,EAAE,oBAAoB,GAAG,cAAc,CAwGlF;AAED,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAGzC,OAAO,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAA;AACtE,cAAc,SAAS,CAAA"}