@blackcode_sa/metaestetics-api 1.6.14 → 1.6.16
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 +343 -2
- package/dist/admin/index.d.ts +343 -2
- package/dist/admin/index.js +390 -217
- package/dist/admin/index.mjs +382 -217
- package/package.json +1 -1
- package/src/admin/aggregation/appointment/appointment.aggregation.service.ts +2 -2
- package/src/admin/booking/booking.admin.ts +8 -4
- package/src/admin/calendar/calendar.admin.service.ts +8 -11
- package/src/admin/index.ts +21 -0
- package/src/admin/notifications/notifications.admin.ts +152 -58
- package/src/admin/requirements/patient-requirements.admin.service.ts +1 -1
- package/src/utils/TimestampUtils.ts +122 -17
package/dist/admin/index.js
CHANGED
|
@@ -30,11 +30,18 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/admin/index.ts
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
|
+
APPOINTMENTS_COLLECTION: () => APPOINTMENTS_COLLECTION,
|
|
33
34
|
AppointmentAggregationService: () => AppointmentAggregationService,
|
|
35
|
+
AppointmentMailingService: () => AppointmentMailingService,
|
|
34
36
|
AppointmentStatus: () => AppointmentStatus,
|
|
35
37
|
BaseMailingService: () => BaseMailingService,
|
|
36
38
|
BookingAdmin: () => BookingAdmin,
|
|
39
|
+
BookingAvailabilityCalculator: () => BookingAvailabilityCalculator,
|
|
40
|
+
CalendarAdminService: () => CalendarAdminService,
|
|
37
41
|
ClinicAggregationService: () => ClinicAggregationService,
|
|
42
|
+
DocumentManagerAdminService: () => DocumentManagerAdminService,
|
|
43
|
+
Logger: () => Logger,
|
|
44
|
+
MediaType: () => MediaType,
|
|
38
45
|
NOTIFICATIONS_COLLECTION: () => NOTIFICATIONS_COLLECTION,
|
|
39
46
|
NotificationStatus: () => NotificationStatus,
|
|
40
47
|
NotificationType: () => NotificationType,
|
|
@@ -44,6 +51,7 @@ __export(index_exports, {
|
|
|
44
51
|
PatientInstructionStatus: () => PatientInstructionStatus,
|
|
45
52
|
PatientRequirementOverallStatus: () => PatientRequirementOverallStatus,
|
|
46
53
|
PatientRequirementsAdminService: () => PatientRequirementsAdminService,
|
|
54
|
+
PaymentStatus: () => PaymentStatus,
|
|
47
55
|
PractitionerAggregationService: () => PractitionerAggregationService,
|
|
48
56
|
PractitionerInviteMailingService: () => PractitionerInviteMailingService,
|
|
49
57
|
PractitionerTokenStatus: () => PractitionerTokenStatus,
|
|
@@ -84,7 +92,7 @@ var NotificationStatus = /* @__PURE__ */ ((NotificationStatus2) => {
|
|
|
84
92
|
})(NotificationStatus || {});
|
|
85
93
|
|
|
86
94
|
// src/admin/notifications/notifications.admin.ts
|
|
87
|
-
var
|
|
95
|
+
var admin = __toESM(require("firebase-admin"));
|
|
88
96
|
var import_expo_server_sdk = require("expo-server-sdk");
|
|
89
97
|
|
|
90
98
|
// src/types/appointment/index.ts
|
|
@@ -101,6 +109,21 @@ var AppointmentStatus = /* @__PURE__ */ ((AppointmentStatus2) => {
|
|
|
101
109
|
AppointmentStatus2["RESCHEDULED_BY_CLINIC"] = "rescheduled_by_clinic";
|
|
102
110
|
return AppointmentStatus2;
|
|
103
111
|
})(AppointmentStatus || {});
|
|
112
|
+
var PaymentStatus = /* @__PURE__ */ ((PaymentStatus2) => {
|
|
113
|
+
PaymentStatus2["UNPAID"] = "unpaid";
|
|
114
|
+
PaymentStatus2["PAID"] = "paid";
|
|
115
|
+
PaymentStatus2["PARTIALLY_PAID"] = "partially_paid";
|
|
116
|
+
PaymentStatus2["REFUNDED"] = "refunded";
|
|
117
|
+
PaymentStatus2["NOT_APPLICABLE"] = "not_applicable";
|
|
118
|
+
return PaymentStatus2;
|
|
119
|
+
})(PaymentStatus || {});
|
|
120
|
+
var MediaType = /* @__PURE__ */ ((MediaType2) => {
|
|
121
|
+
MediaType2["BEFORE_PHOTO"] = "before_photo";
|
|
122
|
+
MediaType2["AFTER_PHOTO"] = "after_photo";
|
|
123
|
+
MediaType2["CONSENT_SCAN"] = "consent_scan";
|
|
124
|
+
MediaType2["OTHER_DOCUMENT"] = "other_document";
|
|
125
|
+
return MediaType2;
|
|
126
|
+
})(MediaType || {});
|
|
104
127
|
var APPOINTMENTS_COLLECTION = "appointments";
|
|
105
128
|
|
|
106
129
|
// src/types/documentation-templates/index.ts
|
|
@@ -119,109 +142,61 @@ var UserRole = /* @__PURE__ */ ((UserRole2) => {
|
|
|
119
142
|
return UserRole2;
|
|
120
143
|
})(UserRole || {});
|
|
121
144
|
|
|
122
|
-
// src/
|
|
123
|
-
var
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
*/
|
|
131
|
-
static adminToClient(adminTimestamp) {
|
|
132
|
-
if (!adminTimestamp) return null;
|
|
133
|
-
return new import_firestore.Timestamp(
|
|
134
|
-
adminTimestamp.seconds,
|
|
135
|
-
adminTimestamp.nanoseconds
|
|
136
|
-
);
|
|
137
|
-
}
|
|
138
|
-
/**
|
|
139
|
-
* Converts a client Firestore Timestamp to an admin Firestore Timestamp
|
|
140
|
-
* @param clientTimestamp - Client SDK Timestamp (from firebase/firestore)
|
|
141
|
-
* @returns An admin SDK Timestamp (from firebase-admin) with same seconds/nanoseconds or null if input is null
|
|
142
|
-
*/
|
|
143
|
-
static clientToAdmin(clientTimestamp) {
|
|
144
|
-
if (!clientTimestamp) return null;
|
|
145
|
-
return new admin.firestore.Timestamp(
|
|
146
|
-
clientTimestamp.seconds,
|
|
147
|
-
clientTimestamp.nanoseconds
|
|
148
|
-
);
|
|
149
|
-
}
|
|
150
|
-
/**
|
|
151
|
-
* Creates a client-compatible timestamp for the current time
|
|
152
|
-
* @returns A client SDK Timestamp (from firebase/firestore) for the current time
|
|
153
|
-
*/
|
|
154
|
-
static nowAsClient() {
|
|
155
|
-
const now = admin.firestore.Timestamp.now();
|
|
156
|
-
return this.adminToClient(now);
|
|
157
|
-
}
|
|
145
|
+
// src/admin/logger/index.ts
|
|
146
|
+
var firebaseFunctionsLogger;
|
|
147
|
+
try {
|
|
148
|
+
firebaseFunctionsLogger = require("firebase-functions/logger");
|
|
149
|
+
require("firebase-functions/logger/compat");
|
|
150
|
+
} catch (e) {
|
|
151
|
+
}
|
|
152
|
+
var Logger = class {
|
|
158
153
|
/**
|
|
159
|
-
*
|
|
160
|
-
* @param
|
|
161
|
-
* @
|
|
154
|
+
* Log an error message
|
|
155
|
+
* @param message Message to log
|
|
156
|
+
* @param data Optional data to include
|
|
162
157
|
*/
|
|
163
|
-
static
|
|
164
|
-
if (
|
|
165
|
-
|
|
158
|
+
static error(message, data) {
|
|
159
|
+
if (firebaseFunctionsLogger) {
|
|
160
|
+
firebaseFunctionsLogger.error(message, data);
|
|
161
|
+
} else {
|
|
162
|
+
console.error(message, data !== void 0 ? data : "");
|
|
163
|
+
}
|
|
166
164
|
}
|
|
167
165
|
/**
|
|
168
|
-
*
|
|
169
|
-
* @param
|
|
170
|
-
* @
|
|
166
|
+
* Log a warning message
|
|
167
|
+
* @param message Message to log
|
|
168
|
+
* @param data Optional data to include
|
|
171
169
|
*/
|
|
172
|
-
static
|
|
173
|
-
if (
|
|
174
|
-
|
|
170
|
+
static warn(message, data) {
|
|
171
|
+
if (firebaseFunctionsLogger) {
|
|
172
|
+
firebaseFunctionsLogger.warn(message, data);
|
|
173
|
+
} else {
|
|
174
|
+
console.warn(message, data !== void 0 ? data : "");
|
|
175
|
+
}
|
|
175
176
|
}
|
|
176
177
|
/**
|
|
177
|
-
*
|
|
178
|
-
* @param
|
|
179
|
-
* @
|
|
178
|
+
* Log an info message
|
|
179
|
+
* @param message Message to log
|
|
180
|
+
* @param data Optional data to include
|
|
180
181
|
*/
|
|
181
|
-
static
|
|
182
|
-
if (
|
|
183
|
-
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
return this.adminToClient(obj);
|
|
187
|
-
}
|
|
188
|
-
if (Array.isArray(obj)) {
|
|
189
|
-
return obj.map(
|
|
190
|
-
(item) => this.convertObjectTimestampsAdminToClient(item)
|
|
191
|
-
);
|
|
192
|
-
}
|
|
193
|
-
const result = { ...obj };
|
|
194
|
-
for (const key in result) {
|
|
195
|
-
if (Object.prototype.hasOwnProperty.call(result, key)) {
|
|
196
|
-
result[key] = this.convertObjectTimestampsAdminToClient(result[key]);
|
|
197
|
-
}
|
|
182
|
+
static info(message, data) {
|
|
183
|
+
if (firebaseFunctionsLogger) {
|
|
184
|
+
firebaseFunctionsLogger.info(message, data);
|
|
185
|
+
} else {
|
|
186
|
+
console.info(message, data !== void 0 ? data : "");
|
|
198
187
|
}
|
|
199
|
-
return result;
|
|
200
188
|
}
|
|
201
189
|
/**
|
|
202
|
-
*
|
|
203
|
-
* @param
|
|
204
|
-
* @
|
|
190
|
+
* Log a debug message
|
|
191
|
+
* @param message Message to log
|
|
192
|
+
* @param data Optional data to include
|
|
205
193
|
*/
|
|
206
|
-
static
|
|
207
|
-
if (
|
|
208
|
-
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
return this.clientToAdmin(obj);
|
|
212
|
-
}
|
|
213
|
-
if (Array.isArray(obj)) {
|
|
214
|
-
return obj.map(
|
|
215
|
-
(item) => this.convertObjectTimestampsClientToAdmin(item)
|
|
216
|
-
);
|
|
217
|
-
}
|
|
218
|
-
const result = { ...obj };
|
|
219
|
-
for (const key in result) {
|
|
220
|
-
if (Object.prototype.hasOwnProperty.call(result, key)) {
|
|
221
|
-
result[key] = this.convertObjectTimestampsClientToAdmin(result[key]);
|
|
222
|
-
}
|
|
194
|
+
static debug(message, data) {
|
|
195
|
+
if (firebaseFunctionsLogger) {
|
|
196
|
+
firebaseFunctionsLogger.debug(message, data);
|
|
197
|
+
} else {
|
|
198
|
+
console.debug(message, data !== void 0 ? data : "");
|
|
223
199
|
}
|
|
224
|
-
return result;
|
|
225
200
|
}
|
|
226
201
|
};
|
|
227
202
|
|
|
@@ -229,7 +204,7 @@ var TimestampUtils = class {
|
|
|
229
204
|
var NotificationsAdmin = class {
|
|
230
205
|
constructor(firestore12) {
|
|
231
206
|
this.expo = new import_expo_server_sdk.Expo();
|
|
232
|
-
this.db = firestore12 ||
|
|
207
|
+
this.db = firestore12 || admin.firestore();
|
|
233
208
|
}
|
|
234
209
|
/**
|
|
235
210
|
* Dohvata notifikaciju po ID-u
|
|
@@ -245,8 +220,8 @@ var NotificationsAdmin = class {
|
|
|
245
220
|
const docRef = await this.db.collection("notifications").add({
|
|
246
221
|
...notification,
|
|
247
222
|
status: "pending" /* PENDING */,
|
|
248
|
-
createdAt:
|
|
249
|
-
updatedAt:
|
|
223
|
+
createdAt: admin.firestore.FieldValue.serverTimestamp(),
|
|
224
|
+
updatedAt: admin.firestore.FieldValue.serverTimestamp()
|
|
250
225
|
});
|
|
251
226
|
return docRef.id;
|
|
252
227
|
}
|
|
@@ -254,7 +229,18 @@ var NotificationsAdmin = class {
|
|
|
254
229
|
* Priprema Expo poruku za slanje
|
|
255
230
|
*/
|
|
256
231
|
prepareExpoMessage(notification) {
|
|
257
|
-
|
|
232
|
+
const validTokens = notification.notificationTokens.filter(
|
|
233
|
+
(token) => import_expo_server_sdk.Expo.isExpoPushToken(token)
|
|
234
|
+
);
|
|
235
|
+
Logger.info(
|
|
236
|
+
`[NotificationsAdmin] Preparing Expo messages for notification ${notification.id}`,
|
|
237
|
+
{
|
|
238
|
+
totalTokens: notification.notificationTokens.length,
|
|
239
|
+
validTokens: validTokens.length,
|
|
240
|
+
invalidTokensCount: notification.notificationTokens.length - validTokens.length
|
|
241
|
+
}
|
|
242
|
+
);
|
|
243
|
+
return validTokens.map((token) => ({
|
|
258
244
|
to: token,
|
|
259
245
|
sound: "default",
|
|
260
246
|
title: notification.title,
|
|
@@ -272,10 +258,10 @@ var NotificationsAdmin = class {
|
|
|
272
258
|
async updateNotificationStatus(notificationId, status, error) {
|
|
273
259
|
const update = {
|
|
274
260
|
status,
|
|
275
|
-
updatedAt:
|
|
261
|
+
updatedAt: admin.firestore.FieldValue.serverTimestamp()
|
|
276
262
|
};
|
|
277
263
|
if (status === "sent" /* SENT */) {
|
|
278
|
-
update.sentAt =
|
|
264
|
+
update.sentAt = admin.firestore.FieldValue.serverTimestamp();
|
|
279
265
|
}
|
|
280
266
|
if (error) {
|
|
281
267
|
update.error = error;
|
|
@@ -286,45 +272,94 @@ var NotificationsAdmin = class {
|
|
|
286
272
|
* Šalje notifikaciju kroz Expo servis sa boljim error handlingom
|
|
287
273
|
*/
|
|
288
274
|
async sendPushNotification(notification) {
|
|
275
|
+
var _a;
|
|
289
276
|
try {
|
|
277
|
+
Logger.info(
|
|
278
|
+
`[NotificationsAdmin] Processing notification ${notification.id} for sending`,
|
|
279
|
+
{
|
|
280
|
+
userId: notification.userId,
|
|
281
|
+
tokenCount: ((_a = notification.notificationTokens) == null ? void 0 : _a.length) || 0,
|
|
282
|
+
type: notification.notificationType
|
|
283
|
+
}
|
|
284
|
+
);
|
|
290
285
|
const messages = this.prepareExpoMessage(notification);
|
|
291
286
|
if (messages.length === 0) {
|
|
287
|
+
const errorMsg = "No valid notification tokens found";
|
|
288
|
+
Logger.error(
|
|
289
|
+
`[NotificationsAdmin] ${errorMsg} for notification ${notification.id}`
|
|
290
|
+
);
|
|
292
291
|
await this.updateNotificationStatus(
|
|
293
292
|
notification.id,
|
|
294
293
|
"failed" /* FAILED */,
|
|
295
|
-
|
|
294
|
+
errorMsg
|
|
296
295
|
);
|
|
297
296
|
return false;
|
|
298
297
|
}
|
|
299
298
|
const chunks = this.expo.chunkPushNotifications(messages);
|
|
299
|
+
Logger.info(
|
|
300
|
+
`[NotificationsAdmin] Sending ${messages.length} messages in ${chunks.length} chunks for notification ${notification.id}`
|
|
301
|
+
);
|
|
300
302
|
const tickets = [];
|
|
301
|
-
for (
|
|
303
|
+
for (let i = 0; i < chunks.length; i++) {
|
|
304
|
+
const chunk = chunks[i];
|
|
302
305
|
try {
|
|
306
|
+
Logger.info(
|
|
307
|
+
`[NotificationsAdmin] Sending chunk ${i + 1}/${chunks.length} with ${chunk.length} messages`
|
|
308
|
+
);
|
|
303
309
|
const ticketChunk = await this.expo.sendPushNotificationsAsync(chunk);
|
|
310
|
+
Logger.info(
|
|
311
|
+
`[NotificationsAdmin] Received ${ticketChunk.length} tickets for chunk ${i + 1}`
|
|
312
|
+
);
|
|
304
313
|
tickets.push(ticketChunk);
|
|
305
314
|
} catch (error) {
|
|
306
|
-
|
|
315
|
+
Logger.error(
|
|
316
|
+
`[NotificationsAdmin] Chunk ${i + 1} sending error:`,
|
|
317
|
+
error
|
|
318
|
+
);
|
|
307
319
|
throw error;
|
|
308
320
|
}
|
|
309
321
|
}
|
|
310
322
|
let hasErrors = false;
|
|
311
323
|
const errors = [];
|
|
312
|
-
tickets.flat()
|
|
324
|
+
const ticketsFlat = tickets.flat();
|
|
325
|
+
const ticketResults = {
|
|
326
|
+
total: ticketsFlat.length,
|
|
327
|
+
success: 0,
|
|
328
|
+
error: 0,
|
|
329
|
+
errorDetails: {}
|
|
330
|
+
};
|
|
331
|
+
ticketsFlat.forEach((ticket, index) => {
|
|
313
332
|
if (ticket.status === "error") {
|
|
314
333
|
hasErrors = true;
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
);
|
|
334
|
+
ticketResults.error++;
|
|
335
|
+
const errorMessage = ticket.message || "Unknown error";
|
|
336
|
+
ticketResults.errorDetails[errorMessage] = (ticketResults.errorDetails[errorMessage] || 0) + 1;
|
|
337
|
+
const tokenInfo = index < notification.notificationTokens.length ? `Token ${notification.notificationTokens[index]}` : `Token at index ${index}`;
|
|
338
|
+
errors.push(`${tokenInfo}: ${errorMessage}`);
|
|
339
|
+
} else {
|
|
340
|
+
ticketResults.success++;
|
|
318
341
|
}
|
|
319
342
|
});
|
|
343
|
+
Logger.info(
|
|
344
|
+
`[NotificationsAdmin] Ticket results for notification ${notification.id}`,
|
|
345
|
+
ticketResults
|
|
346
|
+
);
|
|
320
347
|
if (hasErrors) {
|
|
348
|
+
const errorSummary = errors.join("; ");
|
|
349
|
+
Logger.warn(
|
|
350
|
+
`[NotificationsAdmin] Partial success or errors in notification ${notification.id}`,
|
|
351
|
+
{ errorCount: errors.length, errorSummary }
|
|
352
|
+
);
|
|
321
353
|
await this.updateNotificationStatus(
|
|
322
354
|
notification.id,
|
|
323
|
-
"partialSuccess" /* PARTIAL_SUCCESS */,
|
|
324
|
-
|
|
355
|
+
ticketResults.success > 0 ? "partialSuccess" /* PARTIAL_SUCCESS */ : "failed" /* FAILED */,
|
|
356
|
+
errorSummary
|
|
325
357
|
);
|
|
326
|
-
return
|
|
358
|
+
return ticketResults.success > 0;
|
|
327
359
|
}
|
|
360
|
+
Logger.info(
|
|
361
|
+
`[NotificationsAdmin] Successfully sent notification ${notification.id} to all recipients`
|
|
362
|
+
);
|
|
328
363
|
await this.updateNotificationStatus(
|
|
329
364
|
notification.id,
|
|
330
365
|
"sent" /* SENT */
|
|
@@ -332,7 +367,10 @@ var NotificationsAdmin = class {
|
|
|
332
367
|
return true;
|
|
333
368
|
} catch (error) {
|
|
334
369
|
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
335
|
-
|
|
370
|
+
Logger.error(
|
|
371
|
+
`[NotificationsAdmin] Critical error sending notification ${notification.id}:`,
|
|
372
|
+
error
|
|
373
|
+
);
|
|
336
374
|
await this.updateNotificationStatus(
|
|
337
375
|
notification.id,
|
|
338
376
|
"failed" /* FAILED */,
|
|
@@ -345,14 +383,29 @@ var NotificationsAdmin = class {
|
|
|
345
383
|
* Procesira notifikacije koje čekaju na slanje sa batch limitom
|
|
346
384
|
*/
|
|
347
385
|
async processPendingNotifications(batchSize = 100) {
|
|
348
|
-
const now =
|
|
386
|
+
const now = admin.firestore.Timestamp.now();
|
|
387
|
+
Logger.info(
|
|
388
|
+
`[NotificationsAdmin] Starting to process pending notifications with batch size ${batchSize}`
|
|
389
|
+
);
|
|
349
390
|
const pendingNotifications = await this.db.collection("notifications").where("status", "==", "pending" /* PENDING */).where("notificationTime", "<=", now).limit(batchSize).get();
|
|
391
|
+
Logger.info(
|
|
392
|
+
`[NotificationsAdmin] Found ${pendingNotifications.size} pending notifications to process`
|
|
393
|
+
);
|
|
394
|
+
if (pendingNotifications.empty) {
|
|
395
|
+
Logger.info(
|
|
396
|
+
"[NotificationsAdmin] No pending notifications found to process"
|
|
397
|
+
);
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
350
400
|
const results = await Promise.allSettled(
|
|
351
401
|
pendingNotifications.docs.map(async (doc) => {
|
|
352
402
|
const notification = {
|
|
353
403
|
id: doc.id,
|
|
354
404
|
...doc.data()
|
|
355
405
|
};
|
|
406
|
+
Logger.info(
|
|
407
|
+
`[NotificationsAdmin] Processing notification ${notification.id} of type ${notification.notificationType}`
|
|
408
|
+
);
|
|
356
409
|
return this.sendPushNotification(notification);
|
|
357
410
|
})
|
|
358
411
|
);
|
|
@@ -362,8 +415,8 @@ var NotificationsAdmin = class {
|
|
|
362
415
|
const failed = results.filter(
|
|
363
416
|
(r) => r.status === "rejected" || r.status === "fulfilled" && !r.value
|
|
364
417
|
).length;
|
|
365
|
-
|
|
366
|
-
`Processed ${results.length} notifications: ${successful} successful, ${failed} failed`
|
|
418
|
+
Logger.info(
|
|
419
|
+
`[NotificationsAdmin] Processed ${results.length} notifications: ${successful} successful, ${failed} failed`
|
|
367
420
|
);
|
|
368
421
|
}
|
|
369
422
|
/**
|
|
@@ -372,13 +425,20 @@ var NotificationsAdmin = class {
|
|
|
372
425
|
async cleanupOldNotifications(daysOld = 30, batchSize = 500) {
|
|
373
426
|
const cutoffDate = /* @__PURE__ */ new Date();
|
|
374
427
|
cutoffDate.setDate(cutoffDate.getDate() - daysOld);
|
|
428
|
+
Logger.info(
|
|
429
|
+
`[NotificationsAdmin] Starting cleanup of notifications older than ${daysOld} days`
|
|
430
|
+
);
|
|
431
|
+
let totalDeleted = 0;
|
|
375
432
|
while (true) {
|
|
376
433
|
const oldNotifications = await this.db.collection("notifications").where(
|
|
377
434
|
"createdAt",
|
|
378
435
|
"<=",
|
|
379
|
-
|
|
436
|
+
admin.firestore.Timestamp.fromDate(cutoffDate)
|
|
380
437
|
).limit(batchSize).get();
|
|
381
438
|
if (oldNotifications.empty) {
|
|
439
|
+
Logger.info(
|
|
440
|
+
`[NotificationsAdmin] No more old notifications to delete. Total deleted: ${totalDeleted}`
|
|
441
|
+
);
|
|
382
442
|
break;
|
|
383
443
|
}
|
|
384
444
|
const batch = this.db.batch();
|
|
@@ -386,8 +446,9 @@ var NotificationsAdmin = class {
|
|
|
386
446
|
batch.delete(doc.ref);
|
|
387
447
|
});
|
|
388
448
|
await batch.commit();
|
|
389
|
-
|
|
390
|
-
|
|
449
|
+
totalDeleted += oldNotifications.size;
|
|
450
|
+
Logger.info(
|
|
451
|
+
`[NotificationsAdmin] Deleted batch of ${oldNotifications.size} old notifications. Running total: ${totalDeleted}`
|
|
391
452
|
);
|
|
392
453
|
}
|
|
393
454
|
}
|
|
@@ -418,15 +479,12 @@ var NotificationsAdmin = class {
|
|
|
418
479
|
title = "New Appointment Confirmed";
|
|
419
480
|
body = `Appointment for ${appointment.procedureInfo.name} with ${appointment.patientInfo.fullName} on ${appointment.appointmentStartTime.toDate().toLocaleDateString()} is confirmed.`;
|
|
420
481
|
}
|
|
421
|
-
const
|
|
422
|
-
const clientCompatibleNotificationTime = TimestampUtils.adminToClient(
|
|
423
|
-
adminTsNow
|
|
424
|
-
);
|
|
482
|
+
const notificationTimestampForDb = admin.firestore.Timestamp.now();
|
|
425
483
|
const notificationData = {
|
|
426
484
|
userId: recipientUserId,
|
|
427
485
|
userRole: recipientRole,
|
|
428
486
|
notificationType: "appointmentStatusChange" /* APPOINTMENT_STATUS_CHANGE */,
|
|
429
|
-
notificationTime:
|
|
487
|
+
notificationTime: notificationTimestampForDb,
|
|
430
488
|
notificationTokens: recipientExpoTokens,
|
|
431
489
|
title,
|
|
432
490
|
body,
|
|
@@ -472,15 +530,12 @@ var NotificationsAdmin = class {
|
|
|
472
530
|
body += ` Reason: ${appointment.cancellationReason}`;
|
|
473
531
|
}
|
|
474
532
|
}
|
|
475
|
-
const
|
|
476
|
-
const clientCompatibleNotificationTime = TimestampUtils.adminToClient(
|
|
477
|
-
adminTsNow
|
|
478
|
-
);
|
|
533
|
+
const notificationTimestampForDb = admin.firestore.Timestamp.now();
|
|
479
534
|
const notificationData = {
|
|
480
535
|
userId: recipientUserId,
|
|
481
536
|
userRole: recipientRole,
|
|
482
537
|
notificationType: "appointmentCancelled" /* APPOINTMENT_CANCELLED */,
|
|
483
|
-
notificationTime:
|
|
538
|
+
notificationTime: notificationTimestampForDb,
|
|
484
539
|
notificationTokens: recipientExpoTokens,
|
|
485
540
|
title,
|
|
486
541
|
body,
|
|
@@ -511,15 +566,12 @@ var NotificationsAdmin = class {
|
|
|
511
566
|
}
|
|
512
567
|
const title = "Appointment Reschedule Proposed";
|
|
513
568
|
const body = `Action Required: A new time has been proposed for your appointment for ${appointment.procedureInfo.name}. Please review in the app.`;
|
|
514
|
-
const
|
|
515
|
-
const clientCompatibleNotificationTime = TimestampUtils.adminToClient(
|
|
516
|
-
adminTsNow
|
|
517
|
-
);
|
|
569
|
+
const notificationTimestampForDb = admin.firestore.Timestamp.now();
|
|
518
570
|
const notificationData = {
|
|
519
571
|
userId: patientUserId,
|
|
520
572
|
userRole: "patient" /* PATIENT */,
|
|
521
573
|
notificationType: "appointmentRescheduledProposal" /* APPOINTMENT_RESCHEDULED_PROPOSAL */,
|
|
522
|
-
notificationTime:
|
|
574
|
+
notificationTime: notificationTimestampForDb,
|
|
523
575
|
notificationTokens: patientExpoTokens,
|
|
524
576
|
title,
|
|
525
577
|
body,
|
|
@@ -550,16 +602,13 @@ var NotificationsAdmin = class {
|
|
|
550
602
|
}
|
|
551
603
|
const title = "Payment Updated";
|
|
552
604
|
const body = `Your payment status for the appointment (${appointment.procedureInfo.name}) on ${appointment.appointmentStartTime.toDate().toLocaleDateString()} is now ${appointment.paymentStatus}.`;
|
|
553
|
-
const
|
|
554
|
-
const clientCompatibleNotificationTime = TimestampUtils.adminToClient(
|
|
555
|
-
adminTsNow
|
|
556
|
-
);
|
|
605
|
+
const notificationTimestampForDb = admin.firestore.Timestamp.now();
|
|
557
606
|
const notificationType = appointment.paymentStatus === "paid" /* PAID */ ? "paymentConfirmation" /* PAYMENT_CONFIRMATION */ : "generalMessage" /* GENERAL_MESSAGE */;
|
|
558
607
|
const notificationData = {
|
|
559
608
|
userId: patientUserId,
|
|
560
609
|
userRole: "patient" /* PATIENT */,
|
|
561
610
|
notificationType,
|
|
562
|
-
notificationTime:
|
|
611
|
+
notificationTime: notificationTimestampForDb,
|
|
563
612
|
notificationTokens: patientExpoTokens,
|
|
564
613
|
title,
|
|
565
614
|
body,
|
|
@@ -590,15 +639,12 @@ var NotificationsAdmin = class {
|
|
|
590
639
|
}
|
|
591
640
|
const title = "Leave a Review";
|
|
592
641
|
const body = `How was your recent appointment for ${appointment.procedureInfo.name}? We'd love to hear your feedback!`;
|
|
593
|
-
const
|
|
594
|
-
const clientCompatibleNotificationTime = TimestampUtils.adminToClient(
|
|
595
|
-
adminTsNow
|
|
596
|
-
);
|
|
642
|
+
const notificationTimestampForDb = admin.firestore.Timestamp.now();
|
|
597
643
|
const notificationData = {
|
|
598
644
|
userId: patientUserId,
|
|
599
645
|
userRole: "patient" /* PATIENT */,
|
|
600
646
|
notificationType: "reviewRequest" /* REVIEW_REQUEST */,
|
|
601
|
-
notificationTime:
|
|
647
|
+
notificationTime: notificationTimestampForDb,
|
|
602
648
|
notificationTokens: patientExpoTokens,
|
|
603
649
|
title,
|
|
604
650
|
body,
|
|
@@ -635,16 +681,13 @@ var NotificationsAdmin = class {
|
|
|
635
681
|
}
|
|
636
682
|
const title = "New Review Received";
|
|
637
683
|
const body = `A new review has been added by ${appointment.patientInfo.fullName} for your appointment on ${appointment.appointmentStartTime.toDate().toLocaleDateString()}.`;
|
|
638
|
-
const
|
|
639
|
-
const clientCompatibleNotificationTime = TimestampUtils.adminToClient(
|
|
640
|
-
adminTsNow
|
|
641
|
-
);
|
|
684
|
+
const notificationTimestampForDb = admin.firestore.Timestamp.now();
|
|
642
685
|
const tempNotificationType = "generalMessage" /* GENERAL_MESSAGE */;
|
|
643
686
|
const notificationData = {
|
|
644
687
|
userId: recipientUserId,
|
|
645
688
|
userRole: "practitioner" /* PRACTITIONER */,
|
|
646
689
|
notificationType: tempNotificationType,
|
|
647
|
-
notificationTime:
|
|
690
|
+
notificationTime: notificationTimestampForDb,
|
|
648
691
|
notificationTokens: recipientExpoTokens,
|
|
649
692
|
title,
|
|
650
693
|
body,
|
|
@@ -668,6 +711,187 @@ var NotificationsAdmin = class {
|
|
|
668
711
|
}
|
|
669
712
|
};
|
|
670
713
|
|
|
714
|
+
// src/utils/TimestampUtils.ts
|
|
715
|
+
var admin2 = __toESM(require("firebase-admin"));
|
|
716
|
+
var import_firestore = require("firebase/firestore");
|
|
717
|
+
var IS_SERVER_ENV = process.env.NODE_ENV === "production" || process.env.FUNCTIONS_EMULATOR === "true" || process.env.FIREBASE_CONFIG !== void 0;
|
|
718
|
+
var TimestampUtils = class {
|
|
719
|
+
/**
|
|
720
|
+
* Enables server mode where admin timestamps are preserved when saving
|
|
721
|
+
* to Firestore via the admin SDK
|
|
722
|
+
*/
|
|
723
|
+
static enableServerMode() {
|
|
724
|
+
this.serverMode = true;
|
|
725
|
+
}
|
|
726
|
+
/**
|
|
727
|
+
* Disables server mode - use this only for client-side or mixed environments
|
|
728
|
+
*/
|
|
729
|
+
static disableServerMode() {
|
|
730
|
+
this.serverMode = false;
|
|
731
|
+
}
|
|
732
|
+
/**
|
|
733
|
+
* Converts an admin Firestore Timestamp to a client Firestore Timestamp
|
|
734
|
+
* In server mode, returns the original admin timestamp for storage consistency
|
|
735
|
+
*
|
|
736
|
+
* @param adminTimestamp - Admin SDK Timestamp (from firebase-admin)
|
|
737
|
+
* @returns A client SDK Timestamp (from firebase/firestore) with same seconds/nanoseconds,
|
|
738
|
+
* or the original admin timestamp in server mode,
|
|
739
|
+
* or null if input is null
|
|
740
|
+
*/
|
|
741
|
+
static adminToClient(adminTimestamp) {
|
|
742
|
+
if (!adminTimestamp) return null;
|
|
743
|
+
if (this.serverMode) {
|
|
744
|
+
return adminTimestamp;
|
|
745
|
+
}
|
|
746
|
+
return new import_firestore.Timestamp(
|
|
747
|
+
adminTimestamp.seconds,
|
|
748
|
+
adminTimestamp.nanoseconds
|
|
749
|
+
);
|
|
750
|
+
}
|
|
751
|
+
/**
|
|
752
|
+
* Converts a client Firestore Timestamp to an admin Firestore Timestamp
|
|
753
|
+
* @param clientTimestamp - Client SDK Timestamp (from firebase/firestore)
|
|
754
|
+
* @returns An admin SDK Timestamp (from firebase-admin) with same seconds/nanoseconds or null if input is null
|
|
755
|
+
*/
|
|
756
|
+
static clientToAdmin(clientTimestamp) {
|
|
757
|
+
if (!clientTimestamp) return null;
|
|
758
|
+
return new admin2.firestore.Timestamp(
|
|
759
|
+
clientTimestamp.seconds,
|
|
760
|
+
clientTimestamp.nanoseconds
|
|
761
|
+
);
|
|
762
|
+
}
|
|
763
|
+
/**
|
|
764
|
+
* Creates a timestamp for the current time in the appropriate format based on environment
|
|
765
|
+
* @returns A timestamp for the current time (admin timestamp in server mode, client in client mode)
|
|
766
|
+
*/
|
|
767
|
+
static nowAsTimestamp() {
|
|
768
|
+
const now = admin2.firestore.Timestamp.now();
|
|
769
|
+
if (this.serverMode) {
|
|
770
|
+
return now;
|
|
771
|
+
}
|
|
772
|
+
return this.adminToClient(now);
|
|
773
|
+
}
|
|
774
|
+
/**
|
|
775
|
+
* @deprecated Use nowAsTimestamp() instead for better cross-environment compatibility
|
|
776
|
+
*/
|
|
777
|
+
static nowAsClient() {
|
|
778
|
+
const now = admin2.firestore.Timestamp.now();
|
|
779
|
+
return this.adminToClient(now);
|
|
780
|
+
}
|
|
781
|
+
/**
|
|
782
|
+
* Converts a Date object to a timestamp in the appropriate format based on environment
|
|
783
|
+
* @param date - JavaScript Date object
|
|
784
|
+
* @returns A timestamp (admin timestamp in server mode, client in client mode) or null if input is null
|
|
785
|
+
*/
|
|
786
|
+
static dateToTimestamp(date) {
|
|
787
|
+
if (!date) return null;
|
|
788
|
+
if (this.serverMode) {
|
|
789
|
+
return admin2.firestore.Timestamp.fromDate(date);
|
|
790
|
+
}
|
|
791
|
+
return import_firestore.Timestamp.fromDate(date);
|
|
792
|
+
}
|
|
793
|
+
/**
|
|
794
|
+
* @deprecated Use dateToTimestamp() instead for better cross-environment compatibility
|
|
795
|
+
*/
|
|
796
|
+
static dateToClientTimestamp(date) {
|
|
797
|
+
if (!date) return null;
|
|
798
|
+
return import_firestore.Timestamp.fromDate(date);
|
|
799
|
+
}
|
|
800
|
+
/**
|
|
801
|
+
* @deprecated Use dateToTimestamp() instead for better cross-environment compatibility
|
|
802
|
+
*/
|
|
803
|
+
static dateToAdminTimestamp(date) {
|
|
804
|
+
if (!date) return null;
|
|
805
|
+
return admin2.firestore.Timestamp.fromDate(date);
|
|
806
|
+
}
|
|
807
|
+
/**
|
|
808
|
+
* Gets a server timestamp field value for use in create/update operations
|
|
809
|
+
* Works in both admin and client environments
|
|
810
|
+
*/
|
|
811
|
+
static serverTimestamp() {
|
|
812
|
+
if (this.serverMode) {
|
|
813
|
+
return admin2.firestore.FieldValue.serverTimestamp();
|
|
814
|
+
}
|
|
815
|
+
throw new Error("Server timestamp in client mode not implemented");
|
|
816
|
+
}
|
|
817
|
+
/**
|
|
818
|
+
* For objects with mixed timestamp types, ensures all timestamps are
|
|
819
|
+
* in the correct format for the current environment
|
|
820
|
+
*/
|
|
821
|
+
static normalizeTimestamps(obj) {
|
|
822
|
+
if (!obj || typeof obj !== "object") {
|
|
823
|
+
return obj;
|
|
824
|
+
}
|
|
825
|
+
if (obj instanceof admin2.firestore.Timestamp) {
|
|
826
|
+
return this.serverMode ? obj : this.adminToClient(obj);
|
|
827
|
+
}
|
|
828
|
+
if (obj instanceof import_firestore.Timestamp && this.serverMode) {
|
|
829
|
+
return this.clientToAdmin(obj);
|
|
830
|
+
}
|
|
831
|
+
if (Array.isArray(obj)) {
|
|
832
|
+
return obj.map((item) => this.normalizeTimestamps(item));
|
|
833
|
+
}
|
|
834
|
+
const result = { ...obj };
|
|
835
|
+
for (const key in result) {
|
|
836
|
+
if (Object.prototype.hasOwnProperty.call(result, key)) {
|
|
837
|
+
result[key] = this.normalizeTimestamps(result[key]);
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
return result;
|
|
841
|
+
}
|
|
842
|
+
/**
|
|
843
|
+
* @deprecated Use normalizeTimestamps() instead for better cross-environment compatibility
|
|
844
|
+
*/
|
|
845
|
+
static convertObjectTimestampsAdminToClient(obj) {
|
|
846
|
+
if (!obj || typeof obj !== "object") {
|
|
847
|
+
return obj;
|
|
848
|
+
}
|
|
849
|
+
if (obj instanceof admin2.firestore.Timestamp) {
|
|
850
|
+
return this.adminToClient(obj);
|
|
851
|
+
}
|
|
852
|
+
if (Array.isArray(obj)) {
|
|
853
|
+
return obj.map(
|
|
854
|
+
(item) => this.convertObjectTimestampsAdminToClient(item)
|
|
855
|
+
);
|
|
856
|
+
}
|
|
857
|
+
const result = { ...obj };
|
|
858
|
+
for (const key in result) {
|
|
859
|
+
if (Object.prototype.hasOwnProperty.call(result, key)) {
|
|
860
|
+
result[key] = this.convertObjectTimestampsAdminToClient(result[key]);
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
return result;
|
|
864
|
+
}
|
|
865
|
+
/**
|
|
866
|
+
* @deprecated Use normalizeTimestamps() instead for better cross-environment compatibility
|
|
867
|
+
*/
|
|
868
|
+
static convertObjectTimestampsClientToAdmin(obj) {
|
|
869
|
+
if (!obj || typeof obj !== "object") {
|
|
870
|
+
return obj;
|
|
871
|
+
}
|
|
872
|
+
if (obj instanceof import_firestore.Timestamp) {
|
|
873
|
+
return this.clientToAdmin(obj);
|
|
874
|
+
}
|
|
875
|
+
if (Array.isArray(obj)) {
|
|
876
|
+
return obj.map(
|
|
877
|
+
(item) => this.convertObjectTimestampsClientToAdmin(item)
|
|
878
|
+
);
|
|
879
|
+
}
|
|
880
|
+
const result = { ...obj };
|
|
881
|
+
for (const key in result) {
|
|
882
|
+
if (Object.prototype.hasOwnProperty.call(result, key)) {
|
|
883
|
+
result[key] = this.convertObjectTimestampsClientToAdmin(result[key]);
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
return result;
|
|
887
|
+
}
|
|
888
|
+
};
|
|
889
|
+
/**
|
|
890
|
+
* Flag to force server mode where admin timestamps are preserved
|
|
891
|
+
* This should be true in Cloud Functions environments
|
|
892
|
+
*/
|
|
893
|
+
TimestampUtils.serverMode = IS_SERVER_ENV;
|
|
894
|
+
|
|
671
895
|
// src/admin/aggregation/clinic/clinic.aggregation.service.ts
|
|
672
896
|
var admin3 = __toESM(require("firebase-admin"));
|
|
673
897
|
|
|
@@ -2128,7 +2352,7 @@ var PatientRequirementsAdminService = class {
|
|
|
2128
2352
|
userRole: "patient" /* PATIENT */,
|
|
2129
2353
|
notificationType: "requirementInstructionDue" /* REQUIREMENT_INSTRUCTION_DUE */,
|
|
2130
2354
|
notificationTime: currentInstruction.dueTime,
|
|
2131
|
-
//
|
|
2355
|
+
// dueTime should be an admin.firestore.Timestamp already
|
|
2132
2356
|
notificationTokens: patientExpoTokens,
|
|
2133
2357
|
title: `Reminder: ${instance.requirementName}`,
|
|
2134
2358
|
body: currentInstruction.instructionText,
|
|
@@ -2356,66 +2580,6 @@ var PatientRequirementsAdminService = class {
|
|
|
2356
2580
|
|
|
2357
2581
|
// src/admin/calendar/calendar.admin.service.ts
|
|
2358
2582
|
var admin8 = __toESM(require("firebase-admin"));
|
|
2359
|
-
|
|
2360
|
-
// src/admin/logger/index.ts
|
|
2361
|
-
var firebaseFunctionsLogger;
|
|
2362
|
-
try {
|
|
2363
|
-
firebaseFunctionsLogger = require("firebase-functions/logger");
|
|
2364
|
-
require("firebase-functions/logger/compat");
|
|
2365
|
-
} catch (e) {
|
|
2366
|
-
}
|
|
2367
|
-
var Logger = class {
|
|
2368
|
-
/**
|
|
2369
|
-
* Log an error message
|
|
2370
|
-
* @param message Message to log
|
|
2371
|
-
* @param data Optional data to include
|
|
2372
|
-
*/
|
|
2373
|
-
static error(message, data) {
|
|
2374
|
-
if (firebaseFunctionsLogger) {
|
|
2375
|
-
firebaseFunctionsLogger.error(message, data);
|
|
2376
|
-
} else {
|
|
2377
|
-
console.error(message, data !== void 0 ? data : "");
|
|
2378
|
-
}
|
|
2379
|
-
}
|
|
2380
|
-
/**
|
|
2381
|
-
* Log a warning message
|
|
2382
|
-
* @param message Message to log
|
|
2383
|
-
* @param data Optional data to include
|
|
2384
|
-
*/
|
|
2385
|
-
static warn(message, data) {
|
|
2386
|
-
if (firebaseFunctionsLogger) {
|
|
2387
|
-
firebaseFunctionsLogger.warn(message, data);
|
|
2388
|
-
} else {
|
|
2389
|
-
console.warn(message, data !== void 0 ? data : "");
|
|
2390
|
-
}
|
|
2391
|
-
}
|
|
2392
|
-
/**
|
|
2393
|
-
* Log an info message
|
|
2394
|
-
* @param message Message to log
|
|
2395
|
-
* @param data Optional data to include
|
|
2396
|
-
*/
|
|
2397
|
-
static info(message, data) {
|
|
2398
|
-
if (firebaseFunctionsLogger) {
|
|
2399
|
-
firebaseFunctionsLogger.info(message, data);
|
|
2400
|
-
} else {
|
|
2401
|
-
console.info(message, data !== void 0 ? data : "");
|
|
2402
|
-
}
|
|
2403
|
-
}
|
|
2404
|
-
/**
|
|
2405
|
-
* Log a debug message
|
|
2406
|
-
* @param message Message to log
|
|
2407
|
-
* @param data Optional data to include
|
|
2408
|
-
*/
|
|
2409
|
-
static debug(message, data) {
|
|
2410
|
-
if (firebaseFunctionsLogger) {
|
|
2411
|
-
firebaseFunctionsLogger.debug(message, data);
|
|
2412
|
-
} else {
|
|
2413
|
-
console.debug(message, data !== void 0 ? data : "");
|
|
2414
|
-
}
|
|
2415
|
-
}
|
|
2416
|
-
};
|
|
2417
|
-
|
|
2418
|
-
// src/admin/calendar/calendar.admin.service.ts
|
|
2419
2583
|
var CalendarAdminService = class {
|
|
2420
2584
|
constructor(firestore12) {
|
|
2421
2585
|
this.db = firestore12 || admin8.firestore();
|
|
@@ -2470,14 +2634,8 @@ var CalendarAdminService = class {
|
|
|
2470
2634
|
const batch = this.db.batch();
|
|
2471
2635
|
const serverTimestamp = admin8.firestore.FieldValue.serverTimestamp();
|
|
2472
2636
|
const firestoreCompatibleEventTime = {
|
|
2473
|
-
start:
|
|
2474
|
-
|
|
2475
|
-
nanoseconds: newEventTime.start.nanoseconds
|
|
2476
|
-
},
|
|
2477
|
-
end: {
|
|
2478
|
-
seconds: newEventTime.end.seconds,
|
|
2479
|
-
nanoseconds: newEventTime.end.nanoseconds
|
|
2480
|
-
}
|
|
2637
|
+
start: TimestampUtils.clientToAdmin(newEventTime.start) || admin8.firestore.Timestamp.now(),
|
|
2638
|
+
end: TimestampUtils.clientToAdmin(newEventTime.end) || admin8.firestore.Timestamp.now()
|
|
2481
2639
|
};
|
|
2482
2640
|
if (appointment.calendarEventId) {
|
|
2483
2641
|
const practitionerEventRef = this.db.doc(
|
|
@@ -3225,7 +3383,8 @@ var AppointmentAggregationService = class {
|
|
|
3225
3383
|
status: "pendingNotification" /* PENDING_NOTIFICATION */,
|
|
3226
3384
|
originalNotifyAtValue: notifyAtValue,
|
|
3227
3385
|
originalTimeframeUnit: template.timeframe.unit,
|
|
3228
|
-
updatedAt: admin10.firestore.
|
|
3386
|
+
updatedAt: admin10.firestore.Timestamp.now(),
|
|
3387
|
+
// Use current server timestamp
|
|
3229
3388
|
notificationId: void 0,
|
|
3230
3389
|
actionTakenAt: void 0
|
|
3231
3390
|
};
|
|
@@ -3335,7 +3494,8 @@ var AppointmentAggregationService = class {
|
|
|
3335
3494
|
status: "pendingNotification" /* PENDING_NOTIFICATION */,
|
|
3336
3495
|
originalNotifyAtValue: notifyAtValue,
|
|
3337
3496
|
originalTimeframeUnit: template.timeframe.unit,
|
|
3338
|
-
updatedAt: admin10.firestore.
|
|
3497
|
+
updatedAt: admin10.firestore.Timestamp.now(),
|
|
3498
|
+
// Use current server timestamp
|
|
3339
3499
|
notificationId: void 0,
|
|
3340
3500
|
actionTakenAt: void 0
|
|
3341
3501
|
};
|
|
@@ -4546,8 +4706,12 @@ var BookingAdmin = class {
|
|
|
4546
4706
|
return events.map((event) => ({
|
|
4547
4707
|
...event,
|
|
4548
4708
|
eventTime: {
|
|
4549
|
-
start: TimestampUtils.adminToClient(
|
|
4550
|
-
|
|
4709
|
+
start: TimestampUtils.adminToClient(
|
|
4710
|
+
event.eventTime.start
|
|
4711
|
+
),
|
|
4712
|
+
end: TimestampUtils.adminToClient(
|
|
4713
|
+
event.eventTime.end
|
|
4714
|
+
)
|
|
4551
4715
|
}
|
|
4552
4716
|
// Convert any other timestamps in the event if needed
|
|
4553
4717
|
}));
|
|
@@ -5012,13 +5176,21 @@ var BookingAdmin = class {
|
|
|
5012
5176
|
|
|
5013
5177
|
// src/admin/index.ts
|
|
5014
5178
|
console.log("[Admin Module] Initialized and services exported.");
|
|
5179
|
+
TimestampUtils.enableServerMode();
|
|
5015
5180
|
// Annotate the CommonJS export names for ESM import in node:
|
|
5016
5181
|
0 && (module.exports = {
|
|
5182
|
+
APPOINTMENTS_COLLECTION,
|
|
5017
5183
|
AppointmentAggregationService,
|
|
5184
|
+
AppointmentMailingService,
|
|
5018
5185
|
AppointmentStatus,
|
|
5019
5186
|
BaseMailingService,
|
|
5020
5187
|
BookingAdmin,
|
|
5188
|
+
BookingAvailabilityCalculator,
|
|
5189
|
+
CalendarAdminService,
|
|
5021
5190
|
ClinicAggregationService,
|
|
5191
|
+
DocumentManagerAdminService,
|
|
5192
|
+
Logger,
|
|
5193
|
+
MediaType,
|
|
5022
5194
|
NOTIFICATIONS_COLLECTION,
|
|
5023
5195
|
NotificationStatus,
|
|
5024
5196
|
NotificationType,
|
|
@@ -5028,6 +5200,7 @@ console.log("[Admin Module] Initialized and services exported.");
|
|
|
5028
5200
|
PatientInstructionStatus,
|
|
5029
5201
|
PatientRequirementOverallStatus,
|
|
5030
5202
|
PatientRequirementsAdminService,
|
|
5203
|
+
PaymentStatus,
|
|
5031
5204
|
PractitionerAggregationService,
|
|
5032
5205
|
PractitionerInviteMailingService,
|
|
5033
5206
|
PractitionerTokenStatus,
|