@blackcode_sa/metaestetics-api 1.12.8 → 1.12.10

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
@@ -10,7 +10,8 @@ import {
10
10
  query as query2,
11
11
  limit as limit2,
12
12
  startAfter as startAfter2,
13
- getDocs as getDocs2
13
+ getDocs as getDocs2,
14
+ getCountFromServer
14
15
  } from "firebase/firestore";
15
16
  import { getFunctions } from "firebase/functions";
16
17
 
@@ -1071,24 +1072,18 @@ var AppointmentService = class extends BaseService {
1071
1072
  `[APPOINTMENT_SERVICE] Getting available booking slots via HTTP for clinic: ${clinicId}, practitioner: ${practitionerId}, procedure: ${procedureId}`
1072
1073
  );
1073
1074
  if (!clinicId || !practitionerId || !procedureId || !startDate || !endDate) {
1074
- throw new Error(
1075
- "Missing required parameters for booking slots calculation"
1076
- );
1075
+ throw new Error("Missing required parameters for booking slots calculation");
1077
1076
  }
1078
1077
  if (endDate <= startDate) {
1079
1078
  throw new Error("End date must be after start date");
1080
1079
  }
1081
1080
  const currentUser = this.auth.currentUser;
1082
1081
  if (!currentUser) {
1083
- throw new Error(
1084
- "User must be authenticated to get available booking slots"
1085
- );
1082
+ throw new Error("User must be authenticated to get available booking slots");
1086
1083
  }
1087
1084
  const functionUrl = `https://europe-west6-metaestetics.cloudfunctions.net/bookingApi/getAvailableBookingSlots`;
1088
1085
  const idToken = await currentUser.getIdToken();
1089
- console.log(
1090
- `[APPOINTMENT_SERVICE] Got user token, user ID: ${currentUser.uid}`
1091
- );
1086
+ console.log(`[APPOINTMENT_SERVICE] Got user token, user ID: ${currentUser.uid}`);
1092
1087
  const requestData = {
1093
1088
  clinicId,
1094
1089
  practitionerId,
@@ -1099,9 +1094,7 @@ var AppointmentService = class extends BaseService {
1099
1094
  end: endDate.getTime()
1100
1095
  }
1101
1096
  };
