@blackcode_sa/metaestetics-api 1.12.22 → 1.12.24

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.
@@ -1,22 +1,414 @@
1
- import * as admin from "firebase-admin";
2
- import { BaseMailingService, NewMailgunClient } from "../base.mailing.service";
3
- import { Logger } from "../../logger";
4
- import { Appointment } from "../../../types/appointment";
5
- import { PatientProfile } from "../../../types/patient";
6
- import { Practitioner } from "../../../types/practitioner";
7
- import { Clinic } from "../../../types/clinic";
8
- import { UserRole } from "../../../types";
9
- import {
10
- PractitionerProfileInfo,
11
- PatientProfileInfo,
12
- ClinicInfo,
13
- } from "../../../types/profile";
1
+ import * as admin from 'firebase-admin';
2
+ import { BaseMailingService, NewMailgunClient } from '../base.mailing.service';
3
+ import { Logger } from '../../logger';
4
+ import { Appointment } from '../../../types/appointment';
5
+ import { PatientProfile } from '../../../types/patient';
6
+ import { Practitioner } from '../../../types/practitioner';
7
+ import { Clinic } from '../../../types/clinic';
8
+ import { UserRole } from '../../../types';
9
+ import { PractitionerProfileInfo, PatientProfileInfo, ClinicInfo } from '../../../types/profile';
14
10
 
15
11
  // Simple string literals for placeholders, to be replaced by file loading later
