@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/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 MORA da šalje ceo snapshot (ili barem sva polja po kojima sortiraš, npr. nameLower) kao lastDoc za paginaciju, a ne samo id!
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
- if (typeof filters.lastDoc.data === "function") {
22941
- constraints.push(startAfter14(filters.lastDoc));
22942
- } else if (Array.isArray(filters.lastDoc)) {
22943
- constraints.push(startAfter14(...filters.lastDoc));
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 lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
22965
- return { procedures, lastDoc };
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
- if (typeof filters.lastDoc.data === "function") {
22984
- constraints.push(startAfter14(filters.lastDoc));
22985
- } else if (Array.isArray(filters.lastDoc)) {
22986
- constraints.push(startAfter14(...filters.lastDoc));
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 lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
23008
- return { procedures, lastDoc };
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
- if (typeof filters.lastDoc.data === "function") {
23059
- constraints.push(startAfter14(filters.lastDoc));
23060
- } else if (Array.isArray(filters.lastDoc)) {
23061
- constraints.push(startAfter14(...filters.lastDoc));
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 lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
23095
- return { procedures, lastDoc };
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 lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
23126
- return { procedures, lastDoc };
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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@blackcode_sa/metaestetics-api",
3
3
  "private": false,
4
- "version": "1.14.57",
4
+ "version": "1.14.59",
5
5
  "description": "Firebase authentication service with anonymous upgrade support",
6
6
  "main": "dist/index.js",
7
7
  "module": "dist/index.mjs",
@@ -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
  );