1102
- console.log(
1103
- `[APPOINTMENT_SERVICE] Making fetch request to ${functionUrl}`
1104
- );
1097
+ console.log(`[APPOINTMENT_SERVICE] Making fetch request to ${functionUrl}`);
1105
1098
  const response = await fetch(functionUrl, {
1106
1099
  method: "POST",
1107
1100
  mode: "cors",
@@ -1123,9 +1116,7 @@ var AppointmentService = class extends BaseService {
1123
1116
  );
1124
1117
  if (!response.ok) {
1125
1118
  const errorText = await response.text();
1126
- console.error(
1127
- `[APPOINTMENT_SERVICE] Error response details: ${errorText}`
1128
- );
1119
+ console.error(`[APPOINTMENT_SERVICE] Error response details: ${errorText}`);
1129
1120
  throw new Error(
1130
1121
  `Failed to get available booking slots: ${response.status} ${response.statusText} - ${errorText}`
1131
1122
  );
@@ -1133,24 +1124,15 @@ var AppointmentService = class extends BaseService {
1133
1124
  const result = await response.json();
1134
1125
  console.log(`[APPOINTMENT_SERVICE] Response parsed successfully`, result);
1135
1126
  if (!result.success) {
1136
- throw new Error(
1137
- result.error || "Failed to get available booking slots"
1138
- );
1127
+ throw new Error(result.error || "Failed to get available booking slots");
1139
1128
  }
1140
- const slots = result.availableSlots.map(
1141
- (slot) => ({
1142
- start: new Date(slot.start)
1143
- })
1144
- );
1145
- console.log(
1146
- `[APPOINTMENT_SERVICE] Found ${slots.length} available booking slots via HTTP`
1147
- );
1129
+ const slots = result.availableSlots.map((slot) => ({
1130
+ start: new Date(slot.start)
1131
+ }));
1132
+ console.log(`[APPOINTMENT_SERVICE] Found ${slots.length} available booking slots via HTTP`);
1148
1133
  return slots;
1149
1134
  } catch (error) {
1150
- console.error(
1151
- "[APPOINTMENT_SERVICE] Error getting available booking slots via HTTP:",
1152
- error
1153
- );
1135
+ console.error("[APPOINTMENT_SERVICE] Error getting available booking slots via HTTP:", error);
1154
1136
  throw error;
1155
1137
  }
1156
1138
  }
@@ -1162,9 +1144,7 @@ var AppointmentService = class extends BaseService {
1162
1144
  */
1163
1145
  async createAppointmentHttp(data) {
1164
1146
  try {
1165
- console.log(
1166
- "[APPOINTMENT_SERVICE] Creating appointment via cloud function"
1167
- );
1147
+ console.log("[APPOINTMENT_SERVICE] Creating appointment via cloud function");
1168
1148
  const currentUser = this.auth.currentUser;
1169
1149
  if (!currentUser) {
1170
1150
  throw new Error("User must be authenticated to create an appointment");
@@ -1178,9 +1158,7 @@ var AppointmentService = class extends BaseService {
1178
1158
  appointmentEndTime: data.appointmentEndTime.toMillis ? data.appointmentEndTime.toMillis() : new Date(data.appointmentEndTime).getTime(),
1179
1159
  patientNotes: (data == null ? void 0 : data.patientNotes) || null
1180
1160
  };
1181
- console.log(
1182
- `[APPOINTMENT_SERVICE] Making fetch request to ${functionUrl}`
1183
- );
1161
+ console.log(`[APPOINTMENT_SERVICE] Making fetch request to ${functionUrl}`);
1184
1162
  const response = await fetch(functionUrl, {
1185
1163
  method: "POST",
1186
1164
  mode: "cors",
@@ -1199,9 +1177,7 @@ var AppointmentService = class extends BaseService {
1199
1177
  );
1200
1178
  if (!response.ok) {
1201
1179
  const errorText = await response.text();
1202
- console.error(
1203
- `[APPOINTMENT_SERVICE] Error response details: ${errorText}`
1204
- );
1180
+ console.error(`[APPOINTMENT_SERVICE] Error response details: ${errorText}`);
1205
1181
  throw new Error(
1206
1182
  `Failed to create appointment: ${response.status} ${response.statusText} - ${errorText}`
1207
1183
  );
@@ -1211,25 +1187,16 @@ var AppointmentService = class extends BaseService {
1211
1187
  throw new Error(result.error || "Failed to create appointment");
1212
1188
  }
1213
1189
  if (result.appointmentData) {
1214
- console.log(
1215
- `[APPOINTMENT_SERVICE] Appointment created with ID: ${result.appointmentId}`
1216
- );
1190
+ console.log(`[APPOINTMENT_SERVICE] Appointment created with ID: ${result.appointmentId}`);
1217
1191
  return result.appointmentData;
1218
1192
  }
1219
- const createdAppointment = await this.getAppointmentById(
1220
- result.appointmentId
1221
- );
1193
+ const createdAppointment = await this.getAppointmentById(result.appointmentId);
1222
1194
  if (!createdAppointment) {
1223
- throw new Error(
1224
- `Failed to retrieve created appointment with ID: ${result.appointmentId}`
1225
- );
1195
+ throw new Error(`Failed to retrieve created appointment with ID: ${result.appointmentId}`);
1226
1196
  }
1227
1197
  return createdAppointment;
1228
1198
  } catch (error) {
1229
- console.error(
1230
- "[APPOINTMENT_SERVICE] Error creating appointment via cloud function:",
1231
- error
1232
- );
1199
+ console.error("[APPOINTMENT_SERVICE] Error creating appointment via cloud function:", error);
1233
1200
  throw error;
1234
1201
  }
1235
1202
  }
@@ -1241,19 +1208,14 @@ var AppointmentService = class extends BaseService {
1241
1208
  */
1242
1209
  async getAppointmentById(appointmentId) {
1243
1210
  try {
1244
- console.log(
1245
- `[APPOINTMENT_SERVICE] Getting appointment with ID: ${appointmentId}`
1246
- );
1211
+ console.log(`[APPOINTMENT_SERVICE] Getting appointment with ID: ${appointmentId}`);
1247
1212
  const appointment = await getAppointmentByIdUtil(this.db, appointmentId);
1248
1213
  console.log(
1249
1214
  `[APPOINTMENT_SERVICE] Appointment ${appointmentId} ${appointment ? "found" : "not found"}`
1250
1215
  );
1251
1216
  return appointment;
1252
1217
  } catch (error) {
1253
- console.error(
1254
- `[APPOINTMENT_SERVICE] Error getting appointment ${appointmentId}:`,
1255
- error
1256
- );
1218
+ console.error(`[APPOINTMENT_SERVICE] Error getting appointment ${appointmentId}:`, error);
1257
1219
  throw error;
1258
1220
  }
1259
1221
  }
@@ -1266,24 +1228,13 @@ var AppointmentService = class extends BaseService {
1266
1228
  */
1267
1229
  async updateAppointment(appointmentId, data) {
1268
1230
  try {
1269
- console.log(
1270
- `[APPOINTMENT_SERVICE] Updating appointment with ID: ${appointmentId}`
1271
- );
1231
+ console.log(`[APPOINTMENT_SERVICE] Updating appointment with ID: ${appointmentId}`);
1272
1232
  const validatedData = await updateAppointmentSchema.parseAsync(data);
1273
- const updatedAppointment = await updateAppointmentUtil(
1274
- this.db,
1275
- appointmentId,
1276
- validatedData
1277
- );
1278
- console.log(
1279
- `[APPOINTMENT_SERVICE] Appointment ${appointmentId} updated successfully`
1280
- );
1233
+ const updatedAppointment = await updateAppointmentUtil(this.db, appointmentId, validatedData);
1234
+ console.log(`[APPOINTMENT_SERVICE] Appointment ${appointmentId} updated successfully`);
1281
1235
  return updatedAppointment;
1282
1236
  } catch (error) {
1283
- console.error(
1284
- `[APPOINTMENT_SERVICE] Error updating appointment ${appointmentId}:`,
1285
- error
1286
- );
1237
+ console.error(`[APPOINTMENT_SERVICE] Error updating appointment ${appointmentId}:`, error);
1287
1238
  throw error;
1288
1239
  }
1289
1240
  }
@@ -1295,21 +1246,13 @@ var AppointmentService = class extends BaseService {
1295
1246
  */
1296
1247
  async searchAppointments(params) {
1297
1248
  try {
1298
- console.log(
1299
- "[APPOINTMENT_SERVICE] Searching appointments with params:",
1300
- params
1301
- );
1249
+ console.log("[APPOINTMENT_SERVICE] Searching appointments with params:", params);
1302
1250
  await searchAppointmentsSchema.parseAsync(params);
1303
1251
  const result = await searchAppointmentsUtil(this.db, params);
1304
- console.log(
1305
- `[APPOINTMENT_SERVICE] Found ${result.appointments.length} appointments`
1306
- );
1252
+ console.log(`[APPOINTMENT_SERVICE] Found ${result.appointments.length} appointments`);
1307
1253
  return result;
1308
1254
  } catch (error) {
1309
- console.error(
1310
- "[APPOINTMENT_SERVICE] Error searching appointments:",
1311
- error
1312
- );
1255
+ console.error("[APPOINTMENT_SERVICE] Error searching appointments:", error);
1313
1256
  throw error;
1314
1257
  }
1315
1258
  }
@@ -1321,9 +1264,7 @@ var AppointmentService = class extends BaseService {
1321
1264
  * @returns Found appointments and the last document for pagination
1322
1265
  */
1323
1266
  async getPatientAppointments(patientId, options) {
1324
- console.log(
1325
- `[APPOINTMENT_SERVICE] Getting appointments for patient: ${patientId}`
1326
- );
1267
+ console.log(`[APPOINTMENT_SERVICE] Getting appointments for patient: ${patientId}`);
1327
1268
  const searchParams = {
1328
1269
  patientId,
1329
1270
  startDate: options == null ? void 0 : options.startDate,
@@ -1342,9 +1283,7 @@ var AppointmentService = class extends BaseService {
1342
1283
  * @returns Found appointments and the last document for pagination
1343
1284
  */
1344
1285
  async getPractitionerAppointments(practitionerId, options) {
1345
- console.log(
1346
- `[APPOINTMENT_SERVICE] Getting appointments for practitioner: ${practitionerId}`
1347
- );
1286
+ console.log(`[APPOINTMENT_SERVICE] Getting appointments for practitioner: ${practitionerId}`);
1348
1287
  const searchParams = {
1349
1288
  practitionerId,
1350
1289
  startDate: options == null ? void 0 : options.startDate,
@@ -1363,9 +1302,7 @@ var AppointmentService = class extends BaseService {
1363
1302
  * @returns Found appointments and the last document for pagination
1364
1303
  */
1365
1304
  async getClinicAppointments(clinicBranchId, options) {
1366
- console.log(
1367
- `[APPOINTMENT_SERVICE] Getting appointments for clinic: ${clinicBranchId}`
1368
- );
1305
+ console.log(`[APPOINTMENT_SERVICE] Getting appointments for clinic: ${clinicBranchId}`);
1369
1306
  const searchParams = {
1370
1307
  clinicBranchId,
1371
1308
  practitionerId: options == null ? void 0 : options.practitionerId,
@@ -1416,68 +1353,42 @@ var AppointmentService = class extends BaseService {
1416
1353
  * Confirms a PENDING appointment by an Admin/Clinic.
1417
1354
  */
1418
1355
  async confirmAppointmentAdmin(appointmentId) {
1419
- console.log(
1420
- `[APPOINTMENT_SERVICE] Admin confirming appointment: ${appointmentId}`
1421
- );
1356
+ console.log(`[APPOINTMENT_SERVICE] Admin confirming appointment: ${appointmentId}`);
1422
1357
  const appointment = await this.getAppointmentById(appointmentId);
1423
- if (!appointment)
1424
- throw new Error(`Appointment ${appointmentId} not found.`);
1358
+ if (!appointment) throw new Error(`Appointment ${appointmentId} not found.`);
1425
1359
  if (appointment.status !== "pending" /* PENDING */) {
1426
- throw new Error(
1427
- `Appointment ${appointmentId} is not in PENDING state to be confirmed.`
1428
- );
1360
+ throw new Error(`Appointment ${appointmentId} is not in PENDING state to be confirmed.`);
1429
1361
  }
1430
- return this.updateAppointmentStatus(
1431
- appointmentId,
1432
- "confirmed" /* CONFIRMED */
1433
- );
1362
+ return this.updateAppointmentStatus(appointmentId, "confirmed" /* CONFIRMED */);
1434
1363
  }
1435
1364
  /**
1436
1365
  * Cancels an appointment by the User (Patient).
1437
1366
  */
1438
1367
  async cancelAppointmentUser(appointmentId, reason) {
1439
- console.log(
1440
- `[APPOINTMENT_SERVICE] User canceling appointment: ${appointmentId}`
1441
- );
1442
- return this.updateAppointmentStatus(
1443
- appointmentId,
1444
- "canceled_patient" /* CANCELED_PATIENT */,
1445
- {
1446
- cancellationReason: reason,
1447
- canceledBy: "patient"
1448
- }
1449
- );
1368
+ console.log(`[APPOINTMENT_SERVICE] User canceling appointment: ${appointmentId}`);
1369
+ return this.updateAppointmentStatus(appointmentId, "canceled_patient" /* CANCELED_PATIENT */, {
1370
+ cancellationReason: reason,
1371
+ canceledBy: "patient"
1372
+ });
1450
1373
  }
1451
1374
  /**
1452
1375
  * Cancels an appointment by an Admin/Clinic.
1453
1376
  */
1454
1377
  async cancelAppointmentAdmin(appointmentId, reason) {
1455
- console.log(
1456
- `[APPOINTMENT_SERVICE] Admin canceling appointment: ${appointmentId}`
1457
- );
1458
- return this.updateAppointmentStatus(
1459
- appointmentId,
1460
- "canceled_clinic" /* CANCELED_CLINIC */,
1461
- {
1462
- cancellationReason: reason,
1463
- canceledBy: "clinic"
1464
- }
1465
- );
1378
+ console.log(`[APPOINTMENT_SERVICE] Admin canceling appointment: ${appointmentId}`);
1379
+ return this.updateAppointmentStatus(appointmentId, "canceled_clinic" /* CANCELED_CLINIC */, {
1380
+ cancellationReason: reason,
1381
+ canceledBy: "clinic"
1382
+ });
1466
1383
  }
1467
1384
  /**
1468
1385
  * Admin proposes to reschedule an appointment.
1469
1386
  * Sets status to RESCHEDULED_BY_CLINIC and updates times.
1470
1387
  */
1471
1388
  async rescheduleAppointmentAdmin(params) {
1472
- console.log(
1473
- `[APPOINTMENT_SERVICE] Admin rescheduling appointment: ${params.appointmentId}`
1474
- );
1475
- const validatedParams = await rescheduleAppointmentSchema.parseAsync(
1476
- params
1477
- );
1478
- const startTimestamp = this.convertToTimestamp(
1479
- validatedParams.newStartTime
1480
- );
1389
+ console.log(`[APPOINTMENT_SERVICE] Admin rescheduling appointment: ${params.appointmentId}`);
1390
+ const validatedParams = await rescheduleAppointmentSchema.parseAsync(params);
1391
+ const startTimestamp = this.convertToTimestamp(validatedParams.newStartTime);
1481
1392
  const endTimestamp = this.convertToTimestamp(validatedParams.newEndTime);
1482
1393
  if (endTimestamp.toMillis() <= startTimestamp.toMillis()) {
1483
1394
  throw new Error("New end time must be after new start time.");
@@ -1520,48 +1431,31 @@ var AppointmentService = class extends BaseService {
1520
1431
  if (value && typeof value.seconds === "number") {
1521
1432
  return new Timestamp2(value.seconds, value.nanoseconds || 0);
1522
1433
  }
1523
- throw new Error(
1524
- `Invalid timestamp format: ${typeof value}, value: ${JSON.stringify(
1525
- value
1526
- )}`
1527
- );
1434
+ throw new Error(`Invalid timestamp format: ${typeof value}, value: ${JSON.stringify(value)}`);
1528
1435
  }
1529
1436
  /**
1530
1437
  * User confirms a reschedule proposed by the clinic.
1531
1438
  * Status changes from RESCHEDULED_BY_CLINIC to CONFIRMED.
1532
1439
  */
1533
1440
  async rescheduleAppointmentConfirmUser(appointmentId) {
1534
- console.log(
1535
- `[APPOINTMENT_SERVICE] User confirming reschedule for: ${appointmentId}`
1536
- );
1441
+ console.log(`[APPOINTMENT_SERVICE] User confirming reschedule for: ${appointmentId}`);
1537
1442
  const appointment = await this.getAppointmentById(appointmentId);
1538
- if (!appointment)
1539
- throw new Error(`Appointment ${appointmentId} not found.`);
1443
+ if (!appointment) throw new Error(`Appointment ${appointmentId} not found.`);
1540
1444
  if (appointment.status !== "rescheduled_by_clinic" /* RESCHEDULED_BY_CLINIC */) {
1541
- throw new Error(
1542
- `Appointment ${appointmentId} is not in RESCHEDULED_BY_CLINIC state.`
1543
- );
1445
+ throw new Error(`Appointment ${appointmentId} is not in RESCHEDULED_BY_CLINIC state.`);
1544
1446
  }
1545
- return this.updateAppointmentStatus(
1546
- appointmentId,
1547
- "confirmed" /* CONFIRMED */
1548
- );
1447
+ return this.updateAppointmentStatus(appointmentId, "confirmed" /* CONFIRMED */);
1549
1448
  }
1550
1449
  /**
1551
1450
  * User rejects a reschedule proposed by the clinic.
1552
1451
  * Status changes from RESCHEDULED_BY_CLINIC to CANCELED_PATIENT_RESCHEDULED.
1553
1452
  */
1554
1453
  async rescheduleAppointmentRejectUser(appointmentId, reason) {
1555
- console.log(
1556
- `[APPOINTMENT_SERVICE] User rejecting reschedule for: ${appointmentId}`
1557
- );
1454
+ console.log(`[APPOINTMENT_SERVICE] User rejecting reschedule for: ${appointmentId}`);
1558
1455
  const appointment = await this.getAppointmentById(appointmentId);
1559
- if (!appointment)
1560
- throw new Error(`Appointment ${appointmentId} not found.`);
1456
+ if (!appointment) throw new Error(`Appointment ${appointmentId} not found.`);
1561
1457
  if (appointment.status !== "rescheduled_by_clinic" /* RESCHEDULED_BY_CLINIC */) {
1562
- throw new Error(
1563
- `Appointment ${appointmentId} is not in RESCHEDULED_BY_CLINIC state.`
1564
- );
1458
+ throw new Error(`Appointment ${appointmentId} is not in RESCHEDULED_BY_CLINIC state.`);
1565
1459
  }
1566
1460
  return this.updateAppointmentStatus(
1567
1461
  appointmentId,
@@ -1577,17 +1471,12 @@ var AppointmentService = class extends BaseService {
1577
1471
  * Requires all pending user forms to be completed.
1578
1472
  */
1579
1473
  async checkInPatientAdmin(appointmentId) {
1580
- console.log(
1581
- `[APPOINTMENT_SERVICE] Admin checking in patient for: ${appointmentId}`
1582
- );
1474
+ console.log(`[APPOINTMENT_SERVICE] Admin checking in patient for: ${appointmentId}`);
1583
1475
  const appointment = await this.getAppointmentById(appointmentId);
1584
- if (!appointment)
1585
- throw new Error(`Appointment ${appointmentId} not found.`);
1476
+ if (!appointment) throw new Error(`Appointment ${appointmentId} not found.`);
1586
1477
  if (appointment.pendingUserFormsIds && appointment.pendingUserFormsIds.length > 0) {
1587
1478
  throw new Error(
1588
- `Cannot check in: Patient has ${appointment.pendingUserFormsIds.length} pending required form(s). IDs: ${appointment.pendingUserFormsIds.join(
1589
- ", "
1590
- )}`
1479
+ `Cannot check in: Patient has ${appointment.pendingUserFormsIds.length} pending required form(s). IDs: ${appointment.pendingUserFormsIds.join(", ")}`
1591
1480
  );
1592
1481
  }
1593
1482
  if (appointment.status !== "confirmed" /* CONFIRMED */ && appointment.status !== "rescheduled_by_clinic" /* RESCHEDULED_BY_CLINIC */) {
@@ -1595,25 +1484,17 @@ var AppointmentService = class extends BaseService {
1595
1484
  `Checking in appointment ${appointmentId} with status ${appointment.status}. Ensure this is intended.`
1596
1485
  );
1597
1486
  }
1598
- return this.updateAppointmentStatus(
1599
- appointmentId,
1600
- "checked_in" /* CHECKED_IN */
1601
- );
1487
+ return this.updateAppointmentStatus(appointmentId, "checked_in" /* CHECKED_IN */);
1602
1488
  }
1603
1489
  /**
1604
1490
  * Doctor starts the appointment procedure.
1605
1491
  */
1606
1492
  async startAppointmentDoctor(appointmentId) {
1607
- console.log(
1608
- `[APPOINTMENT_SERVICE] Doctor starting appointment: ${appointmentId}`
1609
- );
1493
+ console.log(`[APPOINTMENT_SERVICE] Doctor starting appointment: ${appointmentId}`);
1610
1494
  const appointment = await this.getAppointmentById(appointmentId);
1611
- if (!appointment)
1612
- throw new Error(`Appointment ${appointmentId} not found.`);
1495
+ if (!appointment) throw new Error(`Appointment ${appointmentId} not found.`);
1613
1496
  if (appointment.status !== "checked_in" /* CHECKED_IN */) {
1614
- throw new Error(
1615
- `Appointment ${appointmentId} must be CHECKED_IN to start.`
1616
- );
1497
+ throw new Error(`Appointment ${appointmentId} must be CHECKED_IN to start.`);
1617
1498
  }
1618
1499
  const updateData = {
1619
1500
  status: "in_progress" /* IN_PROGRESS */,
@@ -1627,24 +1508,18 @@ var AppointmentService = class extends BaseService {
1627
1508
  * Doctor completes and finalizes the appointment.
1628
1509
  */
1629
1510
  async completeAppointmentDoctor(appointmentId, finalizationNotes, actualDurationMinutesInput) {
1630
- console.log(
1631
- `[APPOINTMENT_SERVICE] Doctor completing appointment: ${appointmentId}`
1632
- );
1511
+ console.log(`[APPOINTMENT_SERVICE] Doctor completing appointment: ${appointmentId}`);
1633
1512
  const currentUser = this.auth.currentUser;
1634
- if (!currentUser)
1635
- throw new Error("Authentication required to complete appointment.");
1513
+ if (!currentUser) throw new Error("Authentication required to complete appointment.");
1636
1514
  const appointment = await this.getAppointmentById(appointmentId);
1637
- if (!appointment)
1638
- throw new Error(`Appointment ${appointmentId} not found.`);
1515
+ if (!appointment) throw new Error(`Appointment ${appointmentId} not found.`);
1639
1516
  let calculatedDurationMinutes = actualDurationMinutesInput;
1640
1517
  const procedureCompletionTime = Timestamp2.now();
1641
1518
  if (calculatedDurationMinutes === void 0 && appointment.procedureActualStartTime) {
1642
1519
  const startTimeMillis = appointment.procedureActualStartTime.toMillis();
1643
1520
  const endTimeMillis = procedureCompletionTime.toMillis();
1644
1521
  if (endTimeMillis > startTimeMillis) {
1645
- calculatedDurationMinutes = Math.round(
1646
- (endTimeMillis - startTimeMillis) / 6e4
1647
- );
1522
+ calculatedDurationMinutes = Math.round((endTimeMillis - startTimeMillis) / 6e4);
1648
1523
  }
1649
1524
  }
1650
1525
  const updateData = {
@@ -1668,31 +1543,22 @@ var AppointmentService = class extends BaseService {
1668
1543
  * Admin marks an appointment as No-Show.
1669
1544
  */
1670
1545
  async markNoShowAdmin(appointmentId) {
1671
- console.log(
1672
- `[APPOINTMENT_SERVICE] Admin marking no-show for: ${appointmentId}`
1673
- );
1546
+ console.log(`[APPOINTMENT_SERVICE] Admin marking no-show for: ${appointmentId}`);
1674
1547
  const appointment = await this.getAppointmentById(appointmentId);
1675
- if (!appointment)
1676
- throw new Error(`Appointment ${appointmentId} not found.`);
1548
+ if (!appointment) throw new Error(`Appointment ${appointmentId} not found.`);
1677
1549
  if (Timestamp2.now().toMillis() < appointment.appointmentStartTime.toMillis()) {
1678
1550
  throw new Error("Cannot mark no-show before appointment start time.");
1679
1551
  }
1680
- return this.updateAppointmentStatus(
1681
- appointmentId,
1682
- "no_show" /* NO_SHOW */,
1683
- {
1684
- cancellationReason: "Patient did not show up for the appointment.",
1685
- canceledBy: "clinic"
1686
- }
1687
- );
1552
+ return this.updateAppointmentStatus(appointmentId, "no_show" /* NO_SHOW */, {
1553
+ cancellationReason: "Patient did not show up for the appointment.",
1554
+ canceledBy: "clinic"
1555
+ });
1688
1556
  }
1689
1557
  /**
1690
1558
  * Adds a media item to an appointment.
1691
1559
  */
1692
1560
  async addMediaToAppointment(appointmentId, mediaItemData) {
1693
- console.log(
1694
- `[APPOINTMENT_SERVICE] Adding media to appointment ${appointmentId}`
1695
- );
1561
+ console.log(`[APPOINTMENT_SERVICE] Adding media to appointment ${appointmentId}`);
1696
1562
  const currentUser = this.auth.currentUser;
1697
1563
  if (!currentUser) throw new Error("Authentication required.");
1698
1564
  const newMediaItem = {
@@ -1732,9 +1598,7 @@ var AppointmentService = class extends BaseService {
1732
1598
  * Adds or updates review information for an appointment.
1733
1599
  */
1734
1600
  async addReviewToAppointment(appointmentId, reviewData) {
1735
- console.log(
1736
- `[APPOINTMENT_SERVICE] Adding review to appointment ${appointmentId}`
1737
- );
1601
+ console.log(`[APPOINTMENT_SERVICE] Adding review to appointment ${appointmentId}`);
1738
1602
  const newReviewInfo = {
1739
1603
  ...reviewData,
1740
1604
  reviewId: this.generateId(),
@@ -1768,9 +1632,7 @@ var AppointmentService = class extends BaseService {
1768
1632
  * @returns The updated appointment
1769
1633
  */
1770
1634
  async updateInternalNotes(appointmentId, notes) {
1771
- console.log(
1772
- `[APPOINTMENT_SERVICE] Updating internal notes for appointment: ${appointmentId}`
1773
- );
1635
+ console.log(`[APPOINTMENT_SERVICE] Updating internal notes for appointment: ${appointmentId}`);
1774
1636
  const updateData = {
1775
1637
  internalNotes: notes
1776
1638
  };
@@ -1786,9 +1648,7 @@ var AppointmentService = class extends BaseService {
1786
1648
  */
1787
1649
  async getUpcomingPatientAppointments(patientId, options) {
1788
1650
  try {
1789
- console.log(
1790
- `[APPOINTMENT_SERVICE] Getting upcoming appointments for patient: ${patientId}`
1791
- );
1651
+ console.log(`[APPOINTMENT_SERVICE] Getting upcoming appointments for patient: ${patientId}`);
1792
1652
  const effectiveStartDate = (options == null ? void 0 : options.startDate) || /* @__PURE__ */ new Date();
1793
1653
  const upcomingStatuses = [
1794
1654
  "pending" /* PENDING */,
@@ -1800,21 +1660,9 @@ var AppointmentService = class extends BaseService {
1800
1660
  const constraints = [];
1801
1661
  constraints.push(where2("patientId", "==", patientId));
1802
1662
  constraints.push(where2("status", "in", upcomingStatuses));
1803
- constraints.push(
1804
- where2(
1805
- "appointmentStartTime",
1806
- ">=",
1807
- Timestamp2.fromDate(effectiveStartDate)
1808
- )
1809
- );
1663
+ constraints.push(where2("appointmentStartTime", ">=", Timestamp2.fromDate(effectiveStartDate)));
1810
1664
  if (options == null ? void 0 : options.endDate) {
1811
- constraints.push(
1812
- where2(
1813
- "appointmentStartTime",
1814
- "<=",
1815
- Timestamp2.fromDate(options.endDate)
1816
- )
1817
- );
1665
+ constraints.push(where2("appointmentStartTime", "<=", Timestamp2.fromDate(options.endDate)));
1818
1666
  }
1819
1667
  constraints.push(orderBy2("appointmentStartTime", "asc"));
1820
1668
  if (options == null ? void 0 : options.limit) {
@@ -1823,14 +1671,9 @@ var AppointmentService = class extends BaseService {
1823
1671
  if (options == null ? void 0 : options.startAfter) {
1824
1672
  constraints.push(startAfter2(options.startAfter));
1825
1673
  }
1826
- const q = query2(
1827
- collection2(this.db, APPOINTMENTS_COLLECTION),
1828
- ...constraints
1829
- );
1674
+ const q = query2(collection2(this.db, APPOINTMENTS_COLLECTION), ...constraints);
1830
1675
  const querySnapshot = await getDocs2(q);
1831
- const appointments = querySnapshot.docs.map(
1832
- (doc38) => doc38.data()
1833
- );
1676
+ const appointments = querySnapshot.docs.map((doc38) => doc38.data());
1834
1677
  const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
1835
1678
  console.log(
1836
1679
  `[APPOINTMENT_SERVICE] Found ${appointments.length} upcoming appointments for patient ${patientId}`
@@ -1855,9 +1698,7 @@ var AppointmentService = class extends BaseService {
1855
1698
  */
1856
1699
  async getPastPatientAppointments(patientId, options) {
1857
1700
  try {
1858
- console.log(
1859
- `[APPOINTMENT_SERVICE] Getting past appointments for patient: ${patientId}`
1860
- );
1701
+ console.log(`[APPOINTMENT_SERVICE] Getting past appointments for patient: ${patientId}`);
1861
1702
  const effectiveEndDate = (options == null ? void 0 : options.endDate) || /* @__PURE__ */ new Date();
1862
1703
  const pastStatuses = ["completed" /* COMPLETED */];
1863
1704
  if (options == null ? void 0 : options.showCanceled) {
@@ -1875,20 +1716,10 @@ var AppointmentService = class extends BaseService {
1875
1716
  constraints.push(where2("status", "in", pastStatuses));
1876
1717
  if (options == null ? void 0 : options.startDate) {
1877
1718
  constraints.push(
1878
- where2(
1879
- "appointmentStartTime",
1880
- ">=",
1881
- Timestamp2.fromDate(options.startDate)
1882
- )
1719
+ where2("appointmentStartTime", ">=", Timestamp2.fromDate(options.startDate))
1883
1720
  );
1884
1721
  }
1885
- constraints.push(
1886
- where2(
1887
- "appointmentStartTime",
1888
- "<=",
1889
- Timestamp2.fromDate(effectiveEndDate)
1890
- )
1891
- );
1722
+ constraints.push(where2("appointmentStartTime", "<=", Timestamp2.fromDate(effectiveEndDate)));
1892
1723
  constraints.push(orderBy2("appointmentStartTime", "desc"));
1893
1724
  if (options == null ? void 0 : options.limit) {
1894
1725
  constraints.push(limit2(options.limit));
@@ -1896,14 +1727,9 @@ var AppointmentService = class extends BaseService {
1896
1727
  if (options == null ? void 0 : options.startAfter) {
1897
1728
  constraints.push(startAfter2(options.startAfter));
1898
1729
  }
1899
- const q = query2(
1900
- collection2(this.db, APPOINTMENTS_COLLECTION),
1901
- ...constraints
1902
- );
1730
+ const q = query2(collection2(this.db, APPOINTMENTS_COLLECTION), ...constraints);
1903
1731
  const querySnapshot = await getDocs2(q);
1904
- const appointments = querySnapshot.docs.map(
1905
- (doc38) => doc38.data()
1906
- );
1732
+ const appointments = querySnapshot.docs.map((doc38) => doc38.data());
1907
1733
  const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
1908
1734
  console.log(
1909
1735
  `[APPOINTMENT_SERVICE] Found ${appointments.length} past appointments for patient ${patientId}`
@@ -1917,6 +1743,46 @@ var AppointmentService = class extends BaseService {
1917
1743
  throw error;
1918
1744
  }
1919
1745
  }
1746
+ /**
1747
+ * Counts completed appointments for a patient with optional clinic filtering.
1748
+ *
1749
+ * @param patientId ID of the patient.
1750
+ * @param clinicBranchId Optional ID of the clinic branch to either include or exclude.
1751
+ * @param excludeClinic Optional boolean. If true (default), excludes the specified clinic. If false, includes only that clinic.
1752
+ * @returns The count of completed appointments.
1753
+ */
1754
+ async countCompletedAppointments(patientId, clinicBranchId, excludeClinic = true) {
1755
+ try {
1756
+ console.log(
1757
+ `[APPOINTMENT_SERVICE] Counting completed appointments for patient: ${patientId}`,
1758
+ { clinicBranchId, excludeClinic }
1759
+ );
1760
+ const constraints = [
1761
+ where2("patientId", "==", patientId),
1762
+ where2("status", "==", "completed" /* COMPLETED */)
1763
+ ];
1764
+ if (clinicBranchId) {
1765
+ if (excludeClinic) {
1766
+ constraints.push(where2("clinicBranchId", "!=", clinicBranchId));
1767
+ } else {
1768
+ constraints.push(where2("clinicBranchId", "==", clinicBranchId));
1769
+ }
1770
+ }
1771
+ const q = query2(collection2(this.db, APPOINTMENTS_COLLECTION), ...constraints);
1772
+ const snapshot = await getCountFromServer(q);
1773
+ const count = snapshot.data().count;
1774
+ console.log(
1775
+ `[APPOINTMENT_SERVICE] Found ${count} completed appointments for patient ${patientId}`
1776
+ );
1777
+ return count;
1778
+ } catch (error) {
1779
+ console.error(
1780
+ `[APPOINTMENT_SERVICE] Error counting completed appointments for patient ${patientId}:`,
1781
+ error
1782
+ );
1783
+ throw error;
1784
+ }
1785
+ }
1920
1786
  };
1921
1787
 
1922
1788
  // src/services/auth/auth.service.ts
@@ -1926,15 +1792,13 @@ import {
1926
1792
  signInAnonymously as firebaseSignInAnonymously,
1927
1793
  signOut as firebaseSignOut,
1928
1794
  GoogleAuthProvider,
1929
- FacebookAuthProvider,
1930
- OAuthProvider,
1931
- signInWithPopup,
1932
1795
  linkWithCredential,
1933
1796
  EmailAuthProvider,
1934
1797
  onAuthStateChanged,
1935
1798
  sendPasswordResetEmail,
1936
1799
  verifyPasswordResetCode,
1937
- confirmPasswordReset
1800
+ confirmPasswordReset,
1801
+ signInWithCredential
1938
1802
  } from "firebase/auth";
1939
1803
  import {
1940
1804
  collection as collection18,
@@ -10014,19 +9878,13 @@ var AuthService = class extends BaseService {
10014
9878
  constructor(db, auth, app, userService) {
10015
9879
  super(db, auth, app);
10016
9880
  this.googleProvider = new GoogleAuthProvider();
10017
- this.facebookProvider = new FacebookAuthProvider();
10018
- this.appleProvider = new OAuthProvider("apple.com");
10019
9881
  this.userService = userService || new UserService(db, auth, app);
10020
9882
  }
10021
9883
  /**
10022
9884
  * Registruje novog korisnika sa email-om i lozinkom
10023
9885
  */
10024
9886
  async signUp(email, password, initialRole = "patient" /* PATIENT */, options) {
10025
- const { user: firebaseUser } = await createUserWithEmailAndPassword(
10026
- this.auth,
10027
- email,
10028
- password
10029
- );
9887
+ const { user: firebaseUser } = await createUserWithEmailAndPassword(this.auth, email, password);
10030
9888
  return this.userService.createUser(firebaseUser, [initialRole], options);
10031
9889
  }
10032
9890
  /**
@@ -10045,20 +9903,13 @@ var AuthService = class extends BaseService {
10045
9903
  await clinicAdminSignupSchema.parseAsync(data);
10046
9904
  console.log("[AUTH] Clinic admin signup data validation passed");
10047
9905
  } catch (validationError) {
10048
- console.error(
10049
- "[AUTH] Validation error in signUpClinicAdmin:",
10050
- validationError
10051
- );
9906
+ console.error("[AUTH] Validation error in signUpClinicAdmin:", validationError);
10052
9907
  throw validationError;
10053
9908
  }
10054
9909
  console.log("[AUTH] Creating Firebase user");
10055
9910
  let firebaseUser;
10056
9911
  try {
10057
- const result = await createUserWithEmailAndPassword(
10058
- this.auth,
10059
- data.email,
10060
- data.password
10061
- );
9912
+ const result = await createUserWithEmailAndPassword(this.auth, data.email, data.password);
10062
9913
  firebaseUser = result.user;
10063
9914
  console.log("[AUTH] Firebase user created successfully", {
10064
9915
  uid: firebaseUser.uid
@@ -10070,13 +9921,9 @@ var AuthService = class extends BaseService {
10070
9921
  console.log("[AUTH] Creating user with CLINIC_ADMIN role");
10071
9922
  let user;
10072
9923
  try {
10073
- user = await this.userService.createUser(
10074
- firebaseUser,
10075
- ["clinic_admin" /* CLINIC_ADMIN */],
10076
- {
10077
- skipProfileCreation: true
10078
- }
10079
- );
9924
+ user = await this.userService.createUser(firebaseUser, ["clinic_admin" /* CLINIC_ADMIN */], {
9925
+ skipProfileCreation: true
9926
+ });
10080
9927
  console.log("[AUTH] User with CLINIC_ADMIN role created successfully", {
10081
9928
  userId: user.uid
10082
9929
  });
@@ -10093,11 +9940,7 @@ var AuthService = class extends BaseService {
10093
9940
  };
10094
9941
  console.log("[AUTH] Contact person object created");
10095
9942
  console.log("[AUTH] Initializing clinic services");
10096
- const clinicAdminService = new ClinicAdminService(
10097
- this.db,
10098
- this.auth,
10099
- this.app
10100
- );
9943
+ const clinicAdminService = new ClinicAdminService(this.db, this.auth, this.app);
10101
9944
  const clinicGroupService = new ClinicGroupService(
10102
9945
  this.db,
10103
9946
  this.auth,
@@ -10114,18 +9957,14 @@ var AuthService = class extends BaseService {
10114
9957
  mediaService
10115
9958
  );
10116
9959
  clinicAdminService.setServices(clinicGroupService, clinicService);
10117
- console.log(
10118
- "[AUTH] Services initialized and circular dependencies resolved"
10119
- );
9960
+ console.log("[AUTH] Services initialized and circular dependencies resolved");
10120
9961
  let clinicGroup = null;
10121
9962
  let adminProfile = null;
10122
9963
  if (data.isCreatingNewGroup) {
10123
9964
  console.log("[AUTH] Creating new clinic group flow");
10124
9965
  if (!data.clinicGroupData) {
10125
9966
  console.error("[AUTH] Clinic group data is missing");
10126
- throw new Error(
10127
- "Clinic group data is required when creating a new group"
10128
- );
9967
+ throw new Error("Clinic group data is required when creating a new group");
10129
9968
  }
10130
9969
  console.log("[AUTH] Creating clinic admin first (without group)");
10131
9970
  const createClinicAdminData = {
@@ -10138,17 +9977,12 @@ var AuthService = class extends BaseService {
10138
9977
  // No clinicGroupId yet
10139
9978
  };
10140
9979
  try {
10141
- adminProfile = await clinicAdminService.createClinicAdmin(
10142
- createClinicAdminData
10143
- );
9980
+ adminProfile = await clinicAdminService.createClinicAdmin(createClinicAdminData);
10144
9981
  console.log("[AUTH] Clinic admin created successfully", {
10145
9982
  adminId: adminProfile.id
10146
9983
  });
10147
9984
  } catch (adminCreationError) {
10148
- console.error(
10149
- "[AUTH] Clinic admin creation failed:",
10150
- adminCreationError
10151
- );
9985
+ console.error("[AUTH] Clinic admin creation failed:", adminCreationError);
10152
9986
  throw adminCreationError;
10153
9987
  }
10154
9988
  try {
@@ -10156,14 +9990,9 @@ var AuthService = class extends BaseService {
10156
9990
  user = await this.userService.updateUser(firebaseUser.uid, {
10157
9991
  adminProfile: adminProfile.id
10158
9992
  });
10159
- console.log(
10160
- "[AUTH] User updated with admin profile reference successfully"
10161
- );
9993
+ console.log("[AUTH] User updated with admin profile reference successfully");
10162
9994
  } catch (userUpdateError) {
10163
- console.error(
10164
- "[AUTH] Failed to update user with admin profile:",
10165
- userUpdateError
10166
- );
9995
+ console.error("[AUTH] Failed to update user with admin profile:", userUpdateError);
10167
9996
  throw userUpdateError;
10168
9997
  }
10169
9998
  const createClinicGroupData = {
@@ -10201,23 +10030,16 @@ var AuthService = class extends BaseService {
10201
10030
  clinicGroupId: clinicGroup.id
10202
10031
  });
10203
10032
  console.log("[AUTH] Admin updated with clinic group ID successfully");
10204
- adminProfile = await clinicAdminService.getClinicAdmin(
10205
- adminProfile.id
10206
- );
10033
+ adminProfile = await clinicAdminService.getClinicAdmin(adminProfile.id);
10207
10034
  } catch (groupCreationError) {
10208
- console.error(
10209
- "[AUTH] Clinic group creation failed:",
10210
- groupCreationError
10211
- );
10035
+ console.error("[AUTH] Clinic group creation failed:", groupCreationError);
10212
10036
  throw groupCreationError;
10213
10037
  }
10214
10038
  } else {
10215
10039
  console.log("[AUTH] Joining existing clinic group flow");
10216
10040
  if (!data.inviteToken) {
10217
10041
  console.error("[AUTH] Invite token is missing");
10218
- throw new Error(
10219
- "Invite token is required when joining an existing group"
10220
- );
10042
+ throw new Error("Invite token is required when joining an existing group");
10221
10043
  }
10222
10044
  console.log("[AUTH] Invite token provided", {
10223
10045
  token: data.inviteToken
@@ -10228,11 +10050,7 @@ var AuthService = class extends BaseService {
10228
10050
  const querySnapshot = await getDocs18(q);
10229
10051
  let foundGroup = null;
10230
10052
  let foundToken = null;
10231
- console.log(
10232
- "[AUTH] Found",
10233
- querySnapshot.size,
10234
- "clinic groups to check"
10235
- );
10053
+ console.log("[AUTH] Found", querySnapshot.size, "clinic groups to check");
10236
10054
  for (const docSnapshot of querySnapshot.docs) {
10237
10055
  const group = docSnapshot.data();
10238
10056
  console.log("[AUTH] Checking group", {
@@ -10278,17 +10096,12 @@ var AuthService = class extends BaseService {
10278
10096
  isActive: true
10279
10097
  };
10280
10098
  try {
10281
- adminProfile = await clinicAdminService.createClinicAdmin(
10282
- createClinicAdminData
10283
- );
10099
+ adminProfile = await clinicAdminService.createClinicAdmin(createClinicAdminData);
10284
10100
  console.log("[AUTH] Clinic admin created successfully", {
10285
10101
  adminId: adminProfile.id
10286
10102
  });
10287
10103
  } catch (adminCreationError) {
10288
- console.error(
10289
- "[AUTH] Clinic admin creation failed:",
10290
- adminCreationError
10291
- );
10104
+ console.error("[AUTH] Clinic admin creation failed:", adminCreationError);
10292
10105
  throw adminCreationError;
10293
10106
  }
10294
10107
  try {
@@ -10309,9 +10122,7 @@ var AuthService = class extends BaseService {
10309
10122
  clinicAdminId: (adminProfile == null ? void 0 : adminProfile.id) || "unknown"
10310
10123
  });
10311
10124
  if (!clinicGroup || !adminProfile) {
10312
- throw new Error(
10313
- "Failed to create or retrieve clinic group or admin profile"
10314
- );
10125
+ throw new Error("Failed to create or retrieve clinic group or admin profile");
10315
10126
  }
10316
10127
  return {
10317
10128
  user,
@@ -10339,11 +10150,7 @@ var AuthService = class extends BaseService {
10339
10150
  * Prijavljuje korisnika sa email-om i lozinkom
10340
10151
  */
10341
10152
  async signIn(email, password) {
10342
- const { user: firebaseUser } = await signInWithEmailAndPassword(
10343
- this.auth,
10344
- email,
10345
- password
10346
- );
10153
+ const { user: firebaseUser } = await signInWithEmailAndPassword(this.auth, email, password);
10347
10154
  return this.userService.getOrCreateUser(firebaseUser);
10348
10155
  }
10349
10156
  /**
@@ -10357,11 +10164,7 @@ var AuthService = class extends BaseService {
10357
10164
  async signInClinicAdmin(email, password) {
10358
10165
  var _a;
10359
10166
  try {
10360
- const clinicAdminService = new ClinicAdminService(
10361
- this.db,
10362
- this.auth,
10363
- this.app
10364
- );
10167
+ const clinicAdminService = new ClinicAdminService(this.db, this.auth, this.app);
10365
10168
  const clinicGroupService = new ClinicGroupService(
10366
10169
  this.db,
10367
10170
  this.auth,
@@ -10378,11 +10181,7 @@ var AuthService = class extends BaseService {
10378
10181
  mediaService
10379
10182
  );
10380
10183
  clinicAdminService.setServices(clinicGroupService, clinicService);
10381
- const { user: firebaseUser } = await signInWithEmailAndPassword(
10382
- this.auth,
10383
- email,
10384
- password
10385
- );
10184
+ const { user: firebaseUser } = await signInWithEmailAndPassword(this.auth, email, password);
10386
10185
  const user = await this.userService.getOrCreateUser(firebaseUser);
10387
10186
  if (!((_a = user.roles) == null ? void 0 : _a.includes("clinic_admin" /* CLINIC_ADMIN */))) {
10388
10187
  console.error("[AUTH] User is not a clinic admin:", user.uid);
@@ -10392,21 +10191,14 @@ var AuthService = class extends BaseService {
10392
10191
  console.error("[AUTH] User has no admin profile:", user.uid);
10393
10192
  throw AUTH_ERRORS.NOT_FOUND;
10394
10193
  }
10395
- const adminProfile = await clinicAdminService.getClinicAdmin(
10396
- user.adminProfile
10397
- );
10194
+ const adminProfile = await clinicAdminService.getClinicAdmin(user.adminProfile);
10398
10195
  if (!adminProfile) {
10399
10196
  console.error("[AUTH] Admin profile not found:", user.adminProfile);
10400
10197
  throw AUTH_ERRORS.NOT_FOUND;
10401
10198
  }
10402
- const clinicGroup = await clinicGroupService.getClinicGroup(
10403
- adminProfile.clinicGroupId
10404
- );
10199
+ const clinicGroup = await clinicGroupService.getClinicGroup(adminProfile.clinicGroupId);
10405
10200
  if (!clinicGroup) {
10406
- console.error(
10407
- "[AUTH] Clinic group not found:",
10408
- adminProfile.clinicGroupId
10409
- );
10201
+ console.error("[AUTH] Clinic group not found:", adminProfile.clinicGroupId);
10410
10202
  throw AUTH_ERRORS.NOT_FOUND;
10411
10203
  }
10412
10204
  return {
@@ -10419,39 +10211,6 @@ var AuthService = class extends BaseService {
10419
10211
  throw error;
10420
10212
  }
10421
10213
  }
10422
- /**
10423
- * Prijavljuje korisnika sa Facebook-om
10424
- */
10425
- async signInWithFacebook() {
10426
- const provider = new FacebookAuthProvider();
10427
- const { user: firebaseUser } = await signInWithPopup(this.auth, provider);
10428
- return this.userService.getOrCreateUser(firebaseUser);
10429
- }
10430
- /**
10431
- * Prijavljuje korisnika sa Google nalogom
10432
- */
10433
- async signInWithGoogle(initialRole = "patient" /* PATIENT */, options) {
10434
- const { user: firebaseUser } = await signInWithPopup(
10435
- this.auth,
10436
- this.googleProvider
10437
- );
10438
- const existingUser = await this.userService.getUserByEmail(
10439
- firebaseUser.email
10440
- );
10441
- if (existingUser) {
10442
- await this.userService.updateUserLoginTimestamp(existingUser.uid);
10443
- return existingUser;
10444
- }
10445
- return this.userService.createUser(firebaseUser, [initialRole], options);
10446
- }
10447
- /**
10448
- * Prijavljuje korisnika sa Apple-om
10449
- */
10450
- async signInWithApple() {
10451
- const provider = new OAuthProvider("apple.com");
10452
- const { user: firebaseUser } = await signInWithPopup(this.auth, provider);
10453
- return this.userService.getOrCreateUser(firebaseUser);
10454
- }
10455
10214
  /**
10456
10215
  * Prijavljuje korisnika anonimno
10457
10216
  */
@@ -10488,18 +10247,11 @@ var AuthService = class extends BaseService {
10488
10247
  throw AUTH_ERRORS.NOT_AUTHENTICATED;
10489
10248
  }
10490
10249
  if (!currentUser.isAnonymous) {
10491
- throw new AuthError(
10492
- "User is not anonymous",
10493
- "AUTH/NOT_ANONYMOUS_USER",
10494
- 400
10495
- );
10250
+ throw new AuthError("User is not anonymous", "AUTH/NOT_ANONYMOUS_USER", 400);
10496
10251
  }
10497
10252
  const credential = EmailAuthProvider.credential(email, password);
10498
10253
  await linkWithCredential(currentUser, credential);
10499
- return await this.userService.upgradeAnonymousUser(
10500
- currentUser.uid,
10501
- email
10502
- );
10254
+ return await this.userService.upgradeAnonymousUser(currentUser.uid, email);
10503
10255
  } catch (error) {
10504
10256
  if (error instanceof z23.ZodError) {
10505
10257
  throw AUTH_ERRORS.VALIDATION_ERROR;
@@ -10511,109 +10263,6 @@ var AuthService = class extends BaseService {
10511
10263
  throw error;
10512
10264
  }
10513
10265
  }
10514
- /**
10515
- * Upgrades an anonymous user to a regular user by signing in with a Google account.
10516
- *
10517
- * @throws {AuthError} If the user is not anonymous.
10518
- * @throws {AuthError} If the user is not authenticated.
10519
- * @throws {AuthError} If the popup window is closed by the user.
10520
- * @throws {FirebaseError} If any other Firebase error occurs.
10521
- *
10522
- * @returns {Promise<User>} The upgraded user.
10523
- */
10524
- async upgradeAnonymousUserWithGoogle() {
10525
- try {
10526
- const currentUser = this.auth.currentUser;
10527
- if (!currentUser) {
10528
- throw AUTH_ERRORS.NOT_AUTHENTICATED;
10529
- }
10530
- if (!currentUser.isAnonymous) {
10531
- throw new AuthError(
10532
- "User is not anonymous",
10533
- "AUTH/NOT_ANONYMOUS_USER",
10534
- 400
10535
- );
10536
- }
10537
- const userCredential = await signInWithPopup(
10538
- this.auth,
10539
- this.googleProvider
10540
- );
10541
- if (!userCredential) throw AUTH_ERRORS.INVALID_CREDENTIAL;
10542
- return await this.userService.upgradeAnonymousUser(
10543
- currentUser.uid,
10544
- userCredential.user.email
10545
- );
10546
- } catch (error) {
10547
- const firebaseError = error;
10548
- if (firebaseError.code === "auth/popup-closed-by-user" /* POPUP_CLOSED_BY_USER */) {
10549
- throw AUTH_ERRORS.POPUP_CLOSED;
10550
- }
10551
- throw error;
10552
- }
10553
- }
10554
- async upgradeAnonymousUserWithFacebook() {
10555
- try {
10556
- const currentUser = this.auth.currentUser;
10557
- if (!currentUser) {
10558
- throw AUTH_ERRORS.NOT_AUTHENTICATED;
10559
- }
10560
- if (!currentUser.isAnonymous) {
10561
- throw new AuthError(
10562
- "User is not anonymous",
10563
- "AUTH/NOT_ANONYMOUS_USER",
10564
- 400
10565
- );
10566
- }
10567
- this.facebookProvider.addScope("email");
10568
- const userCredential = await signInWithPopup(
10569
- this.auth,
10570
- this.facebookProvider
10571
- );
10572
- if (!userCredential) throw AUTH_ERRORS.INVALID_CREDENTIAL;
10573
- return await this.userService.upgradeAnonymousUser(
10574
- currentUser.uid,
10575
- userCredential.user.email
10576
- );
10577
- } catch (error) {
10578
- const firebaseError = error;
10579
- if (firebaseError.code === "auth/popup-closed-by-user" /* POPUP_CLOSED_BY_USER */) {
10580
- throw AUTH_ERRORS.POPUP_CLOSED;
10581
- }
10582
- throw error;
10583
- }
10584
- }
10585
- async upgradeAnonymousUserWithApple() {
10586
- try {
10587
- const currentUser = this.auth.currentUser;
10588
- if (!currentUser) {
10589
- throw AUTH_ERRORS.NOT_AUTHENTICATED;
10590
- }
10591
- if (!currentUser.isAnonymous) {
10592
- throw new AuthError(
10593
- "User is not anonymous",
10594
- "AUTH/NOT_ANONYMOUS_USER",
10595
- 400
10596
- );
10597
- }
10598
- this.appleProvider.addScope("email");
10599
- this.appleProvider.addScope("name");
10600
- const userCredential = await signInWithPopup(
10601
- this.auth,
10602
- this.appleProvider
10603
- );
10604
- if (!userCredential) throw AUTH_ERRORS.INVALID_CREDENTIAL;
10605
- return await this.userService.upgradeAnonymousUser(
10606
- currentUser.uid,
10607
- userCredential.user.email
10608
- );
10609
- } catch (error) {
10610
- const firebaseError = error;
10611
- if (firebaseError.code === "auth/popup-closed-by-user" /* POPUP_CLOSED_BY_USER */) {
10612
- throw AUTH_ERRORS.POPUP_CLOSED;
10613
- }
10614
- throw error;
10615
- }
10616
- }
10617
10266
  /**
10618
10267
  * Šalje email za resetovanje lozinke korisniku
10619
10268
  * @param email Email adresa korisnika
@@ -10694,11 +10343,7 @@ var AuthService = class extends BaseService {
10694
10343
  await this.validateSignupData(data);
10695
10344
  console.log("[AUTH] Creating Firebase user");
10696
10345
  try {
10697
- const result = await createUserWithEmailAndPassword(
10698
- this.auth,
10699
- data.email,
10700
- data.password
10701
- );
10346
+ const result = await createUserWithEmailAndPassword(this.auth, data.email, data.password);
10702
10347
  firebaseUser = result.user;
10703
10348
  console.log("[AUTH] Firebase user created successfully", {
10704
10349
  uid: firebaseUser.uid
@@ -10708,64 +10353,43 @@ var AuthService = class extends BaseService {
10708
10353
  throw handleFirebaseError(firebaseError);
10709
10354
  }
10710
10355
  console.log("[AUTH] Starting Firestore transaction");
10711
- const transactionResult = await runTransaction(
10712
- this.db,
10713
- async (transaction) => {
10714
- console.log(
10715
- "[AUTH] Transaction started - creating user and practitioner"
10716
- );
10717
- const practitionerService = new PractitionerService(
10718
- this.db,
10719
- this.auth,
10720
- this.app
10721
- );
10722
- console.log("[AUTH] Creating user document");
10723
- const user = await this.userService.createUser(
10724
- firebaseUser,
10725
- ["practitioner" /* PRACTITIONER */],
10726
- { skipProfileCreation: true }
10356
+ const transactionResult = await runTransaction(this.db, async (transaction) => {
10357
+ console.log("[AUTH] Transaction started - creating user and practitioner");
10358
+ const practitionerService = new PractitionerService(this.db, this.auth, this.app);
10359
+ console.log("[AUTH] Creating user document");
10360
+ const user = await this.userService.createUser(firebaseUser, ["practitioner" /* PRACTITIONER */], {
10361
+ skipProfileCreation: true
10362
+ });
10363
+ let practitioner;
10364
+ if (data.token) {
10365
+ console.log("[AUTH] Claiming existing practitioner profile with token");
10366
+ const claimedPractitioner = await practitionerService.validateTokenAndClaimProfile(
10367
+ data.token,
10368
+ firebaseUser.uid
10727
10369
  );
10728
- let practitioner;
10729
- if (data.token) {
10730
- console.log(
10731
- "[AUTH] Claiming existing practitioner profile with token"
10732
- );
10733
- const claimedPractitioner = await practitionerService.validateTokenAndClaimProfile(
10734
- data.token,
10735
- firebaseUser.uid
10736
- );
10737
- if (!claimedPractitioner) {
10738
- throw new Error("Invalid or expired invitation token");
10739
- }
10740
- practitioner = claimedPractitioner;
10741
- } else {
10742
- console.log("[AUTH] Creating new practitioner profile");
10743
- const practitionerData = buildPractitionerData(
10744
- data,
10745
- firebaseUser.uid
10746
- );
10747
- practitioner = await practitionerService.createPractitioner(
10748
- practitionerData
10749
- );
10370
+ if (!claimedPractitioner) {
10371
+ throw new Error("Invalid or expired invitation token");
10750
10372
  }
10751
- console.log("[AUTH] Linking practitioner to user");
10752
- await this.userService.updateUser(firebaseUser.uid, {
10753
- practitionerProfile: practitioner.id
10754
- });
10755
- console.log("[AUTH] Transaction operations completed successfully");
10756
- return { user, practitioner };
10373
+ practitioner = claimedPractitioner;
10374
+ } else {
10375
+ console.log("[AUTH] Creating new practitioner profile");
10376
+ const practitionerData = buildPractitionerData(data, firebaseUser.uid);
10377
+ practitioner = await practitionerService.createPractitioner(practitionerData);
10757
10378
  }
10758
- );
10379
+ console.log("[AUTH] Linking practitioner to user");
10380
+ await this.userService.updateUser(firebaseUser.uid, {
10381
+ practitionerProfile: practitioner.id
10382
+ });
10383
+ console.log("[AUTH] Transaction operations completed successfully");
10384
+ return { user, practitioner };
10385
+ });
10759
10386
  console.log("[AUTH] Atomic practitioner signup completed successfully", {
10760
10387
  userId: transactionResult.user.uid,
10761
10388
  practitionerId: transactionResult.practitioner.id
10762
10389
  });
10763
10390
  return transactionResult;
10764
10391
  } catch (error) {
10765
- console.error(
10766
- "[AUTH] Atomic signup failed, initiating cleanup...",
10767
- error
10768
- );
10392
+ console.error("[AUTH] Atomic signup failed, initiating cleanup...", error);
10769
10393
  if (firebaseUser) {
10770
10394
  await cleanupFirebaseUser(firebaseUser);
10771
10395
  }
@@ -10788,14 +10412,8 @@ var AuthService = class extends BaseService {
10788
10412
  }
10789
10413
  console.log("[AUTH] Email availability confirmed");
10790
10414
  if (data.token) {
10791
- const practitionerService = new PractitionerService(
10792
- this.db,
10793
- this.auth,
10794
- this.app
10795
- );
10796
- const isValidToken = await practitionerService.validateToken(
10797
- data.token
10798
- );
10415
+ const practitionerService = new PractitionerService(this.db, this.auth, this.app);
10416
+ const isValidToken = await practitionerService.validateToken(data.token);
10799
10417
  if (!isValidToken) {
10800
10418
  console.log("[AUTH] Invalid token provided:", data.token);
10801
10419
  throw new Error("Invalid or expired invitation token");
@@ -10826,16 +10444,8 @@ var AuthService = class extends BaseService {
10826
10444
  console.log("[AUTH] Starting practitioner signin process", {
10827
10445
  email
10828
10446
  });
10829
- const practitionerService = new PractitionerService(
10830
- this.db,
10831
- this.auth,
10832
- this.app
10833
- );
10834
- const { user: firebaseUser } = await signInWithEmailAndPassword(
10835
- this.auth,
10836
- email,
10837
- password
10838
- );
10447
+ const practitionerService = new PractitionerService(this.db, this.auth, this.app);
10448
+ const { user: firebaseUser } = await signInWithEmailAndPassword(this.auth, email, password);
10839
10449
  const user = await this.userService.getOrCreateUser(firebaseUser);
10840
10450
  console.log("[AUTH] User retrieved", { uid: user.uid });
10841
10451
  if (!((_a = user.roles) == null ? void 0 : _a.includes("practitioner" /* PRACTITIONER */))) {
@@ -10846,14 +10456,9 @@ var AuthService = class extends BaseService {
10846
10456
  console.error("[AUTH] User has no practitioner profile:", user.uid);
10847
10457
  throw AUTH_ERRORS.NOT_FOUND;
10848
10458
  }
10849
- const practitioner = await practitionerService.getPractitioner(
10850
- user.practitionerProfile
10851
- );
10459
+ const practitioner = await practitionerService.getPractitioner(user.practitionerProfile);
10852
10460
  if (!practitioner) {
10853
- console.error(
10854
- "[AUTH] Practitioner profile not found:",
10855
- user.practitionerProfile
10856
- );
10461
+ console.error("[AUTH] Practitioner profile not found:", user.practitionerProfile);
10857
10462
  throw AUTH_ERRORS.NOT_FOUND;
10858
10463
  }
10859
10464
  console.log("[AUTH] Practitioner signin completed successfully", {
@@ -10869,6 +10474,64 @@ var AuthService = class extends BaseService {
10869
10474
  throw error;
10870
10475
  }
10871
10476
  }
10477
+ /**
10478
+ * Signs in a user with a Google ID token from a mobile client.
10479
+ * If the user does not exist, a new user is created.
10480
+ * @param idToken - The Google ID token obtained from the mobile app.
10481
+ * @param initialRole - The role to assign to the user if they are being created.
10482
+ * @returns The signed-in or newly created user.
10483
+ */
10484
+ async signInWithGoogleIdToken(idToken, initialRole = "patient" /* PATIENT */) {
10485
+ try {
10486
+ console.log("[AUTH] Signing in with Google ID Token");
10487
+ const credential = GoogleAuthProvider.credential(idToken);
10488
+ const { user: firebaseUser } = await signInWithCredential(this.auth, credential);
10489
+ console.log("[AUTH] Firebase user signed in:", firebaseUser.uid);
10490
+ const existingUser = await this.userService.getUserById(firebaseUser.uid);
10491
+ if (existingUser) {
10492
+ console.log("[AUTH] Existing user found, returning profile:", existingUser.uid);
10493
+ return existingUser;
10494
+ }
10495
+ console.log("[AUTH] No existing user found, creating new profile for:", firebaseUser.uid);
10496
+ return this.userService.createUser(firebaseUser, [initialRole]);
10497
+ } catch (error) {
10498
+ console.error("[AUTH] Error in signInWithGoogleIdToken:", error);
10499
+ throw handleFirebaseError(error);
10500
+ }
10501
+ }
10502
+ /**
10503
+ * Links a Google account to the currently signed-in user using an ID token.
10504
+ * This is used to upgrade an anonymous user or to allow an existing user
10505
+ * to sign in with Google in the future.
10506
+ * @param idToken - The Google ID token obtained from the mobile app.
10507
+ * @returns The updated user profile.
10508
+ */
10509
+ async linkGoogleAccount(idToken) {
10510
+ try {
10511
+ console.log("[AUTH] Linking Google account with ID Token");
10512
+ const currentUser = this.auth.currentUser;
10513
+ if (!currentUser) {
10514
+ throw AUTH_ERRORS.NOT_AUTHENTICATED;
10515
+ }
10516
+ const wasAnonymous = currentUser.isAnonymous;
10517
+ console.log(`[AUTH] Current user is ${wasAnonymous ? "anonymous" : "not anonymous"}`);
10518
+ const credential = GoogleAuthProvider.credential(idToken);
10519
+ const userCredential = await linkWithCredential(currentUser, credential);
10520
+ const linkedFirebaseUser = userCredential.user;
10521
+ console.log("[AUTH] Google account linked successfully to user:", linkedFirebaseUser.uid);
10522
+ if (wasAnonymous) {
10523
+ console.log("[AUTH] Upgrading anonymous user profile");
10524
+ return await this.userService.upgradeAnonymousUser(
10525
+ linkedFirebaseUser.uid,
10526
+ linkedFirebaseUser.email
10527
+ );
10528
+ }
10529
+ return await this.userService.getUserById(linkedFirebaseUser.uid);
10530
+ } catch (error) {
10531
+ console.error("[AUTH] Error in linkGoogleAccount:", error);
10532
+ throw handleFirebaseError(error);
10533
+ }
10534
+ }
10872
10535
  };
10873
10536
 
10874
10537
  // src/services/calendar/calendar.v2.service.ts
@@ -14197,7 +13860,7 @@ import {
14197
13860
  limit as limit13,
14198
13861
  startAfter as startAfter11
14199
13862
  } from "firebase/firestore";
14200
- import { getCountFromServer } from "firebase/firestore";
13863
+ import { getCountFromServer as getCountFromServer2 } from "firebase/firestore";
14201
13864
  var DocumentationTemplateService = class extends BaseService {
14202
13865
  constructor(...args) {
14203
13866
  super(...args);
@@ -14455,7 +14118,7 @@ var DocumentationTemplateService = class extends BaseService {
14455
14118
  constraints.push(where26("sortingOrder", "==", sortingOrder));
14456
14119
  }
14457
14120
  const q = query26(this.collectionRef, ...constraints.filter((c) => c));
14458
- const snapshot = await getCountFromServer(q);
14121
+ const snapshot = await getCountFromServer2(q);
14459
14122
  return snapshot.data().count;
14460
14123
  }
14461
14124
  /**
@@ -16840,7 +16503,7 @@ import {
16840
16503
  limit as limit17,
16841
16504
  orderBy as orderBy19,
16842
16505
  startAfter as startAfter15,
16843
- getCountFromServer as getCountFromServer2
16506
+ getCountFromServer as getCountFromServer3
16844
16507
  } from "firebase/firestore";
16845
16508
 
16846
16509
  // src/backoffice/types/brand.types.ts
@@ -16916,7 +16579,7 @@ var BrandService = class extends BaseService {
16916
16579
  );
16917
16580
  }
16918
16581
  const q = query32(this.getBrandsRef(), ...constraints);
16919
- const snapshot = await getCountFromServer2(q);
16582
+ const snapshot = await getCountFromServer3(q);
16920
16583
  return snapshot.data().count;
16921
16584
  }
16922
16585
  /**
@@ -16978,7 +16641,7 @@ import {
16978
16641
  addDoc as addDoc4,
16979
16642
  collection as collection33,
16980
16643
  doc as doc33,
16981
- getCountFromServer as getCountFromServer3,
16644
+ getCountFromServer as getCountFromServer4,
16982
16645
  getDoc as getDoc35,
16983
16646
  getDocs as getDocs33,
16984
16647
  limit as limit18,
@@ -17030,7 +16693,7 @@ var CategoryService = class extends BaseService {
17030
16693
  where33("family", "==", family),
17031
16694
  where33("isActive", "==", active)
17032
16695
  );
17033
- const snapshot = await getCountFromServer3(q);
16696
+ const snapshot = await getCountFromServer4(q);
17034
16697
  counts[family] = snapshot.data().count;
17035
16698
  }
17036
16699
  return counts;
@@ -17171,7 +16834,7 @@ import {
17171
16834
  collectionGroup as collectionGroup2,
17172
16835
  deleteDoc as deleteDoc20,
17173
16836
  doc as doc34,
17174
- getCountFromServer as getCountFromServer4,
16837
+ getCountFromServer as getCountFromServer5,
17175
16838
  getDoc as getDoc36,
17176
16839
  getDocs as getDocs34,
17177
16840
  limit as limit19,
@@ -17234,7 +16897,7 @@ var SubcategoryService = class extends BaseService {
17234
16897
  const categoryId = categoryDoc.id;
17235
16898
  const subcategoriesRef = this.getSubcategoriesRef(categoryId);
17236
16899
  const q = query34(subcategoriesRef, where34("isActive", "==", active));
17237
- const snapshot = await getCountFromServer4(q);
16900
+ const snapshot = await getCountFromServer5(q);
17238
16901
  counts[categoryId] = snapshot.data().count;
17239
16902
  }
17240
16903
  return counts;
@@ -18036,7 +17699,7 @@ import {
18036
17699
  limit as limit21,
18037
17700
  orderBy as orderBy23,
18038
17701
  startAfter as startAfter19,
18039
- getCountFromServer as getCountFromServer6
17702
+ getCountFromServer as getCountFromServer7
18040
17703
  } from "firebase/firestore";
18041
17704
 
18042
17705
  // src/backoffice/types/product.types.ts
@@ -18115,7 +17778,7 @@ var ProductService = class extends BaseService {
18115
17778
  constraints.push(where36("technologyId", "==", technologyId));
18116
17779
  }
18117
17780
  const q = query36(collectionGroup3(this.db, PRODUCTS_COLLECTION), ...constraints);
18118
- const snapshot = await getCountFromServer6(q);
17781
+ const snapshot = await getCountFromServer7(q);
18119
17782
  return snapshot.data().count;
18120
17783
  }
18121
17784
  /**