@blackcode_sa/metaestetics-api 1.12.72 → 1.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/dist/admin/index.d.mts +872 -1
  2. package/dist/admin/index.d.ts +872 -1
  3. package/dist/admin/index.js +3604 -356
  4. package/dist/admin/index.mjs +3594 -357
  5. package/dist/index.d.mts +1349 -1
  6. package/dist/index.d.ts +1349 -1
  7. package/dist/index.js +5325 -2141
  8. package/dist/index.mjs +4939 -1767
  9. package/package.json +1 -1
  10. package/src/admin/analytics/analytics.admin.service.ts +278 -0
  11. package/src/admin/analytics/index.ts +2 -0
  12. package/src/admin/index.ts +6 -0
  13. package/src/backoffice/services/analytics.service.proposal.md +4 -0
  14. package/src/services/analytics/ARCHITECTURE.md +199 -0
  15. package/src/services/analytics/CLOUD_FUNCTIONS.md +225 -0
  16. package/src/services/analytics/GROUPED_ANALYTICS.md +501 -0
  17. package/src/services/analytics/QUICK_START.md +393 -0
  18. package/src/services/analytics/README.md +304 -0
  19. package/src/services/analytics/SUMMARY.md +141 -0
  20. package/src/services/analytics/TRENDS.md +380 -0
  21. package/src/services/analytics/USAGE_GUIDE.md +518 -0
  22. package/src/services/analytics/analytics-cloud.service.ts +222 -0
  23. package/src/services/analytics/analytics.service.ts +2142 -0
  24. package/src/services/analytics/index.ts +4 -0
  25. package/src/services/analytics/review-analytics.service.ts +941 -0
  26. package/src/services/analytics/utils/appointment-filtering.utils.ts +138 -0
  27. package/src/services/analytics/utils/cost-calculation.utils.ts +182 -0
  28. package/src/services/analytics/utils/grouping.utils.ts +434 -0
  29. package/src/services/analytics/utils/stored-analytics.utils.ts +347 -0
  30. package/src/services/analytics/utils/time-calculation.utils.ts +186 -0
  31. package/src/services/analytics/utils/trend-calculation.utils.ts +200 -0
  32. package/src/services/index.ts +1 -0
  33. package/src/types/analytics/analytics.types.ts +597 -0
  34. package/src/types/analytics/grouped-analytics.types.ts +173 -0
  35. package/src/types/analytics/index.ts +4 -0
  36. package/src/types/analytics/stored-analytics.types.ts +137 -0
  37. package/src/types/index.ts +3 -0
