evo360-types 1.3.390 → 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.
@@ -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
- export type ILeadReferral = ILeadReferralCTWA;
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
- export type ILeadReferral = ILeadReferralCTWA; // | ILeadReferralGoogleAds | ...
80
+ /** Origem genérica derivada da 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
@@ -0,0 +1,193 @@
1
+ import { z } from "zod";
2
+ import type { IFireGlobalDoc } from "../shared";
3
+ export declare const CHATBEE_IMPORT_SCHEMA_VERSION: 1;
4
+ export declare const CHATBEE_IMPORT_FIRESTORE_PATH: "core/nexus/apps/nex-tenants/chatbee_imports";
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__";
14
+ export type ChatbeeImportStatus = "pending" | "running" | "done" | "failed";
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";
20
+ /**
21
+ * Keyset cursor over `chatbee.msgs_raw` ordered by
22
+ * (contact.id, history.id, timestamp, message.id). A page resumes strictly
23
+ * AFTER this tuple. `null` (no cursor) means the run starts from the beginning.
24
+ */
25
+ export interface IChatbeeImportCursor {
26
+ contact_id: string;
27
+ history_id: string;
28
+ /** Message event time as epoch microseconds (BQ TIMESTAMP precision). */
29
+ timestamp_us: string;
30
+ message_id: string;
31
+ }
32
+ /**
33
+ * Running totals committed per page so the UI can render live progress and a
34
+ * re-publish (self-continuation) resumes from a consistent count. Idempotent
35
+ * re-imports inflate `messages_skipped_dup`, not `messages_imported`.
36
+ */
37
+ export interface IChatbeeImportCounters {
38
+ messages_imported: number;
39
+ messages_skipped_dup: number;
40
+ messages_skipped_unsupported: number;
41
+ threads_touched: number;
42
+ tickets_open: number;
43
+ tickets_closed: number;
44
+ contacts_reconciled: number;
45
+ reconcile_conflicts: number;
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;
53
+ }
54
+ export interface IChatbeeImportError {
55
+ code: string;
56
+ message: string;
57
+ }
58
+ export interface IChatbeeImportRequestedBy {
59
+ uid: string;
60
+ email: string;
61
+ loginId: string;
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>;
114
+ export interface IChatbeeImportDoc extends IFireGlobalDoc {
115
+ tenant: string;
116
+ /** Destination hub-waba channel id — threads/tickets/messages carry this channel_id. */
117
+ destination_channel_id: string;
118
+ /** 'analyze' = read-only pre-import pass; 'import' = the real import. */
119
+ mode?: ChatbeeImportMode;
120
+ /** Count-only preview: zero Firestore/PubSub/BQ writes, projects counters + totals. */
121
+ dry_run: boolean;
122
+ /** Read-only analysis report (set when mode === 'analyze'). */
123
+ analysis?: IChatbeeImportAnalysis | null;
124
+ requested_by: IChatbeeImportRequestedBy;
125
+ requested_at: Date | null;
126
+ started_at?: Date | null;
127
+ finished_at?: Date | null;
128
+ status: ChatbeeImportStatus;
129
+ /** Keyset cursor of the last fully-committed page; null before the first page. */
130
+ cursor?: IChatbeeImportCursor | null;
131
+ counters?: IChatbeeImportCounters | null;
132
+ /** Total rows matched for this tenant (filled on dry-run / first page for the UI). */
133
+ total_messages?: number | null;
134
+ error?: IChatbeeImportError | null;
135
+ /** Non-fatal warnings (reconcile conflicts, stale-open demotions). Capped at 50. */
136
+ warnings?: string[] | null;
137
+ import_schema_version: typeof CHATBEE_IMPORT_SCHEMA_VERSION;
138
+ }
139
+ export declare const ChatbeeImportRequestSchema: z.ZodObject<{
140
+ tenant: z.ZodString;
141
+ destination_channel_id: z.ZodString;
142
+ dry_run: z.ZodOptional<z.ZodBoolean>;
143
+ }, "strip", z.ZodTypeAny, {
144
+ tenant: string;
145
+ destination_channel_id: string;
146
+ dry_run?: boolean | undefined;
147
+ }, {
148
+ tenant: string;
149
+ destination_channel_id: string;
150
+ dry_run?: boolean | undefined;
151
+ }>;
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>;
179
+ export interface IChatbeeImportPubSubEvent {
180
+ importId: string;
181
+ tenant: string;
182
+ destination_channel_id: string;
183
+ /** 'analyze' = read-only pass; 'import' = real import. */
184
+ mode: ChatbeeImportMode;
185
+ dry_run: boolean;
186
+ /** Cursor to resume from on a self-continuation; null/absent for the first run. */
187
+ cursor?: IChatbeeImportCursor | null;
188
+ requested_by: {
189
+ uid: string;
190
+ loginId: string;
191
+ };
192
+ trace_id: string;
193
+ }
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
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
+ const zod_1 = require("zod");
5
+ // ── Constants ──
6
+ exports.CHATBEE_IMPORT_SCHEMA_VERSION = 1;
7
+ exports.CHATBEE_IMPORT_FIRESTORE_PATH = "core/nexus/apps/nex-tenants/chatbee_imports";
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__";
17
+ // ── Zod Schemas (callable payloads) ──
18
+ exports.ChatbeeImportRequestSchema = zod_1.z.object({
19
+ tenant: zod_1.z.string().min(1),
20
+ destination_channel_id: zod_1.z.string().min(1),
21
+ dry_run: zod_1.z.boolean().optional(),
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
+ });
@@ -0,0 +1,235 @@
1
+ import { z } from "zod";
2
+ import type { IFireGlobalDoc } from "../shared";
3
+
4
+ // ── Constants ──
5
+
6
+ export const CHATBEE_IMPORT_SCHEMA_VERSION = 1 as const;
7
+
8
+ export const CHATBEE_IMPORT_FIRESTORE_PATH =
9
+ "core/nexus/apps/nex-tenants/chatbee_imports" as const;
10
+
11
+ export const CHATBEE_IMPORT_PUBSUB_TOPIC =
12
+ "app_nexus_chatbee-import_request" as const;
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
+
25
+ // ── Enums / Literals ──
26
+
27
+ export type ChatbeeImportStatus =
28
+ | "pending"
29
+ | "running"
30
+ | "done"
31
+ | "failed";
32
+
33
+ /** A job runs either as a read-only analysis pass or the real import. */
34
+ export type ChatbeeImportMode = "analyze" | "import";
35
+
36
+ export type ChatbeeImportAuditAction =
37
+ | "import_requested"
38
+ | "import_started"
39
+ | "import_page"
40
+ | "import_completed"
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)
53
+
54
+ // ── Sub-interfaces ──
55
+
56
+ /**
57
+ * Keyset cursor over `chatbee.msgs_raw` ordered by
58
+ * (contact.id, history.id, timestamp, message.id). A page resumes strictly
59
+ * AFTER this tuple. `null` (no cursor) means the run starts from the beginning.
60
+ */
61
+ export interface IChatbeeImportCursor {
62
+ contact_id: string;
63
+ history_id: string;
64
+ /** Message event time as epoch microseconds (BQ TIMESTAMP precision). */
65
+ timestamp_us: string;
66
+ message_id: string;
67
+ }
68
+
69
+ /**
70
+ * Running totals committed per page so the UI can render live progress and a
71
+ * re-publish (self-continuation) resumes from a consistent count. Idempotent
72
+ * re-imports inflate `messages_skipped_dup`, not `messages_imported`.
73
+ */
74
+ export interface IChatbeeImportCounters {
75
+ messages_imported: number;
76
+ messages_skipped_dup: number;
77
+ messages_skipped_unsupported: number;
78
+ threads_touched: number;
79
+ tickets_open: number;
80
+ tickets_closed: number;
81
+ contacts_reconciled: number;
82
+ reconcile_conflicts: number;
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;
90
+ }
91
+
92
+ export interface IChatbeeImportError {
93
+ code: string;
94
+ message: string;
95
+ }
96
+
97
+ export interface IChatbeeImportRequestedBy {
98
+ uid: string;
99
+ email: string;
100
+ loginId: string;
101
+ }
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
+
159
+ // ── Main Document ──
160
+
161
+ export interface IChatbeeImportDoc extends IFireGlobalDoc {
162
+ tenant: string;
163
+ /** Destination hub-waba channel id — threads/tickets/messages carry this channel_id. */
164
+ destination_channel_id: string;
165
+ /** 'analyze' = read-only pre-import pass; 'import' = the real import. */
166
+ mode?: ChatbeeImportMode;
167
+ /** Count-only preview: zero Firestore/PubSub/BQ writes, projects counters + totals. */
168
+ dry_run: boolean;
169
+ /** Read-only analysis report (set when mode === 'analyze'). */
170
+ analysis?: IChatbeeImportAnalysis | null;
171
+ requested_by: IChatbeeImportRequestedBy;
172
+ requested_at: Date | null;
173
+ started_at?: Date | null;
174
+ finished_at?: Date | null;
175
+ status: ChatbeeImportStatus;
176
+ /** Keyset cursor of the last fully-committed page; null before the first page. */
177
+ cursor?: IChatbeeImportCursor | null;
178
+ counters?: IChatbeeImportCounters | null;
179
+ /** Total rows matched for this tenant (filled on dry-run / first page for the UI). */
180
+ total_messages?: number | null;
181
+ error?: IChatbeeImportError | null;
182
+ /** Non-fatal warnings (reconcile conflicts, stale-open demotions). Capped at 50. */
183
+ warnings?: string[] | null;
184
+ import_schema_version: typeof CHATBEE_IMPORT_SCHEMA_VERSION;
185
+ }
186
+
187
+ // ── Zod Schemas (callable payloads) ──
188
+
189
+ export const ChatbeeImportRequestSchema = z.object({
190
+ tenant: z.string().min(1),
191
+ destination_channel_id: z.string().min(1),
192
+ dry_run: z.boolean().optional(),
193
+ });
194
+
195
+ export type IChatbeeImportRequestPayload = z.infer<
196
+ typeof ChatbeeImportRequestSchema
197
+ >;
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
+
219
+ // ── PubSub Event ──
220
+
221
+ export interface IChatbeeImportPubSubEvent {
222
+ importId: string;
223
+ tenant: string;
224
+ destination_channel_id: string;
225
+ /** 'analyze' = read-only pass; 'import' = real import. */
226
+ mode: ChatbeeImportMode;
227
+ dry_run: boolean;
228
+ /** Cursor to resume from on a self-continuation; null/absent for the first run. */
229
+ cursor?: IChatbeeImportCursor | null;
230
+ requested_by: {
231
+ uid: string;
232
+ loginId: string;
233
+ };
234
+ trace_id: string;
235
+ }
@@ -1 +1,2 @@
1
1
  export * from "./lgpd-export";
2
+ export * from "./chatbee-import";
@@ -15,3 +15,4 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./lgpd-export"), exports);
18
+ __exportStar(require("./chatbee-import"), exports);
@@ -1 +1,2 @@
1
1
  export * from "./lgpd-export";
2
+ export * from "./chatbee-import";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "evo360-types",
3
- "version": "1.3.390",
3
+ "version": "1.3.392",
4
4
  "description": "HREVO360 Shared Types",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",