evo360-types 1.3.391 → 1.3.392
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/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/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
|
@@ -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
|
|
@@ -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;
|