@blackcode_sa/metaestetics-api 1.14.58 → 1.14.59
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/admin/index.d.mts +36 -1
- package/dist/admin/index.d.ts +36 -1
- package/dist/admin/index.js +520 -7
- package/dist/admin/index.mjs +520 -7
- package/dist/index.d.mts +19 -2
- package/dist/index.d.ts +19 -2
- package/dist/index.js +1 -0
- package/dist/index.mjs +1 -0
- package/package.json +1 -1
- package/src/admin/aggregation/appointment/appointment.aggregation.service.ts +12 -0
- package/src/admin/mailing/appointment/appointment.mailing.service.ts +498 -7
- package/src/admin/notifications/notifications.admin.ts +57 -0
- package/src/types/appointment/index.ts +6 -0
- package/src/types/notifications/index.ts +14 -0
package/dist/admin/index.mjs
CHANGED
|
@@ -429,6 +429,7 @@ var NotificationType = /* @__PURE__ */ ((NotificationType2) => {
|
|
|
429
429
|
NotificationType2["APPOINTMENT_REMINDER"] = "appointmentReminder";
|
|
430
430
|
NotificationType2["APPOINTMENT_STATUS_CHANGE"] = "appointmentStatusChange";
|
|
431
431
|
NotificationType2["APPOINTMENT_RESCHEDULED_PROPOSAL"] = "appointmentRescheduledProposal";
|
|
432
|
+
NotificationType2["APPOINTMENT_RESCHEDULED_REMINDER"] = "appointmentRescheduledReminder";
|
|
432
433
|
NotificationType2["APPOINTMENT_CANCELLED"] = "appointmentCancelled";
|
|
433
434
|
NotificationType2["PRE_REQUIREMENT_INSTRUCTION_DUE"] = "preRequirementInstructionDue";
|
|
434
435
|
NotificationType2["POST_REQUIREMENT_INSTRUCTION_DUE"] = "postRequirementInstructionDue";
|
|
@@ -1124,6 +1125,50 @@ var NotificationsAdmin = class {
|
|
|
1124
1125
|
return null;
|
|
1125
1126
|
}
|
|
1126
1127
|
}
|
|
1128
|
+
/**
|
|
1129
|
+
* Sends a reminder push notification for a pending reschedule request.
|
|
1130
|
+
* Used when a clinic has proposed a reschedule and the patient hasn't responded.
|
|
1131
|
+
* @param appointment The appointment with pending reschedule.
|
|
1132
|
+
* @param patientUserId The ID of the patient.
|
|
1133
|
+
* @param patientExpoTokens Array of Expo push tokens for the patient.
|
|
1134
|
+
* @param reminderCount Optional count of reminders already sent (for tracking).
|
|
1135
|
+
*/
|
|
1136
|
+
async sendRescheduleReminderPush(appointment, patientUserId, patientExpoTokens, reminderCount) {
|
|
1137
|
+
if (!patientExpoTokens || patientExpoTokens.length === 0) {
|
|
1138
|
+
console.log(
|
|
1139
|
+
`[NotificationsAdmin] No expo tokens for patient ${patientUserId} for appointment ${appointment.id} reschedule reminder. Skipping push.`
|
|
1140
|
+
);
|
|
1141
|
+
return null;
|
|
1142
|
+
}
|
|
1143
|
+
const title = "Reminder: Reschedule Request Pending";
|
|
1144
|
+
const body = `You have a pending reschedule request for your ${appointment.procedureInfo.name} appointment. Please respond in the app.`;
|
|
1145
|
+
const notificationTimestampForDb = admin2.firestore.Timestamp.now();
|
|
1146
|
+
const notificationData = {
|
|
1147
|
+
userId: patientUserId,
|
|
1148
|
+
userRole: "patient" /* PATIENT */,
|
|
1149
|
+
notificationType: "appointmentRescheduledReminder" /* APPOINTMENT_RESCHEDULED_REMINDER */,
|
|
1150
|
+
notificationTime: notificationTimestampForDb,
|
|
1151
|
+
notificationTokens: patientExpoTokens,
|
|
1152
|
+
title,
|
|
1153
|
+
body,
|
|
1154
|
+
appointmentId: appointment.id
|
|
1155
|
+
};
|
|
1156
|
+
try {
|
|
1157
|
+
const notificationId = await this.createNotification(
|
|
1158
|
+
notificationData
|
|
1159
|
+
);
|
|
1160
|
+
console.log(
|
|
1161
|
+
`[NotificationsAdmin] Created APPOINTMENT_RESCHEDULED_REMINDER notification ${notificationId} for patient ${patientUserId}. Reminder count: ${reminderCount != null ? reminderCount : 1}.`
|
|
1162
|
+
);
|
|
1163
|
+
return notificationId;
|
|
1164
|
+
} catch (error) {
|
|
1165
|
+
console.error(
|
|
1166
|
+
`[NotificationsAdmin] Error creating APPOINTMENT_RESCHEDULED_REMINDER notification for patient ${patientUserId}:`,
|
|
1167
|
+
error
|
|
1168
|
+
);
|
|
1169
|
+
return null;
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1127
1172
|
};
|
|
1128
1173
|
|
|
1129
1174
|
// src/admin/requirements/patient-requirements.admin.service.ts
|
|
@@ -2287,6 +2332,305 @@ var clinicAppointmentRequestedTemplate = `
|
|
|
2287
2332
|
</body>
|
|
2288
2333
|
</html>
|
|
2289
2334
|
`;
|
|
2335
|
+
var appointmentCancelledTemplate = `
|
|
2336
|
+
<!DOCTYPE html>
|
|
2337
|
+
<html lang="en">
|
|
2338
|
+
<head>
|
|
2339
|
+
<meta charset="UTF-8">
|
|
2340
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
2341
|
+
<title>Appointment Cancelled</title>
|
|
2342
|
+
<style>
|
|
2343
|
+
body {
|
|
2344
|
+
margin: 0;
|
|
2345
|
+
padding: 0;
|
|
2346
|
+
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
|
2347
|
+
background: linear-gradient(135deg, #d4736c 0%, #b85450 100%);
|
|
2348
|
+
min-height: 100vh;
|
|
2349
|
+
}
|
|
2350
|
+
.email-container {
|
|
2351
|
+
max-width: 600px;
|
|
2352
|
+
margin: 0 auto;
|
|
2353
|
+
background: #ffffff;
|
|
2354
|
+
border-radius: 20px;
|
|
2355
|
+
overflow: hidden;
|
|
2356
|
+
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
|
|
2357
|
+
margin-top: 40px;
|
|
2358
|
+
margin-bottom: 40px;
|
|
2359
|
+
}
|
|
2360
|
+
.header {
|
|
2361
|
+
background: linear-gradient(135deg, #d4736c 0%, #b85450 100%);
|
|
2362
|
+
padding: 40px 30px;
|
|
2363
|
+
text-align: center;
|
|
2364
|
+
color: white;
|
|
2365
|
+
}
|
|
2366
|
+
.header h1 {
|
|
2367
|
+
margin: 0;
|
|
2368
|
+
font-size: 28px;
|
|
2369
|
+
font-weight: 300;
|
|
2370
|
+
letter-spacing: 1px;
|
|
2371
|
+
}
|
|
2372
|
+
.header .subtitle {
|
|
2373
|
+
margin: 10px 0 0 0;
|
|
2374
|
+
font-size: 16px;
|
|
2375
|
+
opacity: 0.9;
|
|
2376
|
+
font-weight: 300;
|
|
2377
|
+
}
|
|
2378
|
+
.content {
|
|
2379
|
+
padding: 40px 30px;
|
|
2380
|
+
}
|
|
2381
|
+
.greeting {
|
|
2382
|
+
font-size: 18px;
|
|
2383
|
+
color: #333;
|
|
2384
|
+
margin-bottom: 25px;
|
|
2385
|
+
font-weight: 400;
|
|
2386
|
+
}
|
|
2387
|
+
.cancellation-notice {
|
|
2388
|
+
background: linear-gradient(135deg, #ffebee 0%, #ffcdd2 100%);
|
|
2389
|
+
border-radius: 15px;
|
|
2390
|
+
padding: 25px;
|
|
2391
|
+
margin: 25px 0;
|
|
2392
|
+
border-left: 5px solid #d4736c;
|
|
2393
|
+
}
|
|
2394
|
+
.cancellation-notice p {
|
|
2395
|
+
margin: 0;
|
|
2396
|
+
color: #c62828;
|
|
2397
|
+
font-size: 15px;
|
|
2398
|
+
font-weight: 500;
|
|
2399
|
+
line-height: 1.6;
|
|
2400
|
+
}
|
|
2401
|
+
.cancelled-by-info {
|
|
2402
|
+
background: #fafafa;
|
|
2403
|
+
border-radius: 10px;
|
|
2404
|
+
padding: 15px 20px;
|
|
2405
|
+
margin-top: 15px;
|
|
2406
|
+
}
|
|
2407
|
+
.cancelled-by-info .label {
|
|
2408
|
+
font-size: 12px;
|
|
2409
|
+
color: #757575;
|
|
2410
|
+
text-transform: uppercase;
|
|
2411
|
+
letter-spacing: 0.5px;
|
|
2412
|
+
margin-bottom: 5px;
|
|
2413
|
+
}
|
|
2414
|
+
.cancelled-by-info .value {
|
|
2415
|
+
font-size: 14px;
|
|
2416
|
+
color: #424242;
|
|
2417
|
+
font-weight: 500;
|
|
2418
|
+
}
|
|
2419
|
+
.reason-box {
|
|
2420
|
+
background: linear-gradient(135deg, #fff8e1 0%, #ffecb3 100%);
|
|
2421
|
+
border-radius: 15px;
|
|
2422
|
+
padding: 20px;
|
|
2423
|
+
margin: 20px 0;
|
|
2424
|
+
border-left: 5px solid #ffa000;
|
|
2425
|
+
}
|
|
2426
|
+
.reason-box .label {
|
|
2427
|
+
font-size: 14px;
|
|
2428
|
+
font-weight: 600;
|
|
2429
|
+
color: #e65100;
|
|
2430
|
+
margin-bottom: 8px;
|
|
2431
|
+
}
|
|
2432
|
+
.reason-box .reason-text {
|
|
2433
|
+
font-size: 15px;
|
|
2434
|
+
color: #424242;
|
|
2435
|
+
line-height: 1.6;
|
|
2436
|
+
font-style: italic;
|
|
2437
|
+
}
|
|
2438
|
+
.appointment-card {
|
|
2439
|
+
background: linear-gradient(135deg, #f5f5f5 0%, #eeeeee 100%);
|
|
2440
|
+
border-radius: 15px;
|
|
2441
|
+
padding: 30px;
|
|
2442
|
+
margin: 25px 0;
|
|
2443
|
+
border-left: 5px solid #9e9e9e;
|
|
2444
|
+
opacity: 0.9;
|
|
2445
|
+
}
|
|
2446
|
+
.appointment-title {
|
|
2447
|
+
font-size: 20px;
|
|
2448
|
+
color: #757575;
|
|
2449
|
+
margin-bottom: 20px;
|
|
2450
|
+
font-weight: 600;
|
|
2451
|
+
}
|
|
2452
|
+
.appointment-details {
|
|
2453
|
+
display: grid;
|
|
2454
|
+
gap: 15px;
|
|
2455
|
+
}
|
|
2456
|
+
.detail-row {
|
|
2457
|
+
display: flex;
|
|
2458
|
+
align-items: center;
|
|
2459
|
+
padding: 8px 0;
|
|
2460
|
+
}
|
|
2461
|
+
.detail-label {
|
|
2462
|
+
font-weight: 600;
|
|
2463
|
+
color: #757575;
|
|
2464
|
+
min-width: 120px;
|
|
2465
|
+
font-size: 14px;
|
|
2466
|
+
}
|
|
2467
|
+
.detail-value {
|
|
2468
|
+
color: #616161;
|
|
2469
|
+
font-size: 16px;
|
|
2470
|
+
font-weight: 500;
|
|
2471
|
+
text-decoration: line-through;
|
|
2472
|
+
}
|
|
2473
|
+
.procedure-name {
|
|
2474
|
+
color: #757575;
|
|
2475
|
+
font-weight: 600;
|
|
2476
|
+
}
|
|
2477
|
+
.clinic-name {
|
|
2478
|
+
color: #9e9e9e;
|
|
2479
|
+
font-weight: 600;
|
|
2480
|
+
}
|
|
2481
|
+
.rebook-section {
|
|
2482
|
+
background: linear-gradient(135deg, #e8f5e9 0%, #c8e6c9 100%);
|
|
2483
|
+
border-radius: 15px;
|
|
2484
|
+
padding: 25px;
|
|
2485
|
+
margin: 30px 0;
|
|
2486
|
+
text-align: center;
|
|
2487
|
+
border-left: 5px solid #4caf50;
|
|
2488
|
+
}
|
|
2489
|
+
.rebook-section h3 {
|
|
2490
|
+
margin: 0 0 15px 0;
|
|
2491
|
+
color: #2e7d32;
|
|
2492
|
+
font-weight: 600;
|
|
2493
|
+
font-size: 18px;
|
|
2494
|
+
}
|
|
2495
|
+
.rebook-section p {
|
|
2496
|
+
margin: 0;
|
|
2497
|
+
color: #555;
|
|
2498
|
+
font-size: 15px;
|
|
2499
|
+
line-height: 1.6;
|
|
2500
|
+
}
|
|
2501
|
+
.support-section {
|
|
2502
|
+
background: #f8f9fa;
|
|
2503
|
+
border-radius: 15px;
|
|
2504
|
+
padding: 20px;
|
|
2505
|
+
margin: 25px 0;
|
|
2506
|
+
text-align: center;
|
|
2507
|
+
}
|
|
2508
|
+
.support-section h4 {
|
|
2509
|
+
margin: 0 0 10px 0;
|
|
2510
|
+
color: #555;
|
|
2511
|
+
font-weight: 600;
|
|
2512
|
+
font-size: 16px;
|
|
2513
|
+
}
|
|
2514
|
+
.support-section p {
|
|
2515
|
+
margin: 0;
|
|
2516
|
+
color: #757575;
|
|
2517
|
+
font-size: 14px;
|
|
2518
|
+
line-height: 1.6;
|
|
2519
|
+
}
|
|
2520
|
+
.footer {
|
|
2521
|
+
background: #f8f9fa;
|
|
2522
|
+
padding: 25px 30px;
|
|
2523
|
+
text-align: center;
|
|
2524
|
+
color: #666;
|
|
2525
|
+
font-size: 14px;
|
|
2526
|
+
border-top: 1px solid #eee;
|
|
2527
|
+
}
|
|
2528
|
+
.logo {
|
|
2529
|
+
font-size: 24px;
|
|
2530
|
+
font-weight: 700;
|
|
2531
|
+
color: white;
|
|
2532
|
+
margin-bottom: 5px;
|
|
2533
|
+
}
|
|
2534
|
+
.divider {
|
|
2535
|
+
height: 2px;
|
|
2536
|
+
background: linear-gradient(90deg, #d4736c, #b85450);
|
|
2537
|
+
margin: 25px 0;
|
|
2538
|
+
border-radius: 1px;
|
|
2539
|
+
}
|
|
2540
|
+
.icon {
|
|
2541
|
+
text-align: center;
|
|
2542
|
+
margin: 20px 0;
|
|
2543
|
+
font-size: 48px;
|
|
2544
|
+
}
|
|
2545
|
+
</style>
|
|
2546
|
+
</head>
|
|
2547
|
+
<body>
|
|
2548
|
+
<div class="email-container">
|
|
2549
|
+
<div class="header">
|
|
2550
|
+
<div class="logo">MetaEstetics</div>
|
|
2551
|
+
<h1>Appointment Cancelled</h1>
|
|
2552
|
+
<div class="subtitle">We're Sorry to See This Change</div>
|
|
2553
|
+
</div>
|
|
2554
|
+
|
|
2555
|
+
<div class="content">
|
|
2556
|
+
<div class="icon">❌</div>
|
|
2557
|
+
|
|
2558
|
+
<div class="greeting">
|
|
2559
|
+
Dear <strong>{{recipientName}}</strong>,
|
|
2560
|
+
</div>
|
|
2561
|
+
|
|
2562
|
+
<div class="cancellation-notice">
|
|
2563
|
+
<p><strong>Your appointment has been cancelled.</strong> We wanted to let you know that the following appointment is no longer scheduled.</p>
|
|
2564
|
+
<div class="cancelled-by-info">
|
|
2565
|
+
<div class="label">Cancelled By</div>
|
|
2566
|
+
<div class="value">{{cancelledByDisplay}}</div>
|
|
2567
|
+
</div>
|
|
2568
|
+
</div>
|
|
2569
|
+
|
|
2570
|
+
{{#if cancellationReason}}
|
|
2571
|
+
<div class="reason-box">
|
|
2572
|
+
<div class="label">Reason for Cancellation</div>
|
|
2573
|
+
<div class="reason-text">"{{cancellationReason}}"</div>
|
|
2574
|
+
</div>
|
|
2575
|
+
{{/if}}
|
|
2576
|
+
|
|
2577
|
+
<div class="appointment-card">
|
|
2578
|
+
<div class="appointment-title">Cancelled Appointment Details</div>
|
|
2579
|
+
<div class="appointment-details">
|
|
2580
|
+
<div class="detail-row">
|
|
2581
|
+
<div class="detail-label">Procedure:</div>
|
|
2582
|
+
<div class="detail-value procedure-name">{{procedureName}}</div>
|
|
2583
|
+
</div>
|
|
2584
|
+
<div class="detail-row">
|
|
2585
|
+
<div class="detail-label">Date:</div>
|
|
2586
|
+
<div class="detail-value">{{appointmentDate}}</div>
|
|
2587
|
+
</div>
|
|
2588
|
+
<div class="detail-row">
|
|
2589
|
+
<div class="detail-label">Time:</div>
|
|
2590
|
+
<div class="detail-value">{{appointmentTime}}</div>
|
|
2591
|
+
</div>
|
|
2592
|
+
<div class="detail-row">
|
|
2593
|
+
<div class="detail-label">Practitioner:</div>
|
|
2594
|
+
<div class="detail-value">{{practitionerName}}</div>
|
|
2595
|
+
</div>
|
|
2596
|
+
<div class="detail-row">
|
|
2597
|
+
<div class="detail-label">Location:</div>
|
|
2598
|
+
<div class="detail-value clinic-name">{{clinicName}}</div>
|
|
2599
|
+
</div>
|
|
2600
|
+
</div>
|
|
2601
|
+
</div>
|
|
2602
|
+
|
|
2603
|
+
<div class="divider"></div>
|
|
2604
|
+
|
|
2605
|
+
<div class="rebook-section">
|
|
2606
|
+
<h3>Would You Like to Reschedule?</h3>
|
|
2607
|
+
<p>
|
|
2608
|
+
We'd love to see you! If you'd like to book a new appointment,
|
|
2609
|
+
simply open the MetaEstetics app and browse available times that work for you.
|
|
2610
|
+
</p>
|
|
2611
|
+
</div>
|
|
2612
|
+
|
|
2613
|
+
<div class="support-section">
|
|
2614
|
+
<h4>Need Assistance?</h4>
|
|
2615
|
+
<p>
|
|
2616
|
+
If you have any questions about this cancellation or need help rebooking,
|
|
2617
|
+
please contact {{clinicName}} directly through the app or reach out to our support team.
|
|
2618
|
+
</p>
|
|
2619
|
+
</div>
|
|
2620
|
+
</div>
|
|
2621
|
+
|
|
2622
|
+
<div class="footer">
|
|
2623
|
+
<p style="margin: 0 0 10px 0;">
|
|
2624
|
+
<strong>MetaEstetics</strong> - Premium Aesthetic Services
|
|
2625
|
+
</p>
|
|
2626
|
+
<p style="margin: 0; font-size: 12px; color: #999;">
|
|
2627
|
+
This is an automated message. Please do not reply to this email.
|
|
2628
|
+
</p>
|
|
2629
|
+
</div>
|
|
2630
|
+
</div>
|
|
2631
|
+
</body>
|
|
2632
|
+
</html>
|
|
2633
|
+
`;
|
|
2290
2634
|
var appointmentRescheduledProposalTemplate = `
|
|
2291
2635
|
<!DOCTYPE html>
|
|
2292
2636
|
<html lang="en">
|
|
@@ -2447,6 +2791,42 @@ var appointmentRescheduledProposalTemplate = `
|
|
|
2447
2791
|
font-size: 15px;
|
|
2448
2792
|
line-height: 1.6;
|
|
2449
2793
|
}
|
|
2794
|
+
.action-required-box {
|
|
2795
|
+
background: linear-gradient(135deg, #fff3e0 0%, #ffecb3 100%);
|
|
2796
|
+
border: 2px solid #ff9800;
|
|
2797
|
+
border-radius: 15px;
|
|
2798
|
+
padding: 25px;
|
|
2799
|
+
margin: 25px 0;
|
|
2800
|
+
text-align: center;
|
|
2801
|
+
}
|
|
2802
|
+
.action-required-box h3 {
|
|
2803
|
+
margin: 0 0 15px 0;
|
|
2804
|
+
color: #e65100;
|
|
2805
|
+
font-weight: 700;
|
|
2806
|
+
font-size: 18px;
|
|
2807
|
+
}
|
|
2808
|
+
.action-required-box p {
|
|
2809
|
+
margin: 0 0 12px 0;
|
|
2810
|
+
color: #bf360c;
|
|
2811
|
+
font-size: 15px;
|
|
2812
|
+
line-height: 1.6;
|
|
2813
|
+
}
|
|
2814
|
+
.action-required-box p:last-child {
|
|
2815
|
+
margin-bottom: 0;
|
|
2816
|
+
}
|
|
2817
|
+
.pending-notice {
|
|
2818
|
+
background: #fff8e1;
|
|
2819
|
+
border-radius: 8px;
|
|
2820
|
+
padding: 12px 15px;
|
|
2821
|
+
margin-top: 15px;
|
|
2822
|
+
display: inline-block;
|
|
2823
|
+
}
|
|
2824
|
+
.pending-notice p {
|
|
2825
|
+
margin: 0;
|
|
2826
|
+
color: #f57c00;
|
|
2827
|
+
font-size: 14px;
|
|
2828
|
+
font-weight: 600;
|
|
2829
|
+
}
|
|
2450
2830
|
.footer {
|
|
2451
2831
|
background: #f8f9fa;
|
|
2452
2832
|
padding: 25px 30px;
|
|
@@ -2538,16 +2918,29 @@ var appointmentRescheduledProposalTemplate = `
|
|
|
2538
2918
|
</div>
|
|
2539
2919
|
|
|
2540
2920
|
<div class="divider"></div>
|
|
2541
|
-
|
|
2921
|
+
|
|
2922
|
+
<div class="action-required-box">
|
|
2923
|
+
<h3>Your Response is Required</h3>
|
|
2924
|
+
<p>
|
|
2925
|
+
<strong>Missed our notification?</strong> Please open the MetaEstetics app to confirm or reject this reschedule request.
|
|
2926
|
+
</p>
|
|
2927
|
+
<p>
|
|
2928
|
+
Please respond as soon as possible so we can finalize your appointment.
|
|
2929
|
+
</p>
|
|
2930
|
+
<div class="pending-notice">
|
|
2931
|
+
<p>Your appointment will remain pending until you respond.</p>
|
|
2932
|
+
</div>
|
|
2933
|
+
</div>
|
|
2934
|
+
|
|
2542
2935
|
<div class="action-section">
|
|
2543
|
-
<h3>
|
|
2936
|
+
<h3>How to Respond</h3>
|
|
2544
2937
|
<p>
|
|
2545
|
-
|
|
2546
|
-
If the new time works for you, simply tap "Accept Reschedule".
|
|
2938
|
+
Open the MetaEstetics app and navigate to your appointments.
|
|
2939
|
+
If the new time works for you, simply tap "Accept Reschedule".
|
|
2547
2940
|
If not, you can reject it and we'll work with you to find an alternative time.
|
|
2548
2941
|
</p>
|
|
2549
2942
|
</div>
|
|
2550
|
-
|
|
2943
|
+
|
|
2551
2944
|
<p style="color: #555; font-size: 14px; line-height: 1.6; margin-top: 25px;">
|
|
2552
2945
|
<strong>Need Help?</strong> If you have any questions or concerns about this reschedule, please contact us directly through the app or reach out to {{clinicName}}.
|
|
2553
2946
|
</p>
|
|
@@ -2763,11 +3156,121 @@ var AppointmentMailingService = class extends BaseMailingService {
|
|
|
2763
3156
|
throw error;
|
|
2764
3157
|
}
|
|
2765
3158
|
}
|
|
3159
|
+
/**
|
|
3160
|
+
* Gets a user-friendly display text for who cancelled the appointment
|
|
3161
|
+
* @param cancelledBy - The entity that cancelled the appointment
|
|
3162
|
+
* @param clinicName - The clinic name for context
|
|
3163
|
+
* @returns User-friendly cancellation source text
|
|
3164
|
+
*/
|
|
3165
|
+
getCancelledByDisplayText(cancelledBy, clinicName) {
|
|
3166
|
+
switch (cancelledBy) {
|
|
3167
|
+
case "patient":
|
|
3168
|
+
return "Patient Request";
|
|
3169
|
+
case "clinic":
|
|
3170
|
+
return `${clinicName} (Clinic)`;
|
|
3171
|
+
case "practitioner":
|
|
3172
|
+
return "Your Practitioner";
|
|
3173
|
+
case "system":
|
|
3174
|
+
return "System (Automatic)";
|
|
3175
|
+
default:
|
|
3176
|
+
return "Unknown";
|
|
3177
|
+
}
|
|
3178
|
+
}
|
|
3179
|
+
/**
|
|
3180
|
+
* Sends an appointment cancellation email to the recipient
|
|
3181
|
+
* @param data - Appointment cancellation email data
|
|
3182
|
+
* @returns Promise with the sending result
|
|
3183
|
+
*/
|
|
2766
3184
|
async sendAppointmentCancelledEmail(data) {
|
|
3185
|
+
var _a, _b, _c, _d;
|
|
2767
3186
|
Logger.info(
|
|
2768
|
-
`[AppointmentMailingService]
|
|
3187
|
+
`[AppointmentMailingService] Preparing to send appointment cancellation email to ${data.recipientRole}: ${data.recipientProfile.id}`
|
|
2769
3188
|
);
|
|
2770
|
-
|
|
3189
|
+
const recipientEmail = data.recipientProfile.email;
|
|
3190
|
+
if (!recipientEmail) {
|
|
3191
|
+
Logger.error("[AppointmentMailingService] Recipient email not found for cancellation.", {
|
|
3192
|
+
recipientId: data.recipientProfile.id,
|
|
3193
|
+
role: data.recipientRole
|
|
3194
|
+
});
|
|
3195
|
+
throw new Error("Recipient email address is missing.");
|
|
3196
|
+
}
|
|
3197
|
+
const clinicTimezone = data.appointment.clinic_tz || "UTC";
|
|
3198
|
+
Logger.debug("[AppointmentMailingService] Formatting appointment time for cancellation", {
|
|
3199
|
+
clinicTimezone,
|
|
3200
|
+
utcTime: data.appointment.appointmentStartTime.toDate().toISOString()
|
|
3201
|
+
});
|
|
3202
|
+
const formattedTime = this.formatTimestampInClinicTimezone(
|
|
3203
|
+
data.appointment.appointmentStartTime,
|
|
3204
|
+
clinicTimezone,
|
|
3205
|
+
"time"
|
|
3206
|
+
);
|
|
3207
|
+
const timezoneName = this.getTimezoneDisplayName(clinicTimezone);
|
|
3208
|
+
const cancelledBy = data.appointment.canceledBy || "system";
|
|
3209
|
+
const cancelledByDisplay = this.getCancelledByDisplayText(
|
|
3210
|
+
cancelledBy,
|
|
3211
|
+
data.appointment.clinicInfo.name
|
|
3212
|
+
);
|
|
3213
|
+
const recipientName = data.recipientRole === "patient" ? data.appointment.patientInfo.fullName : data.appointment.practitionerInfo.name;
|
|
3214
|
+
const templateVariables = {
|
|
3215
|
+
recipientName,
|
|
3216
|
+
procedureName: data.appointment.procedureInfo.name,
|
|
3217
|
+
appointmentDate: this.formatTimestampInClinicTimezone(
|
|
3218
|
+
data.appointment.appointmentStartTime,
|
|
3219
|
+
clinicTimezone,
|
|
3220
|
+
"date"
|
|
3221
|
+
),
|
|
3222
|
+
appointmentTime: `${formattedTime} (${timezoneName})`,
|
|
3223
|
+
practitionerName: data.appointment.practitionerInfo.name,
|
|
3224
|
+
clinicName: data.appointment.clinicInfo.name,
|
|
3225
|
+
cancelledByDisplay
|
|
3226
|
+
};
|
|
3227
|
+
const cancellationReason = data.cancellationReason || data.appointment.cancellationReason;
|
|
3228
|
+
let html = appointmentCancelledTemplate;
|
|
3229
|
+
if (cancellationReason) {
|
|
3230
|
+
templateVariables.cancellationReason = cancellationReason;
|
|
3231
|
+
html = html.replace(
|
|
3232
|
+
/\{\{#if cancellationReason\}\}([\s\S]*?)\{\{\/if\}\}/g,
|
|
3233
|
+
"$1"
|
|
3234
|
+
);
|
|
3235
|
+
} else {
|
|
3236
|
+
html = html.replace(/\{\{#if cancellationReason\}\}[\s\S]*?\{\{\/if\}\}/g, "");
|
|
3237
|
+
}
|
|
3238
|
+
html = this.renderTemplate(html, templateVariables);
|
|
3239
|
+
const subject = ((_a = data.options) == null ? void 0 : _a.customSubject) || `Appointment Cancelled: ${data.appointment.procedureInfo.name}`;
|
|
3240
|
+
const fromAddress = ((_b = data.options) == null ? void 0 : _b.fromAddress) || `MetaEstetics <no-reply@${((_c = data.options) == null ? void 0 : _c.mailgunDomain) || this.DEFAULT_MAILGUN_DOMAIN}>`;
|
|
3241
|
+
const domainToSendFrom = ((_d = data.options) == null ? void 0 : _d.mailgunDomain) || this.DEFAULT_MAILGUN_DOMAIN;
|
|
3242
|
+
const mailgunSendData = {
|
|
3243
|
+
to: recipientEmail,
|
|
3244
|
+
from: fromAddress,
|
|
3245
|
+
subject,
|
|
3246
|
+
html
|
|
3247
|
+
};
|
|
3248
|
+
try {
|
|
3249
|
+
const result = await this.sendEmail(domainToSendFrom, mailgunSendData);
|
|
3250
|
+
await this.logEmailAttempt(
|
|
3251
|
+
{ to: recipientEmail, subject, templateName: "appointment_cancelled" },
|
|
3252
|
+
true
|
|
3253
|
+
);
|
|
3254
|
+
Logger.info(
|
|
3255
|
+
`[AppointmentMailingService] Successfully sent cancellation email to ${recipientEmail}`
|
|
3256
|
+
);
|
|
3257
|
+
return result;
|
|
3258
|
+
} catch (error) {
|
|
3259
|
+
await this.logEmailAttempt(
|
|
3260
|
+
{
|
|
3261
|
+
to: recipientEmail,
|
|
3262
|
+
subject,
|
|
3263
|
+
templateName: "appointment_cancelled"
|
|
3264
|
+
},
|
|
3265
|
+
false,
|
|
3266
|
+
error
|
|
3267
|
+
);
|
|
3268
|
+
Logger.error(
|
|
3269
|
+
`[AppointmentMailingService] Error sending cancellation email to ${recipientEmail}:`,
|
|
3270
|
+
error
|
|
3271
|
+
);
|
|
3272
|
+
throw error;
|
|
3273
|
+
}
|
|
2771
3274
|
}
|
|
2772
3275
|
/**
|
|
2773
3276
|
* Sends a reschedule proposal email to the patient
|
|
@@ -3232,6 +3735,16 @@ var AppointmentAggregationService = class {
|
|
|
3232
3735
|
// TODO: Properly import PatientProfileInfo and types
|
|
3233
3736
|
);
|
|
3234
3737
|
}
|
|
3738
|
+
if ((patientProfile == null ? void 0 : patientProfile.expoTokens) && patientProfile.expoTokens.length > 0) {
|
|
3739
|
+
Logger.info(
|
|
3740
|
+
`[AggService] Sending reschedule proposal push notification to patient ${after.patientId}`
|
|
3741
|
+
);
|
|
3742
|
+
await this.notificationsAdmin.sendAppointmentRescheduledProposalPush(
|
|
3743
|
+
after,
|
|
3744
|
+
after.patientId,
|
|
3745
|
+
patientProfile.expoTokens
|
|
3746
|
+
);
|
|
3747
|
+
}
|
|
3235
3748
|
Logger.info(
|
|
3236
3749
|
`[AggService] TODO: Send reschedule proposal notifications to practitioner as well.`
|
|
3237
3750
|
);
|
package/dist/index.d.mts
CHANGED
|
@@ -3232,6 +3232,7 @@ declare enum NotificationType {
|
|
|
3232
3232
|
APPOINTMENT_REMINDER = "appointmentReminder",// For upcoming appointments
|
|
3233
3233
|
APPOINTMENT_STATUS_CHANGE = "appointmentStatusChange",// Generic for status changes like confirmed, checked-in etc.
|
|
3234
3234
|
APPOINTMENT_RESCHEDULED_PROPOSAL = "appointmentRescheduledProposal",// When clinic proposes a new time
|
|
3235
|
+
APPOINTMENT_RESCHEDULED_REMINDER = "appointmentRescheduledReminder",// Reminder for pending reschedule request
|
|
3235
3236
|
APPOINTMENT_CANCELLED = "appointmentCancelled",// When an appointment is cancelled
|
|
3236
3237
|
PRE_REQUIREMENT_INSTRUCTION_DUE = "preRequirementInstructionDue",
|
|
3237
3238
|
POST_REQUIREMENT_INSTRUCTION_DUE = "postRequirementInstructionDue",
|
|
@@ -3367,6 +3368,16 @@ interface AppointmentRescheduledProposalNotification extends BaseNotification {
|
|
|
3367
3368
|
newProposedEndTime: Timestamp;
|
|
3368
3369
|
procedureName?: string;
|
|
3369
3370
|
}
|
|
3371
|
+
/**
|
|
3372
|
+
* Notification reminding the patient about a pending reschedule request they haven't responded to.
|
|
3373
|
+
* Example: "Reminder: You have a pending reschedule request for your [Procedure Name] appointment."
|
|
3374
|
+
*/
|
|
3375
|
+
interface AppointmentRescheduledReminderNotification extends BaseNotification {
|
|
3376
|
+
notificationType: NotificationType.APPOINTMENT_RESCHEDULED_REMINDER;
|
|
3377
|
+
appointmentId: string;
|
|
3378
|
+
procedureName?: string;
|
|
3379
|
+
reminderCount?: number;
|
|
3380
|
+
}
|
|
3370
3381
|
/**
|
|
3371
3382
|
* Notification informing about a cancelled appointment.
|
|
3372
3383
|
* Example: "Your appointment for [Procedure Name] on [Date] has been cancelled."
|
|
@@ -3442,7 +3453,7 @@ interface PaymentConfirmationNotification extends BaseNotification {
|
|
|
3442
3453
|
/**
|
|
3443
3454
|
* Unija svih tipova notifikacija
|
|
3444
3455
|
*/
|
|
3445
|
-
type Notification = PreRequirementNotification | PostRequirementNotification | RequirementInstructionDueNotification | AppointmentReminderNotification | AppointmentStatusChangeNotification | AppointmentRescheduledProposalNotification | AppointmentCancelledNotification | FormReminderNotification | FormSubmissionConfirmationNotification | ReviewRequestNotification | ProcedureRecommendationNotification | GeneralMessageNotification | PaymentConfirmationNotification;
|
|
3456
|
+
type Notification = PreRequirementNotification | PostRequirementNotification | RequirementInstructionDueNotification | AppointmentReminderNotification | AppointmentStatusChangeNotification | AppointmentRescheduledProposalNotification | AppointmentRescheduledReminderNotification | AppointmentCancelledNotification | FormReminderNotification | FormSubmissionConfirmationNotification | ReviewRequestNotification | ProcedureRecommendationNotification | GeneralMessageNotification | PaymentConfirmationNotification;
|
|
3446
3457
|
|
|
3447
3458
|
declare enum AllergyType {
|
|
3448
3459
|
MEDICATION = "medication",
|
|
@@ -5787,6 +5798,9 @@ interface Appointment {
|
|
|
5787
5798
|
confirmationTime?: Timestamp | null;
|
|
5788
5799
|
cancellationTime?: Timestamp | null;
|
|
5789
5800
|
rescheduleTime?: Timestamp | null;
|
|
5801
|
+
/** Reschedule reminder tracking */
|
|
5802
|
+
rescheduleReminderSentAt?: Timestamp | null;
|
|
5803
|
+
rescheduleReminderCount?: number;
|
|
5790
5804
|
appointmentStartTime: Timestamp;
|
|
5791
5805
|
appointmentEndTime: Timestamp;
|
|
5792
5806
|
procedureActualStartTime?: Timestamp | null;
|
|
@@ -5870,6 +5884,9 @@ interface UpdateAppointmentData {
|
|
|
5870
5884
|
confirmationTime?: Timestamp | FieldValue | null;
|
|
5871
5885
|
cancellationTime?: Timestamp | FieldValue | null;
|
|
5872
5886
|
rescheduleTime?: Timestamp | FieldValue | null;
|
|
5887
|
+
/** Reschedule reminder tracking */
|
|
5888
|
+
rescheduleReminderSentAt?: Timestamp | FieldValue | null;
|
|
5889
|
+
rescheduleReminderCount?: number | FieldValue;
|
|
5873
5890
|
procedureActualStartTime?: Timestamp | FieldValue | null;
|
|
5874
5891
|
actualDurationMinutes?: number;
|
|
5875
5892
|
cancellationReason?: string | null;
|
|
@@ -9326,4 +9343,4 @@ declare const getFirebaseApp: () => Promise<FirebaseApp>;
|
|
|
9326
9343
|
declare const getFirebaseStorage: () => Promise<FirebaseStorage>;
|
|
9327
9344
|
declare const getFirebaseFunctions: () => Promise<Functions>;
|
|
9328
9345
|
|
|
9329
|
-
export { AESTHETIC_ANALYSIS_COLLECTION, ANALYTICS_COLLECTION, APPOINTMENTS_COLLECTION, AcquisitionSource, type AddAllergyData, type AddBlockingConditionData, type AddContraindicationData, type AddMedicationData, type AddressData, type AdminInfo, type AdminToken, AdminTokenStatus, type AestheticAnalysis, type AestheticAnalysisStatus, type Allergy, type AllergySubtype, AllergyType, type AllergyTypeWithSubtype, AnalyticsCloudService, type AnalyticsDateRange, type AnalyticsFilters, type AnalyticsMetadata, type AnalyticsPeriod, AnalyticsService, type Appointment, type AppointmentCancelledNotification, type AppointmentMediaItem, type AppointmentMetadata, type AppointmentProductMetadata, type AppointmentReminderNotification, type AppointmentRescheduledProposalNotification, AppointmentService, AppointmentStatus, type AppointmentStatusChangeNotification, type AppointmentTrend, type AssessmentScales, AuthService, type BaseDocumentElement, type BaseMetrics, type BaseNotification, BaseService, type BeforeAfterPerZone, type BillingInfo, type BillingPerZone, type BillingTransaction, BillingTransactionType, BillingTransactionsService, type BinaryChoiceElement, BlockingCondition, type Brand, BrandService, type Break, CALENDAR_COLLECTION, CANCELLATION_ANALYTICS_SUBCOLLECTION, CLINICS_COLLECTION, CLINIC_ADMINS_COLLECTION, CLINIC_ANALYTICS_SUBCOLLECTION, CLINIC_GROUPS_COLLECTION, type CalendarEvent, CalendarEventStatus, type CalendarEventTime, CalendarEventType, CalendarServiceV2, CalendarServiceV3, CalendarSyncStatus, type CancellationMetrics, type CancellationRateTrend, type CancellationReasonStats, type Category, CategoryService, CertificationLevel, CertificationSpecialty, type Clinic, type ClinicAdmin, ClinicAdminService, type ClinicAdminSignupData, type ClinicAnalytics, type ClinicBranchSetupData, type ClinicComparisonMetrics, type ClinicContactInfo, type ClinicGroup, ClinicGroupService, type ClinicGroupSetupData, type ClinicInfo, type ClinicLocation, ClinicPhotoTag, type ClinicReview, type ClinicReviewInfo, ClinicService, ClinicTag, type ClinicTags, type ClinicalFindingDetail, type ClinicalFindings, ConstantsService, type ContactPerson, Contraindication, type ContraindicationDynamic, CosmeticAllergySubtype, type CostPerPatientMetrics, type CreateAdminTokenData, type CreateAestheticAnalysisData, type CreateAppointmentData, type CreateAppointmentHttpData, type CreateAppointmentParams, type CreateBillingTransactionData, type CreateBlockingEventParams, type CreateCalendarEventData, type CreateClinicAdminData, type CreateClinicData, type CreateClinicGroupData, type CreateDefaultClinicGroupData, type CreateDocumentTemplateData, type CreateDraftPractitionerData, type CreateManualPatientData, type CreatePatientLocationInfoData, type CreatePatientMedicalInfoData, type CreatePatientProfileData, type CreatePatientSensitiveInfoData, type CreatePatientTokenData, type CreatePractitionerData, type CreatePractitionerInviteData, type CreatePractitionerTokenData, type CreateProcedureData, type CreateSyncedCalendarData, type CreateUserData, Currency, DASHBOARD_ANALYTICS_SUBCOLLECTION, DEFAULT_MEDICAL_INFO, DOCTOR_FORMS_SUBCOLLECTION, DOCUMENTATION_TEMPLATES_COLLECTION, type DashboardAnalytics, type DatePickerElement, type DateRange, type DigitalSignatureElement, type DoctorInfo, type DocumentElement, DocumentElementType, type DocumentTemplate, DocumentationTemplateService, type DurationTrend, type DynamicTextElement, DynamicVariable, type EmergencyContact, type EntityType, EnvironmentalAllergySubtype, type ExtendedProcedureInfo, ExternalCalendarService, FILLED_DOCUMENTS_COLLECTION, type FileUploadElement, type FilledDocument, type FilledDocumentFileValue, FilledDocumentService, FilledDocumentStatus, type FinalBilling, type FirebaseUser, FoodAllergySubtype, type FormReminderNotification, type FormSubmissionConfirmationNotification, type GamificationInfo, Gender, type GeneralMessageNotification, type GroupedAnalyticsBase, type GroupedPatientBehaviorMetrics, type GroupedPatientRetentionMetrics, type GroupedPractitionerPerformanceMetrics, type GroupedProcedurePerformanceMetrics, type GroupedProductUsageMetrics, type GroupedRevenueMetrics, type GroupedTimeEfficiencyMetrics, type GroupingPeriod, type HeadingElement, HeadingLevel, INVITE_TOKENS_COLLECTION, Language, type LinkedFormInfo, type ListElement, ListType, type LocationData, MEDIA_METADATA_COLLECTION, MediaAccessLevel, type MediaMetadata, type MediaResource, MediaService, MediaType, MedicationAllergySubtype, type MultipleChoiceElement, NOTIFICATIONS_COLLECTION, NO_SHOW_ANALYTICS_SUBCOLLECTION, type NextStepsRecommendation, type NoShowMetrics, type Notification, NotificationService, NotificationStatus, NotificationType, type OverallReviewAverages, PATIENTS_COLLECTION, PATIENT_APPOINTMENTS_COLLECTION, PATIENT_LOCATION_INFO_COLLECTION, PATIENT_MEDICAL_HISTORY_COLLECTION, PATIENT_MEDICAL_INFO_COLLECTION, PATIENT_REQUIREMENTS_SUBCOLLECTION_NAME, PATIENT_SENSITIVE_INFO_COLLECTION, PRACTITIONERS_COLLECTION, PRACTITIONER_ANALYTICS_SUBCOLLECTION, PRACTITIONER_INVITES_COLLECTION, PROCEDURES_COLLECTION, PROCEDURE_ANALYTICS_SUBCOLLECTION, type ParagraphElement, type PatientAnalytics, type PatientClinic, type PatientDoctor, type PatientGoals, PatientInstructionStatus, type PatientLifetimeValueMetrics, type PatientLocationInfo, type PatientMedicalInfo, type PatientProfile, type PatientProfileComplete, type PatientProfileForDoctor, type PatientProfileInfo, type PatientRequirementInstance, type PatientRequirementInstruction, PatientRequirementOverallStatus, type PatientRequirementsFilters, PatientRequirementsService, type PatientRetentionMetrics, type PatientReviewInfo, type PatientSensitiveInfo, PatientService, type PatientToken, PatientTokenStatus, type PaymentConfirmationNotification, PaymentStatus, type PaymentStatusBreakdown, type PlanDetails, type PostRequirementNotification, PracticeType, type Practitioner, type PractitionerAnalytics, type PractitionerBasicInfo, type PractitionerCertification, type PractitionerClinicProcedures, type PractitionerClinicWorkingHours, type PractitionerInvite, type PractitionerInviteFilters, PractitionerInviteService, PractitionerInviteStatus, type PractitionerProfileInfo, type PractitionerReview, type PractitionerReviewInfo, PractitionerService, PractitionerStatus, type PractitionerToken, PractitionerTokenStatus, type PractitionerWorkingHours, type PreRequirementNotification, PricingMeasure, type Procedure, type ProcedureAnalytics, type ProcedureCategorization, type ProcedureExtendedInfo, ProcedureFamily, type ProcedureInfo, type ProcedurePopularity, type ProcedureProduct, type ProcedureProfitability, type ProcedureRecommendationNotification, type ProcedureReview, type ProcedureReviewInfo, ProcedureService, type ProcedureSummaryInfo, type Product, type ProductRevenueMetrics, ProductService, type ProductUsageByProcedure, type ProductUsageMetrics, type ProposedWorkingHours, REGISTER_TOKENS_COLLECTION, REVENUE_ANALYTICS_SUBCOLLECTION, REVIEWS_COLLECTION, type RatingScaleElement, type ReadStoredAnalyticsOptions, type RecommendedProcedure, type RequesterInfo, type Requirement, type RequirementInstructionDueNotification, type RequirementSourceProcedure, RequirementType, type RevenueMetrics, type RevenueTrend, type Review, type ReviewAnalyticsMetrics, ReviewAnalyticsService, type ReviewDetail, type ReviewMetrics, type ReviewRequestNotification, ReviewService, type ReviewTrend, SYNCED_CALENDARS_COLLECTION, type SearchAppointmentsParams, type SearchCalendarEventsParams, SearchLocationEnum, type SearchPatientsParams, type SignatureElement, type SingleChoiceElement, type StoredCancellationMetrics, type StoredClinicAnalytics, type StoredDashboardAnalytics, type StoredNoShowMetrics, type StoredPractitionerAnalytics, type StoredProcedureAnalytics, type StoredRevenueMetrics, type StoredTimeEfficiencyMetrics, type StripeTransactionData, type Subcategory, SubcategoryService, SubscriptionModel, SubscriptionStatus, type SyncedCalendar, type SyncedCalendarEvent, SyncedCalendarProvider, SyncedCalendarsService, TIME_EFFICIENCY_ANALYTICS_SUBCOLLECTION, type Technology, type TechnologyDocumentationTemplate, TechnologyService, type TextInputElement, type TimeEfficiencyMetrics, type TimeSlot, TimeUnit, TreatmentBenefit, type TreatmentBenefitDynamic, type TrendPeriod, USERS_COLLECTION, USER_FORMS_SUBCOLLECTION, type UpdateAestheticAnalysisData, type UpdateAllergyData, type UpdateAppointmentData, type UpdateAppointmentParams, type UpdateBlockingConditionData, type UpdateBlockingEventParams, type UpdateCalendarEventData, type UpdateClinicAdminData, type UpdateClinicData, type UpdateClinicGroupData, type UpdateContraindicationData, type UpdateDocumentTemplateData, type UpdateMedicationData, type UpdatePatientLocationInfoData, type UpdatePatientMedicalInfoData, type UpdatePatientProfileData, type UpdatePatientSensitiveInfoData, type UpdatePractitionerData, type UpdatePractitionerInviteData, type UpdateProcedureData, type UpdateSyncedCalendarData, type UpdateVitalStatsData, type User, UserRole, UserService, type VitalStats, type WorkingHours, type ZoneItemData, type ZonePhotoUploadData, getFirebaseApp, getFirebaseAuth, getFirebaseDB, getFirebaseFunctions, getFirebaseInstance, getFirebaseStorage, initializeFirebase };
|
|
9346
|
+
export { AESTHETIC_ANALYSIS_COLLECTION, ANALYTICS_COLLECTION, APPOINTMENTS_COLLECTION, AcquisitionSource, type AddAllergyData, type AddBlockingConditionData, type AddContraindicationData, type AddMedicationData, type AddressData, type AdminInfo, type AdminToken, AdminTokenStatus, type AestheticAnalysis, type AestheticAnalysisStatus, type Allergy, type AllergySubtype, AllergyType, type AllergyTypeWithSubtype, AnalyticsCloudService, type AnalyticsDateRange, type AnalyticsFilters, type AnalyticsMetadata, type AnalyticsPeriod, AnalyticsService, type Appointment, type AppointmentCancelledNotification, type AppointmentMediaItem, type AppointmentMetadata, type AppointmentProductMetadata, type AppointmentReminderNotification, type AppointmentRescheduledProposalNotification, type AppointmentRescheduledReminderNotification, AppointmentService, AppointmentStatus, type AppointmentStatusChangeNotification, type AppointmentTrend, type AssessmentScales, AuthService, type BaseDocumentElement, type BaseMetrics, type BaseNotification, BaseService, type BeforeAfterPerZone, type BillingInfo, type BillingPerZone, type BillingTransaction, BillingTransactionType, BillingTransactionsService, type BinaryChoiceElement, BlockingCondition, type Brand, BrandService, type Break, CALENDAR_COLLECTION, CANCELLATION_ANALYTICS_SUBCOLLECTION, CLINICS_COLLECTION, CLINIC_ADMINS_COLLECTION, CLINIC_ANALYTICS_SUBCOLLECTION, CLINIC_GROUPS_COLLECTION, type CalendarEvent, CalendarEventStatus, type CalendarEventTime, CalendarEventType, CalendarServiceV2, CalendarServiceV3, CalendarSyncStatus, type CancellationMetrics, type CancellationRateTrend, type CancellationReasonStats, type Category, CategoryService, CertificationLevel, CertificationSpecialty, type Clinic, type ClinicAdmin, ClinicAdminService, type ClinicAdminSignupData, type ClinicAnalytics, type ClinicBranchSetupData, type ClinicComparisonMetrics, type ClinicContactInfo, type ClinicGroup, ClinicGroupService, type ClinicGroupSetupData, type ClinicInfo, type ClinicLocation, ClinicPhotoTag, type ClinicReview, type ClinicReviewInfo, ClinicService, ClinicTag, type ClinicTags, type ClinicalFindingDetail, type ClinicalFindings, ConstantsService, type ContactPerson, Contraindication, type ContraindicationDynamic, CosmeticAllergySubtype, type CostPerPatientMetrics, type CreateAdminTokenData, type CreateAestheticAnalysisData, type CreateAppointmentData, type CreateAppointmentHttpData, type CreateAppointmentParams, type CreateBillingTransactionData, type CreateBlockingEventParams, type CreateCalendarEventData, type CreateClinicAdminData, type CreateClinicData, type CreateClinicGroupData, type CreateDefaultClinicGroupData, type CreateDocumentTemplateData, type CreateDraftPractitionerData, type CreateManualPatientData, type CreatePatientLocationInfoData, type CreatePatientMedicalInfoData, type CreatePatientProfileData, type CreatePatientSensitiveInfoData, type CreatePatientTokenData, type CreatePractitionerData, type CreatePractitionerInviteData, type CreatePractitionerTokenData, type CreateProcedureData, type CreateSyncedCalendarData, type CreateUserData, Currency, DASHBOARD_ANALYTICS_SUBCOLLECTION, DEFAULT_MEDICAL_INFO, DOCTOR_FORMS_SUBCOLLECTION, DOCUMENTATION_TEMPLATES_COLLECTION, type DashboardAnalytics, type DatePickerElement, type DateRange, type DigitalSignatureElement, type DoctorInfo, type DocumentElement, DocumentElementType, type DocumentTemplate, DocumentationTemplateService, type DurationTrend, type DynamicTextElement, DynamicVariable, type EmergencyContact, type EntityType, EnvironmentalAllergySubtype, type ExtendedProcedureInfo, ExternalCalendarService, FILLED_DOCUMENTS_COLLECTION, type FileUploadElement, type FilledDocument, type FilledDocumentFileValue, FilledDocumentService, FilledDocumentStatus, type FinalBilling, type FirebaseUser, FoodAllergySubtype, type FormReminderNotification, type FormSubmissionConfirmationNotification, type GamificationInfo, Gender, type GeneralMessageNotification, type GroupedAnalyticsBase, type GroupedPatientBehaviorMetrics, type GroupedPatientRetentionMetrics, type GroupedPractitionerPerformanceMetrics, type GroupedProcedurePerformanceMetrics, type GroupedProductUsageMetrics, type GroupedRevenueMetrics, type GroupedTimeEfficiencyMetrics, type GroupingPeriod, type HeadingElement, HeadingLevel, INVITE_TOKENS_COLLECTION, Language, type LinkedFormInfo, type ListElement, ListType, type LocationData, MEDIA_METADATA_COLLECTION, MediaAccessLevel, type MediaMetadata, type MediaResource, MediaService, MediaType, MedicationAllergySubtype, type MultipleChoiceElement, NOTIFICATIONS_COLLECTION, NO_SHOW_ANALYTICS_SUBCOLLECTION, type NextStepsRecommendation, type NoShowMetrics, type Notification, NotificationService, NotificationStatus, NotificationType, type OverallReviewAverages, PATIENTS_COLLECTION, PATIENT_APPOINTMENTS_COLLECTION, PATIENT_LOCATION_INFO_COLLECTION, PATIENT_MEDICAL_HISTORY_COLLECTION, PATIENT_MEDICAL_INFO_COLLECTION, PATIENT_REQUIREMENTS_SUBCOLLECTION_NAME, PATIENT_SENSITIVE_INFO_COLLECTION, PRACTITIONERS_COLLECTION, PRACTITIONER_ANALYTICS_SUBCOLLECTION, PRACTITIONER_INVITES_COLLECTION, PROCEDURES_COLLECTION, PROCEDURE_ANALYTICS_SUBCOLLECTION, type ParagraphElement, type PatientAnalytics, type PatientClinic, type PatientDoctor, type PatientGoals, PatientInstructionStatus, type PatientLifetimeValueMetrics, type PatientLocationInfo, type PatientMedicalInfo, type PatientProfile, type PatientProfileComplete, type PatientProfileForDoctor, type PatientProfileInfo, type PatientRequirementInstance, type PatientRequirementInstruction, PatientRequirementOverallStatus, type PatientRequirementsFilters, PatientRequirementsService, type PatientRetentionMetrics, type PatientReviewInfo, type PatientSensitiveInfo, PatientService, type PatientToken, PatientTokenStatus, type PaymentConfirmationNotification, PaymentStatus, type PaymentStatusBreakdown, type PlanDetails, type PostRequirementNotification, PracticeType, type Practitioner, type PractitionerAnalytics, type PractitionerBasicInfo, type PractitionerCertification, type PractitionerClinicProcedures, type PractitionerClinicWorkingHours, type PractitionerInvite, type PractitionerInviteFilters, PractitionerInviteService, PractitionerInviteStatus, type PractitionerProfileInfo, type PractitionerReview, type PractitionerReviewInfo, PractitionerService, PractitionerStatus, type PractitionerToken, PractitionerTokenStatus, type PractitionerWorkingHours, type PreRequirementNotification, PricingMeasure, type Procedure, type ProcedureAnalytics, type ProcedureCategorization, type ProcedureExtendedInfo, ProcedureFamily, type ProcedureInfo, type ProcedurePopularity, type ProcedureProduct, type ProcedureProfitability, type ProcedureRecommendationNotification, type ProcedureReview, type ProcedureReviewInfo, ProcedureService, type ProcedureSummaryInfo, type Product, type ProductRevenueMetrics, ProductService, type ProductUsageByProcedure, type ProductUsageMetrics, type ProposedWorkingHours, REGISTER_TOKENS_COLLECTION, REVENUE_ANALYTICS_SUBCOLLECTION, REVIEWS_COLLECTION, type RatingScaleElement, type ReadStoredAnalyticsOptions, type RecommendedProcedure, type RequesterInfo, type Requirement, type RequirementInstructionDueNotification, type RequirementSourceProcedure, RequirementType, type RevenueMetrics, type RevenueTrend, type Review, type ReviewAnalyticsMetrics, ReviewAnalyticsService, type ReviewDetail, type ReviewMetrics, type ReviewRequestNotification, ReviewService, type ReviewTrend, SYNCED_CALENDARS_COLLECTION, type SearchAppointmentsParams, type SearchCalendarEventsParams, SearchLocationEnum, type SearchPatientsParams, type SignatureElement, type SingleChoiceElement, type StoredCancellationMetrics, type StoredClinicAnalytics, type StoredDashboardAnalytics, type StoredNoShowMetrics, type StoredPractitionerAnalytics, type StoredProcedureAnalytics, type StoredRevenueMetrics, type StoredTimeEfficiencyMetrics, type StripeTransactionData, type Subcategory, SubcategoryService, SubscriptionModel, SubscriptionStatus, type SyncedCalendar, type SyncedCalendarEvent, SyncedCalendarProvider, SyncedCalendarsService, TIME_EFFICIENCY_ANALYTICS_SUBCOLLECTION, type Technology, type TechnologyDocumentationTemplate, TechnologyService, type TextInputElement, type TimeEfficiencyMetrics, type TimeSlot, TimeUnit, TreatmentBenefit, type TreatmentBenefitDynamic, type TrendPeriod, USERS_COLLECTION, USER_FORMS_SUBCOLLECTION, type UpdateAestheticAnalysisData, type UpdateAllergyData, type UpdateAppointmentData, type UpdateAppointmentParams, type UpdateBlockingConditionData, type UpdateBlockingEventParams, type UpdateCalendarEventData, type UpdateClinicAdminData, type UpdateClinicData, type UpdateClinicGroupData, type UpdateContraindicationData, type UpdateDocumentTemplateData, type UpdateMedicationData, type UpdatePatientLocationInfoData, type UpdatePatientMedicalInfoData, type UpdatePatientProfileData, type UpdatePatientSensitiveInfoData, type UpdatePractitionerData, type UpdatePractitionerInviteData, type UpdateProcedureData, type UpdateSyncedCalendarData, type UpdateVitalStatsData, type User, UserRole, UserService, type VitalStats, type WorkingHours, type ZoneItemData, type ZonePhotoUploadData, getFirebaseApp, getFirebaseAuth, getFirebaseDB, getFirebaseFunctions, getFirebaseInstance, getFirebaseStorage, initializeFirebase };
|