16
- const patientAppointmentConfirmedTemplate =
17
- "<h1>Appointment Confirmed</h1><p>Dear {{patientName}},</p><p>Your appointment for {{procedureName}} on {{appointmentDate}} at {{appointmentTime}} with {{practitionerName}} at {{clinicName}} has been confirmed.</p><p>Thank you!</p>";
18
- const clinicAppointmentRequestedTemplate =
19
- "<h1>New Appointment Request</h1><p>Hello {{clinicName}} Admin,</p><p>A new appointment for {{procedureName}} has been requested by {{patientName}} for {{appointmentDate}} at {{appointmentTime}} with {{practitionerName}}.</p><p>Please review and confirm in the admin panel.</p>";
12
+ const patientAppointmentConfirmedTemplate = `
13
+ <!DOCTYPE html>
14
+ <html lang="en">
15
+ <head>
16
+ <meta charset="UTF-8">
17
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
18
+ <title>Appointment Confirmed</title>
19
+ <style>
20
+ body {
21
+ margin: 0;
22
+ padding: 0;
23
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
24
+ background: linear-gradient(135deg, #a48a76 0%, #67574A 100%);
25
+ min-height: 100vh;
26
+ }
27
+ .email-container {
28
+ max-width: 600px;
29
+ margin: 0 auto;
30
+ background: #ffffff;
31
+ border-radius: 20px;
32
+ overflow: hidden;
33
+ box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
34
+ margin-top: 40px;
35
+ margin-bottom: 40px;
36
+ }
37
+ .header {
38
+ background: linear-gradient(135deg, #a48a76 0%, #67574A 100%);
39
+ padding: 40px 30px;
40
+ text-align: center;
41
+ color: white;
42
+ }
43
+ .header h1 {
44
+ margin: 0;
45
+ font-size: 28px;
46
+ font-weight: 300;
47
+ letter-spacing: 1px;
48
+ }
49
+ .header .subtitle {
50
+ margin: 10px 0 0 0;
51
+ font-size: 16px;
52
+ opacity: 0.9;
53
+ font-weight: 300;
54
+ }
55
+ .content {
56
+ padding: 40px 30px;
57
+ }
58
+ .greeting {
59
+ font-size: 18px;
60
+ color: #333;
61
+ margin-bottom: 25px;
62
+ font-weight: 400;
63
+ }
64
+ .appointment-card {
65
+ background: linear-gradient(135deg, #f8f6f5 0%, #f5f3f2 100%);
66
+ border-radius: 15px;
67
+ padding: 30px;
68
+ margin: 25px 0;
69
+ border-left: 5px solid #a48a76;
70
+ }
71
+ .appointment-title {
72
+ font-size: 20px;
73
+ color: #a48a76;
74
+ margin-bottom: 20px;
75
+ font-weight: 600;
76
+ }
77
+ .appointment-details {
78
+ display: grid;
79
+ gap: 15px;
80
+ }
81
+ .detail-row {
82
+ display: flex;
83
+ align-items: center;
84
+ padding: 8px 0;
85
+ }
86
+ .detail-label {
87
+ font-weight: 600;
88
+ color: #555;
89
+ min-width: 120px;
90
+ font-size: 14px;
91
+ }
92
+ .detail-value {
93
+ color: #333;
94
+ font-size: 16px;
95
+ font-weight: 500;
96
+ }
97
+ .procedure-name {
98
+ color: #67574A;
99
+ font-weight: 600;
100
+ }
101
+ .clinic-name {
102
+ color: #a48a76;
103
+ font-weight: 600;
104
+ }
105
+ .success-icon {
106
+ text-align: center;
107
+ margin: 20px 0;
108
+ font-size: 48px;
109
+ }
110
+ .footer {
111
+ background: #f8f9fa;
112
+ padding: 25px 30px;
113
+ text-align: center;
114
+ color: #666;
115
+ font-size: 14px;
116
+ border-top: 1px solid #eee;
117
+ }
118
+ .logo {
119
+ font-size: 24px;
120
+ font-weight: 700;
121
+ color: white;
122
+ margin-bottom: 5px;
123
+ }
124
+ .divider {
125
+ height: 2px;
126
+ background: linear-gradient(90deg, #a48a76, #67574A);
127
+ margin: 25px 0;
128
+ border-radius: 1px;
129
+ }
130
+ .thank-you {
131
+ background: linear-gradient(135deg, #f0ede8 0%, #ebe6e0 100%);
132
+ border-radius: 15px;
133
+ padding: 25px;
134
+ margin: 25px 0;
135
+ text-align: center;
136
+ border-left: 5px solid #4CAF50;
137
+ }
138
+ </style>
139
+ </head>
140
+ <body>
141
+ <div class="email-container">
142
+ <div class="header">
143
+ <div class="logo">MetaEstetics</div>
144
+ <h1>Appointment Confirmed</h1>
145
+ <div class="subtitle">Your Beauty Journey Awaits</div>
146
+ </div>
147
+
148
+ <div class="content">
149
+ <div class="success-icon">✨</div>
150
+
151
+ <div class="greeting">
152
+ Dear <strong>{{patientName}}</strong>,
153
+ </div>
154
+
155
+ <p style="color: #555; font-size: 16px; line-height: 1.6; margin-bottom: 25px;">
156
+ We're delighted to confirm your appointment! Your journey to enhanced beauty and confidence is just around the corner.
157
+ </p>
158
+
159
+ <div class="appointment-card">
160
+ <div class="appointment-title">📅 Your Appointment Details</div>
161
+ <div class="appointment-details">
162
+ <div class="detail-row">
163
+ <div class="detail-label">Procedure:</div>
164
+ <div class="detail-value procedure-name">{{procedureName}}</div>
165
+ </div>
166
+ <div class="detail-row">
167
+ <div class="detail-label">Date:</div>
168
+ <div class="detail-value">{{appointmentDate}}</div>
169
+ </div>
170
+ <div class="detail-row">
171
+ <div class="detail-label">Time:</div>
172
+ <div class="detail-value">{{appointmentTime}}</div>
173
+ </div>
174
+ <div class="detail-row">
175
+ <div class="detail-label">Practitioner:</div>
176
+ <div class="detail-value">{{practitionerName}}</div>
177
+ </div>
178
+ <div class="detail-row">
179
+ <div class="detail-label">Location:</div>
180
+ <div class="detail-value clinic-name">{{clinicName}}</div>
181
+ </div>
182
+ </div>
183
+ </div>
184
+
185
+ <div class="divider"></div>
186
+
187
+ <div class="thank-you">
188
+ <h3 style="margin: 0 0 10px 0; color: #4caf50; font-weight: 600;">Thank You for Choosing MetaEstetics!</h3>
189
+ <p style="margin: 0; color: #555; font-size: 14px;">
190
+ We look forward to providing you with exceptional care and outstanding results.
191
+ </p>
192
+ </div>
193
+
194
+ <p style="color: #555; font-size: 14px; line-height: 1.6; margin-top: 25px;">
195
+ <strong>Important:</strong> Please arrive 15 minutes early for your appointment. If you need to reschedule or have any questions, please contact us as soon as possible.
196
+ </p>
197
+ </div>
198
+
199
+ <div class="footer">
200
+ <p style="margin: 0 0 10px 0;">
201
+ <strong>MetaEstetics</strong> - Premium Aesthetic Services
202
+ </p>
203
+ <p style="margin: 0; font-size: 12px; color: #999;">
204
+ This is an automated message. Please do not reply to this email.
205
+ </p>
206
+ </div>
207
+ </div>
208
+ </body>
209
+ </html>
210
+ `;
211
+ const clinicAppointmentRequestedTemplate = `
212
+ <!DOCTYPE html>
213
+ <html lang="en">
214
+ <head>
215
+ <meta charset="UTF-8">
216
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
217
+ <title>New Appointment Request</title>
218
+ <style>
219
+ body {
220
+ margin: 0;
221
+ padding: 0;
222
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
223
+ background: linear-gradient(135deg, #a48a76 0%, #67574A 100%);
224
+ min-height: 100vh;
225
+ }
226
+ .email-container {
227
+ max-width: 600px;
228
+ margin: 0 auto;
229
+ background: #ffffff;
230
+ border-radius: 20px;
231
+ overflow: hidden;
232
+ box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
233
+ margin-top: 40px;
234
+ margin-bottom: 40px;
235
+ }
236
+ .header {
237
+ background: linear-gradient(135deg, #a48a76 0%, #67574A 100%);
238
+ padding: 40px 30px;
239
+ text-align: center;
240
+ color: white;
241
+ }
242
+ .header h1 {
243
+ margin: 0;
244
+ font-size: 28px;
245
+ font-weight: 300;
246
+ letter-spacing: 1px;
247
+ }
248
+ .header .subtitle {
249
+ margin: 10px 0 0 0;
250
+ font-size: 16px;
251
+ opacity: 0.9;
252
+ font-weight: 300;
253
+ }
254
+ .content {
255
+ padding: 40px 30px;
256
+ }
257
+ .greeting {
258
+ font-size: 18px;
259
+ color: #333;
260
+ margin-bottom: 25px;
261
+ font-weight: 400;
262
+ }
263
+ .appointment-card {
264
+ background: linear-gradient(135deg, #f8f6f5 0%, #f5f3f2 100%);
265
+ border-radius: 15px;
266
+ padding: 30px;
267
+ margin: 25px 0;
268
+ border-left: 5px solid #a48a76;
269
+ }
270
+ .appointment-title {
271
+ font-size: 20px;
272
+ color: #a48a76;
273
+ margin-bottom: 20px;
274
+ font-weight: 600;
275
+ }
276
+ .appointment-details {
277
+ display: grid;
278
+ gap: 15px;
279
+ }
280
+ .detail-row {
281
+ display: flex;
282
+ align-items: center;
283
+ padding: 8px 0;
284
+ }
285
+ .detail-label {
286
+ font-weight: 600;
287
+ color: #555;
288
+ min-width: 120px;
289
+ font-size: 14px;
290
+ }
291
+ .detail-value {
292
+ color: #333;
293
+ font-size: 16px;
294
+ font-weight: 500;
295
+ }
296
+ .patient-name {
297
+ color: #a48a76;
298
+ font-weight: 600;
299
+ }
300
+ .procedure-name {
301
+ color: #67574A;
302
+ font-weight: 600;
303
+ }
304
+ .cta-section {
305
+ text-align: center;
306
+ margin: 35px 0;
307
+ }
308
+ .cta-button {
309
+ display: inline-block;
310
+ background: linear-gradient(135deg, #a48a76 0%, #67574A 100%);
311
+ color: white;
312
+ text-decoration: none;
313
+ padding: 15px 35px;
314
+ border-radius: 50px;
315
+ font-weight: 600;
316
+ font-size: 16px;
317
+ letter-spacing: 0.5px;
318
+ transition: transform 0.3s ease;
319
+ box-shadow: 0 8px 25px rgba(164, 138, 118, 0.3);
320
+ }
321
+ .cta-button:hover {
322
+ transform: translateY(-2px);
323
+ }
324
+ .footer {
325
+ background: #f8f9fa;
326
+ padding: 25px 30px;
327
+ text-align: center;
328
+ color: #666;
329
+ font-size: 14px;
330
+ border-top: 1px solid #eee;
331
+ }
332
+ .logo {
333
+ font-size: 24px;
334
+ font-weight: 700;
335
+ color: white;
336
+ margin-bottom: 5px;
337
+ }
338
+ .divider {
339
+ height: 2px;
340
+ background: linear-gradient(90deg, #a48a76, #67574A);
341
+ margin: 25px 0;
342
+ border-radius: 1px;
343
+ }
344
+ </style>
345
+ </head>
346
+ <body>
347
+ <div class="email-container">
348
+ <div class="header">
349
+ <div class="logo">MetaEstetics</div>
350
+ <h1>New Appointment Request</h1>
351
+ <div class="subtitle">Requires Your Attention</div>
352
+ </div>
353
+
354
+ <div class="content">
355
+ <div class="greeting">
356
+ Dear <strong>{{clinicName}}</strong> Team,
357
+ </div>
358
+
359
+ <p style="color: #555; font-size: 16px; line-height: 1.6; margin-bottom: 25px;">
360
+ We hope this message finds you well. You have received a new appointment request that requires your review and confirmation.
361
+ </p>
362
+
363
+ <div class="appointment-card">
364
+ <div class="appointment-title">📅 Appointment Details</div>
365
+ <div class="appointment-details">
366
+ <div class="detail-row">
367
+ <div class="detail-label">Patient:</div>
368
+ <div class="detail-value patient-name">{{patientName}}</div>
369
+ </div>
370
+ <div class="detail-row">
371
+ <div class="detail-label">Procedure:</div>
372
+ <div class="detail-value procedure-name">{{procedureName}}</div>
373
+ </div>
374
+ <div class="detail-row">
375
+ <div class="detail-label">Date:</div>
376
+ <div class="detail-value">{{appointmentDate}}</div>
377
+ </div>
378
+ <div class="detail-row">
379
+ <div class="detail-label">Time:</div>
380
+ <div class="detail-value">{{appointmentTime}}</div>
381
+ </div>
382
+ <div class="detail-row">
383
+ <div class="detail-label">Practitioner:</div>
384
+ <div class="detail-value">{{practitionerName}}</div>
385
+ </div>
386
+ </div>
387
+ </div>
388
+
389
+ <div class="divider"></div>
390
+
391
+ <p style="color: #555; font-size: 16px; line-height: 1.6; margin-bottom: 30px;">
392
+ Please review this appointment request and confirm availability in your admin panel. The patient is waiting for your confirmation to finalize their booking.
393
+ </p>
394
+
395
+ <div class="cta-section">
396
+ <a href="#" class="cta-button">Review & Confirm Appointment</a>
397
+ </div>
398
+ </div>
399
+
400
+ <div class="footer">
401
+ <p style="margin: 0 0 10px 0;">
402
+ <strong>MetaEstetics</strong> - Premium Aesthetic Services
403
+ </p>
404
+ <p style="margin: 0; font-size: 12px; color: #999;">
405
+ This is an automated message. Please do not reply to this email.
406
+ </p>
407
+ </div>
408
+ </div>
409
+ </body>
410
+ </html>
411
+ `;
20
412
 
