evo360-types 1.3.391 → 1.3.394
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.
- package/dist/apps/evo-notifications/zod-schemas.d.ts +7 -4
- package/dist/apps/evo-notifications/zod-schemas.js +2 -0
- package/dist/apps/evo-notifications/zod-schemas.ts +2 -0
- package/dist/types/evo-crm/dic/index.d.ts +10 -0
- package/dist/types/evo-crm/dic/index.ts +12 -0
- package/dist/types/evo-crm/lead/index.d.ts +11 -2
- package/dist/types/evo-crm/lead/index.ts +20 -1
- package/dist/types/evo-notifications/index.d.ts +2 -0
- package/dist/types/evo-notifications/index.js +1 -0
- package/dist/types/evo-notifications/index.ts +2 -0
- package/dist/types/nex-tenants/chatbee-import.d.ts +102 -1
- package/dist/types/nex-tenants/chatbee-import.js +19 -1
- package/dist/types/nex-tenants/chatbee-import.ts +114 -1
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
export declare const zNotificationChannelTypeSchema: z.ZodEnum<["whatsapp", "email", "sms", "push"]>;
|
|
3
|
-
export declare const zNotificationMessageStatusSchema: z.ZodEnum<["queued", "processing", "sent", "delivered", "failed", "cancelled", "suppressed"]>;
|
|
3
|
+
export declare const zNotificationMessageStatusSchema: z.ZodEnum<["queued", "processing", "sent", "delivered", "read", "failed", "cancelled", "suppressed"]>;
|
|
4
4
|
export declare const zNotificationAttemptStatusSchema: z.ZodEnum<["success", "retry", "error"]>;
|
|
5
5
|
export declare const zNotificationRecipientKindSchema: z.ZodEnum<["patient", "professional", "preconfigured", "raw"]>;
|
|
6
6
|
export declare const zNotificationLogLevelSchema: z.ZodEnum<["debug", "info", "warn", "error"]>;
|
|
@@ -139,11 +139,12 @@ export declare const zNotificationMessageSchema: z.ZodObject<{
|
|
|
139
139
|
display_name: z.ZodOptional<z.ZodString>;
|
|
140
140
|
}, z.ZodTypeAny, "passthrough">>;
|
|
141
141
|
variables: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
142
|
-
status: z.ZodEnum<["queued", "processing", "sent", "delivered", "failed", "cancelled", "suppressed"]>;
|
|
142
|
+
status: z.ZodEnum<["queued", "processing", "sent", "delivered", "read", "failed", "cancelled", "suppressed"]>;
|
|
143
143
|
queued_at: z.ZodDate;
|
|
144
144
|
processing_started_at: z.ZodOptional<z.ZodDate>;
|
|
145
145
|
sent_at: z.ZodOptional<z.ZodDate>;
|
|
146
146
|
delivered_at: z.ZodOptional<z.ZodDate>;
|
|
147
|
+
read_at: z.ZodOptional<z.ZodDate>;
|
|
147
148
|
completed_at: z.ZodOptional<z.ZodDate>;
|
|
148
149
|
provider: z.ZodOptional<z.ZodObject<{
|
|
149
150
|
name: z.ZodOptional<z.ZodString>;
|
|
@@ -255,11 +256,12 @@ export declare const zNotificationMessageSchema: z.ZodObject<{
|
|
|
255
256
|
display_name: z.ZodOptional<z.ZodString>;
|
|
256
257
|
}, z.ZodTypeAny, "passthrough">>;
|
|
257
258
|
variables: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
258
|
-
status: z.ZodEnum<["queued", "processing", "sent", "delivered", "failed", "cancelled", "suppressed"]>;
|
|
259
|
+
status: z.ZodEnum<["queued", "processing", "sent", "delivered", "read", "failed", "cancelled", "suppressed"]>;
|
|
259
260
|
queued_at: z.ZodDate;
|
|
260
261
|
processing_started_at: z.ZodOptional<z.ZodDate>;
|
|
261
262
|
sent_at: z.ZodOptional<z.ZodDate>;
|
|
262
263
|
delivered_at: z.ZodOptional<z.ZodDate>;
|
|
264
|
+
read_at: z.ZodOptional<z.ZodDate>;
|
|
263
265
|
completed_at: z.ZodOptional<z.ZodDate>;
|
|
264
266
|
provider: z.ZodOptional<z.ZodObject<{
|
|
265
267
|
name: z.ZodOptional<z.ZodString>;
|
|
@@ -371,11 +373,12 @@ export declare const zNotificationMessageSchema: z.ZodObject<{
|
|
|
371
373
|
display_name: z.ZodOptional<z.ZodString>;
|
|
372
374
|
}, z.ZodTypeAny, "passthrough">>;
|
|
373
375
|
variables: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
374
|
-
status: z.ZodEnum<["queued", "processing", "sent", "delivered", "failed", "cancelled", "suppressed"]>;
|
|
376
|
+
status: z.ZodEnum<["queued", "processing", "sent", "delivered", "read", "failed", "cancelled", "suppressed"]>;
|
|
375
377
|
queued_at: z.ZodDate;
|
|
376
378
|
processing_started_at: z.ZodOptional<z.ZodDate>;
|
|
377
379
|
sent_at: z.ZodOptional<z.ZodDate>;
|
|
378
380
|
delivered_at: z.ZodOptional<z.ZodDate>;
|
|
381
|
+
read_at: z.ZodOptional<z.ZodDate>;
|
|
379
382
|
completed_at: z.ZodOptional<z.ZodDate>;
|
|
380
383
|
provider: z.ZodOptional<z.ZodObject<{
|
|
381
384
|
name: z.ZodOptional<z.ZodString>;
|
|
@@ -16,6 +16,7 @@ exports.zNotificationMessageStatusSchema = zod_1.z.enum([
|
|
|
16
16
|
"processing",
|
|
17
17
|
"sent",
|
|
18
18
|
"delivered",
|
|
19
|
+
"read",
|
|
19
20
|
"failed",
|
|
20
21
|
"cancelled",
|
|
21
22
|
"suppressed",
|
|
@@ -95,6 +96,7 @@ exports.zNotificationMessageSchema = zod_schemas_1.zFireDocSchema
|
|
|
95
96
|
processing_started_at: zod_1.z.coerce.date().optional(),
|
|
96
97
|
sent_at: zod_1.z.coerce.date().optional(),
|
|
97
98
|
delivered_at: zod_1.z.coerce.date().optional(),
|
|
99
|
+
read_at: zod_1.z.coerce.date().optional(),
|
|
98
100
|
completed_at: zod_1.z.coerce.date().optional(),
|
|
99
101
|
provider: exports.zNotificationProviderMetaSchema.optional(),
|
|
100
102
|
error: exports.zNotificationErrorSchema.optional(),
|
|
@@ -16,6 +16,7 @@ export const zNotificationMessageStatusSchema = z.enum([
|
|
|
16
16
|
"processing",
|
|
17
17
|
"sent",
|
|
18
18
|
"delivered",
|
|
19
|
+
"read",
|
|
19
20
|
"failed",
|
|
20
21
|
"cancelled",
|
|
21
22
|
"suppressed",
|
|
@@ -106,6 +107,7 @@ export const zNotificationMessageSchema = zFireDocSchema
|
|
|
106
107
|
processing_started_at: z.coerce.date().optional(),
|
|
107
108
|
sent_at: z.coerce.date().optional(),
|
|
108
109
|
delivered_at: z.coerce.date().optional(),
|
|
110
|
+
read_at: z.coerce.date().optional(),
|
|
109
111
|
completed_at: z.coerce.date().optional(),
|
|
110
112
|
provider: zNotificationProviderMetaSchema.optional(),
|
|
111
113
|
error: zNotificationErrorSchema.optional(),
|
|
@@ -33,6 +33,16 @@ export interface IDistChannel extends IFireDoc {
|
|
|
33
33
|
type_name?: string;
|
|
34
34
|
typeRef?: FirestoreDocumentReference;
|
|
35
35
|
tags?: ITag[] | null;
|
|
36
|
+
rules?: {
|
|
37
|
+
regex: string;
|
|
38
|
+
flags?: string;
|
|
39
|
+
priority?: number;
|
|
40
|
+
description?: string | null;
|
|
41
|
+
examples?: string[] | null;
|
|
42
|
+
} | null;
|
|
43
|
+
/** Tipo FINO de aquisição (feat-039) — override explícito da derivação por `code`.
|
|
44
|
+
* String crua p/ evitar ciclo de import com ../lead; o core estreita p/ LeadReferralType. */
|
|
45
|
+
acq_type?: string | null;
|
|
36
46
|
[key: string]: unknown;
|
|
37
47
|
}
|
|
38
48
|
export type QualificationFunnelLevel = "invalid" | "new" | "trying_contact" | "contacted" | "qualifying" | "qualified" | "disqualified" | "presented" | "proposal_sent" | "negotiating" | "won" | "lost" | "waiting_reply" | "follow_up_later";
|
|
@@ -54,6 +54,18 @@ export interface IDistChannel extends IFireDoc {
|
|
|
54
54
|
type_name?: string;
|
|
55
55
|
typeRef?: FirestoreDocumentReference;
|
|
56
56
|
tags?: ITag[] | null;
|
|
57
|
+
// Regras de parsing da 1ª mensagem (feat-039) — regex casada contra o texto
|
|
58
|
+
// p/ atribuir este canal; `priority` ordena entre múltiplos matches.
|
|
59
|
+
rules?: {
|
|
60
|
+
regex: string;
|
|
61
|
+
flags?: string;
|
|
62
|
+
priority?: number;
|
|
63
|
+
description?: string | null;
|
|
64
|
+
examples?: string[] | null;
|
|
65
|
+
} | null;
|
|
66
|
+
/** Tipo FINO de aquisição (feat-039) — override explícito da derivação por `code`.
|
|
67
|
+
* String crua p/ evitar ciclo de import com ../lead; o core estreita p/ LeadReferralType. */
|
|
68
|
+
acq_type?: string | null;
|
|
57
69
|
[key: string]: unknown; // index signature
|
|
58
70
|
}
|
|
59
71
|
|
|
@@ -32,13 +32,15 @@ export interface ILeadDistChannel {
|
|
|
32
32
|
/** Origem de aquisição do lead (feat-038) — base expansível p/ múltiplas
|
|
33
33
|
* plataformas. v1 implementa só `type:'ctwa'` (Click-to-WhatsApp da Meta);
|
|
34
34
|
* Google Ads / Instagram Direct / TikTok / site ficam para fast-follow. */
|
|
35
|
-
export type LeadReferralType = "ctwa" | "instagram_direct" | "google_ads" | "tiktok" | "site" | "other";
|
|
35
|
+
export type LeadReferralType = "ctwa" | "instagram_direct" | "google_ads" | "tiktok" | "site" | "meta_organic" | "referral" | "other";
|
|
36
36
|
export interface ILeadReferralBase {
|
|
37
37
|
type: LeadReferralType;
|
|
38
38
|
captured_at?: Date | string | null;
|
|
39
39
|
source_url?: string | null;
|
|
40
40
|
label?: string | null;
|
|
41
41
|
raw?: Record<string, unknown> | null;
|
|
42
|
+
dist_channel_code?: string | null;
|
|
43
|
+
matched_rule?: string | null;
|
|
42
44
|
}
|
|
43
45
|
export interface ILeadReferralCTWA extends ILeadReferralBase {
|
|
44
46
|
type: "ctwa";
|
|
@@ -49,7 +51,14 @@ export interface ILeadReferralCTWA extends ILeadReferralBase {
|
|
|
49
51
|
media_type?: string | null;
|
|
50
52
|
ctwa_clid?: string | null;
|
|
51
53
|
}
|
|
52
|
-
|
|
54
|
+
/** Origem genérica derivada da 1ª mensagem (feat-039) — Google/TikTok/site/
|
|
55
|
+
* FB-IG orgânico/indicação. O `type` FINO vem do `acq_type` do dict ou do mapa
|
|
56
|
+
* por `code`; `dist_channel_code`/`matched_rule` ficam na base p/ auditoria. */
|
|
57
|
+
export interface ILeadReferralGeneric extends ILeadReferralBase {
|
|
58
|
+
type: "google_ads" | "tiktok" | "site" | "instagram_direct" | "meta_organic" | "referral" | "other";
|
|
59
|
+
headline?: string | null;
|
|
60
|
+
}
|
|
61
|
+
export type ILeadReferral = ILeadReferralCTWA | ILeadReferralGeneric;
|
|
53
62
|
/** Estado da régua de reativação de lead (feat-037).
|
|
54
63
|
* Mantido pelo subscriber `ticket_closed` (streak) e pela task
|
|
55
64
|
* `lead_reactivation` (último disparo). Semântica do streak alinhada ao
|
|
@@ -53,6 +53,8 @@ export type LeadReferralType =
|
|
|
53
53
|
| "google_ads" // gclid / token em ?text= ← futuro
|
|
54
54
|
| "tiktok"
|
|
55
55
|
| "site"
|
|
56
|
+
| "meta_organic" // menção/URL orgânica de FB/IG (não-pago, sem referral) ← feat-039
|
|
57
|
+
| "referral" // indicação ("me indicaram", "amigo falou") ← feat-039
|
|
56
58
|
| "other"; // ← futuro
|
|
57
59
|
|
|
58
60
|
export interface ILeadReferralBase {
|
|
@@ -61,6 +63,8 @@ export interface ILeadReferralBase {
|
|
|
61
63
|
source_url?: string | null; // link clicável
|
|
62
64
|
label?: string | null; // rótulo de exibição (headline || ad_id || domínio)
|
|
63
65
|
raw?: Record<string, unknown> | null; // payload original lossless → re-parse futuro sem backfill
|
|
66
|
+
dist_channel_code?: string | null; // code do entry do dict que casou (ex.: 'GOOGLE') ← feat-039
|
|
67
|
+
matched_rule?: string | null; // regex/priority que casou (debug; opcional) ← feat-039
|
|
64
68
|
}
|
|
65
69
|
|
|
66
70
|
export interface ILeadReferralCTWA extends ILeadReferralBase {
|
|
@@ -73,7 +77,22 @@ export interface ILeadReferralCTWA extends ILeadReferralBase {
|
|
|
73
77
|
ctwa_clid?: string | null; // espelho; o de topo (CAPI/feat-034) permanece na raiz
|
|
74
78
|
}
|
|
75
79
|
|
|
76
|
-
|
|
80
|
+
/** Origem genérica derivada da 1ª mensagem (feat-039) — Google/TikTok/site/
|
|
81
|
+
* FB-IG orgânico/indicação. O `type` FINO vem do `acq_type` do dict ou do mapa
|
|
82
|
+
* por `code`; `dist_channel_code`/`matched_rule` ficam na base p/ auditoria. */
|
|
83
|
+
export interface ILeadReferralGeneric extends ILeadReferralBase {
|
|
84
|
+
type:
|
|
85
|
+
| "google_ads"
|
|
86
|
+
| "tiktok"
|
|
87
|
+
| "site"
|
|
88
|
+
| "instagram_direct"
|
|
89
|
+
| "meta_organic"
|
|
90
|
+
| "referral"
|
|
91
|
+
| "other";
|
|
92
|
+
headline?: string | null; // paridade com CTWA p/ o FE existente
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export type ILeadReferral = ILeadReferralCTWA | ILeadReferralGeneric;
|
|
77
96
|
|
|
78
97
|
/** Estado da régua de reativação de lead (feat-037).
|
|
79
98
|
* Mantido pelo subscriber `ticket_closed` (streak) e pela task
|
|
@@ -37,6 +37,7 @@ export declare const NotificationMessageStatusEnum: {
|
|
|
37
37
|
readonly Processing: "processing";
|
|
38
38
|
readonly Sent: "sent";
|
|
39
39
|
readonly Delivered: "delivered";
|
|
40
|
+
readonly Read: "read";
|
|
40
41
|
readonly Failed: "failed";
|
|
41
42
|
readonly Cancelled: "cancelled";
|
|
42
43
|
readonly Suppressed: "suppressed";
|
|
@@ -130,6 +131,7 @@ export interface INotificationMessage extends IFireDoc {
|
|
|
130
131
|
processing_started_at?: Date;
|
|
131
132
|
sent_at?: Date;
|
|
132
133
|
delivered_at?: Date;
|
|
134
|
+
read_at?: Date;
|
|
133
135
|
completed_at?: Date;
|
|
134
136
|
provider?: INotificationProviderMeta;
|
|
135
137
|
error?: INotificationError;
|
|
@@ -73,6 +73,7 @@ exports.NotificationMessageStatusEnum = {
|
|
|
73
73
|
Processing: "processing", // being handled by notifications service
|
|
74
74
|
Sent: "sent", // accepted by provider
|
|
75
75
|
Delivered: "delivered", // delivery confirmed (when supported)
|
|
76
|
+
Read: "read", // read receipt confirmed (when supported, e.g. WhatsApp/WABA)
|
|
76
77
|
Failed: "failed", // permanent failure
|
|
77
78
|
Cancelled: "cancelled",
|
|
78
79
|
Suppressed: "suppressed", // dedup/skip decision
|
|
@@ -80,6 +80,7 @@ export const NotificationMessageStatusEnum = {
|
|
|
80
80
|
Processing: "processing", // being handled by notifications service
|
|
81
81
|
Sent: "sent", // accepted by provider
|
|
82
82
|
Delivered: "delivered", // delivery confirmed (when supported)
|
|
83
|
+
Read: "read", // read receipt confirmed (when supported, e.g. WhatsApp/WABA)
|
|
83
84
|
Failed: "failed", // permanent failure
|
|
84
85
|
Cancelled: "cancelled",
|
|
85
86
|
Suppressed: "suppressed", // dedup/skip decision
|
|
@@ -242,6 +243,7 @@ export interface INotificationMessage extends IFireDoc {
|
|
|
242
243
|
processing_started_at?: Date;
|
|
243
244
|
sent_at?: Date;
|
|
244
245
|
delivered_at?: Date;
|
|
246
|
+
read_at?: Date; // read receipt time (WABA-first; channel-neutral field)
|
|
245
247
|
completed_at?: Date;
|
|
246
248
|
|
|
247
249
|
// Final outcome
|
|
@@ -3,8 +3,20 @@ import type { IFireGlobalDoc } from "../shared";
|
|
|
3
3
|
export declare const CHATBEE_IMPORT_SCHEMA_VERSION: 1;
|
|
4
4
|
export declare const CHATBEE_IMPORT_FIRESTORE_PATH: "core/nexus/apps/nex-tenants/chatbee_imports";
|
|
5
5
|
export declare const CHATBEE_IMPORT_PUBSUB_TOPIC: "app_nexus_chatbee-import_request";
|
|
6
|
+
/**
|
|
7
|
+
* Per-(tenant, channel) department mapping doc, keyed `{tenant}__{channelId}`.
|
|
8
|
+
* Holds the operator's resolution of unresolved Chatbee departments before a
|
|
9
|
+
* real import is allowed to run.
|
|
10
|
+
*/
|
|
11
|
+
export declare const CHATBEE_IMPORT_DEPT_MAP_PATH: "core/nexus/apps/nex-tenants/chatbee_import_dept_maps";
|
|
12
|
+
/** Mapping sentinel: route this Chatbee department to the channel's default. */
|
|
13
|
+
export declare const CHANNEL_DEFAULT_DEPT_SENTINEL: "__channel_default__";
|
|
6
14
|
export type ChatbeeImportStatus = "pending" | "running" | "done" | "failed";
|
|
7
|
-
|
|
15
|
+
/** A job runs either as a read-only analysis pass or the real import. */
|
|
16
|
+
export type ChatbeeImportMode = "analyze" | "import";
|
|
17
|
+
export type ChatbeeImportAuditAction = "import_requested" | "import_started" | "import_page" | "import_completed" | "import_failed" | "analyze_requested" | "analyze_completed" | "dept_map_saved";
|
|
18
|
+
/** Resolution status of one Chatbee department against the platform. */
|
|
19
|
+
export type ChatbeeDeptResolutionStatus = "resolved" | "resolved_no_inbox" | "missing" | "no_external_id" | "mapped";
|
|
8
20
|
/**
|
|
9
21
|
* Keyset cursor over `chatbee.msgs_raw` ordered by
|
|
10
22
|
* (contact.id, history.id, timestamp, message.id). A page resumes strictly
|
|
@@ -32,6 +44,12 @@ export interface IChatbeeImportCounters {
|
|
|
32
44
|
contacts_reconciled: number;
|
|
33
45
|
reconcile_conflicts: number;
|
|
34
46
|
pages_processed: number;
|
|
47
|
+
/** Distinct Chatbee departments seen across the run. */
|
|
48
|
+
departments_found: number;
|
|
49
|
+
/** Departments still blocking (not resolved nor mapped). */
|
|
50
|
+
departments_unresolved: number;
|
|
51
|
+
/** Lead links dropped because the referenced lead was missing/deleted. */
|
|
52
|
+
lead_links_missing: number;
|
|
35
53
|
}
|
|
36
54
|
export interface IChatbeeImportError {
|
|
37
55
|
code: string;
|
|
@@ -42,12 +60,67 @@ export interface IChatbeeImportRequestedBy {
|
|
|
42
60
|
email: string;
|
|
43
61
|
loginId: string;
|
|
44
62
|
}
|
|
63
|
+
/** Resolution of one DISTINCT Chatbee department against the platform. */
|
|
64
|
+
export interface IChatbeeDepartmentResolution {
|
|
65
|
+
/** Chatbee department numeric id (stable) — the mapping key. */
|
|
66
|
+
chatbee_id: string;
|
|
67
|
+
/** Chatbee department external_id (may be null → no platform correlation). */
|
|
68
|
+
external_id: string | null;
|
|
69
|
+
/** Chatbee department display name. */
|
|
70
|
+
name: string;
|
|
71
|
+
/** Distinct attendances seen under this department in the window. */
|
|
72
|
+
event_count: number;
|
|
73
|
+
tickets_open: number;
|
|
74
|
+
tickets_closed: number;
|
|
75
|
+
status: ChatbeeDeptResolutionStatus;
|
|
76
|
+
/**
|
|
77
|
+
* The platform department this resolves to: the validated `external_id` for
|
|
78
|
+
* `resolved`/`resolved_no_inbox`, or the operator mapping target for `mapped`
|
|
79
|
+
* (a platform dept id, or CHANNEL_DEFAULT_DEPT_SENTINEL). Null while blocking.
|
|
80
|
+
*/
|
|
81
|
+
mapped_to?: string | null;
|
|
82
|
+
}
|
|
83
|
+
/** Read-only pre-import analysis report, stored on the job-doc. */
|
|
84
|
+
export interface IChatbeeImportAnalysis {
|
|
85
|
+
departments: IChatbeeDepartmentResolution[];
|
|
86
|
+
channel_default: {
|
|
87
|
+
department_id: string | null;
|
|
88
|
+
exists: boolean;
|
|
89
|
+
chat_enabled: boolean;
|
|
90
|
+
has_inbox: boolean;
|
|
91
|
+
/** True when the default is usable (exists + chat_enabled + has_inbox). */
|
|
92
|
+
valid: boolean;
|
|
93
|
+
};
|
|
94
|
+
lead_links: {
|
|
95
|
+
contacts_total: number;
|
|
96
|
+
/** Contacts whose contact.lead_id points at a missing/deleted lead. */
|
|
97
|
+
lead_missing_count: number;
|
|
98
|
+
};
|
|
99
|
+
contacts: {
|
|
100
|
+
total: number;
|
|
101
|
+
/** wa-index already points at a different contact (kept Chatbee canonical). */
|
|
102
|
+
index_conflicts: number;
|
|
103
|
+
};
|
|
104
|
+
/** True if any department is unresolved/unmapped or the channel default is invalid. */
|
|
105
|
+
blocking: boolean;
|
|
106
|
+
analyzed_at?: Date | null;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Operator's department mapping: Chatbee department id → platform department id
|
|
110
|
+
* (or CHANNEL_DEFAULT_DEPT_SENTINEL). Stored per (tenant, channel) at
|
|
111
|
+
* CHATBEE_IMPORT_DEPT_MAP_PATH/{tenant}__{channelId}.
|
|
112
|
+
*/
|
|
113
|
+
export type IChatbeeImportDeptMapping = Record<string, string>;
|
|
45
114
|
export interface IChatbeeImportDoc extends IFireGlobalDoc {
|
|
46
115
|
tenant: string;
|
|
47
116
|
/** Destination hub-waba channel id — threads/tickets/messages carry this channel_id. */
|
|
48
117
|
destination_channel_id: string;
|
|
118
|
+
/** 'analyze' = read-only pre-import pass; 'import' = the real import. */
|
|
119
|
+
mode?: ChatbeeImportMode;
|
|
49
120
|
/** Count-only preview: zero Firestore/PubSub/BQ writes, projects counters + totals. */
|
|
50
121
|
dry_run: boolean;
|
|
122
|
+
/** Read-only analysis report (set when mode === 'analyze'). */
|
|
123
|
+
analysis?: IChatbeeImportAnalysis | null;
|
|
51
124
|
requested_by: IChatbeeImportRequestedBy;
|
|
52
125
|
requested_at: Date | null;
|
|
53
126
|
started_at?: Date | null;
|
|
@@ -77,10 +150,38 @@ export declare const ChatbeeImportRequestSchema: z.ZodObject<{
|
|
|
77
150
|
dry_run?: boolean | undefined;
|
|
78
151
|
}>;
|
|
79
152
|
export type IChatbeeImportRequestPayload = z.infer<typeof ChatbeeImportRequestSchema>;
|
|
153
|
+
export declare const ChatbeeImportAnalyzeSchema: z.ZodObject<{
|
|
154
|
+
tenant: z.ZodString;
|
|
155
|
+
destination_channel_id: z.ZodString;
|
|
156
|
+
}, "strip", z.ZodTypeAny, {
|
|
157
|
+
tenant: string;
|
|
158
|
+
destination_channel_id: string;
|
|
159
|
+
}, {
|
|
160
|
+
tenant: string;
|
|
161
|
+
destination_channel_id: string;
|
|
162
|
+
}>;
|
|
163
|
+
export type IChatbeeImportAnalyzePayload = z.infer<typeof ChatbeeImportAnalyzeSchema>;
|
|
164
|
+
export declare const ChatbeeImportSaveDeptMapSchema: z.ZodObject<{
|
|
165
|
+
tenant: z.ZodString;
|
|
166
|
+
channel_id: z.ZodString;
|
|
167
|
+
/** chatbee_id → platform dept id (or CHANNEL_DEFAULT_DEPT_SENTINEL). */
|
|
168
|
+
map: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
169
|
+
}, "strip", z.ZodTypeAny, {
|
|
170
|
+
map: Record<string, string>;
|
|
171
|
+
tenant: string;
|
|
172
|
+
channel_id: string;
|
|
173
|
+
}, {
|
|
174
|
+
map: Record<string, string>;
|
|
175
|
+
tenant: string;
|
|
176
|
+
channel_id: string;
|
|
177
|
+
}>;
|
|
178
|
+
export type IChatbeeImportSaveDeptMapPayload = z.infer<typeof ChatbeeImportSaveDeptMapSchema>;
|
|
80
179
|
export interface IChatbeeImportPubSubEvent {
|
|
81
180
|
importId: string;
|
|
82
181
|
tenant: string;
|
|
83
182
|
destination_channel_id: string;
|
|
183
|
+
/** 'analyze' = read-only pass; 'import' = real import. */
|
|
184
|
+
mode: ChatbeeImportMode;
|
|
84
185
|
dry_run: boolean;
|
|
85
186
|
/** Cursor to resume from on a self-continuation; null/absent for the first run. */
|
|
86
187
|
cursor?: IChatbeeImportCursor | null;
|
|
@@ -1,14 +1,32 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ChatbeeImportRequestSchema = exports.CHATBEE_IMPORT_PUBSUB_TOPIC = exports.CHATBEE_IMPORT_FIRESTORE_PATH = exports.CHATBEE_IMPORT_SCHEMA_VERSION = void 0;
|
|
3
|
+
exports.ChatbeeImportSaveDeptMapSchema = exports.ChatbeeImportAnalyzeSchema = exports.ChatbeeImportRequestSchema = exports.CHANNEL_DEFAULT_DEPT_SENTINEL = exports.CHATBEE_IMPORT_DEPT_MAP_PATH = exports.CHATBEE_IMPORT_PUBSUB_TOPIC = exports.CHATBEE_IMPORT_FIRESTORE_PATH = exports.CHATBEE_IMPORT_SCHEMA_VERSION = void 0;
|
|
4
4
|
const zod_1 = require("zod");
|
|
5
5
|
// ── Constants ──
|
|
6
6
|
exports.CHATBEE_IMPORT_SCHEMA_VERSION = 1;
|
|
7
7
|
exports.CHATBEE_IMPORT_FIRESTORE_PATH = "core/nexus/apps/nex-tenants/chatbee_imports";
|
|
8
8
|
exports.CHATBEE_IMPORT_PUBSUB_TOPIC = "app_nexus_chatbee-import_request";
|
|
9
|
+
/**
|
|
10
|
+
* Per-(tenant, channel) department mapping doc, keyed `{tenant}__{channelId}`.
|
|
11
|
+
* Holds the operator's resolution of unresolved Chatbee departments before a
|
|
12
|
+
* real import is allowed to run.
|
|
13
|
+
*/
|
|
14
|
+
exports.CHATBEE_IMPORT_DEPT_MAP_PATH = "core/nexus/apps/nex-tenants/chatbee_import_dept_maps";
|
|
15
|
+
/** Mapping sentinel: route this Chatbee department to the channel's default. */
|
|
16
|
+
exports.CHANNEL_DEFAULT_DEPT_SENTINEL = "__channel_default__";
|
|
9
17
|
// ── Zod Schemas (callable payloads) ──
|
|
10
18
|
exports.ChatbeeImportRequestSchema = zod_1.z.object({
|
|
11
19
|
tenant: zod_1.z.string().min(1),
|
|
12
20
|
destination_channel_id: zod_1.z.string().min(1),
|
|
13
21
|
dry_run: zod_1.z.boolean().optional(),
|
|
14
22
|
});
|
|
23
|
+
exports.ChatbeeImportAnalyzeSchema = zod_1.z.object({
|
|
24
|
+
tenant: zod_1.z.string().min(1),
|
|
25
|
+
destination_channel_id: zod_1.z.string().min(1),
|
|
26
|
+
});
|
|
27
|
+
exports.ChatbeeImportSaveDeptMapSchema = zod_1.z.object({
|
|
28
|
+
tenant: zod_1.z.string().min(1),
|
|
29
|
+
channel_id: zod_1.z.string().min(1),
|
|
30
|
+
/** chatbee_id → platform dept id (or CHANNEL_DEFAULT_DEPT_SENTINEL). */
|
|
31
|
+
map: zod_1.z.record(zod_1.z.string(), zod_1.z.string().min(1)),
|
|
32
|
+
});
|
|
@@ -11,6 +11,17 @@ export const CHATBEE_IMPORT_FIRESTORE_PATH =
|
|
|
11
11
|
export const CHATBEE_IMPORT_PUBSUB_TOPIC =
|
|
12
12
|
"app_nexus_chatbee-import_request" as const;
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* Per-(tenant, channel) department mapping doc, keyed `{tenant}__{channelId}`.
|
|
16
|
+
* Holds the operator's resolution of unresolved Chatbee departments before a
|
|
17
|
+
* real import is allowed to run.
|
|
18
|
+
*/
|
|
19
|
+
export const CHATBEE_IMPORT_DEPT_MAP_PATH =
|
|
20
|
+
"core/nexus/apps/nex-tenants/chatbee_import_dept_maps" as const;
|
|
21
|
+
|
|
22
|
+
/** Mapping sentinel: route this Chatbee department to the channel's default. */
|
|
23
|
+
export const CHANNEL_DEFAULT_DEPT_SENTINEL = "__channel_default__" as const;
|
|
24
|
+
|
|
14
25
|
// ── Enums / Literals ──
|
|
15
26
|
|
|
16
27
|
export type ChatbeeImportStatus =
|
|
@@ -19,12 +30,26 @@ export type ChatbeeImportStatus =
|
|
|
19
30
|
| "done"
|
|
20
31
|
| "failed";
|
|
21
32
|
|
|
33
|
+
/** A job runs either as a read-only analysis pass or the real import. */
|
|
34
|
+
export type ChatbeeImportMode = "analyze" | "import";
|
|
35
|
+
|
|
22
36
|
export type ChatbeeImportAuditAction =
|
|
23
37
|
| "import_requested"
|
|
24
38
|
| "import_started"
|
|
25
39
|
| "import_page"
|
|
26
40
|
| "import_completed"
|
|
27
|
-
| "import_failed"
|
|
41
|
+
| "import_failed"
|
|
42
|
+
| "analyze_requested"
|
|
43
|
+
| "analyze_completed"
|
|
44
|
+
| "dept_map_saved";
|
|
45
|
+
|
|
46
|
+
/** Resolution status of one Chatbee department against the platform. */
|
|
47
|
+
export type ChatbeeDeptResolutionStatus =
|
|
48
|
+
| "resolved" // external_id → dept exists, chat_enabled, has dept inbox sub
|
|
49
|
+
| "resolved_no_inbox" // dept exists but no dept inbox subscription (ticket invisible)
|
|
50
|
+
| "missing" // external_id set but dept doc does not exist
|
|
51
|
+
| "no_external_id" // external_id NULL (no correlation possible)
|
|
52
|
+
| "mapped"; // operator mapped chatbee_id → platform dept (or channel default)
|
|
28
53
|
|
|
29
54
|
// ── Sub-interfaces ──
|
|
30
55
|
|
|
@@ -56,6 +81,12 @@ export interface IChatbeeImportCounters {
|
|
|
56
81
|
contacts_reconciled: number;
|
|
57
82
|
reconcile_conflicts: number;
|
|
58
83
|
pages_processed: number;
|
|
84
|
+
/** Distinct Chatbee departments seen across the run. */
|
|
85
|
+
departments_found: number;
|
|
86
|
+
/** Departments still blocking (not resolved nor mapped). */
|
|
87
|
+
departments_unresolved: number;
|
|
88
|
+
/** Lead links dropped because the referenced lead was missing/deleted. */
|
|
89
|
+
lead_links_missing: number;
|
|
59
90
|
}
|
|
60
91
|
|
|
61
92
|
export interface IChatbeeImportError {
|
|
@@ -69,14 +100,74 @@ export interface IChatbeeImportRequestedBy {
|
|
|
69
100
|
loginId: string;
|
|
70
101
|
}
|
|
71
102
|
|
|
103
|
+
// ── Analysis sub-interfaces ──
|
|
104
|
+
|
|
105
|
+
/** Resolution of one DISTINCT Chatbee department against the platform. */
|
|
106
|
+
export interface IChatbeeDepartmentResolution {
|
|
107
|
+
/** Chatbee department numeric id (stable) — the mapping key. */
|
|
108
|
+
chatbee_id: string;
|
|
109
|
+
/** Chatbee department external_id (may be null → no platform correlation). */
|
|
110
|
+
external_id: string | null;
|
|
111
|
+
/** Chatbee department display name. */
|
|
112
|
+
name: string;
|
|
113
|
+
/** Distinct attendances seen under this department in the window. */
|
|
114
|
+
event_count: number;
|
|
115
|
+
tickets_open: number;
|
|
116
|
+
tickets_closed: number;
|
|
117
|
+
status: ChatbeeDeptResolutionStatus;
|
|
118
|
+
/**
|
|
119
|
+
* The platform department this resolves to: the validated `external_id` for
|
|
120
|
+
* `resolved`/`resolved_no_inbox`, or the operator mapping target for `mapped`
|
|
121
|
+
* (a platform dept id, or CHANNEL_DEFAULT_DEPT_SENTINEL). Null while blocking.
|
|
122
|
+
*/
|
|
123
|
+
mapped_to?: string | null;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** Read-only pre-import analysis report, stored on the job-doc. */
|
|
127
|
+
export interface IChatbeeImportAnalysis {
|
|
128
|
+
departments: IChatbeeDepartmentResolution[];
|
|
129
|
+
channel_default: {
|
|
130
|
+
department_id: string | null;
|
|
131
|
+
exists: boolean;
|
|
132
|
+
chat_enabled: boolean;
|
|
133
|
+
has_inbox: boolean;
|
|
134
|
+
/** True when the default is usable (exists + chat_enabled + has_inbox). */
|
|
135
|
+
valid: boolean;
|
|
136
|
+
};
|
|
137
|
+
lead_links: {
|
|
138
|
+
contacts_total: number;
|
|
139
|
+
/** Contacts whose contact.lead_id points at a missing/deleted lead. */
|
|
140
|
+
lead_missing_count: number;
|
|
141
|
+
};
|
|
142
|
+
contacts: {
|
|
143
|
+
total: number;
|
|
144
|
+
/** wa-index already points at a different contact (kept Chatbee canonical). */
|
|
145
|
+
index_conflicts: number;
|
|
146
|
+
};
|
|
147
|
+
/** True if any department is unresolved/unmapped or the channel default is invalid. */
|
|
148
|
+
blocking: boolean;
|
|
149
|
+
analyzed_at?: Date | null;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Operator's department mapping: Chatbee department id → platform department id
|
|
154
|
+
* (or CHANNEL_DEFAULT_DEPT_SENTINEL). Stored per (tenant, channel) at
|
|
155
|
+
* CHATBEE_IMPORT_DEPT_MAP_PATH/{tenant}__{channelId}.
|
|
156
|
+
*/
|
|
157
|
+
export type IChatbeeImportDeptMapping = Record<string, string>;
|
|
158
|
+
|
|
72
159
|
// ── Main Document ──
|
|
73
160
|
|
|
74
161
|
export interface IChatbeeImportDoc extends IFireGlobalDoc {
|
|
75
162
|
tenant: string;
|
|
76
163
|
/** Destination hub-waba channel id — threads/tickets/messages carry this channel_id. */
|
|
77
164
|
destination_channel_id: string;
|
|
165
|
+
/** 'analyze' = read-only pre-import pass; 'import' = the real import. */
|
|
166
|
+
mode?: ChatbeeImportMode;
|
|
78
167
|
/** Count-only preview: zero Firestore/PubSub/BQ writes, projects counters + totals. */
|
|
79
168
|
dry_run: boolean;
|
|
169
|
+
/** Read-only analysis report (set when mode === 'analyze'). */
|
|
170
|
+
analysis?: IChatbeeImportAnalysis | null;
|
|
80
171
|
requested_by: IChatbeeImportRequestedBy;
|
|
81
172
|
requested_at: Date | null;
|
|
82
173
|
started_at?: Date | null;
|
|
@@ -105,12 +196,34 @@ export type IChatbeeImportRequestPayload = z.infer<
|
|
|
105
196
|
typeof ChatbeeImportRequestSchema
|
|
106
197
|
>;
|
|
107
198
|
|
|
199
|
+
export const ChatbeeImportAnalyzeSchema = z.object({
|
|
200
|
+
tenant: z.string().min(1),
|
|
201
|
+
destination_channel_id: z.string().min(1),
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
export type IChatbeeImportAnalyzePayload = z.infer<
|
|
205
|
+
typeof ChatbeeImportAnalyzeSchema
|
|
206
|
+
>;
|
|
207
|
+
|
|
208
|
+
export const ChatbeeImportSaveDeptMapSchema = z.object({
|
|
209
|
+
tenant: z.string().min(1),
|
|
210
|
+
channel_id: z.string().min(1),
|
|
211
|
+
/** chatbee_id → platform dept id (or CHANNEL_DEFAULT_DEPT_SENTINEL). */
|
|
212
|
+
map: z.record(z.string(), z.string().min(1)),
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
export type IChatbeeImportSaveDeptMapPayload = z.infer<
|
|
216
|
+
typeof ChatbeeImportSaveDeptMapSchema
|
|
217
|
+
>;
|
|
218
|
+
|
|
108
219
|
// ── PubSub Event ──
|
|
109
220
|
|
|
110
221
|
export interface IChatbeeImportPubSubEvent {
|
|
111
222
|
importId: string;
|
|
112
223
|
tenant: string;
|
|
113
224
|
destination_channel_id: string;
|
|
225
|
+
/** 'analyze' = read-only pass; 'import' = real import. */
|
|
226
|
+
mode: ChatbeeImportMode;
|
|
114
227
|
dry_run: boolean;
|
|
115
228
|
/** Cursor to resume from on a self-continuation; null/absent for the first run. */
|
|
116
229
|
cursor?: IChatbeeImportCursor | null;
|