@blackcode_sa/metaestetics-api 1.14.57 → 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 +41 -1
- package/dist/admin/index.d.ts +41 -1
- package/dist/admin/index.js +1104 -227
- package/dist/admin/index.mjs +1104 -227
- package/dist/index.d.mts +39 -3
- package/dist/index.d.ts +39 -3
- package/dist/index.js +95 -27
- package/dist/index.mjs +96 -27
- 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 +876 -4
- package/src/admin/notifications/notifications.admin.ts +57 -0
- package/src/services/procedure/procedure.service.ts +137 -40
- package/src/types/appointment/index.ts +6 -0
- package/src/types/notifications/index.ts +14 -0
package/dist/index.mjs
CHANGED
|
@@ -7678,6 +7678,7 @@ var NotificationType = /* @__PURE__ */ ((NotificationType3) => {
|
|
|
7678
7678
|
NotificationType3["APPOINTMENT_REMINDER"] = "appointmentReminder";
|
|
7679
7679
|
NotificationType3["APPOINTMENT_STATUS_CHANGE"] = "appointmentStatusChange";
|
|
7680
7680
|
NotificationType3["APPOINTMENT_RESCHEDULED_PROPOSAL"] = "appointmentRescheduledProposal";
|
|
7681
|
+
NotificationType3["APPOINTMENT_RESCHEDULED_REMINDER"] = "appointmentRescheduledReminder";
|
|
7681
7682
|
NotificationType3["APPOINTMENT_CANCELLED"] = "appointmentCancelled";
|
|
7682
7683
|
NotificationType3["PRE_REQUIREMENT_INSTRUCTION_DUE"] = "preRequirementInstructionDue";
|
|
7683
7684
|
NotificationType3["POST_REQUIREMENT_INSTRUCTION_DUE"] = "postRequirementInstructionDue";
|
|
@@ -21583,6 +21584,7 @@ import {
|
|
|
21583
21584
|
updateDoc as updateDoc36,
|
|
21584
21585
|
setDoc as setDoc27,
|
|
21585
21586
|
deleteDoc as deleteDoc19,
|
|
21587
|
+
Timestamp as Timestamp36,
|
|
21586
21588
|
serverTimestamp as serverTimestamp31,
|
|
21587
21589
|
writeBatch as writeBatch6,
|
|
21588
21590
|
orderBy as orderBy18,
|
|
@@ -22838,10 +22840,72 @@ var ProcedureService = class extends BaseService {
|
|
|
22838
22840
|
throw error;
|
|
22839
22841
|
}
|
|
22840
22842
|
}
|
|
22843
|
+
/**
|
|
22844
|
+
* Creates a serializable cursor from a DocumentSnapshot or returns the cursor values.
|
|
22845
|
+
* This format can be passed through React Native state/Redux without losing data.
|
|
22846
|
+
*
|
|
22847
|
+
* @param doc - The Firestore DocumentSnapshot
|
|
22848
|
+
* @param orderByField - The field used in orderBy clause
|
|
22849
|
+
* @returns Serializable cursor object with values needed for startAfter
|
|
22850
|
+
*/
|
|
22851
|
+
createSerializableCursor(doc47, orderByField = "createdAt") {
|
|
22852
|
+
if (!doc47) return null;
|
|
22853
|
+
const data = typeof doc47.data === "function" ? doc47.data() : doc47;
|
|
22854
|
+
const docId = doc47.id || (data == null ? void 0 : data.id);
|
|
22855
|
+
if (!docId) return null;
|
|
22856
|
+
let orderByValue = data == null ? void 0 : data[orderByField];
|
|
22857
|
+
if (orderByValue && typeof orderByValue.toDate === "function") {
|
|
22858
|
+
orderByValue = orderByValue.toMillis();
|
|
22859
|
+
} else if (orderByValue && orderByValue.seconds) {
|
|
22860
|
+
orderByValue = orderByValue.seconds * 1e3 + (orderByValue.nanoseconds || 0) / 1e6;
|
|
22861
|
+
}
|
|
22862
|
+
return {
|
|
22863
|
+
__cursor: true,
|
|
22864
|
+
values: [orderByValue],
|
|
22865
|
+
id: docId,
|
|
22866
|
+
orderByField
|
|
22867
|
+
};
|
|
22868
|
+
}
|
|
22869
|
+
/**
|
|
22870
|
+
* Converts a serializable cursor back to values for startAfter.
|
|
22871
|
+
* Handles both native DocumentSnapshots and serialized cursor objects.
|
|
22872
|
+
*
|
|
22873
|
+
* @param lastDoc - Either a DocumentSnapshot or a serializable cursor object
|
|
22874
|
+
* @param orderByField - The field used in orderBy clause (for validation)
|
|
22875
|
+
* @returns Values to spread into startAfter, or null if invalid
|
|
22876
|
+
*/
|
|
22877
|
+
getCursorValuesForStartAfter(lastDoc, orderByField = "createdAt") {
|
|
22878
|
+
if (!lastDoc) return null;
|
|
22879
|
+
if (typeof lastDoc.data === "function") {
|
|
22880
|
+
return [lastDoc];
|
|
22881
|
+
}
|
|
22882
|
+
if (lastDoc.__cursor && Array.isArray(lastDoc.values)) {
|
|
22883
|
+
if (orderByField === "createdAt" && typeof lastDoc.values[0] === "number") {
|
|
22884
|
+
const timestamp = Timestamp36.fromMillis(lastDoc.values[0]);
|
|
22885
|
+
return [timestamp];
|
|
22886
|
+
}
|
|
22887
|
+
return lastDoc.values;
|
|
22888
|
+
}
|
|
22889
|
+
if (Array.isArray(lastDoc)) {
|
|
22890
|
+
return lastDoc;
|
|
22891
|
+
}
|
|
22892
|
+
if (lastDoc[orderByField]) {
|
|
22893
|
+
let value = lastDoc[orderByField];
|
|
22894
|
+
if (typeof value === "number" && orderByField === "createdAt") {
|
|
22895
|
+
value = Timestamp36.fromMillis(value);
|
|
22896
|
+
} else if (value.seconds && orderByField === "createdAt") {
|
|
22897
|
+
value = new Timestamp36(value.seconds, value.nanoseconds || 0);
|
|
22898
|
+
}
|
|
22899
|
+
return [value];
|
|
22900
|
+
}
|
|
22901
|
+
console.warn("[PROCEDURE_SERVICE] Could not parse lastDoc cursor:", typeof lastDoc);
|
|
22902
|
+
return null;
|
|
22903
|
+
}
|
|
22841
22904
|
/**
|
|
22842
22905
|
* Searches and filters procedures based on multiple criteria
|
|
22843
22906
|
*
|
|
22844
|
-
* @note Frontend
|
|
22907
|
+
* @note Frontend can now send either a DocumentSnapshot or a serializable cursor object.
|
|
22908
|
+
* The serializable cursor format is: { __cursor: true, values: [...], id: string, orderByField: string }
|
|
22845
22909
|
*
|
|
22846
22910
|
* @param filters - Various filters to apply
|
|
22847
22911
|
* @param filters.nameSearch - Optional search text for procedure name
|
|
@@ -22937,12 +23001,10 @@ var ProcedureService = class extends BaseService {
|
|
|
22937
23001
|
constraints.push(where33("nameLower", "<=", searchTerm + "\uF8FF"));
|
|
22938
23002
|
constraints.push(orderBy18("nameLower"));
|
|
22939
23003
|
if (filters.lastDoc) {
|
|
22940
|
-
|
|
22941
|
-
|
|
22942
|
-
|
|
22943
|
-
|
|
22944
|
-
} else {
|
|
22945
|
-
constraints.push(startAfter14(filters.lastDoc));
|
|
23004
|
+
const cursorValues = this.getCursorValuesForStartAfter(filters.lastDoc, "nameLower");
|
|
23005
|
+
if (cursorValues) {
|
|
23006
|
+
constraints.push(startAfter14(...cursorValues));
|
|
23007
|
+
console.log("[PROCEDURE_SERVICE] Strategy 1: Using cursor for pagination");
|
|
22946
23008
|
}
|
|
22947
23009
|
}
|
|
22948
23010
|
constraints.push(limit16(filters.pagination || 10));
|
|
@@ -22961,8 +23023,9 @@ var ProcedureService = class extends BaseService {
|
|
|
22961
23023
|
if (querySnapshot.docs.length < (filters.pagination || 10)) {
|
|
22962
23024
|
return { procedures, lastDoc: null };
|
|
22963
23025
|
}
|
|
22964
|
-
const
|
|
22965
|
-
|
|
23026
|
+
const lastDocSnapshot = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
23027
|
+
const serializableCursor = this.createSerializableCursor(lastDocSnapshot, "nameLower");
|
|
23028
|
+
return { procedures, lastDoc: serializableCursor };
|
|
22966
23029
|
} catch (error) {
|
|
22967
23030
|
console.log("[PROCEDURE_SERVICE] Strategy 1 failed:", error);
|
|
22968
23031
|
}
|
|
@@ -22980,12 +23043,10 @@ var ProcedureService = class extends BaseService {
|
|
|
22980
23043
|
constraints.push(where33("name", "<=", searchTerm + "\uF8FF"));
|
|
22981
23044
|
constraints.push(orderBy18("name"));
|
|
22982
23045
|
if (filters.lastDoc) {
|
|
22983
|
-
|
|
22984
|
-
|
|
22985
|
-
|
|
22986
|
-
|
|
22987
|
-
} else {
|
|
22988
|
-
constraints.push(startAfter14(filters.lastDoc));
|
|
23046
|
+
const cursorValues = this.getCursorValuesForStartAfter(filters.lastDoc, "name");
|
|
23047
|
+
if (cursorValues) {
|
|
23048
|
+
constraints.push(startAfter14(...cursorValues));
|
|
23049
|
+
console.log("[PROCEDURE_SERVICE] Strategy 2: Using cursor for pagination");
|
|
22989
23050
|
}
|
|
22990
23051
|
}
|
|
22991
23052
|
constraints.push(limit16(filters.pagination || 10));
|
|
@@ -23004,8 +23065,9 @@ var ProcedureService = class extends BaseService {
|
|
|
23004
23065
|
if (querySnapshot.docs.length < (filters.pagination || 10)) {
|
|
23005
23066
|
return { procedures, lastDoc: null };
|
|
23006
23067
|
}
|
|
23007
|
-
const
|
|
23008
|
-
|
|
23068
|
+
const lastDocSnapshot = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
23069
|
+
const serializableCursor = this.createSerializableCursor(lastDocSnapshot, "name");
|
|
23070
|
+
return { procedures, lastDoc: serializableCursor };
|
|
23009
23071
|
} catch (error) {
|
|
23010
23072
|
console.log("[PROCEDURE_SERVICE] Strategy 2 failed:", error);
|
|
23011
23073
|
}
|
|
@@ -23055,12 +23117,10 @@ var ProcedureService = class extends BaseService {
|
|
|
23055
23117
|
);
|
|
23056
23118
|
constraints.push(orderBy18("createdAt", "desc"));
|
|
23057
23119
|
if (filters.lastDoc) {
|
|
23058
|
-
|
|
23059
|
-
|
|
23060
|
-
|
|
23061
|
-
|
|
23062
|
-
} else {
|
|
23063
|
-
constraints.push(startAfter14(filters.lastDoc));
|
|
23120
|
+
const cursorValues = this.getCursorValuesForStartAfter(filters.lastDoc, "createdAt");
|
|
23121
|
+
if (cursorValues) {
|
|
23122
|
+
constraints.push(startAfter14(...cursorValues));
|
|
23123
|
+
console.log("[PROCEDURE_SERVICE] Strategy 3: Using cursor for pagination");
|
|
23064
23124
|
}
|
|
23065
23125
|
}
|
|
23066
23126
|
constraints.push(limit16(filters.pagination || 10));
|
|
@@ -23091,8 +23151,9 @@ var ProcedureService = class extends BaseService {
|
|
|
23091
23151
|
if (querySnapshot.docs.length < (filters.pagination || 10)) {
|
|
23092
23152
|
return { procedures, lastDoc: null };
|
|
23093
23153
|
}
|
|
23094
|
-
const
|
|
23095
|
-
|
|
23154
|
+
const lastDocSnapshot = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
23155
|
+
const serializableCursor = this.createSerializableCursor(lastDocSnapshot, "createdAt");
|
|
23156
|
+
return { procedures, lastDoc: serializableCursor };
|
|
23096
23157
|
} catch (error) {
|
|
23097
23158
|
console.log("[PROCEDURE_SERVICE] Strategy 3 failed:", error);
|
|
23098
23159
|
}
|
|
@@ -23108,6 +23169,13 @@ var ProcedureService = class extends BaseService {
|
|
|
23108
23169
|
if (filters.clinicId) {
|
|
23109
23170
|
constraints.push(where33("clinicBranchId", "==", filters.clinicId));
|
|
23110
23171
|
}
|
|
23172
|
+
if (filters.lastDoc) {
|
|
23173
|
+
const cursorValues = this.getCursorValuesForStartAfter(filters.lastDoc, "createdAt");
|
|
23174
|
+
if (cursorValues) {
|
|
23175
|
+
constraints.push(startAfter14(...cursorValues));
|
|
23176
|
+
console.log("[PROCEDURE_SERVICE] Strategy 4: Using cursor for pagination");
|
|
23177
|
+
}
|
|
23178
|
+
}
|
|
23111
23179
|
constraints.push(limit16(filters.pagination || 10));
|
|
23112
23180
|
const q = query33(collection33(this.db, PROCEDURES_COLLECTION), ...constraints);
|
|
23113
23181
|
const querySnapshot = await getDocs33(q);
|
|
@@ -23122,8 +23190,9 @@ var ProcedureService = class extends BaseService {
|
|
|
23122
23190
|
if (querySnapshot.docs.length < (filters.pagination || 10)) {
|
|
23123
23191
|
return { procedures, lastDoc: null };
|
|
23124
23192
|
}
|
|
23125
|
-
const
|
|
23126
|
-
|
|
23193
|
+
const lastDocSnapshot = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
23194
|
+
const serializableCursor = this.createSerializableCursor(lastDocSnapshot, "createdAt");
|
|
23195
|
+
return { procedures, lastDoc: serializableCursor };
|
|
23127
23196
|
} catch (error) {
|
|
23128
23197
|
console.log("[PROCEDURE_SERVICE] Strategy 4 failed:", error);
|
|
23129
23198
|
}
|
package/package.json
CHANGED
|
@@ -497,6 +497,18 @@ export class AppointmentAggregationService {
|
|
|
497
497
|
);
|
|
498
498
|
}
|
|
499
499
|
|
|
500
|
+
// Send reschedule proposal push notification to patient
|
|
501
|
+
if (patientProfile?.expoTokens && patientProfile.expoTokens.length > 0) {
|
|
502
|
+
Logger.info(
|
|
503
|
+
`[AggService] Sending reschedule proposal push notification to patient ${after.patientId}`,
|
|
504
|
+
);
|
|
505
|
+
await this.notificationsAdmin.sendAppointmentRescheduledProposalPush(
|
|
506
|
+
after,
|
|
507
|
+
after.patientId,
|
|
508
|
+
patientProfile.expoTokens,
|
|
509
|
+
);
|
|
510
|
+
}
|
|
511
|
+
|
|
500
512
|
Logger.info(
|
|
501
513
|
`[AggService] TODO: Send reschedule proposal notifications to practitioner as well.`,
|
|
502
514
|
);
|