21
413
  // --- Interface Definitions for Email Data ---
22
414
 
@@ -29,26 +421,22 @@ export interface AppointmentEmailDataBase {
29
421
  };
30
422
  }
31
423
 
32
- export interface AppointmentConfirmationEmailData
33
- extends AppointmentEmailDataBase {
424
+ export interface AppointmentConfirmationEmailData extends AppointmentEmailDataBase {
34
425
  recipientProfile: PatientProfileInfo | PractitionerProfileInfo;
35
- recipientRole: "patient" | "practitioner";
426
+ recipientRole: 'patient' | 'practitioner';
36
427
  }
37
428
 
38
- export interface AppointmentRequestedEmailData
39
- extends AppointmentEmailDataBase {
429
+ export interface AppointmentRequestedEmailData extends AppointmentEmailDataBase {
40
430
  clinicProfile: ClinicInfo;
41
431
  }
42
432
 
43
- export interface AppointmentCancellationEmailData
44
- extends AppointmentEmailDataBase {
433
+ export interface AppointmentCancellationEmailData extends AppointmentEmailDataBase {
45
434
  recipientProfile: PatientProfileInfo | PractitionerProfileInfo;
46
- recipientRole: "patient" | "practitioner";
435
+ recipientRole: 'patient' | 'practitioner';
47
436
  cancellationReason?: string;
48
437
  }
49
438
 
50
- export interface AppointmentRescheduledProposalEmailData
51
- extends AppointmentEmailDataBase {
439
+ export interface AppointmentRescheduledProposalEmailData extends AppointmentEmailDataBase {
52
440
  patientProfile: PatientProfileInfo;
53
441
  previousStartTime: admin.firestore.Timestamp;
54
442
  previousEndTime: admin.firestore.Timestamp;
@@ -61,7 +449,7 @@ export interface ReviewRequestEmailData extends AppointmentEmailDataBase {
61
449
 
62
450
  export interface ReviewAddedEmailData extends AppointmentEmailDataBase {
63
451
  recipientProfile: PractitionerProfileInfo | ClinicInfo;
64
- recipientRole: "practitioner" | "clinic";
452
+ recipientRole: 'practitioner' | 'clinic';
65
453
  reviewerName: string;
66
454
  reviewRating: number;
67
455
  reviewComment?: string;
@@ -71,59 +459,43 @@ export interface ReviewAddedEmailData extends AppointmentEmailDataBase {
71
459
  * Service for sending appointment-related emails.
72
460
  */
73
461
  export class AppointmentMailingService extends BaseMailingService {
74
- private readonly DEFAULT_MAILGUN_DOMAIN = "mg.metaesthetics.net";
462
+ private readonly DEFAULT_MAILGUN_DOMAIN = 'mg.metaesthetics.net';
75
463
 
76
- constructor(
77
- firestore: admin.firestore.Firestore,
78
- mailgunClient: NewMailgunClient
79
- ) {
464
+ constructor(firestore: admin.firestore.Firestore, mailgunClient: NewMailgunClient) {
80
465
  super(firestore, mailgunClient);
81
- Logger.info("[AppointmentMailingService] Initialized.");
466
+ Logger.info('[AppointmentMailingService] Initialized.');
82
467
  }
83
468
 
84
- async sendAppointmentConfirmedEmail(
85
- data: AppointmentConfirmationEmailData
86
- ): Promise<any> {
469
+ async sendAppointmentConfirmedEmail(data: AppointmentConfirmationEmailData): Promise<any> {
87
470
  Logger.info(
88
- `[AppointmentMailingService] Preparing to send appointment confirmation email to ${data.recipientRole}: ${data.recipientProfile.id}`
471
+ `[AppointmentMailingService] Preparing to send appointment confirmation email to ${data.recipientRole}: ${data.recipientProfile.id}`,
89
472
  );
90
473
 
91
474
  const recipientEmail = data.recipientProfile.email;
92
475
 
93
476
  if (!recipientEmail) {
94
- Logger.error(
95
- "[AppointmentMailingService] Recipient email not found for confirmation.",
96
- { recipientId: data.recipientProfile.id, role: data.recipientRole }
97
- );
98
- throw new Error("Recipient email address is missing.");
477
+ Logger.error('[AppointmentMailingService] Recipient email not found for confirmation.', {
478
+ recipientId: data.recipientProfile.id,
479
+ role: data.recipientRole,
480
+ });
481
+ throw new Error('Recipient email address is missing.');
99
482
  }
100
483
 
101
484
  const templateVariables = {
102
485
  patientName: data.appointment.patientInfo.fullName,
103
486
  procedureName: data.appointment.procedureInfo.name,
104
- appointmentDate: data.appointment.appointmentStartTime
105
- .toDate()
106
- .toLocaleDateString(),
107
- appointmentTime: data.appointment.appointmentStartTime
108
- .toDate()
109
- .toLocaleTimeString(),
487
+ appointmentDate: data.appointment.appointmentStartTime.toDate().toLocaleDateString(),
488
+ appointmentTime: data.appointment.appointmentStartTime.toDate().toLocaleTimeString(),
110
489
  practitionerName: data.appointment.practitionerInfo.name,
111
490
  clinicName: data.appointment.clinicInfo.name,
112
491
  };
113
492
 
114
- const html = this.renderTemplate(
115
- patientAppointmentConfirmedTemplate,
116
- templateVariables
117
- );
118
- const subject =
119
- data.options?.customSubject || "Your Appointment is Confirmed!";
493
+ const html = this.renderTemplate(patientAppointmentConfirmedTemplate, templateVariables);
494
+ const subject = data.options?.customSubject || 'Your Appointment is Confirmed!';
120
495
  const fromAddress =
121
496
  data.options?.fromAddress ||
122
- `MetaEstetics <no-reply@${
123
- data.options?.mailgunDomain || this.DEFAULT_MAILGUN_DOMAIN
124
- }>`;
125
- const domainToSendFrom =
126
- data.options?.mailgunDomain || this.DEFAULT_MAILGUN_DOMAIN;
497
+ `MetaEstetics <no-reply@${data.options?.mailgunDomain || this.DEFAULT_MAILGUN_DOMAIN}>`;
498
+ const domainToSendFrom = data.options?.mailgunDomain || this.DEFAULT_MAILGUN_DOMAIN;
127
499
 
128
500
  const mailgunSendData = {
129
501
  to: recipientEmail,
@@ -135,67 +507,53 @@ export class AppointmentMailingService extends BaseMailingService {
135
507
  try {
136
508
  const result = await this.sendEmail(domainToSendFrom, mailgunSendData);
137
509
  await this.logEmailAttempt(
138
- { to: recipientEmail, subject, templateName: "appointment_confirmed" },
139
- true
510
+ { to: recipientEmail, subject, templateName: 'appointment_confirmed' },
511
+ true,
140
512
  );
141
513
  return result;
142
514
  } catch (error) {
143
515
  await this.logEmailAttempt(
144
516
  {
145
517
  to: recipientEmail,
146
- subject:
147
- data.options?.customSubject || "Your Appointment is Confirmed!",
148
- templateName: "appointment_confirmed",
518
+ subject: data.options?.customSubject || 'Your Appointment is Confirmed!',
519
+ templateName: 'appointment_confirmed',
149
520
  },
150
521
  false,
151
- error
522
+ error,
152
523
  );
153
524
  throw error;
154
525
  }
155
526
  }
156
527
 
157
- async sendAppointmentRequestedEmailToClinic(
158
- data: AppointmentRequestedEmailData
159
- ): Promise<any> {
528
+ async sendAppointmentRequestedEmailToClinic(data: AppointmentRequestedEmailData): Promise<any> {
160
529
  Logger.info(
161
- `[AppointmentMailingService] Preparing to send appointment requested email to clinic: ${data.clinicProfile.id}`
530
+ `[AppointmentMailingService] Preparing to send appointment requested email to clinic: ${data.clinicProfile.id}`,
162
531
  );
163
532
 
164
533
  const clinicEmail = data.clinicProfile.contactInfo?.email;
165
534
  if (!clinicEmail) {
166
535
  Logger.error(
167
- "[AppointmentMailingService] Clinic contact email not found for request notification.",
168
- { clinicId: data.clinicProfile.id }
536
+ '[AppointmentMailingService] Clinic contact email not found for request notification.',
537
+ { clinicId: data.clinicProfile.id },
169
538
  );
170
- throw new Error("Clinic contact email address is missing.");
539
+ throw new Error('Clinic contact email address is missing.');
171
540
  }
172
541
 
173
542
  const templateVariables = {
174
543
  clinicName: data.clinicProfile.name,
175
544
  patientName: data.appointment.patientInfo.fullName,
176
545
  procedureName: data.appointment.procedureInfo.name,
177
- appointmentDate: data.appointment.appointmentStartTime
178
- .toDate()
179
- .toLocaleDateString(),
180
- appointmentTime: data.appointment.appointmentStartTime
181
- .toDate()
182
- .toLocaleTimeString(),
546
+ appointmentDate: data.appointment.appointmentStartTime.toDate().toLocaleDateString(),
547
+ appointmentTime: data.appointment.appointmentStartTime.toDate().toLocaleTimeString(),
183
548
  practitionerName: data.appointment.practitionerInfo.name,
184
549
  };
185
550
 
186
- const html = this.renderTemplate(
187
- clinicAppointmentRequestedTemplate,
188
- templateVariables
189
- );
190
- const subject =
191
- data.options?.customSubject || "New Appointment Request Received";
551
+ const html = this.renderTemplate(clinicAppointmentRequestedTemplate, templateVariables);
552
+ const subject = data.options?.customSubject || 'New Appointment Request Received';
192
553
  const fromAddress =
193
554
  data.options?.fromAddress ||
194
- `MetaEstetics <no-reply@${
195
- data.options?.mailgunDomain || this.DEFAULT_MAILGUN_DOMAIN
196
- }>`;
197
- const domainToSendFrom =
198
- data.options?.mailgunDomain || this.DEFAULT_MAILGUN_DOMAIN;
555
+ `MetaEstetics <no-reply@${data.options?.mailgunDomain || this.DEFAULT_MAILGUN_DOMAIN}>`;
556
+ const domainToSendFrom = data.options?.mailgunDomain || this.DEFAULT_MAILGUN_DOMAIN;
199
557
 
200
558
  const mailgunSendData = {
201
559
  to: clinicEmail,
@@ -210,54 +568,51 @@ export class AppointmentMailingService extends BaseMailingService {
210
568
  {
211
569
  to: clinicEmail,
212
570
  subject,
213
- templateName: "appointment_requested_clinic",
571
+ templateName: 'appointment_requested_clinic',
214
572
  },
215
- true
573
+ true,
216
574
  );
217
575
  return result;
218
576
  } catch (error) {
219
577
  await this.logEmailAttempt(
220
578
  {
221
579
  to: clinicEmail,
222
- subject:
223
- data.options?.customSubject || "New Appointment Request Received",
224
- templateName: "appointment_requested_clinic",
580
+ subject: data.options?.customSubject || 'New Appointment Request Received',
581
+ templateName: 'appointment_requested_clinic',
225
582
  },
226
583
  false,
227
- error
584
+ error,
228
585
  );
229
586
  throw error;
230
587
  }
231
588
  }
232
589
 
233
- async sendAppointmentCancelledEmail(
234
- data: AppointmentCancellationEmailData
235
- ): Promise<any> {
590
+ async sendAppointmentCancelledEmail(data: AppointmentCancellationEmailData): Promise<any> {
236
591
  Logger.info(
237
- `[AppointmentMailingService] Placeholder for sendAppointmentCancelledEmail for ${data.recipientRole}: ${data.recipientProfile.id}`
592
+ `[AppointmentMailingService] Placeholder for sendAppointmentCancelledEmail for ${data.recipientRole}: ${data.recipientProfile.id}`,
238
593
  );
239
594
  return Promise.resolve();
240
595
  }
241
596
 
242
597
  async sendAppointmentRescheduledProposalEmail(
243
- data: AppointmentRescheduledProposalEmailData
598
+ data: AppointmentRescheduledProposalEmailData,
244
599
  ): Promise<any> {
245
600
  Logger.info(
246
- `[AppointmentMailingService] Placeholder for sendAppointmentRescheduledProposalEmail to patient: ${data.patientProfile.id}`
601
+ `[AppointmentMailingService] Placeholder for sendAppointmentRescheduledProposalEmail to patient: ${data.patientProfile.id}`,
247
602
  );
248
603
  return Promise.resolve();
249
604
  }
250
605
 
251
606
  async sendReviewRequestEmail(data: ReviewRequestEmailData): Promise<any> {
252
607
  Logger.info(
253
- `[AppointmentMailingService] Placeholder for sendReviewRequestEmail to patient: ${data.patientProfile.id}`
608
+ `[AppointmentMailingService] Placeholder for sendReviewRequestEmail to patient: ${data.patientProfile.id}`,
254
609
  );
255
610
  return Promise.resolve();
256
611
  }
257
612
 
258
613
  async sendReviewAddedEmail(data: ReviewAddedEmailData): Promise<any> {
259
614
  Logger.info(
260
- `[AppointmentMailingService] Placeholder for sendReviewAddedEmail to ${data.recipientRole}: ${data.recipientProfile.id}`
615
+ `[AppointmentMailingService] Placeholder for sendReviewAddedEmail to ${data.recipientRole}: ${data.recipientProfile.id}`,
261
616
  );
262
617
  return Promise.resolve();
263
618
  }