hvp-shared 13.1.0 → 13.3.0
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/constants/google-calendar.constants.d.ts +124 -0
- package/dist/constants/google-calendar.constants.js +261 -0
- package/dist/constants/google-calendar.constants.test.d.ts +1 -0
- package/dist/constants/google-calendar.constants.test.js +115 -0
- package/dist/constants/index.d.ts +1 -0
- package/dist/constants/index.js +1 -0
- package/dist/contracts/document/requests.d.ts +23 -0
- package/dist/contracts/document/responses.d.ts +72 -0
- package/dist/contracts/google-calendar/index.d.ts +2 -0
- package/dist/contracts/google-calendar/index.js +18 -0
- package/dist/contracts/google-calendar/requests.d.ts +29 -0
- package/dist/contracts/google-calendar/requests.js +8 -0
- package/dist/contracts/google-calendar/responses.d.ts +138 -0
- package/dist/contracts/google-calendar/responses.js +8 -0
- package/dist/contracts/index.d.ts +1 -0
- package/dist/contracts/index.js +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Google Calendar Integration — Constants
|
|
3
|
+
*
|
|
4
|
+
* Implements the appointment standard documented in
|
|
5
|
+
* `resources/notes/google-calendar/standard-v1.md`.
|
|
6
|
+
*
|
|
7
|
+
* Consumed by:
|
|
8
|
+
* - Backend diagnostic engine (parser + scorer)
|
|
9
|
+
* - Frontend dashboard (dimension labels, weights)
|
|
10
|
+
*
|
|
11
|
+
* Changes to scoring rules require a new version (STANDARD_V2) so previous
|
|
12
|
+
* runs remain reproducible.
|
|
13
|
+
*/
|
|
14
|
+
import type { AppointmentBranch, AppointmentStandard } from "../contracts/google-calendar/responses";
|
|
15
|
+
/**
|
|
16
|
+
* Multi-factor appointment standard v1.
|
|
17
|
+
*
|
|
18
|
+
* Weights sum to 100. An event is "compliant" when its score >= 80.
|
|
19
|
+
*/
|
|
20
|
+
export declare const STANDARD_V1: AppointmentStandard;
|
|
21
|
+
/**
|
|
22
|
+
* Google Calendar colorId values mapped to HVP branches.
|
|
23
|
+
*
|
|
24
|
+
* Confirmed via Phase 0 discovery (2026-05-18):
|
|
25
|
+
* - 11 (Tomato) → Urban
|
|
26
|
+
* - 9 (Blueberry) → Harbor
|
|
27
|
+
* - 3 (Grape) → Montejo (citas) + recordatorios operacionales
|
|
28
|
+
*/
|
|
29
|
+
export declare const BRANCH_COLOR_IDS: Readonly<Record<AppointmentBranch, string>>;
|
|
30
|
+
/**
|
|
31
|
+
* Reverse lookup: colorId → branch.
|
|
32
|
+
*/
|
|
33
|
+
export declare const COLOR_ID_TO_BRANCH: Readonly<Record<string, AppointmentBranch>>;
|
|
34
|
+
/** colorId 8 (Graphite) = cancelled. Tracked as a separate metric. */
|
|
35
|
+
export declare const CANCELLED_COLOR_ID = "8";
|
|
36
|
+
/**
|
|
37
|
+
* colorIds reserved for special events (NOT patient appointments).
|
|
38
|
+
*
|
|
39
|
+
* - 2 (Sage) — aniversario HVP, "no agendar nada", recordatorios admin
|
|
40
|
+
* - 4 (Flamingo) — cumpleaños colaboradores
|
|
41
|
+
* - 5 (Banana) — bloqueos (vacaciones, cursos, NO AGENDAR)
|
|
42
|
+
* - 10 (Basil) — reuniones, eventos importantes
|
|
43
|
+
*/
|
|
44
|
+
export declare const EXCLUDED_COLOR_IDS: ReadonlySet<string>;
|
|
45
|
+
/**
|
|
46
|
+
* Canonical service codes — the diagnostic engine's source of truth.
|
|
47
|
+
*
|
|
48
|
+
* The parser maps free-form prefixes (via SERVICE_CODE_SYNONYMS) to one of these.
|
|
49
|
+
*/
|
|
50
|
+
export declare const CANONICAL_SERVICE_CODES: readonly ["CONSULTA", "SIN_CITA", "CONSULTA_SEGUIMIENTO", "VACUNA", "CIRUGIA", "CERTIFICADO", "ULTRASONIDO", "RADIOGRAFIA", "ECOCARDIO", "OVH", "PROFILAXIS_DENTAL", "ANESTESIA", "EUTANASIA", "RETIRO_PUNTOS", "REVISION", "DESPARASITACION", "APLICACION", "TOMA_MUESTRAS", "TERAPIA_MEDICA", "INYECCION"];
|
|
51
|
+
export type CanonicalServiceCode = (typeof CANONICAL_SERVICE_CODES)[number];
|
|
52
|
+
/**
|
|
53
|
+
* Maps free-form prefixes (uppercase, trimmed, period-stripped) to canonical codes.
|
|
54
|
+
*
|
|
55
|
+
* Example: title "C. OFT LUKE RGL*" → first token "C" → CONSULTA.
|
|
56
|
+
*/
|
|
57
|
+
export declare const SERVICE_CODE_SYNONYMS: Readonly<Record<string, CanonicalServiceCode>>;
|
|
58
|
+
/**
|
|
59
|
+
* Title prefixes (uppercase, first token) that mark NON-clinical events.
|
|
60
|
+
*
|
|
61
|
+
* Diagnostic engine excludes these — they are not patient appointments.
|
|
62
|
+
*/
|
|
63
|
+
export declare const EXCLUDED_TITLE_PREFIXES: ReadonlySet<string>;
|
|
64
|
+
/**
|
|
65
|
+
* Detects an asterisk at the end of the title (after optional whitespace).
|
|
66
|
+
* `*` at the end = "médico preferido" = the vet earns a commission.
|
|
67
|
+
*
|
|
68
|
+
* Examples that match:
|
|
69
|
+
* "C. ARCHIE AAT*"
|
|
70
|
+
* "C. ARCHIE AAT *"
|
|
71
|
+
* "AM CAMILA APL*"
|
|
72
|
+
*/
|
|
73
|
+
export declare const PREFERRED_VET_REGEX: RegExp;
|
|
74
|
+
/**
|
|
75
|
+
* Detects "ENVIAR RECORDATORIOS *" titles (Montejo recordatorios). Even though
|
|
76
|
+
* they live in colorId 3 (Montejo), they are NOT patient appointments.
|
|
77
|
+
*/
|
|
78
|
+
export declare const REMINDER_TITLE_REGEX: RegExp;
|
|
79
|
+
/**
|
|
80
|
+
* Detects cancelled events by title content. Used in addition to colorId 8.
|
|
81
|
+
*/
|
|
82
|
+
export declare const CANCELLED_TITLE_REGEX: RegExp;
|
|
83
|
+
/**
|
|
84
|
+
* Matches a QVET client id ("Q123456").
|
|
85
|
+
* Range chosen wide enough to cover historical and future ids.
|
|
86
|
+
*/
|
|
87
|
+
export declare const QVET_ID_REGEX: RegExp;
|
|
88
|
+
/**
|
|
89
|
+
* Captures Mexican phone numbers in any of the common written formats.
|
|
90
|
+
* Returns the full match; caller normalizes to last 10 digits.
|
|
91
|
+
*
|
|
92
|
+
* Tolerates:
|
|
93
|
+
* +52 1 999 442 9488
|
|
94
|
+
* +52 999 442 9488
|
|
95
|
+
* 999 442 9488
|
|
96
|
+
* 999-442-9488
|
|
97
|
+
* 9994429488
|
|
98
|
+
*/
|
|
99
|
+
export declare const PHONE_REGEX: RegExp;
|
|
100
|
+
/**
|
|
101
|
+
* Captures the "agendado por" col_code from a description.
|
|
102
|
+
*
|
|
103
|
+
* Variants observed:
|
|
104
|
+
* "AGENDO XZA 16 MAYO"
|
|
105
|
+
* "AG YMP 14.05.26"
|
|
106
|
+
* "AGENDADA POR SLR 02/12/25"
|
|
107
|
+
* "AGENDADO POR SLR 02/12/25"
|
|
108
|
+
*/
|
|
109
|
+
export declare const SCHEDULER_REGEX: RegExp;
|
|
110
|
+
/**
|
|
111
|
+
* Keys used in Google Calendar `extendedProperties.private` for events created
|
|
112
|
+
* from HVP. Phase 2 writes these; Phase 1 reads them for full attribution.
|
|
113
|
+
*/
|
|
114
|
+
export declare const HVP_EVENT_PROP_KEYS: {
|
|
115
|
+
readonly qvetClientId: "qvetClientId";
|
|
116
|
+
readonly qvetPetId: "qvetPetId";
|
|
117
|
+
readonly serviceCode: "serviceCode";
|
|
118
|
+
readonly branchId: "branchId";
|
|
119
|
+
readonly vetColCode: "vetColCode";
|
|
120
|
+
readonly scheduledByColCode: "scheduledByColCode";
|
|
121
|
+
readonly isPreferredVet: "isPreferredVet";
|
|
122
|
+
readonly source: "source";
|
|
123
|
+
readonly standardVersion: "standardVersion";
|
|
124
|
+
};
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Google Calendar Integration — Constants
|
|
4
|
+
*
|
|
5
|
+
* Implements the appointment standard documented in
|
|
6
|
+
* `resources/notes/google-calendar/standard-v1.md`.
|
|
7
|
+
*
|
|
8
|
+
* Consumed by:
|
|
9
|
+
* - Backend diagnostic engine (parser + scorer)
|
|
10
|
+
* - Frontend dashboard (dimension labels, weights)
|
|
11
|
+
*
|
|
12
|
+
* Changes to scoring rules require a new version (STANDARD_V2) so previous
|
|
13
|
+
* runs remain reproducible.
|
|
14
|
+
*/
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.HVP_EVENT_PROP_KEYS = exports.SCHEDULER_REGEX = exports.PHONE_REGEX = exports.QVET_ID_REGEX = exports.CANCELLED_TITLE_REGEX = exports.REMINDER_TITLE_REGEX = exports.PREFERRED_VET_REGEX = exports.EXCLUDED_TITLE_PREFIXES = exports.SERVICE_CODE_SYNONYMS = exports.CANONICAL_SERVICE_CODES = exports.EXCLUDED_COLOR_IDS = exports.CANCELLED_COLOR_ID = exports.COLOR_ID_TO_BRANCH = exports.BRANCH_COLOR_IDS = exports.STANDARD_V1 = void 0;
|
|
17
|
+
// ─── Standard v1 ─────────────────────────────────────────────────────────────
|
|
18
|
+
/**
|
|
19
|
+
* Multi-factor appointment standard v1.
|
|
20
|
+
*
|
|
21
|
+
* Weights sum to 100. An event is "compliant" when its score >= 80.
|
|
22
|
+
*/
|
|
23
|
+
exports.STANDARD_V1 = {
|
|
24
|
+
version: "1.0",
|
|
25
|
+
minimumCompliantScore: 80,
|
|
26
|
+
dimensions: [
|
|
27
|
+
{ id: "branch", weight: 15, label: "Sucursal" },
|
|
28
|
+
{ id: "service", weight: 20, label: "Servicio" },
|
|
29
|
+
{ id: "pet", weight: 15, label: "Paciente" },
|
|
30
|
+
{ id: "client", weight: 25, label: "Cliente (linkable a QVET)" },
|
|
31
|
+
{ id: "context", weight: 10, label: "Motivo / contexto" },
|
|
32
|
+
{ id: "vet", weight: 15, label: "Atribución del vet" },
|
|
33
|
+
],
|
|
34
|
+
};
|
|
35
|
+
// ─── ColorId → Branch mapping ────────────────────────────────────────────────
|
|
36
|
+
/**
|
|
37
|
+
* Google Calendar colorId values mapped to HVP branches.
|
|
38
|
+
*
|
|
39
|
+
* Confirmed via Phase 0 discovery (2026-05-18):
|
|
40
|
+
* - 11 (Tomato) → Urban
|
|
41
|
+
* - 9 (Blueberry) → Harbor
|
|
42
|
+
* - 3 (Grape) → Montejo (citas) + recordatorios operacionales
|
|
43
|
+
*/
|
|
44
|
+
exports.BRANCH_COLOR_IDS = {
|
|
45
|
+
urban: "11",
|
|
46
|
+
harbor: "9",
|
|
47
|
+
montejo: "3",
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Reverse lookup: colorId → branch.
|
|
51
|
+
*/
|
|
52
|
+
exports.COLOR_ID_TO_BRANCH = {
|
|
53
|
+
"11": "urban",
|
|
54
|
+
"9": "harbor",
|
|
55
|
+
"3": "montejo",
|
|
56
|
+
};
|
|
57
|
+
// ─── Excluded colorIds ───────────────────────────────────────────────────────
|
|
58
|
+
/** colorId 8 (Graphite) = cancelled. Tracked as a separate metric. */
|
|
59
|
+
exports.CANCELLED_COLOR_ID = "8";
|
|
60
|
+
/**
|
|
61
|
+
* colorIds reserved for special events (NOT patient appointments).
|
|
62
|
+
*
|
|
63
|
+
* - 2 (Sage) — aniversario HVP, "no agendar nada", recordatorios admin
|
|
64
|
+
* - 4 (Flamingo) — cumpleaños colaboradores
|
|
65
|
+
* - 5 (Banana) — bloqueos (vacaciones, cursos, NO AGENDAR)
|
|
66
|
+
* - 10 (Basil) — reuniones, eventos importantes
|
|
67
|
+
*/
|
|
68
|
+
exports.EXCLUDED_COLOR_IDS = new Set([
|
|
69
|
+
"2",
|
|
70
|
+
"4",
|
|
71
|
+
"5",
|
|
72
|
+
"10",
|
|
73
|
+
]);
|
|
74
|
+
// ─── Service codes ───────────────────────────────────────────────────────────
|
|
75
|
+
/**
|
|
76
|
+
* Canonical service codes — the diagnostic engine's source of truth.
|
|
77
|
+
*
|
|
78
|
+
* The parser maps free-form prefixes (via SERVICE_CODE_SYNONYMS) to one of these.
|
|
79
|
+
*/
|
|
80
|
+
exports.CANONICAL_SERVICE_CODES = [
|
|
81
|
+
"CONSULTA",
|
|
82
|
+
"SIN_CITA",
|
|
83
|
+
"CONSULTA_SEGUIMIENTO",
|
|
84
|
+
"VACUNA",
|
|
85
|
+
"CIRUGIA",
|
|
86
|
+
"CERTIFICADO",
|
|
87
|
+
"ULTRASONIDO",
|
|
88
|
+
"RADIOGRAFIA",
|
|
89
|
+
"ECOCARDIO",
|
|
90
|
+
"OVH",
|
|
91
|
+
"PROFILAXIS_DENTAL",
|
|
92
|
+
"ANESTESIA",
|
|
93
|
+
"EUTANASIA",
|
|
94
|
+
"RETIRO_PUNTOS",
|
|
95
|
+
"REVISION",
|
|
96
|
+
"DESPARASITACION",
|
|
97
|
+
"APLICACION",
|
|
98
|
+
"TOMA_MUESTRAS",
|
|
99
|
+
"TERAPIA_MEDICA",
|
|
100
|
+
"INYECCION",
|
|
101
|
+
];
|
|
102
|
+
/**
|
|
103
|
+
* Maps free-form prefixes (uppercase, trimmed, period-stripped) to canonical codes.
|
|
104
|
+
*
|
|
105
|
+
* Example: title "C. OFT LUKE RGL*" → first token "C" → CONSULTA.
|
|
106
|
+
*/
|
|
107
|
+
exports.SERVICE_CODE_SYNONYMS = {
|
|
108
|
+
// Consulta
|
|
109
|
+
C: "CONSULTA",
|
|
110
|
+
CONSULTA: "CONSULTA",
|
|
111
|
+
// Sin Cita (walk-in)
|
|
112
|
+
SC: "SIN_CITA",
|
|
113
|
+
// Consulta Seguimiento
|
|
114
|
+
CS: "CONSULTA_SEGUIMIENTO",
|
|
115
|
+
// Vacuna
|
|
116
|
+
V: "VACUNA",
|
|
117
|
+
VAC: "VACUNA",
|
|
118
|
+
VACUNA: "VACUNA",
|
|
119
|
+
// Cirugía
|
|
120
|
+
CX: "CIRUGIA",
|
|
121
|
+
CIRUGIA: "CIRUGIA",
|
|
122
|
+
CIRUGÍA: "CIRUGIA",
|
|
123
|
+
// Certificado
|
|
124
|
+
CERT: "CERTIFICADO",
|
|
125
|
+
CERTIF: "CERTIFICADO",
|
|
126
|
+
CERTIFICADO: "CERTIFICADO",
|
|
127
|
+
// Ultrasonido
|
|
128
|
+
USG: "ULTRASONIDO",
|
|
129
|
+
ULTRASONIDO: "ULTRASONIDO",
|
|
130
|
+
// Radiografía
|
|
131
|
+
RX: "RADIOGRAFIA",
|
|
132
|
+
RADIOGRAFIA: "RADIOGRAFIA",
|
|
133
|
+
RADIOGRAFÍA: "RADIOGRAFIA",
|
|
134
|
+
// Ecocardio
|
|
135
|
+
ECO: "ECOCARDIO",
|
|
136
|
+
ECOCARDIOGRAMA: "ECOCARDIO",
|
|
137
|
+
// OVH (ovariohisterectomía)
|
|
138
|
+
OVH: "OVH",
|
|
139
|
+
// Profilaxis dental
|
|
140
|
+
PROFILAXIS: "PROFILAXIS_DENTAL",
|
|
141
|
+
LIMPIEZA: "PROFILAXIS_DENTAL",
|
|
142
|
+
// Anestesia
|
|
143
|
+
ANESTESIA: "ANESTESIA",
|
|
144
|
+
// Eutanasia
|
|
145
|
+
EUTANASIA: "EUTANASIA",
|
|
146
|
+
// Retiro de puntos / vendaje
|
|
147
|
+
RETIRO: "RETIRO_PUNTOS",
|
|
148
|
+
RET: "RETIRO_PUNTOS",
|
|
149
|
+
// Revisión
|
|
150
|
+
REV: "REVISION",
|
|
151
|
+
REVISION: "REVISION",
|
|
152
|
+
REVISIÓN: "REVISION",
|
|
153
|
+
// Desparasitación
|
|
154
|
+
D: "DESPARASITACION",
|
|
155
|
+
DES: "DESPARASITACION",
|
|
156
|
+
DESPARASITACION: "DESPARASITACION",
|
|
157
|
+
DESPARASITACIÓN: "DESPARASITACION",
|
|
158
|
+
// Aplicación de medicamento / microchip / etc.
|
|
159
|
+
AP: "APLICACION",
|
|
160
|
+
APL: "APLICACION",
|
|
161
|
+
AM: "APLICACION",
|
|
162
|
+
APLI: "APLICACION",
|
|
163
|
+
APLICACION: "APLICACION",
|
|
164
|
+
APLICACIÓN: "APLICACION",
|
|
165
|
+
// Toma de muestras
|
|
166
|
+
TM: "TOMA_MUESTRAS",
|
|
167
|
+
// Terapia médica (visto como "T.M." → TM)
|
|
168
|
+
T: "TERAPIA_MEDICA",
|
|
169
|
+
// Inyección
|
|
170
|
+
INYECCION: "INYECCION",
|
|
171
|
+
INYECCIÓN: "INYECCION",
|
|
172
|
+
};
|
|
173
|
+
/**
|
|
174
|
+
* Title prefixes (uppercase, first token) that mark NON-clinical events.
|
|
175
|
+
*
|
|
176
|
+
* Diagnostic engine excludes these — they are not patient appointments.
|
|
177
|
+
*/
|
|
178
|
+
exports.EXCLUDED_TITLE_PREFIXES = new Set([
|
|
179
|
+
"RECIBIR",
|
|
180
|
+
"ENTREGAR",
|
|
181
|
+
"DESPACHAR",
|
|
182
|
+
"DESP",
|
|
183
|
+
"ENVIAR",
|
|
184
|
+
"RECEPCION",
|
|
185
|
+
"RECEPCIÓN",
|
|
186
|
+
"RECORDATORIO",
|
|
187
|
+
"RECORDATORIOS",
|
|
188
|
+
"ENTREVISTA",
|
|
189
|
+
"REUNION",
|
|
190
|
+
"REUNIÓN",
|
|
191
|
+
"CUMPLE",
|
|
192
|
+
"CUMPLEAÑOS",
|
|
193
|
+
"ANIVERSARIO",
|
|
194
|
+
"VACACIONES",
|
|
195
|
+
"NO",
|
|
196
|
+
"IMPORTANTE",
|
|
197
|
+
]);
|
|
198
|
+
// ─── Title patterns ──────────────────────────────────────────────────────────
|
|
199
|
+
/**
|
|
200
|
+
* Detects an asterisk at the end of the title (after optional whitespace).
|
|
201
|
+
* `*` at the end = "médico preferido" = the vet earns a commission.
|
|
202
|
+
*
|
|
203
|
+
* Examples that match:
|
|
204
|
+
* "C. ARCHIE AAT*"
|
|
205
|
+
* "C. ARCHIE AAT *"
|
|
206
|
+
* "AM CAMILA APL*"
|
|
207
|
+
*/
|
|
208
|
+
exports.PREFERRED_VET_REGEX = /\*\s*$/;
|
|
209
|
+
/**
|
|
210
|
+
* Detects "ENVIAR RECORDATORIOS *" titles (Montejo recordatorios). Even though
|
|
211
|
+
* they live in colorId 3 (Montejo), they are NOT patient appointments.
|
|
212
|
+
*/
|
|
213
|
+
exports.REMINDER_TITLE_REGEX = /^ENVIAR\s+RECORDATORIOS\b/i;
|
|
214
|
+
/**
|
|
215
|
+
* Detects cancelled events by title content. Used in addition to colorId 8.
|
|
216
|
+
*/
|
|
217
|
+
exports.CANCELLED_TITLE_REGEX = /\b(?:CANCEL(?:ADA|ADO))\b|\bse\s+cancela\b/i;
|
|
218
|
+
// ─── Description patterns ────────────────────────────────────────────────────
|
|
219
|
+
/**
|
|
220
|
+
* Matches a QVET client id ("Q123456").
|
|
221
|
+
* Range chosen wide enough to cover historical and future ids.
|
|
222
|
+
*/
|
|
223
|
+
exports.QVET_ID_REGEX = /Q\d{4,7}/;
|
|
224
|
+
/**
|
|
225
|
+
* Captures Mexican phone numbers in any of the common written formats.
|
|
226
|
+
* Returns the full match; caller normalizes to last 10 digits.
|
|
227
|
+
*
|
|
228
|
+
* Tolerates:
|
|
229
|
+
* +52 1 999 442 9488
|
|
230
|
+
* +52 999 442 9488
|
|
231
|
+
* 999 442 9488
|
|
232
|
+
* 999-442-9488
|
|
233
|
+
* 9994429488
|
|
234
|
+
*/
|
|
235
|
+
exports.PHONE_REGEX = /(?:\+?52\s?1?\s?)?(?:\d{3}[\s-]?\d{3}[\s-]?\d{4}|\d{10})/;
|
|
236
|
+
/**
|
|
237
|
+
* Captures the "agendado por" col_code from a description.
|
|
238
|
+
*
|
|
239
|
+
* Variants observed:
|
|
240
|
+
* "AGENDO XZA 16 MAYO"
|
|
241
|
+
* "AG YMP 14.05.26"
|
|
242
|
+
* "AGENDADA POR SLR 02/12/25"
|
|
243
|
+
* "AGENDADO POR SLR 02/12/25"
|
|
244
|
+
*/
|
|
245
|
+
exports.SCHEDULER_REGEX = /\b(?:AG|AGENDO|AGENDAD[OA]\s+POR)\s+([A-Z]{2,4})\b/i;
|
|
246
|
+
// ─── HVP-created extendedProperties keys ─────────────────────────────────────
|
|
247
|
+
/**
|
|
248
|
+
* Keys used in Google Calendar `extendedProperties.private` for events created
|
|
249
|
+
* from HVP. Phase 2 writes these; Phase 1 reads them for full attribution.
|
|
250
|
+
*/
|
|
251
|
+
exports.HVP_EVENT_PROP_KEYS = {
|
|
252
|
+
qvetClientId: "qvetClientId",
|
|
253
|
+
qvetPetId: "qvetPetId",
|
|
254
|
+
serviceCode: "serviceCode",
|
|
255
|
+
branchId: "branchId",
|
|
256
|
+
vetColCode: "vetColCode",
|
|
257
|
+
scheduledByColCode: "scheduledByColCode",
|
|
258
|
+
isPreferredVet: "isPreferredVet",
|
|
259
|
+
source: "source", // "hvp"
|
|
260
|
+
standardVersion: "standardVersion",
|
|
261
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const google_calendar_constants_1 = require("./google-calendar.constants");
|
|
4
|
+
describe("STANDARD_V1", () => {
|
|
5
|
+
it("has weights that sum to 100", () => {
|
|
6
|
+
const total = google_calendar_constants_1.STANDARD_V1.dimensions.reduce((sum, d) => sum + d.weight, 0);
|
|
7
|
+
expect(total).toBe(100);
|
|
8
|
+
});
|
|
9
|
+
it("declares exactly the six expected dimensions", () => {
|
|
10
|
+
const ids = google_calendar_constants_1.STANDARD_V1.dimensions.map((d) => d.id).sort();
|
|
11
|
+
expect(ids).toEqual(["branch", "client", "context", "pet", "service", "vet"]);
|
|
12
|
+
});
|
|
13
|
+
it("has a sensible compliance threshold", () => {
|
|
14
|
+
expect(google_calendar_constants_1.STANDARD_V1.minimumCompliantScore).toBeGreaterThanOrEqual(50);
|
|
15
|
+
expect(google_calendar_constants_1.STANDARD_V1.minimumCompliantScore).toBeLessThanOrEqual(100);
|
|
16
|
+
});
|
|
17
|
+
it("has a non-empty version string", () => {
|
|
18
|
+
expect(google_calendar_constants_1.STANDARD_V1.version).toMatch(/^\d+\.\d+$/);
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
describe("Branch ↔ colorId mapping", () => {
|
|
22
|
+
it("is a round-trip", () => {
|
|
23
|
+
for (const [branch, colorId] of Object.entries(google_calendar_constants_1.BRANCH_COLOR_IDS)) {
|
|
24
|
+
expect(google_calendar_constants_1.COLOR_ID_TO_BRANCH[colorId]).toBe(branch);
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
it("uses the discovery-confirmed colorIds", () => {
|
|
28
|
+
expect(google_calendar_constants_1.BRANCH_COLOR_IDS.urban).toBe("11");
|
|
29
|
+
expect(google_calendar_constants_1.BRANCH_COLOR_IDS.harbor).toBe("9");
|
|
30
|
+
expect(google_calendar_constants_1.BRANCH_COLOR_IDS.montejo).toBe("3");
|
|
31
|
+
});
|
|
32
|
+
it("does not collide with excluded or cancelled colorIds", () => {
|
|
33
|
+
for (const colorId of Object.values(google_calendar_constants_1.BRANCH_COLOR_IDS)) {
|
|
34
|
+
expect(google_calendar_constants_1.EXCLUDED_COLOR_IDS.has(colorId)).toBe(false);
|
|
35
|
+
expect(colorId).not.toBe(google_calendar_constants_1.CANCELLED_COLOR_ID);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
describe("Service code synonyms", () => {
|
|
40
|
+
it("only maps to canonical codes", () => {
|
|
41
|
+
const canonicalSet = new Set(google_calendar_constants_1.CANONICAL_SERVICE_CODES);
|
|
42
|
+
for (const [synonym, canonical] of Object.entries(google_calendar_constants_1.SERVICE_CODE_SYNONYMS)) {
|
|
43
|
+
expect(canonicalSet.has(canonical)).toBe(true);
|
|
44
|
+
// synonyms should be uppercase (we normalize before lookup)
|
|
45
|
+
expect(synonym).toBe(synonym.toUpperCase());
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
it("covers every canonical code with at least one synonym", () => {
|
|
49
|
+
const reverseIndex = new Map();
|
|
50
|
+
for (const [syn, canonical] of Object.entries(google_calendar_constants_1.SERVICE_CODE_SYNONYMS)) {
|
|
51
|
+
const arr = reverseIndex.get(canonical) ?? [];
|
|
52
|
+
arr.push(syn);
|
|
53
|
+
reverseIndex.set(canonical, arr);
|
|
54
|
+
}
|
|
55
|
+
for (const canonical of google_calendar_constants_1.CANONICAL_SERVICE_CODES) {
|
|
56
|
+
expect(reverseIndex.get(canonical)?.length ?? 0).toBeGreaterThan(0);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
it("collapses VACUNA variants", () => {
|
|
60
|
+
expect(google_calendar_constants_1.SERVICE_CODE_SYNONYMS.V).toBe("VACUNA");
|
|
61
|
+
expect(google_calendar_constants_1.SERVICE_CODE_SYNONYMS.VAC).toBe("VACUNA");
|
|
62
|
+
expect(google_calendar_constants_1.SERVICE_CODE_SYNONYMS.VACUNA).toBe("VACUNA");
|
|
63
|
+
});
|
|
64
|
+
it("collapses APLICACION variants (AP/APL/AM)", () => {
|
|
65
|
+
expect(google_calendar_constants_1.SERVICE_CODE_SYNONYMS.AP).toBe("APLICACION");
|
|
66
|
+
expect(google_calendar_constants_1.SERVICE_CODE_SYNONYMS.APL).toBe("APLICACION");
|
|
67
|
+
expect(google_calendar_constants_1.SERVICE_CODE_SYNONYMS.AM).toBe("APLICACION");
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
describe("Title regexes", () => {
|
|
71
|
+
it("detects the preferred-vet asterisk at the end of titles", () => {
|
|
72
|
+
expect(google_calendar_constants_1.PREFERRED_VET_REGEX.test("C. ARCHIE AAT*")).toBe(true);
|
|
73
|
+
expect(google_calendar_constants_1.PREFERRED_VET_REGEX.test("AM CAMILA APL* ")).toBe(true);
|
|
74
|
+
expect(google_calendar_constants_1.PREFERRED_VET_REGEX.test("CX. DANNA MAT*")).toBe(true);
|
|
75
|
+
});
|
|
76
|
+
it("does not flag asterisk in the middle of a title", () => {
|
|
77
|
+
expect(google_calendar_constants_1.PREFERRED_VET_REGEX.test("C. MILO *fpo IS A BUG")).toBe(false);
|
|
78
|
+
});
|
|
79
|
+
it("identifies Montejo reminder events", () => {
|
|
80
|
+
expect(google_calendar_constants_1.REMINDER_TITLE_REGEX.test("ENVIAR RECORDATORIOS MONT")).toBe(true);
|
|
81
|
+
expect(google_calendar_constants_1.REMINDER_TITLE_REGEX.test("enviar recordatorios mont")).toBe(true);
|
|
82
|
+
expect(google_calendar_constants_1.REMINDER_TITLE_REGEX.test("D. MILO")).toBe(false);
|
|
83
|
+
});
|
|
84
|
+
it("flags operational prefixes as excluded", () => {
|
|
85
|
+
expect(google_calendar_constants_1.EXCLUDED_TITLE_PREFIXES.has("RECIBIR")).toBe(true);
|
|
86
|
+
expect(google_calendar_constants_1.EXCLUDED_TITLE_PREFIXES.has("ENTREGAR")).toBe(true);
|
|
87
|
+
expect(google_calendar_constants_1.EXCLUDED_TITLE_PREFIXES.has("ENTREVISTA")).toBe(true);
|
|
88
|
+
expect(google_calendar_constants_1.EXCLUDED_TITLE_PREFIXES.has("C")).toBe(false);
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
describe("Description regexes", () => {
|
|
92
|
+
it("captures Q{id} client ids", () => {
|
|
93
|
+
expect("DIANA LAURA | KIRI | Q913562".match(google_calendar_constants_1.QVET_ID_REGEX)?.[0]).toBe("Q913562");
|
|
94
|
+
expect("Q966648".match(google_calendar_constants_1.QVET_ID_REGEX)?.[0]).toBe("Q966648");
|
|
95
|
+
expect("no qvet here".match(google_calendar_constants_1.QVET_ID_REGEX)).toBeNull();
|
|
96
|
+
});
|
|
97
|
+
it("captures phones in common Mexican formats", () => {
|
|
98
|
+
expect("+52 1 999 442 9488".match(google_calendar_constants_1.PHONE_REGEX)?.[0]).toBe("+52 1 999 442 9488");
|
|
99
|
+
expect("999 442 9488".match(google_calendar_constants_1.PHONE_REGEX)?.[0]).toBe("999 442 9488");
|
|
100
|
+
expect("9994429488".match(google_calendar_constants_1.PHONE_REGEX)?.[0]).toBe("9994429488");
|
|
101
|
+
expect("999-442-9488".match(google_calendar_constants_1.PHONE_REGEX)?.[0]).toBe("999-442-9488");
|
|
102
|
+
});
|
|
103
|
+
it("captures scheduler col_code from descriptions", () => {
|
|
104
|
+
const cases = [
|
|
105
|
+
["AGENDO XZA 16 MAYO", "XZA"],
|
|
106
|
+
["AG YMP 14.05.26", "YMP"],
|
|
107
|
+
["AGENDADA POR SLR 02/12/25", "SLR"],
|
|
108
|
+
["AGENDADO POR SCP", "SCP"],
|
|
109
|
+
];
|
|
110
|
+
for (const [text, expected] of cases) {
|
|
111
|
+
const match = text.match(google_calendar_constants_1.SCHEDULER_REGEX);
|
|
112
|
+
expect(match?.[1]?.toUpperCase()).toBe(expected);
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
});
|
package/dist/constants/index.js
CHANGED
|
@@ -44,3 +44,4 @@ __exportStar(require("./settlement.enums"), exports);
|
|
|
44
44
|
__exportStar(require("./client-billing.enums"), exports);
|
|
45
45
|
__exportStar(require("./sat-income-invoice"), exports);
|
|
46
46
|
__exportStar(require("./global-invoice.enums"), exports);
|
|
47
|
+
__exportStar(require("./google-calendar.constants"), exports);
|
|
@@ -240,3 +240,26 @@ export interface SearchDocumentsQuery {
|
|
|
240
240
|
q: string;
|
|
241
241
|
limit?: number;
|
|
242
242
|
}
|
|
243
|
+
/**
|
|
244
|
+
* Compliance Matrix Query
|
|
245
|
+
*
|
|
246
|
+
* Filters for the admin/manager `GET /api/documents/acknowledgments/matrix`
|
|
247
|
+
* endpoint. All optional. The endpoint always restricts to docs that
|
|
248
|
+
* `requiresAcknowledgment === true` AND `status === "current"` — those are
|
|
249
|
+
* the only docs an ack is meaningful on.
|
|
250
|
+
*
|
|
251
|
+
* @example GET /api/documents/acknowledgments/matrix?category=operational
|
|
252
|
+
*/
|
|
253
|
+
export interface DocumentComplianceMatrixQuery {
|
|
254
|
+
/** Restrict rows to these doc IDs. */
|
|
255
|
+
documentIds?: string[];
|
|
256
|
+
/** Restrict columns to these collaborator IDs. */
|
|
257
|
+
collaboratorIds?: string[];
|
|
258
|
+
/** Filter rows by document category. */
|
|
259
|
+
category?: Category;
|
|
260
|
+
/**
|
|
261
|
+
* Filter rows whose audience includes this role (or `"all"`). Lets admin
|
|
262
|
+
* scope the matrix to docs everyone has to read, or only managers, etc.
|
|
263
|
+
*/
|
|
264
|
+
audienceRole?: DocumentAudienceEntry;
|
|
265
|
+
}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* - Use Public → View → Admin inheritance where it adds value.
|
|
7
7
|
*/
|
|
8
8
|
import { Category, ChangeType, ContentType, Criticality, DocumentStatus, DocumentType, ExternalLinkProvider } from "../../constants/document.enums";
|
|
9
|
+
import { WebAppRole } from "../../constants/collaborator.constants";
|
|
9
10
|
import { DocumentAudienceEntry } from "./requests";
|
|
10
11
|
/**
|
|
11
12
|
* Compact reference to a document's current version, embedded in
|
|
@@ -160,3 +161,74 @@ export interface PendingAcknowledgmentResponse {
|
|
|
160
161
|
pendingDays: number;
|
|
161
162
|
isFromOnboarding: boolean;
|
|
162
163
|
}
|
|
164
|
+
/**
|
|
165
|
+
* Compliance Matrix Cell
|
|
166
|
+
*
|
|
167
|
+
* Per `(document, collaborator)` pair, the read state.
|
|
168
|
+
*
|
|
169
|
+
* - `acked` — collaborator has a valid ack on the doc's current version
|
|
170
|
+
* (covers patch chains; major/minor invalidates).
|
|
171
|
+
* - `pending` — collaborator is in the doc's audience and has NOT acked.
|
|
172
|
+
* - `not_applicable` — collaborator's role is outside the doc's audience.
|
|
173
|
+
*/
|
|
174
|
+
export type DocumentComplianceCellStatus = "acked" | "pending" | "not_applicable";
|
|
175
|
+
export interface DocumentComplianceCell {
|
|
176
|
+
collaboratorId: string;
|
|
177
|
+
status: DocumentComplianceCellStatus;
|
|
178
|
+
/** Present when `status === "acked"`. ISO 8601. */
|
|
179
|
+
acknowledgedAt?: string;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Compliance Matrix Collaborator (column header)
|
|
183
|
+
*
|
|
184
|
+
* Compact info needed to render a column header. Order of `collaborators[]`
|
|
185
|
+
* in the response matches order of `cells[]` on each row.
|
|
186
|
+
*/
|
|
187
|
+
export interface DocumentComplianceCollaborator {
|
|
188
|
+
id: string;
|
|
189
|
+
col_code: string;
|
|
190
|
+
fullName: string;
|
|
191
|
+
role: WebAppRole;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Compliance Matrix Row (one document)
|
|
195
|
+
*/
|
|
196
|
+
export interface DocumentComplianceRow {
|
|
197
|
+
documentId: string;
|
|
198
|
+
documentSlug: string;
|
|
199
|
+
documentTitle: string;
|
|
200
|
+
category: Category;
|
|
201
|
+
criticality: Criticality;
|
|
202
|
+
audience: DocumentAudienceEntry[];
|
|
203
|
+
currentVersionId: string | null;
|
|
204
|
+
/** Semver string of the current version, e.g. "1.2.0". */
|
|
205
|
+
versionNumber: string | null;
|
|
206
|
+
/** Number of collaborators in the matrix that the audience applies to. */
|
|
207
|
+
totalApplicable: number;
|
|
208
|
+
/** Of those, how many have a valid ack on the current version. */
|
|
209
|
+
totalAcked: number;
|
|
210
|
+
/** `totalAcked / totalApplicable * 100`, rounded to 0 decimals. 0 when N/A. */
|
|
211
|
+
pctRead: number;
|
|
212
|
+
/** Index-aligned with the response's `collaborators[]`. */
|
|
213
|
+
cells: DocumentComplianceCell[];
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Document Compliance Matrix Response
|
|
217
|
+
*
|
|
218
|
+
* Admin/manager view: matrix of "who has read what". Rows are documents,
|
|
219
|
+
* columns are collaborators.
|
|
220
|
+
*
|
|
221
|
+
* Used for: `GET /api/documents/acknowledgments/matrix`
|
|
222
|
+
*/
|
|
223
|
+
export interface DocumentComplianceMatrixResponse {
|
|
224
|
+
collaborators: DocumentComplianceCollaborator[];
|
|
225
|
+
rows: DocumentComplianceRow[];
|
|
226
|
+
/** Total docs in the response (rows.length, surfaced for KPI). */
|
|
227
|
+
totalDocs: number;
|
|
228
|
+
/** Total applicable doc-collaborator pairs across all rows. */
|
|
229
|
+
totalApplicablePairs: number;
|
|
230
|
+
/** Of those, how many are acked. */
|
|
231
|
+
totalAckedPairs: number;
|
|
232
|
+
/** Average of pctRead across rows (or `totalAckedPairs / totalApplicablePairs * 100`). */
|
|
233
|
+
pctReadOverall: number;
|
|
234
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./requests"), exports);
|
|
18
|
+
__exportStar(require("./responses"), exports);
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Google Calendar Integration - Request Types
|
|
3
|
+
*
|
|
4
|
+
* Request contracts for the Google Calendar diagnostic + appointment management feature.
|
|
5
|
+
* See: resources/notes/google-calendar/standard-v1.md
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Diagnose request — score appointments against the standard for a date window.
|
|
9
|
+
*
|
|
10
|
+
* @example GET /api/google-calendar/diagnose?from=2026-04-01&to=2026-05-01
|
|
11
|
+
*/
|
|
12
|
+
export interface DiagnoseAppointmentsRequest {
|
|
13
|
+
/** ISO date (inclusive). Start of the window in Mexico_City timezone. */
|
|
14
|
+
from: string;
|
|
15
|
+
/** ISO date (inclusive). End of the window in Mexico_City timezone. */
|
|
16
|
+
to: string;
|
|
17
|
+
/** Filter results to a single branch. Optional. */
|
|
18
|
+
branchId?: 'urban' | 'harbor' | 'montejo';
|
|
19
|
+
/** Filter to events where this col_code is detected as the attending vet. Optional. */
|
|
20
|
+
vetColCode?: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Single-event diagnose request.
|
|
24
|
+
*
|
|
25
|
+
* @example GET /api/google-calendar/diagnose/event/:eventId
|
|
26
|
+
*/
|
|
27
|
+
export interface DiagnoseEventRequest {
|
|
28
|
+
eventId: string;
|
|
29
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Google Calendar Integration - Request Types
|
|
4
|
+
*
|
|
5
|
+
* Request contracts for the Google Calendar diagnostic + appointment management feature.
|
|
6
|
+
* See: resources/notes/google-calendar/standard-v1.md
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Google Calendar Integration - Response Types
|
|
3
|
+
*
|
|
4
|
+
* Response contracts for the Google Calendar diagnostic feature.
|
|
5
|
+
* See: resources/notes/google-calendar/standard-v1.md
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* One of the six dimensions of the appointment standard.
|
|
9
|
+
*/
|
|
10
|
+
export type AppointmentStandardDimensionId = 'branch' | 'service' | 'pet' | 'client' | 'context' | 'vet';
|
|
11
|
+
/**
|
|
12
|
+
* One of the three branches.
|
|
13
|
+
*/
|
|
14
|
+
export type AppointmentBranch = 'urban' | 'harbor' | 'montejo';
|
|
15
|
+
/**
|
|
16
|
+
* Definition of the multi-factor scoring standard used by the diagnostic engine.
|
|
17
|
+
*
|
|
18
|
+
* Defined in code as a constant (see constants/google-calendar.constants.ts).
|
|
19
|
+
* Sent to the frontend so the dashboard can render dimension names/weights without
|
|
20
|
+
* hardcoding.
|
|
21
|
+
*/
|
|
22
|
+
export interface AppointmentStandard {
|
|
23
|
+
/** Semver-like version identifier (e.g. "1.0"). */
|
|
24
|
+
version: string;
|
|
25
|
+
/** Dimensions and their weights (must sum to 100). */
|
|
26
|
+
dimensions: ReadonlyArray<{
|
|
27
|
+
id: AppointmentStandardDimensionId;
|
|
28
|
+
weight: number;
|
|
29
|
+
/** Display label in Spanish. */
|
|
30
|
+
label: string;
|
|
31
|
+
}>;
|
|
32
|
+
/** Threshold above which an event is considered "compliant". */
|
|
33
|
+
minimumCompliantScore: number;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* A single dimension result for one event.
|
|
37
|
+
*/
|
|
38
|
+
export interface DiagnosticDimensionResult {
|
|
39
|
+
id: AppointmentStandardDimensionId;
|
|
40
|
+
passed: boolean;
|
|
41
|
+
weight: number;
|
|
42
|
+
/** Human-readable evidence ("Q966648 in description", "phone 999... matched", etc.) or null when missing. */
|
|
43
|
+
evidence: string | null;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Reason an event was excluded from scoring.
|
|
47
|
+
*/
|
|
48
|
+
export type DiagnosticExclusionReason = 'cancelled' | 'blocker' | 'special' | 'operational' | 'interview' | 'reminder';
|
|
49
|
+
/**
|
|
50
|
+
* Full diagnostic result for one event.
|
|
51
|
+
*/
|
|
52
|
+
export interface DiagnosticResult {
|
|
53
|
+
eventId: string;
|
|
54
|
+
/** ISO datetime in Mexico_City. */
|
|
55
|
+
eventStart: string;
|
|
56
|
+
/** Duration in minutes. */
|
|
57
|
+
durationMinutes: number;
|
|
58
|
+
/** Title as it appears in GCal. */
|
|
59
|
+
title: string;
|
|
60
|
+
/** htmlLink to the event in Google Calendar. */
|
|
61
|
+
htmlLink: string | null;
|
|
62
|
+
/** Score 0-100. */
|
|
63
|
+
score: number;
|
|
64
|
+
/** Score >= minimumCompliantScore. */
|
|
65
|
+
isCompliant: boolean;
|
|
66
|
+
/** Per-dimension results. */
|
|
67
|
+
dimensions: DiagnosticDimensionResult[];
|
|
68
|
+
/** Inferred branch from colorId or extendedProperties. Null if undetected. */
|
|
69
|
+
inferredBranch: AppointmentBranch | null;
|
|
70
|
+
/** Inferred canonical service code (e.g. "CONSULTA"). Null if undetected. */
|
|
71
|
+
inferredService: string | null;
|
|
72
|
+
/** Inferred pet name from the title. */
|
|
73
|
+
inferredPetName: string | null;
|
|
74
|
+
/** col_code of the attending vet, when detectable. */
|
|
75
|
+
inferredVetColCode: string | null;
|
|
76
|
+
/** col_code of the person who scheduled, when detectable in description. */
|
|
77
|
+
inferredSchedulerColCode: string | null;
|
|
78
|
+
/** Q{id} or null. */
|
|
79
|
+
inferredQvetClientId: string | null;
|
|
80
|
+
/** Last 10 digits of phone, normalized. Null if not found. */
|
|
81
|
+
inferredPhone: string | null;
|
|
82
|
+
/** Asterisk at end of title => preferred vet => generates commission. */
|
|
83
|
+
isPreferredVet: boolean;
|
|
84
|
+
/** True iff event has extendedProperties.private (HVP-created). */
|
|
85
|
+
isHvpCreated: boolean;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Excluded event — does not have a score.
|
|
89
|
+
*/
|
|
90
|
+
export interface DiagnosticExcludedEvent {
|
|
91
|
+
eventId: string;
|
|
92
|
+
eventStart: string;
|
|
93
|
+
title: string;
|
|
94
|
+
reason: DiagnosticExclusionReason;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Aggregated summary of diagnostic results for a window.
|
|
98
|
+
*
|
|
99
|
+
* @example GET /api/google-calendar/diagnose
|
|
100
|
+
*/
|
|
101
|
+
export interface DiagnoseAppointmentsResponse {
|
|
102
|
+
/** Window inputs echoed back. */
|
|
103
|
+
window: {
|
|
104
|
+
from: string;
|
|
105
|
+
to: string;
|
|
106
|
+
};
|
|
107
|
+
/** Standard used for scoring (so frontend can render labels/weights). */
|
|
108
|
+
standard: AppointmentStandard;
|
|
109
|
+
/** Counts. */
|
|
110
|
+
totalFetched: number;
|
|
111
|
+
totalEvaluated: number;
|
|
112
|
+
totalExcluded: number;
|
|
113
|
+
totalCancelled: number;
|
|
114
|
+
/** totalEvaluated where score >= minimumCompliantScore. */
|
|
115
|
+
compliantCount: number;
|
|
116
|
+
/** Average score across evaluated events (0-100). */
|
|
117
|
+
averageScore: number;
|
|
118
|
+
/** Per-dimension breakdown: how many evaluated events passed each dimension. */
|
|
119
|
+
byDimension: Record<AppointmentStandardDimensionId, {
|
|
120
|
+
passed: number;
|
|
121
|
+
total: number;
|
|
122
|
+
}>;
|
|
123
|
+
/** Per-branch breakdown. */
|
|
124
|
+
byBranch: Record<AppointmentBranch | 'unknown', {
|
|
125
|
+
count: number;
|
|
126
|
+
avgScore: number;
|
|
127
|
+
}>;
|
|
128
|
+
/** Per-vet breakdown (top 20 by count). col_code => { totalCount, preferredCount }. */
|
|
129
|
+
byVet: Record<string, {
|
|
130
|
+
count: number;
|
|
131
|
+
preferredCount: number;
|
|
132
|
+
avgScore: number;
|
|
133
|
+
}>;
|
|
134
|
+
/** Detailed results per evaluated event (paginated by caller — capped at 500 in response for now). */
|
|
135
|
+
results: DiagnosticResult[];
|
|
136
|
+
/** Detailed excluded events (capped at 100). */
|
|
137
|
+
excluded: DiagnosticExcludedEvent[];
|
|
138
|
+
}
|
|
@@ -16,6 +16,7 @@ export * from './client-billing';
|
|
|
16
16
|
export * from './global-invoice';
|
|
17
17
|
export * from './inventory-report';
|
|
18
18
|
export * from './google-contacts';
|
|
19
|
+
export * from './google-calendar';
|
|
19
20
|
export * from './study-type-catalog';
|
|
20
21
|
export * from './supplier-overlay';
|
|
21
22
|
export * from './external-study';
|
package/dist/contracts/index.js
CHANGED
|
@@ -32,6 +32,7 @@ __exportStar(require("./client-billing"), exports);
|
|
|
32
32
|
__exportStar(require("./global-invoice"), exports);
|
|
33
33
|
__exportStar(require("./inventory-report"), exports);
|
|
34
34
|
__exportStar(require("./google-contacts"), exports);
|
|
35
|
+
__exportStar(require("./google-calendar"), exports);
|
|
35
36
|
__exportStar(require("./study-type-catalog"), exports);
|
|
36
37
|
__exportStar(require("./supplier-overlay"), exports);
|
|
37
38
|
__exportStar(require("./external-study"), exports);
|