@@ -0,0 +1,518 @@
1
+ # Analytics Service - Complete Usage Guide
2
+
3
+ ## Table of Contents
4
+ 1. [What We Can Do](#what-we-can-do)
5
+ 2. [How It Works: Computed vs On-Demand](#how-it-works)
6
+ 3. [Available Analytics](#available-analytics)
7
+ 4. [Specific Use Cases](#specific-use-cases)
8
+
9
+ ---
10
+
11
+ ## What We Can Do
12
+
13
+ The Analytics Service provides comprehensive insights into your clinic operations:
14
+
15
+ ### 📊 **Financial Intelligence**
16
+ - Track total revenue, average revenue per appointment
17
+ - Monitor payment status (paid, unpaid, refunded)
18
+ - Analyze costs per patient
19
+ - Revenue trends over time
20
+
21
+ ### 👨‍⚕️ **Practitioner Performance**
22
+ - Appointment counts and completion rates
23
+ - Cancellation and no-show rates per doctor
24
+ - Time efficiency (booked vs actual time)
25
+ - Revenue generation per practitioner
26
+ - Patient retention rates
27
+
28
+ ### 🏥 **Procedure Analytics**
29
+ - Most popular procedures
30
+ - Most profitable procedures
31
+ - Procedure performance by category/technology
32
+ - Product usage per procedure
33
+
34
+ ### ⏱️ **Time Management**
35
+ - Booked time vs actual time spent
36
+ - Efficiency percentages
37
+ - Overrun/underutilization analysis
38
+ - Peak hours identification
39
+
40
+ ### ❌ **Cancellation & No-Show Analysis**
41
+ - Rates by clinic, practitioner, patient, or procedure
42
+ - Cancellation reasons breakdown
43
+ - Average cancellation lead time
44
+ - Patterns and trends
45
+
46
+ ### 👥 **Patient Insights**
47
+ - Patient lifetime value
48
+ - Retention rates
49
+ - Appointment frequency
50
+ - Cancellation patterns per patient
51
+
52
+ ### 📦 **Product Usage**
53
+ - Products used per appointment
54
+ - Revenue contribution by product
55
+ - Usage by procedure
56
+ - Quantity and pricing trends
57
+
58
+ ### 🏢 **Clinic Performance**
59
+ - Overall clinic metrics
60
+ - Practitioner comparisons
61
+ - Procedure popularity
62
+ - Efficiency metrics
63
+
64
+ ---
65
+
66
+ ## How It Works: Computed vs On-Demand
67
+
68
+ ### 🔄 **Hybrid Architecture**
69
+
70
+ The service uses a **smart hybrid approach**:
71
+
72
+ ```
73
+ ┌─────────────────────────────────────────┐
74
+ │ Client Request │
75
+ └──────────────┬──────────────────────────┘
76
+
77
+
78
+ ┌─────────────────────────────────────────┐
79
+ │ Check Stored Analytics? │
80
+ └──────┬──────────────────────┬───────────┘
81
+ │ │
82
+ YES │ │ NO
83
+ ▼ ▼
84
+ ┌──────────────┐ ┌─────────────────────┐
85
+ │ Data Fresh? │ │ Calculate On-Demand │
86
+ │ (< 12 hours) │ │ (Query Appointments)│
87
+ └──┬───────┬───┘ └─────────────────────┘
88
+ │ │
89
+ YES│ │NO
90
+ │ │
91
+ ▼ ▼
92
+ ┌─────┐ ┌─────────────────────┐
93
+ │ ✅ │ │ Calculate On-Demand │
94
+ │Fast │ │ (Query Appointments)│
95
+ └─────┘ └─────────────────────┘
96
+ ```
97
+
98
+ ### 📦 **Pre-Computed Analytics (Recommended)**
99
+
100
+ **How it works:**
101
+ 1. **Cloud Function runs every 12 hours** (scheduled)
102
+ 2. Computes analytics for all clinics
103
+ 3. Stores results in Firestore: `clinics/{clinicBranchId}/analytics/`
104
+ 4. Client reads cached data (1 document read = instant)
105
+
106
+ **Benefits:**
107
+ - ⚡ **Fast**: Instant response (single document read)
108
+ - 💰 **Cheap**: 1 read vs hundreds/thousands
109
+ - 📈 **Scalable**: Works with large datasets
110
+
111
+ **Example:**
112
+ ```typescript
113
+ // Automatically uses cached data if available and fresh (< 12 hours)
114
+ const dashboard = await analyticsService.getDashboardData(
115
+ { clinicBranchId: 'clinic-123' },
116
+ { start: new Date('2024-01-01'), end: new Date('2024-12-31') }
117
+ );
118
+ // Returns instantly from cache! ⚡
119
+ ```
120
+
121
+ ### 🔄 **On-Demand Calculation (Fallback)**
122
+
123
+ **When it happens:**
124
+ - No cached data exists yet
125
+ - Cached data is stale (> 12 hours old)
126
+ - You explicitly disable cache: `{ useCache: false }`
127
+ - Custom date range that doesn't match pre-computed periods
128
+
129
+ **How it works:**
130
+ 1. Queries all appointments matching filters
131
+ 2. Calculates metrics in real-time
132
+ 3. Returns results
133
+
134
+ **Example:**
135
+ ```typescript
136
+ // Force on-demand calculation
137
+ const dashboard = await analyticsService.getDashboardData(
138
+ { clinicBranchId: 'clinic-123' },
139
+ { start: new Date('2024-01-01'), end: new Date('2024-12-31') },
140
+ { useCache: false } // Force calculation
141
+ );
142
+ // Calculates from scratch (slower but always fresh)
143
+ ```
144
+
145
+ ### ⚙️ **Configuration**
146
+
147
+ **Change cache freshness:**
148
+ ```typescript
149
+ // Use cache if data is less than 6 hours old
150
+ const dashboard = await analyticsService.getDashboardData(
151
+ { clinicBranchId: 'clinic-123' },
152
+ dateRange,
153
+ { maxCacheAgeHours: 6 }
154
+ );
155
+ ```
156
+
157
+ **Change Cloud Function schedule:**
158
+ Edit `Cloud/functions/src/analytics/computeAnalytics.ts`:
159
+ ```typescript
160
+ schedule: "every 6 hours" // or "every 24 hours", etc.
161
+ ```
162
+
163
+ ---
164
+
165
+ ## Available Analytics
166
+
167
+ ### 1. **Practitioner Analytics** 👨‍⚕️
168
+
169
+ ```typescript
170
+ const metrics = await analyticsService.getPractitionerAnalytics(
171
+ 'practitioner-id-123',
172
+ { start: new Date('2024-01-01'), end: new Date('2024-12-31') }
173
+ );
174
+
175
+ // Returns:
176
+ {
177
+ practitionerId: string;
178
+ practitionerName: string;
179
+ totalAppointments: number;
180
+ completedAppointments: number;
181
+ canceledAppointments: number;
182
+ noShowAppointments: number;
183
+ cancellationRate: number; // percentage
184
+ noShowRate: number; // percentage
185
+ averageBookedTime: number; // minutes
186
+ averageActualTime: number; // minutes
187
+ timeEfficiency: number; // percentage
188
+ totalRevenue: number;
189
+ averageRevenuePerAppointment: number;
190
+ topProcedures: Array<{ procedureId, procedureName, count, revenue }>;
191
+ patientRetentionRate: number;
192
+ uniquePatients: number;
193
+ }
194
+ ```
195
+
196
+ ### 2. **Procedure Analytics** 🏥
197
+
198
+ ```typescript
199
+ // Single procedure
200
+ const procedure = await analyticsService.getProcedureAnalytics(
201
+ 'procedure-id-123',
202
+ dateRange
203
+ );
204
+
205
+ // All procedures
206
+ const allProcedures = await analyticsService.getProcedureAnalytics(
207
+ undefined,
208
+ dateRange
209
+ );
210
+
211
+ // Most popular
212
+ const popular = await analyticsService.getProcedurePopularity(dateRange, 10);
213
+
214
+ // Most profitable
215
+ const profitable = await analyticsService.getProcedureProfitability(dateRange, 10);
216
+ ```
217
+
218
+ ### 3. **Time Efficiency** ⏱️
219
+
220
+ ```typescript
221
+ const timeMetrics = await analyticsService.getTimeEfficiencyMetrics(
222
+ { clinicBranchId: 'clinic-123' },
223
+ dateRange
224
+ );
225
+
226
+ // Returns:
227
+ {
228
+ averageBookedDuration: number; // minutes
229
+ averageActualDuration: number; // minutes
230
+ averageEfficiency: number; // percentage
231
+ averageOverrun: number; // minutes
232
+ averageUnderutilization: number; // minutes
233
+ efficiencyDistribution: Array<{ range: string, count: number }>;
234
+ }
235
+ ```
236
+
237
+ ### 4. **Cancellation Metrics** ❌
238
+
239
+ ```typescript
240
+ // Grouped by clinic, practitioner, patient, or procedure
241
+ const cancellations = await analyticsService.getCancellationMetrics(
242
+ 'practitioner', // or 'clinic', 'patient', 'procedure'
243
+ dateRange
244
+ );
245
+
246
+ // Returns array of:
247
+ {
248
+ entityId: string;
249
+ entityName: string;
250
+ entityType: 'practitioner' | 'clinic' | 'patient' | 'procedure';
251
+ totalAppointments: number;
252
+ canceledAppointments: number;
253
+ cancellationRate: number; // percentage
254
+ canceledByPatient: number;
255
+ canceledByClinic: number;
256
+ averageCancellationLeadTime: number; // hours
257
+ cancellationReasons: Array<{ reason: string, count: number }>;
258
+ }
259
+ ```
260
+
261
+ ### 5. **No-Show Metrics** 🚫
262
+
263
+ ```typescript
264
+ const noShows = await analyticsService.getNoShowMetrics(
265
+ 'practitioner', // or 'clinic', 'patient', 'procedure'
266
+ dateRange
267
+ );
268
+
269
+ // Returns array of:
270
+ {
271
+ entityId: string;
272
+ entityName: string;
273
+ entityType: 'practitioner' | 'clinic' | 'patient' | 'procedure';
274
+ totalAppointments: number;
275
+ noShowAppointments: number;
276
+ noShowRate: number; // percentage
277
+ }
278
+ ```
279
+
280
+ ### 6. **Revenue Metrics** 💰
281
+
282
+ ```typescript
283
+ const revenue = await analyticsService.getRevenueMetrics(
284
+ { clinicBranchId: 'clinic-123' },
285
+ dateRange
286
+ );
287
+
288
+ // Returns:
289
+ {
290
+ totalRevenue: number;
291
+ averageRevenuePerAppointment: number;
292
+ revenueByStatus: Record<AppointmentStatus, number>;
293
+ revenueByPaymentStatus: Record<PaymentStatus, number>;
294
+ unpaidRevenue: number;
295
+ refundedRevenue: number;
296
+ }
297
+ ```
298
+
299
+ ### 7. **Product Usage** 📦
300
+
301
+ ```typescript
302
+ // All products
303
+ const products = await analyticsService.getProductUsageMetrics(
304
+ undefined,
305
+ dateRange
306
+ );
307
+
308
+ // Specific product
309
+ const product = await analyticsService.getProductUsageMetrics(
310
+ 'product-id-123',
311
+ dateRange
312
+ );
313
+ ```
314
+
315
+ ### 8. **Patient Analytics** 👥
316
+
317
+ ```typescript
318
+ // Single patient
319
+ const patient = await analyticsService.getPatientAnalytics(
320
+ 'patient-id-123',
321
+ dateRange
322
+ );
323
+
324
+ // All patients
325
+ const allPatients = await analyticsService.getPatientAnalytics(
326
+ undefined,
327
+ dateRange
328
+ );
329
+ ```
330
+
331
+ ### 9. **Dashboard Data** 📊
332
+
333
+ ```typescript
334
+ const dashboard = await analyticsService.getDashboardData(
335
+ { clinicBranchId: 'clinic-123' },
336
+ dateRange
337
+ );
338
+
339
+ // Returns comprehensive dashboard with:
340
+ // - Overview metrics
341
+ // - Top practitioners
342
+ // - Top procedures
343
+ // - Cancellation/no-show metrics
344
+ // - Revenue trends
345
+ // - Time efficiency
346
+ // - Top products
347
+ // - Recent activity
348
+ ```
349
+
350
+ ---
351
+
352
+ ## Specific Use Cases
353
+
354
+ ### Use Case 1: Patient No-Show Per Doctor 👨‍⚕️
355
+
356
+ **Question**: "Which patients have the highest no-show rate for each doctor?"
357
+
358
+ **Solution**: Get no-show metrics grouped by practitioner, then filter by patient if needed.
359
+
360
+ ```typescript
361
+ // Step 1: Get no-show metrics grouped by practitioner
362
+ const noShowByPractitioner = await analyticsService.getNoShowMetrics(
363
+ 'practitioner',
364
+ { start: new Date('2024-01-01'), end: new Date('2024-12-31') }
365
+ );
366
+
367
+ // Returns array like:
368
+ // [
369
+ // {
370
+ // entityId: 'practitioner-123',
371
+ // entityName: 'Dr. Smith',
372
+ // entityType: 'practitioner',
373
+ // totalAppointments: 100,
374
+ // noShowAppointments: 15,
375
+ // noShowRate: 15.0
376
+ // },
377
+ // ...
378
+ // ]
379
+
380
+ // Step 2: For a specific practitioner, get patient-level no-shows
381
+ // (You can filter appointments and calculate manually, or use patient analytics)
382
+
383
+ // Get all appointments for a specific practitioner
384
+ const practitionerAppointments = await analyticsService['fetchAppointments'](
385
+ { practitionerId: 'practitioner-123' },
386
+ dateRange
387
+ );
388
+
389
+ // Group no-shows by patient
390
+ const noShowByPatient = new Map<string, { name: string; noShows: number; total: number }>();
391
+
392
+ practitionerAppointments.forEach(appointment => {
393
+ const patientId = appointment.patientId;
394
+ const patientName = appointment.patientInfo?.fullName || 'Unknown';
395
+
396
+ if (!noShowByPatient.has(patientId)) {
397
+ noShowByPatient.set(patientId, { name: patientName, noShows: 0, total: 0 });
398
+ }
399
+
400
+ const patientData = noShowByPatient.get(patientId)!;
401
+ patientData.total++;
402
+
403
+ if (appointment.status === AppointmentStatus.NO_SHOW) {
404
+ patientData.noShows++;
405
+ }
406
+ });
407
+
408
+ // Calculate rates
409
+ const patientNoShowRates = Array.from(noShowByPatient.entries()).map(([patientId, data]) => ({
410
+ patientId,
411
+ patientName: data.name,
412
+ totalAppointments: data.total,
413
+ noShowCount: data.noShows,
414
+ noShowRate: (data.noShows / data.total) * 100
415
+ })).sort((a, b) => b.noShowRate - a.noShowRate);
416
+
417
+ console.log('Patient no-show rates for this practitioner:');
418
+ patientNoShowRates.forEach(patient => {
419
+ console.log(`${patient.patientName}: ${patient.noShowRate.toFixed(1)}% (${patient.noShowCount}/${patient.total})`);
420
+ });
421
+ ```
422
+
423
+ ### Use Case 2: Doctor Performance Comparison 📊
424
+
425
+ ```typescript
426
+ // Get all practitioners for a clinic
427
+ const practitioners = await analyticsService.getCancellationMetrics(
428
+ 'practitioner',
429
+ { start: new Date('2024-01-01'), end: new Date('2024-12-31') }
430
+ );
431
+
432
+ // Filter by clinic if needed (would need clinicBranchId in filters)
433
+ const clinicPractitioners = practitioners.filter(p =>
434
+ // Filter logic based on your needs
435
+ );
436
+
437
+ // Compare metrics
438
+ practitioners.forEach(practitioner => {
439
+ console.log(`${practitioner.entityName}:`);
440
+ console.log(` Cancellation Rate: ${practitioner.cancellationRate}%`);
441
+ console.log(` Total Appointments: ${practitioner.totalAppointments}`);
442
+ });
443
+ ```
444
+
445
+ ### Use Case 3: Procedure Profitability Analysis 💰
446
+
447
+ ```typescript
448
+ // Get most profitable procedures
449
+ const profitable = await analyticsService.getProcedureProfitability(
450
+ { start: new Date('2024-01-01'), end: new Date('2024-12-31') },
451
+ 10 // Top 10
452
+ );
453
+
454
+ profitable.forEach((procedure, index) => {
455
+ console.log(`${index + 1}. ${procedure.procedureName}`);
456
+ console.log(` Revenue: ${procedure.totalRevenue} ${procedure.currency || 'CHF'}`);
457
+ console.log(` Appointments: ${procedure.appointmentCount}`);
458
+ console.log(` Avg Revenue: ${procedure.averageRevenue.toFixed(2)}`);
459
+ });
460
+ ```
461
+
462
+ ### Use Case 4: Time Efficiency by Doctor ⏱️
463
+
464
+ ```typescript
465
+ // Get time efficiency for a specific practitioner
466
+ const practitionerMetrics = await analyticsService.getPractitionerAnalytics(
467
+ 'practitioner-id-123',
468
+ dateRange
469
+ );
470
+
471
+ console.log(`Time Efficiency: ${practitionerMetrics.timeEfficiency}%`);
472
+ console.log(`Avg Booked Time: ${practitionerMetrics.averageBookedTime} min`);
473
+ console.log(`Avg Actual Time: ${practitionerMetrics.averageActualTime} min`);
474
+
475
+ // Or get overall time efficiency with filters
476
+ const timeMetrics = await analyticsService.getTimeEfficiencyMetrics(
477
+ { practitionerId: 'practitioner-id-123' },
478
+ dateRange
479
+ );
480
+ ```
481
+
482
+ ### Use Case 5: Product Cost Analysis 📦
483
+
484
+ ```typescript
485
+ // Get product usage for a specific procedure
486
+ const procedureAnalytics = await analyticsService.getProcedureAnalytics(
487
+ 'procedure-id-123',
488
+ dateRange
489
+ );
490
+
491
+ // Check product usage
492
+ procedureAnalytics.productUsage.forEach(product => {
493
+ console.log(`${product.productName}:`);
494
+ console.log(` Total Quantity: ${product.totalQuantity}`);
495
+ console.log(` Total Revenue: ${product.totalRevenue}`);
496
+ console.log(` Used in ${product.usageCount} appointments`);
497
+ });
498
+ ```
499
+
500
+ ---
501
+
502
+ ## Performance Tips
503
+
504
+ 1. **Use pre-computed analytics** when possible (default behavior)
505
+ 2. **Specify clinicBranchId** in filters to enable caching
506
+ 3. **Use standard date ranges** (daily, weekly, monthly) for better cache hits
507
+ 4. **Batch requests** when getting multiple practitioners/procedures
508
+ 5. **Cache results** on the client side for frequently accessed dashboards
509
+
510
+ ---
511
+
512
+ ## Next Steps
513
+
514
+ 1. **Deploy Cloud Function**: Deploy `computeAnalytics.ts` to start pre-computing
515
+ 2. **Test with real data**: Verify analytics accuracy
516
+ 3. **Monitor performance**: Check Cloud Function logs and Firestore usage
517
+ 4. **Customize**: Adjust cache age and computation schedule as needed
518
+