@blackcode_sa/metaestetics-api 1.14.65 → 1.14.69
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 +203 -19
- package/dist/admin/index.d.ts +203 -19
- package/dist/admin/index.js +436 -2
- package/dist/admin/index.mjs +435 -2
- package/package.json +1 -1
- package/src/admin/aggregation/appointment/appointment.aggregation.service.ts +19 -2
- package/src/admin/index.ts +1 -1
- package/src/admin/mailing/clinicWelcome/clinicWelcome.mailing.ts +292 -0
- package/src/admin/mailing/clinicWelcome/index.ts +1 -0
- package/src/admin/mailing/clinicWelcome/templates/welcome.template.ts +252 -0
- package/src/admin/mailing/index.ts +1 -0
package/dist/admin/index.mjs
CHANGED
|
@@ -3055,6 +3055,11 @@ var AppointmentMailingService = class extends BaseMailingService {
|
|
|
3055
3055
|
};
|
|
3056
3056
|
|
|
3057
3057
|
// src/admin/aggregation/appointment/appointment.aggregation.service.ts
|
|
3058
|
+
function sanitizeCancellationReason(reason) {
|
|
3059
|
+
if (!reason) return void 0;
|
|
3060
|
+
const isLikelyId = !reason.includes(" ") && /^[a-zA-Z0-9_-]+$/.test(reason) && reason.length > 15;
|
|
3061
|
+
return isLikelyId ? void 0 : reason;
|
|
3062
|
+
}
|
|
3058
3063
|
var AppointmentAggregationService = class {
|
|
3059
3064
|
/**
|
|
3060
3065
|
* Constructor for AppointmentAggregationService.
|
|
@@ -3336,7 +3341,7 @@ var AppointmentAggregationService = class {
|
|
|
3336
3341
|
appointment: after,
|
|
3337
3342
|
recipientProfile: after.patientInfo,
|
|
3338
3343
|
recipientRole: "patient",
|
|
3339
|
-
cancellationReason: after.cancellationReason
|
|
3344
|
+
cancellationReason: sanitizeCancellationReason(after.cancellationReason)
|
|
3340
3345
|
};
|
|
3341
3346
|
await this.appointmentMailingService.sendAppointmentCancelledEmail(
|
|
3342
3347
|
patientCancellationData
|
|
@@ -3350,7 +3355,7 @@ var AppointmentAggregationService = class {
|
|
|
3350
3355
|
appointment: after,
|
|
3351
3356
|
recipientProfile: after.practitionerInfo,
|
|
3352
3357
|
recipientRole: "practitioner",
|
|
3353
|
-
cancellationReason: after.cancellationReason
|
|
3358
|
+
cancellationReason: sanitizeCancellationReason(after.cancellationReason)
|
|
3354
3359
|
};
|
|
3355
3360
|
await this.appointmentMailingService.sendAppointmentCancelledEmail(
|
|
3356
3361
|
practitionerCancellationData
|
|
@@ -14012,6 +14017,433 @@ var PatientInviteMailingService = class extends BaseMailingService {
|
|
|
14012
14017
|
}
|
|
14013
14018
|
};
|
|
14014
14019
|
|
|
14020
|
+
// src/admin/mailing/clinicWelcome/templates/welcome.template.ts
|
|
14021
|
+
var clinicWelcomeTemplate = `
|
|
14022
|
+
<!DOCTYPE html>
|
|
14023
|
+
<html>
|
|
14024
|
+
<head>
|
|
14025
|
+
<meta charset="UTF-8">
|
|
14026
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
14027
|
+
<title>Welcome to MetaEsthetics</title>
|
|
14028
|
+
<style>
|
|
14029
|
+
body {
|
|
14030
|
+
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
|
14031
|
+
line-height: 1.6;
|
|
14032
|
+
color: #333;
|
|
14033
|
+
margin: 0;
|
|
14034
|
+
padding: 0;
|
|
14035
|
+
background-color: #f5f5f5;
|
|
14036
|
+
}
|
|
14037
|
+
.container {
|
|
14038
|
+
max-width: 600px;
|
|
14039
|
+
margin: 0 auto;
|
|
14040
|
+
padding: 20px;
|
|
14041
|
+
}
|
|
14042
|
+
.header {
|
|
14043
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
14044
|
+
padding: 40px 20px;
|
|
14045
|
+
text-align: center;
|
|
14046
|
+
border-radius: 8px 8px 0 0;
|
|
14047
|
+
}
|
|
14048
|
+
.header h1 {
|
|
14049
|
+
color: white;
|
|
14050
|
+
margin: 0;
|
|
14051
|
+
font-size: 28px;
|
|
14052
|
+
font-weight: 600;
|
|
14053
|
+
}
|
|
14054
|
+
.header p {
|
|
14055
|
+
color: rgba(255, 255, 255, 0.9);
|
|
14056
|
+
margin: 10px 0 0 0;
|
|
14057
|
+
font-size: 16px;
|
|
14058
|
+
}
|
|
14059
|
+
.content {
|
|
14060
|
+
padding: 40px 30px;
|
|
14061
|
+
background-color: #ffffff;
|
|
14062
|
+
}
|
|
14063
|
+
.greeting {
|
|
14064
|
+
font-size: 18px;
|
|
14065
|
+
color: #333;
|
|
14066
|
+
margin-bottom: 20px;
|
|
14067
|
+
}
|
|
14068
|
+
.message {
|
|
14069
|
+
font-size: 16px;
|
|
14070
|
+
color: #555;
|
|
14071
|
+
margin-bottom: 25px;
|
|
14072
|
+
}
|
|
14073
|
+
.highlight-box {
|
|
14074
|
+
background: linear-gradient(135deg, #f6f9fc 0%, #eef2f7 100%);
|
|
14075
|
+
border-left: 4px solid #667eea;
|
|
14076
|
+
padding: 20px;
|
|
14077
|
+
margin: 25px 0;
|
|
14078
|
+
border-radius: 0 8px 8px 0;
|
|
14079
|
+
}
|
|
14080
|
+
.highlight-box h3 {
|
|
14081
|
+
margin: 0 0 10px 0;
|
|
14082
|
+
color: #333;
|
|
14083
|
+
font-size: 16px;
|
|
14084
|
+
}
|
|
14085
|
+
.highlight-box p {
|
|
14086
|
+
margin: 0;
|
|
14087
|
+
color: #555;
|
|
14088
|
+
}
|
|
14089
|
+
.next-steps {
|
|
14090
|
+
margin: 30px 0;
|
|
14091
|
+
}
|
|
14092
|
+
.next-steps h3 {
|
|
14093
|
+
color: #333;
|
|
14094
|
+
font-size: 18px;
|
|
14095
|
+
margin-bottom: 15px;
|
|
14096
|
+
}
|
|
14097
|
+
.step {
|
|
14098
|
+
display: flex;
|
|
14099
|
+
align-items: flex-start;
|
|
14100
|
+
margin-bottom: 15px;
|
|
14101
|
+
}
|
|
14102
|
+
.step-number {
|
|
14103
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
14104
|
+
color: white;
|
|
14105
|
+
width: 28px;
|
|
14106
|
+
height: 28px;
|
|
14107
|
+
border-radius: 50%;
|
|
14108
|
+
display: flex;
|
|
14109
|
+
align-items: center;
|
|
14110
|
+
justify-content: center;
|
|
14111
|
+
font-weight: 600;
|
|
14112
|
+
font-size: 14px;
|
|
14113
|
+
margin-right: 12px;
|
|
14114
|
+
flex-shrink: 0;
|
|
14115
|
+
}
|
|
14116
|
+
.step-content {
|
|
14117
|
+
flex: 1;
|
|
14118
|
+
padding-top: 3px;
|
|
14119
|
+
}
|
|
14120
|
+
.step-title {
|
|
14121
|
+
font-weight: 600;
|
|
14122
|
+
color: #333;
|
|
14123
|
+
margin-bottom: 3px;
|
|
14124
|
+
}
|
|
14125
|
+
.step-description {
|
|
14126
|
+
color: #666;
|
|
14127
|
+
font-size: 14px;
|
|
14128
|
+
}
|
|
14129
|
+
.button-container {
|
|
14130
|
+
text-align: center;
|
|
14131
|
+
margin: 35px 0;
|
|
14132
|
+
}
|
|
14133
|
+
.button {
|
|
14134
|
+
display: inline-block;
|
|
14135
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
14136
|
+
color: white;
|
|
14137
|
+
text-decoration: none;
|
|
14138
|
+
padding: 14px 32px;
|
|
14139
|
+
border-radius: 6px;
|
|
14140
|
+
font-weight: 600;
|
|
14141
|
+
font-size: 16px;
|
|
14142
|
+
transition: transform 0.2s;
|
|
14143
|
+
}
|
|
14144
|
+
.button:hover {
|
|
14145
|
+
transform: translateY(-2px);
|
|
14146
|
+
}
|
|
14147
|
+
.support-box {
|
|
14148
|
+
background-color: #f8f9fa;
|
|
14149
|
+
padding: 20px;
|
|
14150
|
+
border-radius: 8px;
|
|
14151
|
+
margin-top: 30px;
|
|
14152
|
+
text-align: center;
|
|
14153
|
+
}
|
|
14154
|
+
.support-box p {
|
|
14155
|
+
margin: 0 0 10px 0;
|
|
14156
|
+
color: #555;
|
|
14157
|
+
}
|
|
14158
|
+
.support-box a {
|
|
14159
|
+
color: #667eea;
|
|
14160
|
+
text-decoration: none;
|
|
14161
|
+
font-weight: 500;
|
|
14162
|
+
}
|
|
14163
|
+
.footer {
|
|
14164
|
+
padding: 30px;
|
|
14165
|
+
text-align: center;
|
|
14166
|
+
background-color: #f8f9fa;
|
|
14167
|
+
border-radius: 0 0 8px 8px;
|
|
14168
|
+
}
|
|
14169
|
+
.footer p {
|
|
14170
|
+
margin: 5px 0;
|
|
14171
|
+
font-size: 13px;
|
|
14172
|
+
color: #888;
|
|
14173
|
+
}
|
|
14174
|
+
.footer a {
|
|
14175
|
+
color: #667eea;
|
|
14176
|
+
text-decoration: none;
|
|
14177
|
+
}
|
|
14178
|
+
.social-links {
|
|
14179
|
+
margin: 15px 0;
|
|
14180
|
+
}
|
|
14181
|
+
.social-links a {
|
|
14182
|
+
display: inline-block;
|
|
14183
|
+
margin: 0 8px;
|
|
14184
|
+
color: #888;
|
|
14185
|
+
text-decoration: none;
|
|
14186
|
+
}
|
|
14187
|
+
</style>
|
|
14188
|
+
</head>
|
|
14189
|
+
<body>
|
|
14190
|
+
<div class="container">
|
|
14191
|
+
<div class="header">
|
|
14192
|
+
<h1>Welcome to MetaEsthetics!</h1>
|
|
14193
|
+
<p>Your clinic registration is complete</p>
|
|
14194
|
+
</div>
|
|
14195
|
+
<div class="content">
|
|
14196
|
+
<p class="greeting">Hello {{adminName}},</p>
|
|
14197
|
+
|
|
14198
|
+
<p class="message">
|
|
14199
|
+
Thank you for registering <strong>{{clinicGroupName}}</strong> with MetaEsthetics.
|
|
14200
|
+
Your account has been successfully created and you're now ready to start managing
|
|
14201
|
+
your aesthetic practice with our comprehensive platform.
|
|
14202
|
+
</p>
|
|
14203
|
+
|
|
14204
|
+
<div class="highlight-box">
|
|
14205
|
+
<h3>Account Details</h3>
|
|
14206
|
+
<p><strong>Clinic Group:</strong> {{clinicGroupName}}</p>
|
|
14207
|
+
<p><strong>Admin Email:</strong> {{adminEmail}}</p>
|
|
14208
|
+
<p><strong>Registration Date:</strong> {{registrationDate}}</p>
|
|
14209
|
+
</div>
|
|
14210
|
+
|
|
14211
|
+
<div class="next-steps">
|
|
14212
|
+
<h3>Get Started with MetaEsthetics</h3>
|
|
14213
|
+
|
|
14214
|
+
<div class="step">
|
|
14215
|
+
<div class="step-number">1</div>
|
|
14216
|
+
<div class="step-content">
|
|
14217
|
+
<div class="step-title">Complete Your Profile</div>
|
|
14218
|
+
<div class="step-description">Add your clinic details, working hours, and upload photos to attract patients.</div>
|
|
14219
|
+
</div>
|
|
14220
|
+
</div>
|
|
14221
|
+
|
|
14222
|
+
<div class="step">
|
|
14223
|
+
<div class="step-number">2</div>
|
|
14224
|
+
<div class="step-content">
|
|
14225
|
+
<div class="step-title">Add Your Team</div>
|
|
14226
|
+
<div class="step-description">Invite practitioners and staff members to join your clinic.</div>
|
|
14227
|
+
</div>
|
|
14228
|
+
</div>
|
|
14229
|
+
|
|
14230
|
+
<div class="step">
|
|
14231
|
+
<div class="step-number">3</div>
|
|
14232
|
+
<div class="step-content">
|
|
14233
|
+
<div class="step-title">Set Up Services</div>
|
|
14234
|
+
<div class="step-description">Configure your procedures, pricing, and availability for bookings.</div>
|
|
14235
|
+
</div>
|
|
14236
|
+
</div>
|
|
14237
|
+
|
|
14238
|
+
<div class="step">
|
|
14239
|
+
<div class="step-number">4</div>
|
|
14240
|
+
<div class="step-content">
|
|
14241
|
+
<div class="step-title">Start Accepting Patients</div>
|
|
14242
|
+
<div class="step-description">Begin managing appointments and growing your practice.</div>
|
|
14243
|
+
</div>
|
|
14244
|
+
</div>
|
|
14245
|
+
</div>
|
|
14246
|
+
|
|
14247
|
+
<div class="button-container">
|
|
14248
|
+
<a href="{{dashboardUrl}}" class="button">Go to Dashboard</a>
|
|
14249
|
+
</div>
|
|
14250
|
+
|
|
14251
|
+
<div class="support-box">
|
|
14252
|
+
<p>Need help getting started?</p>
|
|
14253
|
+
<p>Contact our support team at <a href="mailto:{{supportEmail}}">{{supportEmail}}</a></p>
|
|
14254
|
+
</div>
|
|
14255
|
+
</div>
|
|
14256
|
+
<div class="footer">
|
|
14257
|
+
<p>This is an automated message from MetaEsthetics.</p>
|
|
14258
|
+
<p>© {{currentYear}} MetaEsthetics. All rights reserved.</p>
|
|
14259
|
+
<div class="social-links">
|
|
14260
|
+
<a href="https://metaesthetics.net">Website</a> |
|
|
14261
|
+
<a href="https://metaesthetics.net/privacy">Privacy Policy</a> |
|
|
14262
|
+
<a href="https://metaesthetics.net/terms">Terms of Service</a>
|
|
14263
|
+
</div>
|
|
14264
|
+
</div>
|
|
14265
|
+
</div>
|
|
14266
|
+
</body>
|
|
14267
|
+
</html>
|
|
14268
|
+
`;
|
|
14269
|
+
|
|
14270
|
+
// src/admin/mailing/clinicWelcome/clinicWelcome.mailing.ts
|
|
14271
|
+
var ClinicWelcomeMailingService = class extends BaseMailingService {
|
|
14272
|
+
/**
|
|
14273
|
+
* Constructor for ClinicWelcomeMailingService
|
|
14274
|
+
* @param firestore - Firestore instance provided by the caller
|
|
14275
|
+
* @param mailgunClient - Mailgun client instance (mailgun.js v10+) provided by the caller
|
|
14276
|
+
*/
|
|
14277
|
+
constructor(firestore19, mailgunClient) {
|
|
14278
|
+
super(firestore19, mailgunClient);
|
|
14279
|
+
this.DEFAULT_DASHBOARD_URL = "https://clinic.metaesthetics.net/dashboard";
|
|
14280
|
+
this.DEFAULT_SUPPORT_EMAIL = "support@metaesthetics.net";
|
|
14281
|
+
this.DEFAULT_SUBJECT = "Welcome to MetaEsthetics - Your Clinic Registration is Complete";
|
|
14282
|
+
this.DEFAULT_MAILGUN_DOMAIN = "mg.metaesthetics.net";
|
|
14283
|
+
}
|
|
14284
|
+
/**
|
|
14285
|
+
* Sends a clinic welcome email
|
|
14286
|
+
* @param data - The clinic welcome email data
|
|
14287
|
+
* @returns Promise resolved when email is sent
|
|
14288
|
+
*/
|
|
14289
|
+
async sendWelcomeEmail(data) {
|
|
14290
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
14291
|
+
try {
|
|
14292
|
+
Logger.info(
|
|
14293
|
+
"[ClinicWelcomeMailingService] Sending welcome email to",
|
|
14294
|
+
data.admin.email
|
|
14295
|
+
);
|
|
14296
|
+
const registrationDate = (/* @__PURE__ */ new Date()).toLocaleDateString("en-US", {
|
|
14297
|
+
weekday: "long",
|
|
14298
|
+
year: "numeric",
|
|
14299
|
+
month: "long",
|
|
14300
|
+
day: "numeric"
|
|
14301
|
+
});
|
|
14302
|
+
const dashboardUrl = ((_a = data.options) == null ? void 0 : _a.dashboardUrl) || this.DEFAULT_DASHBOARD_URL;
|
|
14303
|
+
const supportEmail = ((_b = data.options) == null ? void 0 : _b.supportEmail) || this.DEFAULT_SUPPORT_EMAIL;
|
|
14304
|
+
const subject = ((_c = data.options) == null ? void 0 : _c.customSubject) || this.DEFAULT_SUBJECT;
|
|
14305
|
+
const fromAddress = ((_d = data.options) == null ? void 0 : _d.fromAddress) || `MetaEsthetics <no-reply@${((_e = data.options) == null ? void 0 : _e.mailgunDomain) || this.DEFAULT_MAILGUN_DOMAIN}>`;
|
|
14306
|
+
const currentYear = (/* @__PURE__ */ new Date()).getFullYear().toString();
|
|
14307
|
+
const templateVariables = {
|
|
14308
|
+
adminName: data.admin.name,
|
|
14309
|
+
adminEmail: data.admin.email,
|
|
14310
|
+
clinicGroupName: data.clinicGroup.name,
|
|
14311
|
+
registrationDate,
|
|
14312
|
+
dashboardUrl,
|
|
14313
|
+
supportEmail,
|
|
14314
|
+
currentYear
|
|
14315
|
+
};
|
|
14316
|
+
Logger.info("[ClinicWelcomeMailingService] Template variables:", {
|
|
14317
|
+
adminName: templateVariables.adminName,
|
|
14318
|
+
adminEmail: templateVariables.adminEmail,
|
|
14319
|
+
clinicGroupName: templateVariables.clinicGroupName,
|
|
14320
|
+
dashboardUrl: templateVariables.dashboardUrl
|
|
14321
|
+
});
|
|
14322
|
+
const html = this.renderTemplate(clinicWelcomeTemplate, templateVariables);
|
|
14323
|
+
const mailgunSendData = {
|
|
14324
|
+
to: data.admin.email,
|
|
14325
|
+
from: fromAddress,
|
|
14326
|
+
subject,
|
|
14327
|
+
html
|
|
14328
|
+
};
|
|
14329
|
+
const domainToSendFrom = ((_f = data.options) == null ? void 0 : _f.mailgunDomain) || this.DEFAULT_MAILGUN_DOMAIN;
|
|
14330
|
+
Logger.info("[ClinicWelcomeMailingService] Sending email with data:", {
|
|
14331
|
+
domain: domainToSendFrom,
|
|
14332
|
+
to: mailgunSendData.to,
|
|
14333
|
+
from: mailgunSendData.from,
|
|
14334
|
+
subject: mailgunSendData.subject,
|
|
14335
|
+
hasHtml: !!mailgunSendData.html
|
|
14336
|
+
});
|
|
14337
|
+
const result = await this.sendEmail(domainToSendFrom, mailgunSendData);
|
|
14338
|
+
await this.logEmailAttempt(
|
|
14339
|
+
{
|
|
14340
|
+
to: data.admin.email,
|
|
14341
|
+
subject,
|
|
14342
|
+
templateName: "clinic_welcome"
|
|
14343
|
+
},
|
|
14344
|
+
true
|
|
14345
|
+
);
|
|
14346
|
+
return result;
|
|
14347
|
+
} catch (error) {
|
|
14348
|
+
Logger.error(
|
|
14349
|
+
"[ClinicWelcomeMailingService] Error sending welcome email:",
|
|
14350
|
+
{
|
|
14351
|
+
errorMessage: error.message,
|
|
14352
|
+
errorDetails: error.details,
|
|
14353
|
+
errorStatus: error.status,
|
|
14354
|
+
stack: error.stack
|
|
14355
|
+
}
|
|
14356
|
+
);
|
|
14357
|
+
await this.logEmailAttempt(
|
|
14358
|
+
{
|
|
14359
|
+
to: data.admin.email,
|
|
14360
|
+
subject: ((_g = data.options) == null ? void 0 : _g.customSubject) || this.DEFAULT_SUBJECT,
|
|
14361
|
+
templateName: "clinic_welcome"
|
|
14362
|
+
},
|
|
14363
|
+
false,
|
|
14364
|
+
error
|
|
14365
|
+
);
|
|
14366
|
+
throw error;
|
|
14367
|
+
}
|
|
14368
|
+
}
|
|
14369
|
+
/**
|
|
14370
|
+
* Handles the clinic group creation event from Cloud Functions.
|
|
14371
|
+
* Fetches necessary data and sends the welcome email to the admin.
|
|
14372
|
+
* @param clinicGroupData - The clinic group data including id
|
|
14373
|
+
* @param mailgunConfig - Mailgun configuration (from, domain)
|
|
14374
|
+
* @returns Promise resolved when the email is sent
|
|
14375
|
+
*/
|
|
14376
|
+
async handleClinicGroupCreationEvent(clinicGroupData, mailgunConfig) {
|
|
14377
|
+
try {
|
|
14378
|
+
Logger.info(
|
|
14379
|
+
"[ClinicWelcomeMailingService] Handling clinic group creation event for:",
|
|
14380
|
+
clinicGroupData.id
|
|
14381
|
+
);
|
|
14382
|
+
if (!clinicGroupData || !clinicGroupData.id) {
|
|
14383
|
+
throw new Error(
|
|
14384
|
+
`Invalid clinic group data: Missing required properties.`
|
|
14385
|
+
);
|
|
14386
|
+
}
|
|
14387
|
+
if (!clinicGroupData.contactPerson) {
|
|
14388
|
+
throw new Error(
|
|
14389
|
+
`Clinic group ${clinicGroupData.id} is missing contactPerson`
|
|
14390
|
+
);
|
|
14391
|
+
}
|
|
14392
|
+
if (!clinicGroupData.contactPerson.email) {
|
|
14393
|
+
throw new Error(
|
|
14394
|
+
`Clinic group ${clinicGroupData.id} is missing admin email`
|
|
14395
|
+
);
|
|
14396
|
+
}
|
|
14397
|
+
const adminName = `${clinicGroupData.contactPerson.firstName || ""} ${clinicGroupData.contactPerson.lastName || ""}`.trim() || "Clinic Admin";
|
|
14398
|
+
const adminEmail = clinicGroupData.contactPerson.email;
|
|
14399
|
+
Logger.info(
|
|
14400
|
+
`[ClinicWelcomeMailingService] Sending welcome email to admin: ${adminName} (${adminEmail})`
|
|
14401
|
+
);
|
|
14402
|
+
if (!mailgunConfig.fromAddress) {
|
|
14403
|
+
Logger.warn(
|
|
14404
|
+
"[ClinicWelcomeMailingService] No fromAddress provided, using default"
|
|
14405
|
+
);
|
|
14406
|
+
mailgunConfig.fromAddress = `MetaEsthetics <no-reply@${this.DEFAULT_MAILGUN_DOMAIN}>`;
|
|
14407
|
+
}
|
|
14408
|
+
const emailData = {
|
|
14409
|
+
admin: {
|
|
14410
|
+
name: adminName,
|
|
14411
|
+
email: adminEmail
|
|
14412
|
+
},
|
|
14413
|
+
clinicGroup: {
|
|
14414
|
+
id: clinicGroupData.id,
|
|
14415
|
+
name: clinicGroupData.name || "Your Clinic"
|
|
14416
|
+
},
|
|
14417
|
+
options: {
|
|
14418
|
+
fromAddress: mailgunConfig.fromAddress,
|
|
14419
|
+
mailgunDomain: mailgunConfig.domain,
|
|
14420
|
+
dashboardUrl: mailgunConfig.dashboardUrl,
|
|
14421
|
+
supportEmail: mailgunConfig.supportEmail
|
|
14422
|
+
}
|
|
14423
|
+
};
|
|
14424
|
+
Logger.info(
|
|
14425
|
+
"[ClinicWelcomeMailingService] Email data prepared, sending welcome email"
|
|
14426
|
+
);
|
|
14427
|
+
await this.sendWelcomeEmail(emailData);
|
|
14428
|
+
Logger.info(
|
|
14429
|
+
"[ClinicWelcomeMailingService] Welcome email sent successfully"
|
|
14430
|
+
);
|
|
14431
|
+
} catch (error) {
|
|
14432
|
+
Logger.error(
|
|
14433
|
+
"[ClinicWelcomeMailingService] Error handling clinic group creation event:",
|
|
14434
|
+
{
|
|
14435
|
+
errorMessage: error.message,
|
|
14436
|
+
errorDetails: error.details,
|
|
14437
|
+
errorStatus: error.status,
|
|
14438
|
+
stack: error.stack,
|
|
14439
|
+
clinicGroupId: clinicGroupData == null ? void 0 : clinicGroupData.id
|
|
14440
|
+
}
|
|
14441
|
+
);
|
|
14442
|
+
throw error;
|
|
14443
|
+
}
|
|
14444
|
+
}
|
|
14445
|
+
};
|
|
14446
|
+
|
|
14015
14447
|
// src/admin/users/user-profile.admin.ts
|
|
14016
14448
|
import * as admin18 from "firebase-admin";
|
|
14017
14449
|
var UserProfileAdminService = class {
|
|
@@ -14313,6 +14745,7 @@ export {
|
|
|
14313
14745
|
CLINIC_ANALYTICS_SUBCOLLECTION,
|
|
14314
14746
|
CalendarAdminService,
|
|
14315
14747
|
ClinicAggregationService,
|
|
14748
|
+
ClinicWelcomeMailingService,
|
|
14316
14749
|
DASHBOARD_ANALYTICS_SUBCOLLECTION,
|
|
14317
14750
|
DocumentManagerAdminService,
|
|
14318
14751
|
ExistingPractitionerInviteMailingService,
|
package/package.json
CHANGED
|
@@ -41,6 +41,23 @@ import { NotificationType } from '../../../types/notifications';
|
|
|
41
41
|
|
|
42
42
|
// Mailgun client will be injected via constructor
|
|
43
43
|
|
|
44
|
+
/**
|
|
45
|
+
* Helper to sanitize cancellation reason - filters out ID-like strings
|
|
46
|
+
* that were mistakenly passed as reasons (e.g., clinicId)
|
|
47
|
+
*/
|
|
48
|
+
function sanitizeCancellationReason(reason: string | null | undefined): string | undefined {
|
|
49
|
+
if (!reason) return undefined;
|
|
50
|
+
|
|
51
|
+
// Detect if the reason looks like an ID rather than actual text
|
|
52
|
+
// IDs typically: no spaces, alphanumeric with dashes/underscores, length > 15
|
|
53
|
+
const isLikelyId =
|
|
54
|
+
!reason.includes(' ') &&
|
|
55
|
+
/^[a-zA-Z0-9_-]+$/.test(reason) &&
|
|
56
|
+
reason.length > 15;
|
|
57
|
+
|
|
58
|
+
return isLikelyId ? undefined : reason;
|
|
59
|
+
}
|
|
60
|
+
|
|
44
61
|
/**
|
|
45
62
|
* Type for requirement with source procedure tracking
|
|
46
63
|
*/
|
|
@@ -408,7 +425,7 @@ export class AppointmentAggregationService {
|
|
|
408
425
|
appointment: after,
|
|
409
426
|
recipientProfile: after.patientInfo,
|
|
410
427
|
recipientRole: 'patient' as const,
|
|
411
|
-
cancellationReason: after.cancellationReason,
|
|
428
|
+
cancellationReason: sanitizeCancellationReason(after.cancellationReason),
|
|
412
429
|
};
|
|
413
430
|
await this.appointmentMailingService.sendAppointmentCancelledEmail(
|
|
414
431
|
patientCancellationData as any,
|
|
@@ -424,7 +441,7 @@ export class AppointmentAggregationService {
|
|
|
424
441
|
appointment: after,
|
|
425
442
|
recipientProfile: after.practitionerInfo,
|
|
426
443
|
recipientRole: 'practitioner' as const,
|
|
427
|
-
cancellationReason: after.cancellationReason,
|
|
444
|
+
cancellationReason: sanitizeCancellationReason(after.cancellationReason),
|
|
428
445
|
};
|
|
429
446
|
await this.appointmentMailingService.sendAppointmentCancelledEmail(
|
|
430
447
|
practitionerCancellationData as any,
|
package/src/admin/index.ts
CHANGED
|
@@ -10,7 +10,7 @@ export type {
|
|
|
10
10
|
} from '../types/notifications';
|
|
11
11
|
export type { PatientProfile, PatientSensitiveInfo } from '../types/patient';
|
|
12
12
|
export { PATIENTS_COLLECTION, PATIENT_SENSITIVE_INFO_COLLECTION } from '../types/patient';
|
|
13
|
-
export type { Clinic, ClinicLocation } from '../types/clinic';
|
|
13
|
+
export type { Clinic, ClinicLocation, ClinicGroup } from '../types/clinic';
|
|
14
14
|
export type { ClinicInfo } from '../types/profile';
|
|
15
15
|
export type { Practitioner, PractitionerToken } from '../types/practitioner';
|
|
16
16
|
export type { PractitionerInvite } from '../types/clinic/practitioner-invite.types';
|