@blackcode_sa/metaestetics-api 1.4.2 → 1.4.4

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
@@ -56,6 +56,11 @@ import {
56
56
  verifyPasswordResetCode,
57
57
  confirmPasswordReset
58
58
  } from "firebase/auth";
59
+ import {
60
+ collection as collection9,
61
+ query as query8,
62
+ getDocs as getDocs8
63
+ } from "firebase/firestore";
59
64
 
60
65
  // src/types/documentation-templates/index.ts
61
66
  var DocumentElementType = /* @__PURE__ */ ((DocumentElementType2) => {
@@ -116,7 +121,7 @@ var DOCUMENTATION_TEMPLATES_COLLECTION = "documentation-templates";
116
121
  var FILLED_DOCUMENTS_COLLECTION = "filled-documents";
117
122
 
118
123
  // src/services/auth.service.ts
119
- import { z as z13 } from "zod";
124
+ import { z as z15 } from "zod";
120
125
 
121
126
  // src/validations/schemas.ts
122
127
  import { z as z2 } from "zod";
@@ -546,7 +551,7 @@ import {
546
551
  where as where4,
547
552
  updateDoc as updateDoc9,
548
553
  deleteDoc as deleteDoc3,
549
- Timestamp as Timestamp8,
554
+ Timestamp as Timestamp9,
550
555
  setDoc as setDoc8,
551
556
  serverTimestamp as serverTimestamp10
552
557
  } from "firebase/firestore";
@@ -1271,9 +1276,9 @@ var addAllergyUtil = async (db, patientId, data, userRef) => {
1271
1276
  var updateAllergyUtil = async (db, patientId, data, userRef) => {
1272
1277
  const validatedData = updateAllergySchema.parse(data);
1273
1278
  const { allergyIndex, ...updateData } = validatedData;
1274
- const doc13 = await getDoc3(getMedicalInfoDocRef(db, patientId));
1275
- if (!doc13.exists()) throw new Error("Medical info not found");
1276
- const medicalInfo = doc13.data();
1279
+ const doc14 = await getDoc3(getMedicalInfoDocRef(db, patientId));
1280
+ if (!doc14.exists()) throw new Error("Medical info not found");
1281
+ const medicalInfo = doc14.data();
1277
1282
  if (allergyIndex >= medicalInfo.allergies.length) {
1278
1283
  throw new Error("Invalid allergy index");
1279
1284
  }
@@ -1289,9 +1294,9 @@ var updateAllergyUtil = async (db, patientId, data, userRef) => {
1289
1294
  });
1290
1295
  };
1291
1296
  var removeAllergyUtil = async (db, patientId, allergyIndex, userRef) => {
1292
- const doc13 = await getDoc3(getMedicalInfoDocRef(db, patientId));
1293
- if (!doc13.exists()) throw new Error("Medical info not found");
1294
- const medicalInfo = doc13.data();
1297
+ const doc14 = await getDoc3(getMedicalInfoDocRef(db, patientId));
1298
+ if (!doc14.exists()) throw new Error("Medical info not found");
1299
+ const medicalInfo = doc14.data();
1295
1300
  if (allergyIndex >= medicalInfo.allergies.length) {
1296
1301
  throw new Error("Invalid allergy index");
1297
1302
  }
@@ -1316,9 +1321,9 @@ var addBlockingConditionUtil = async (db, patientId, data, userRef) => {
1316
1321
  var updateBlockingConditionUtil = async (db, patientId, data, userRef) => {
1317
1322
  const validatedData = updateBlockingConditionSchema.parse(data);
1318
1323
  const { conditionIndex, ...updateData } = validatedData;
1319
- const doc13 = await getDoc3(getMedicalInfoDocRef(db, patientId));
1320
- if (!doc13.exists()) throw new Error("Medical info not found");
1321
- const medicalInfo = doc13.data();
1324
+ const doc14 = await getDoc3(getMedicalInfoDocRef(db, patientId));
1325
+ if (!doc14.exists()) throw new Error("Medical info not found");
1326
+ const medicalInfo = doc14.data();
1322
1327
  if (conditionIndex >= medicalInfo.blockingConditions.length) {
1323
1328
  throw new Error("Invalid blocking condition index");
1324
1329
  }
@@ -1334,9 +1339,9 @@ var updateBlockingConditionUtil = async (db, patientId, data, userRef) => {
1334
1339
  });
1335
1340
  };
1336
1341
  var removeBlockingConditionUtil = async (db, patientId, conditionIndex, userRef) => {
1337
- const doc13 = await getDoc3(getMedicalInfoDocRef(db, patientId));
1338
- if (!doc13.exists()) throw new Error("Medical info not found");
1339
- const medicalInfo = doc13.data();
1342
+ const doc14 = await getDoc3(getMedicalInfoDocRef(db, patientId));
1343
+ if (!doc14.exists()) throw new Error("Medical info not found");
1344
+ const medicalInfo = doc14.data();
1340
1345
  if (conditionIndex >= medicalInfo.blockingConditions.length) {
1341
1346
  throw new Error("Invalid blocking condition index");
1342
1347
  }
@@ -1361,9 +1366,9 @@ var addContraindicationUtil = async (db, patientId, data, userRef) => {
1361
1366
  var updateContraindicationUtil = async (db, patientId, data, userRef) => {
1362
1367
  const validatedData = updateContraindicationSchema.parse(data);
1363
1368
  const { contraindicationIndex, ...updateData } = validatedData;
1364
- const doc13 = await getDoc3(getMedicalInfoDocRef(db, patientId));
1365
- if (!doc13.exists()) throw new Error("Medical info not found");
1366
- const medicalInfo = doc13.data();
1369
+ const doc14 = await getDoc3(getMedicalInfoDocRef(db, patientId));
1370
+ if (!doc14.exists()) throw new Error("Medical info not found");
1371
+ const medicalInfo = doc14.data();
1367
1372
  if (contraindicationIndex >= medicalInfo.contraindications.length) {
1368
1373
  throw new Error("Invalid contraindication index");
1369
1374
  }
@@ -1379,9 +1384,9 @@ var updateContraindicationUtil = async (db, patientId, data, userRef) => {
1379
1384
  });
1380
1385
  };
1381
1386
  var removeContraindicationUtil = async (db, patientId, contraindicationIndex, userRef) => {
1382
- const doc13 = await getDoc3(getMedicalInfoDocRef(db, patientId));
1383
- if (!doc13.exists()) throw new Error("Medical info not found");
1384
- const medicalInfo = doc13.data();
1387
+ const doc14 = await getDoc3(getMedicalInfoDocRef(db, patientId));
1388
+ if (!doc14.exists()) throw new Error("Medical info not found");
1389
+ const medicalInfo = doc14.data();
1385
1390
  if (contraindicationIndex >= medicalInfo.contraindications.length) {
1386
1391
  throw new Error("Invalid contraindication index");
1387
1392
  }
@@ -1406,9 +1411,9 @@ var addMedicationUtil = async (db, patientId, data, userRef) => {
1406
1411
  var updateMedicationUtil = async (db, patientId, data, userRef) => {
1407
1412
  const validatedData = updateMedicationSchema.parse(data);
1408
1413
  const { medicationIndex, ...updateData } = validatedData;
1409
- const doc13 = await getDoc3(getMedicalInfoDocRef(db, patientId));
1410
- if (!doc13.exists()) throw new Error("Medical info not found");
1411
- const medicalInfo = doc13.data();
1414
+ const doc14 = await getDoc3(getMedicalInfoDocRef(db, patientId));
1415
+ if (!doc14.exists()) throw new Error("Medical info not found");
1416
+ const medicalInfo = doc14.data();
1412
1417
  if (medicationIndex >= medicalInfo.currentMedications.length) {
1413
1418
  throw new Error("Invalid medication index");
1414
1419
  }
@@ -1424,9 +1429,9 @@ var updateMedicationUtil = async (db, patientId, data, userRef) => {
1424
1429
  });
1425
1430
  };
1426
1431
  var removeMedicationUtil = async (db, patientId, medicationIndex, userRef) => {
1427
- const doc13 = await getDoc3(getMedicalInfoDocRef(db, patientId));
1428
- if (!doc13.exists()) throw new Error("Medical info not found");
1429
- const medicalInfo = doc13.data();
1432
+ const doc14 = await getDoc3(getMedicalInfoDocRef(db, patientId));
1433
+ if (!doc14.exists()) throw new Error("Medical info not found");
1434
+ const medicalInfo = doc14.data();
1430
1435
  if (medicationIndex >= medicalInfo.currentMedications.length) {
1431
1436
  throw new Error("Invalid medication index");
1432
1437
  }
@@ -2083,20 +2088,42 @@ import {
2083
2088
  updateDoc as updateDoc7,
2084
2089
  setDoc as setDoc6,
2085
2090
  deleteDoc,
2086
- Timestamp as Timestamp5,
2091
+ Timestamp as Timestamp6,
2087
2092
  serverTimestamp as serverTimestamp8
2088
2093
  } from "firebase/firestore";
2089
2094
 
2090
- // src/types/clinic/index.ts
2091
- var CLINIC_GROUPS_COLLECTION = "clinic_groups";
2092
- var CLINIC_ADMINS_COLLECTION = "clinic_admins";
2093
- var CLINICS_COLLECTION = "clinics";
2094
- var AdminTokenStatus = /* @__PURE__ */ ((AdminTokenStatus2) => {
2095
- AdminTokenStatus2["ACTIVE"] = "active";
2096
- AdminTokenStatus2["USED"] = "used";
2097
- AdminTokenStatus2["EXPIRED"] = "expired";
2098
- return AdminTokenStatus2;
2099
- })(AdminTokenStatus || {});
2095
+ // src/types/clinic/preferences.types.ts
2096
+ var PracticeType = /* @__PURE__ */ ((PracticeType2) => {
2097
+ PracticeType2["GENERAL_PRACTICE"] = "general_practice";
2098
+ PracticeType2["DENTAL"] = "dental";
2099
+ PracticeType2["DERMATOLOGY"] = "dermatology";
2100
+ PracticeType2["CARDIOLOGY"] = "cardiology";
2101
+ PracticeType2["ORTHOPEDICS"] = "orthopedics";
2102
+ PracticeType2["GYNECOLOGY"] = "gynecology";
2103
+ PracticeType2["PEDIATRICS"] = "pediatrics";
2104
+ PracticeType2["OPHTHALMOLOGY"] = "ophthalmology";
2105
+ PracticeType2["NEUROLOGY"] = "neurology";
2106
+ PracticeType2["PSYCHIATRY"] = "psychiatry";
2107
+ PracticeType2["UROLOGY"] = "urology";
2108
+ PracticeType2["ONCOLOGY"] = "oncology";
2109
+ PracticeType2["ENDOCRINOLOGY"] = "endocrinology";
2110
+ PracticeType2["GASTROENTEROLOGY"] = "gastroenterology";
2111
+ PracticeType2["PULMONOLOGY"] = "pulmonology";
2112
+ PracticeType2["RHEUMATOLOGY"] = "rheumatology";
2113
+ PracticeType2["PHYSICAL_THERAPY"] = "physical_therapy";
2114
+ PracticeType2["NUTRITION"] = "nutrition";
2115
+ PracticeType2["ALTERNATIVE_MEDICINE"] = "alternative_medicine";
2116
+ PracticeType2["OTHER"] = "other";
2117
+ return PracticeType2;
2118
+ })(PracticeType || {});
2119
+ var Language = /* @__PURE__ */ ((Language2) => {
2120
+ Language2["ENGLISH"] = "english";
2121
+ Language2["GERMAN"] = "german";
2122
+ Language2["ITALIAN"] = "italian";
2123
+ Language2["FRENCH"] = "french";
2124
+ Language2["SPANISH"] = "spanish";
2125
+ return Language2;
2126
+ })(Language || {});
2100
2127
  var ClinicTag = /* @__PURE__ */ ((ClinicTag4) => {
2101
2128
  ClinicTag4["PARKING"] = "parking";
2102
2129
  ClinicTag4["WIFI"] = "wifi";
@@ -2147,8 +2174,27 @@ var ClinicTag = /* @__PURE__ */ ((ClinicTag4) => {
2147
2174
  return ClinicTag4;
2148
2175
  })(ClinicTag || {});
2149
2176
 
2177
+ // src/types/clinic/index.ts
2178
+ var CLINIC_GROUPS_COLLECTION = "clinic_groups";
2179
+ var CLINIC_ADMINS_COLLECTION = "clinic_admins";
2180
+ var CLINICS_COLLECTION = "clinics";
2181
+ var AdminTokenStatus = /* @__PURE__ */ ((AdminTokenStatus2) => {
2182
+ AdminTokenStatus2["ACTIVE"] = "active";
2183
+ AdminTokenStatus2["USED"] = "used";
2184
+ AdminTokenStatus2["EXPIRED"] = "expired";
2185
+ return AdminTokenStatus2;
2186
+ })(AdminTokenStatus || {});
2187
+ var SubscriptionModel = /* @__PURE__ */ ((SubscriptionModel2) => {
2188
+ SubscriptionModel2["NO_SUBSCRIPTION"] = "no_subscription";
2189
+ SubscriptionModel2["BASIC"] = "basic";
2190
+ SubscriptionModel2["PREMIUM"] = "premium";
2191
+ SubscriptionModel2["ENTERPRISE"] = "enterprise";
2192
+ return SubscriptionModel2;
2193
+ })(SubscriptionModel || {});
2194
+
2150
2195
  // src/validations/clinic.schema.ts
2151
2196
  import { z as z9 } from "zod";
2197
+ import { Timestamp as Timestamp5 } from "firebase/firestore";
2152
2198
 
2153
2199
  // src/backoffice/types/static/procedure-family.types.ts
2154
2200
  var ProcedureFamily = /* @__PURE__ */ ((ProcedureFamily2) => {
@@ -2200,8 +2246,8 @@ var Currency = /* @__PURE__ */ ((Currency2) => {
2200
2246
  var clinicContactInfoSchema = z9.object({
2201
2247
  email: z9.string().email(),
2202
2248
  phoneNumber: z9.string(),
2203
- alternativePhoneNumber: z9.string().nullable(),
2204
- website: z9.string().nullable()
2249
+ alternativePhoneNumber: z9.string().nullable().optional(),
2250
+ website: z9.string().nullable().optional()
2205
2251
  });
2206
2252
  var clinicLocationSchema = z9.object({
2207
2253
  address: z9.string(),
@@ -2210,20 +2256,26 @@ var clinicLocationSchema = z9.object({
2210
2256
  postalCode: z9.string(),
2211
2257
  latitude: z9.number().min(-90).max(90),
2212
2258
  longitude: z9.number().min(-180).max(180),
2213
- geohash: z9.string().nullable()
2259
+ geohash: z9.string().nullable().optional()
2214
2260
  });
2215
2261
  var workingHoursTimeSchema = z9.object({
2216
2262
  open: z9.string(),
2217
- close: z9.string()
2263
+ close: z9.string(),
2264
+ breaks: z9.array(
2265
+ z9.object({
2266
+ start: z9.string(),
2267
+ end: z9.string()
2268
+ })
2269
+ ).optional()
2218
2270
  });
2219
2271
  var workingHoursSchema = z9.object({
2220
- monday: workingHoursTimeSchema,
2221
- tuesday: workingHoursTimeSchema,
2222
- wednesday: workingHoursTimeSchema,
2223
- thursday: workingHoursTimeSchema,
2224
- friday: workingHoursTimeSchema,
2225
- saturday: workingHoursTimeSchema,
2226
- sunday: workingHoursTimeSchema
2272
+ monday: workingHoursTimeSchema.nullable(),
2273
+ tuesday: workingHoursTimeSchema.nullable(),
2274
+ wednesday: workingHoursTimeSchema.nullable(),
2275
+ thursday: workingHoursTimeSchema.nullable(),
2276
+ friday: workingHoursTimeSchema.nullable(),
2277
+ saturday: workingHoursTimeSchema.nullable(),
2278
+ sunday: workingHoursTimeSchema.nullable()
2227
2279
  });
2228
2280
  var clinicTagsSchema = z9.object({
2229
2281
  tags: z9.array(z9.nativeEnum(ClinicTag))
@@ -2244,14 +2296,14 @@ var clinicInfoSchema = z9.object({
2244
2296
  id: z9.string(),
2245
2297
  featuredPhoto: z9.string(),
2246
2298
  name: z9.string(),
2247
- description: z9.string().nullable(),
2299
+ description: z9.string().nullable().optional(),
2248
2300
  location: clinicLocationSchema,
2249
2301
  contactInfo: clinicContactInfoSchema
2250
2302
  });
2251
2303
  var doctorInfoSchema = z9.object({
2252
2304
  id: z9.string(),
2253
2305
  name: z9.string(),
2254
- description: z9.string().nullable(),
2306
+ description: z9.string().nullable().optional(),
2255
2307
  photo: z9.string(),
2256
2308
  rating: z9.number().min(0).max(5),
2257
2309
  services: z9.array(z9.string())
@@ -2259,7 +2311,7 @@ var doctorInfoSchema = z9.object({
2259
2311
  var serviceInfoSchema = z9.object({
2260
2312
  id: z9.string(),
2261
2313
  name: z9.string(),
2262
- description: z9.string().nullable(),
2314
+ description: z9.string().nullable().optional(),
2263
2315
  photo: z9.string(),
2264
2316
  procedureFamily: z9.nativeEnum(ProcedureFamily),
2265
2317
  category: z9.string(),
@@ -2280,9 +2332,9 @@ var reviewInfoSchema = z9.object({
2280
2332
  patientId: z9.string(),
2281
2333
  patientName: z9.string(),
2282
2334
  patientPhoto: z9.string(),
2283
- createdAt: z9.date(),
2335
+ createdAt: z9.instanceof(Date).or(z9.instanceof(Timestamp5)),
2284
2336
  // Timestamp
2285
- updatedAt: z9.date()
2337
+ updatedAt: z9.instanceof(Date).or(z9.instanceof(Timestamp5))
2286
2338
  // Timestamp
2287
2339
  });
2288
2340
  var clinicAdminSchema = z9.object({
@@ -2294,8 +2346,8 @@ var clinicAdminSchema = z9.object({
2294
2346
  clinicsManagedInfo: z9.array(clinicInfoSchema),
2295
2347
  contactInfo: contactPersonSchema,
2296
2348
  roleTitle: z9.string(),
2297
- createdAt: z9.date(),
2298
- updatedAt: z9.date(),
2349
+ createdAt: z9.instanceof(Date).or(z9.instanceof(Timestamp5)),
2350
+ updatedAt: z9.instanceof(Date).or(z9.instanceof(Timestamp5)),
2299
2351
  isActive: z9.boolean()
2300
2352
  });
2301
2353
  var adminTokenSchema = z9.object({
@@ -2303,9 +2355,9 @@ var adminTokenSchema = z9.object({
2303
2355
  token: z9.string(),
2304
2356
  status: z9.nativeEnum(AdminTokenStatus),
2305
2357
  usedByUserRef: z9.string().optional(),
2306
- createdAt: z9.date(),
2358
+ createdAt: z9.instanceof(Date).or(z9.instanceof(Timestamp5)),
2307
2359
  // Timestamp
2308
- expiresAt: z9.date()
2360
+ expiresAt: z9.instanceof(Date).or(z9.instanceof(Timestamp5))
2309
2361
  // Timestamp
2310
2362
  });
2311
2363
  var createAdminTokenSchema = z9.object({
@@ -2314,7 +2366,7 @@ var createAdminTokenSchema = z9.object({
2314
2366
  var clinicGroupSchema = z9.object({
2315
2367
  id: z9.string(),
2316
2368
  name: z9.string(),
2317
- description: z9.string().nullable(),
2369
+ description: z9.string().nullable().optional(),
2318
2370
  hqLocation: clinicLocationSchema,
2319
2371
  contactInfo: clinicContactInfoSchema,
2320
2372
  contactPerson: contactPersonSchema,
@@ -2324,11 +2376,17 @@ var clinicGroupSchema = z9.object({
2324
2376
  adminsInfo: z9.array(adminInfoSchema),
2325
2377
  adminTokens: z9.array(adminTokenSchema),
2326
2378
  ownerId: z9.string(),
2327
- createdAt: z9.date(),
2379
+ createdAt: z9.instanceof(Date).or(z9.instanceof(Timestamp5)),
2328
2380
  // Timestamp
2329
- updatedAt: z9.date(),
2381
+ updatedAt: z9.instanceof(Date).or(z9.instanceof(Timestamp5)),
2330
2382
  // Timestamp
2331
- isActive: z9.boolean()
2383
+ isActive: z9.boolean(),
2384
+ logo: z9.string().optional(),
2385
+ practiceType: z9.nativeEnum(PracticeType).optional(),
2386
+ languages: z9.array(z9.nativeEnum(Language)).optional(),
2387
+ subscriptionModel: z9.nativeEnum(SubscriptionModel),
2388
+ calendarSyncEnabled: z9.boolean().optional(),
2389
+ autoConfirmAppointments: z9.boolean().optional()
2332
2390
  });
2333
2391
  var clinicReviewSchema = z9.object({
2334
2392
  id: z9.string(),
@@ -2336,9 +2394,9 @@ var clinicReviewSchema = z9.object({
2336
2394
  patientId: z9.string(),
2337
2395
  rating: z9.number().min(1).max(5),
2338
2396
  comment: z9.string(),
2339
- createdAt: z9.date(),
2397
+ createdAt: z9.instanceof(Date).or(z9.instanceof(Timestamp5)),
2340
2398
  // Timestamp
2341
- updatedAt: z9.date(),
2399
+ updatedAt: z9.instanceof(Date).or(z9.instanceof(Timestamp5)),
2342
2400
  // Timestamp
2343
2401
  isVerified: z9.boolean()
2344
2402
  });
@@ -2346,13 +2404,19 @@ var clinicSchema = z9.object({
2346
2404
  id: z9.string(),
2347
2405
  clinicGroupId: z9.string(),
2348
2406
  name: z9.string(),
2349
- description: z9.string().nullable(),
2407
+ description: z9.string().nullable().optional(),
2350
2408
  location: clinicLocationSchema,
2351
2409
  contactInfo: clinicContactInfoSchema,
2352
2410
  workingHours: workingHoursSchema,
2353
2411
  tags: z9.array(z9.nativeEnum(ClinicTag)),
2354
2412
  featuredPhotos: z9.array(z9.string()),
2355
2413
  photos: z9.array(z9.string()),
2414
+ photosWithTags: z9.array(
2415
+ z9.object({
2416
+ url: z9.string(),
2417
+ tag: z9.string()
2418
+ })
2419
+ ).optional(),
2356
2420
  doctors: z9.array(z9.string()),
2357
2421
  doctorsInfo: z9.array(doctorInfoSchema),
2358
2422
  services: z9.array(z9.string()),
@@ -2364,12 +2428,13 @@ var clinicSchema = z9.object({
2364
2428
  count: z9.number().min(0)
2365
2429
  }).nullable(),
2366
2430
  admins: z9.array(z9.string()),
2367
- createdAt: z9.date(),
2431
+ createdAt: z9.instanceof(Date).or(z9.instanceof(Timestamp5)),
2368
2432
  // Timestamp
2369
- updatedAt: z9.date(),
2433
+ updatedAt: z9.instanceof(Date).or(z9.instanceof(Timestamp5)),
2370
2434
  // Timestamp
2371
2435
  isActive: z9.boolean(),
2372
- isVerified: z9.boolean()
2436
+ isVerified: z9.boolean(),
2437
+ logo: z9.string().optional()
2373
2438
  });
2374
2439
  var createClinicAdminSchema = z9.object({
2375
2440
  userRef: z9.string(),
@@ -2382,39 +2447,99 @@ var createClinicAdminSchema = z9.object({
2382
2447
  });
2383
2448
  var createClinicGroupSchema = z9.object({
2384
2449
  name: z9.string(),
2385
- description: z9.string().nullable(),
2450
+ description: z9.string().optional(),
2386
2451
  hqLocation: clinicLocationSchema,
2387
2452
  contactInfo: clinicContactInfoSchema,
2388
2453
  contactPerson: contactPersonSchema,
2389
2454
  ownerId: z9.string(),
2390
- isActive: z9.boolean()
2455
+ isActive: z9.boolean(),
2456
+ logo: z9.string().optional(),
2457
+ practiceType: z9.nativeEnum(PracticeType).optional(),
2458
+ languages: z9.array(z9.nativeEnum(Language)).optional(),
2459
+ subscriptionModel: z9.nativeEnum(SubscriptionModel).optional().default("no_subscription" /* NO_SUBSCRIPTION */),
2460
+ calendarSyncEnabled: z9.boolean().optional(),
2461
+ autoConfirmAppointments: z9.boolean().optional()
2391
2462
  });
2392
2463
  var createClinicSchema = z9.object({
2393
2464
  clinicGroupId: z9.string(),
2394
2465
  name: z9.string(),
2395
- description: z9.string().nullable(),
2466
+ description: z9.string().optional(),
2396
2467
  location: clinicLocationSchema,
2397
2468
  contactInfo: clinicContactInfoSchema,
2398
2469
  workingHours: workingHoursSchema,
2399
2470
  tags: z9.array(z9.nativeEnum(ClinicTag)),
2400
2471
  photos: z9.array(z9.string()),
2472
+ photosWithTags: z9.array(
2473
+ z9.object({
2474
+ url: z9.string(),
2475
+ tag: z9.string()
2476
+ })
2477
+ ).optional(),
2401
2478
  doctors: z9.array(z9.string()),
2402
2479
  services: z9.array(z9.string()),
2403
2480
  admins: z9.array(z9.string()),
2404
2481
  isActive: z9.boolean(),
2405
- isVerified: z9.boolean()
2482
+ isVerified: z9.boolean(),
2483
+ logo: z9.string().optional(),
2484
+ featuredPhotos: z9.array(z9.string()).optional()
2406
2485
  });
2407
2486
  var createDefaultClinicGroupSchema = z9.object({
2408
2487
  name: z9.string(),
2409
2488
  ownerId: z9.string(),
2410
2489
  contactPerson: contactPersonSchema,
2411
- contactInfo: z9.object({
2412
- email: z9.string().email(),
2413
- phoneNumber: z9.string()
2414
- }),
2490
+ contactInfo: clinicContactInfoSchema,
2415
2491
  hqLocation: clinicLocationSchema,
2416
- isActive: z9.boolean()
2492
+ isActive: z9.boolean(),
2493
+ logo: z9.string().optional(),
2494
+ practiceType: z9.nativeEnum(PracticeType).optional(),
2495
+ languages: z9.array(z9.nativeEnum(Language)).optional(),
2496
+ subscriptionModel: z9.nativeEnum(SubscriptionModel).optional().default("no_subscription" /* NO_SUBSCRIPTION */)
2497
+ });
2498
+ var clinicAdminSignupSchema = z9.object({
2499
+ email: z9.string().email(),
2500
+ password: z9.string().min(8),
2501
+ firstName: z9.string(),
2502
+ lastName: z9.string(),
2503
+ title: z9.string(),
2504
+ phoneNumber: z9.string(),
2505
+ isCreatingNewGroup: z9.boolean(),
2506
+ inviteToken: z9.string().optional(),
2507
+ clinicGroupData: z9.object({
2508
+ name: z9.string(),
2509
+ hqLocation: clinicLocationSchema,
2510
+ logo: z9.string().optional(),
2511
+ contactInfo: clinicContactInfoSchema,
2512
+ subscriptionModel: z9.nativeEnum(SubscriptionModel).optional().default("no_subscription" /* NO_SUBSCRIPTION */)
2513
+ }).optional()
2514
+ });
2515
+ var clinicGroupSetupSchema = z9.object({
2516
+ languages: z9.array(z9.nativeEnum(Language)),
2517
+ practiceType: z9.nativeEnum(PracticeType),
2518
+ description: z9.string(),
2519
+ logo: z9.string(),
2520
+ calendarSyncEnabled: z9.boolean(),
2521
+ autoConfirmAppointments: z9.boolean()
2522
+ });
2523
+ var clinicBranchSetupSchema = z9.object({
2524
+ name: z9.string(),
2525
+ location: clinicLocationSchema,
2526
+ description: z9.string().optional(),
2527
+ contactInfo: clinicContactInfoSchema,
2528
+ workingHours: workingHoursSchema,
2529
+ tags: z9.array(z9.nativeEnum(ClinicTag)),
2530
+ logo: z9.string().optional(),
2531
+ photos: z9.array(z9.string()),
2532
+ photosWithTags: z9.array(
2533
+ z9.object({
2534
+ url: z9.string(),
2535
+ tag: z9.string()
2536
+ })
2537
+ ).optional(),
2538
+ featuredPhotos: z9.array(z9.string()).optional()
2417
2539
  });
2540
+ var updateClinicAdminSchema = createClinicAdminSchema.partial();
2541
+ var updateClinicGroupSchema = createClinicGroupSchema.partial();
2542
+ var updateClinicSchema = createClinicSchema.partial();
2418
2543
 
2419
2544
  // src/services/clinic/utils/admin.utils.ts
2420
2545
  async function createClinicAdmin(db, data, clinicGroupService) {
@@ -2481,8 +2606,8 @@ async function createClinicAdmin(db, data, clinicGroupService) {
2481
2606
  };
2482
2607
  clinicAdminSchema.parse({
2483
2608
  ...adminData,
2484
- createdAt: Timestamp5.now(),
2485
- updatedAt: Timestamp5.now()
2609
+ createdAt: Timestamp6.now(),
2610
+ updatedAt: Timestamp6.now()
2486
2611
  });
2487
2612
  await setDoc6(doc4(db, CLINIC_ADMINS_COLLECTION, adminData.id), adminData);
2488
2613
  if (clinicGroupId) {
@@ -2523,7 +2648,7 @@ async function getClinicAdminsByGroup(db, clinicGroupId) {
2523
2648
  where2("clinicGroupId", "==", clinicGroupId)
2524
2649
  );
2525
2650
  const querySnapshot = await getDocs2(q);
2526
- return querySnapshot.docs.map((doc13) => doc13.data());
2651
+ return querySnapshot.docs.map((doc14) => doc14.data());
2527
2652
  }
2528
2653
  async function updateClinicAdmin(db, adminId, data) {
2529
2654
  const admin = await getClinicAdmin(db, adminId);
@@ -2797,7 +2922,7 @@ import {
2797
2922
  updateDoc as updateDoc8,
2798
2923
  setDoc as setDoc7,
2799
2924
  deleteDoc as deleteDoc2,
2800
- Timestamp as Timestamp7,
2925
+ Timestamp as Timestamp8,
2801
2926
  serverTimestamp as serverTimestamp9
2802
2927
  } from "firebase/firestore";
2803
2928
 
@@ -2806,7 +2931,7 @@ var PRACTITIONERS_COLLECTION = "practitioners";
2806
2931
 
2807
2932
  // src/validations/practitioner.schema.ts
2808
2933
  import { z as z10 } from "zod";
2809
- import { Timestamp as Timestamp6 } from "firebase/firestore";
2934
+ import { Timestamp as Timestamp7 } from "firebase/firestore";
2810
2935
 
2811
2936
  // src/backoffice/types/static/certification.types.ts
2812
2937
  var CertificationLevel = /* @__PURE__ */ ((CertificationLevel2) => {
@@ -2839,7 +2964,7 @@ var practitionerBasicInfoSchema = z10.object({
2839
2964
  title: z10.string().min(2).max(100),
2840
2965
  email: z10.string().email(),
2841
2966
  phoneNumber: z10.string().regex(/^\+?[1-9]\d{1,14}$/, "Invalid phone number"),
2842
- dateOfBirth: z10.instanceof(Timestamp6),
2967
+ dateOfBirth: z10.instanceof(Timestamp7),
2843
2968
  gender: z10.enum(["male", "female", "other"]),
2844
2969
  profileImageUrl: z10.string().url().optional(),
2845
2970
  bio: z10.string().max(1e3).optional(),
@@ -2850,8 +2975,8 @@ var practitionerCertificationSchema = z10.object({
2850
2975
  specialties: z10.array(z10.nativeEnum(CertificationSpecialty)),
2851
2976
  licenseNumber: z10.string().min(3).max(50),
2852
2977
  issuingAuthority: z10.string().min(2).max(100),
2853
- issueDate: z10.instanceof(Timestamp6),
2854
- expiryDate: z10.instanceof(Timestamp6).optional(),
2978
+ issueDate: z10.instanceof(Timestamp7),
2979
+ expiryDate: z10.instanceof(Timestamp7).optional(),
2855
2980
  verificationStatus: z10.enum(["pending", "verified", "rejected"])
2856
2981
  });
2857
2982
  var timeSlotSchema = z10.object({
@@ -2868,8 +2993,8 @@ var practitionerWorkingHoursSchema = z10.object({
2868
2993
  friday: timeSlotSchema,
2869
2994
  saturday: timeSlotSchema,
2870
2995
  sunday: timeSlotSchema,
2871
- createdAt: z10.instanceof(Timestamp6),
2872
- updatedAt: z10.instanceof(Timestamp6)
2996
+ createdAt: z10.instanceof(Timestamp7),
2997
+ updatedAt: z10.instanceof(Timestamp7)
2873
2998
  });
2874
2999
  var practitionerReviewSchema = z10.object({
2875
3000
  id: z10.string().min(1),
@@ -2878,8 +3003,8 @@ var practitionerReviewSchema = z10.object({
2878
3003
  clinicId: z10.string().min(1),
2879
3004
  rating: z10.number().min(1).max(5),
2880
3005
  comment: z10.string().max(1e3),
2881
- createdAt: z10.instanceof(Timestamp6),
2882
- updatedAt: z10.instanceof(Timestamp6),
3006
+ createdAt: z10.instanceof(Timestamp7),
3007
+ updatedAt: z10.instanceof(Timestamp7),
2883
3008
  isVerified: z10.boolean()
2884
3009
  });
2885
3010
  var practitionerClinicProceduresSchema = z10.object({
@@ -2887,8 +3012,8 @@ var practitionerClinicProceduresSchema = z10.object({
2887
3012
  clinicId: z10.string().min(1),
2888
3013
  procedures: z10.array(z10.string()).min(1),
2889
3014
  isActive: z10.boolean(),
2890
- createdAt: z10.instanceof(Timestamp6),
2891
- updatedAt: z10.instanceof(Timestamp6)
3015
+ createdAt: z10.instanceof(Timestamp7),
3016
+ updatedAt: z10.instanceof(Timestamp7)
2892
3017
  });
2893
3018
  var practitionerSchema = z10.object({
2894
3019
  id: z10.string().min(1),
@@ -2898,8 +3023,8 @@ var practitionerSchema = z10.object({
2898
3023
  clinics: z10.array(z10.string()),
2899
3024
  isActive: z10.boolean(),
2900
3025
  isVerified: z10.boolean(),
2901
- createdAt: z10.instanceof(Timestamp6),
2902
- updatedAt: z10.instanceof(Timestamp6)
3026
+ createdAt: z10.instanceof(Timestamp7),
3027
+ updatedAt: z10.instanceof(Timestamp7)
2903
3028
  });
2904
3029
  var createPractitionerSchema = z10.object({
2905
3030
  userRef: z10.string().min(1),
@@ -2962,8 +3087,8 @@ var PractitionerService = class extends BaseService {
2962
3087
  };
2963
3088
  practitionerSchema.parse({
2964
3089
  ...practitionerData,
2965
- createdAt: Timestamp7.now(),
2966
- updatedAt: Timestamp7.now()
3090
+ createdAt: Timestamp8.now(),
3091
+ updatedAt: Timestamp8.now()
2967
3092
  });
2968
3093
  await setDoc7(
2969
3094
  doc5(this.db, PRACTITIONERS_COLLECTION, practitionerData.id),
@@ -3017,7 +3142,7 @@ var PractitionerService = class extends BaseService {
3017
3142
  where3("isActive", "==", true)
3018
3143
  );
3019
3144
  const querySnapshot = await getDocs3(q);
3020
- return querySnapshot.docs.map((doc13) => doc13.data());
3145
+ return querySnapshot.docs.map((doc14) => doc14.data());
3021
3146
  }
3022
3147
  /**
3023
3148
  * Ažurira profil zdravstvenog radnika
@@ -3048,7 +3173,7 @@ var PractitionerService = class extends BaseService {
3048
3173
  practitionerSchema.parse({
3049
3174
  ...practitionerDoc.data(),
3050
3175
  ...data,
3051
- updatedAt: Timestamp7.now()
3176
+ updatedAt: Timestamp8.now()
3052
3177
  });
3053
3178
  await updateDoc8(practitionerRef, updateData);
3054
3179
  const updatedPractitioner = await this.getPractitioner(practitionerId);
@@ -3209,6 +3334,9 @@ var UserService = class extends BaseService {
3209
3334
  profiles.patientProfile = patientProfile.id;
3210
3335
  break;
3211
3336
  case "clinic_admin" /* CLINIC_ADMIN */:
3337
+ if (options == null ? void 0 : options.skipProfileCreation) {
3338
+ break;
3339
+ }
3212
3340
  if (((_a = options == null ? void 0 : options.clinicAdminData) == null ? void 0 : _a.groupToken) && ((_b = options == null ? void 0 : options.clinicAdminData) == null ? void 0 : _b.groupId)) {
3213
3341
  const isValid = await this.getClinicAdminService().getClinicGroupService().verifyAndUseAdminToken(
3214
3342
  options.clinicAdminData.groupId,
@@ -3245,7 +3373,7 @@ var UserService = class extends BaseService {
3245
3373
  email: "",
3246
3374
  phoneNumber: "",
3247
3375
  title: "",
3248
- dateOfBirth: Timestamp8.now(),
3376
+ dateOfBirth: Timestamp9.now(),
3249
3377
  gender: "other",
3250
3378
  languages: ["Serbian"]
3251
3379
  },
@@ -3254,7 +3382,7 @@ var UserService = class extends BaseService {
3254
3382
  specialties: [],
3255
3383
  licenseNumber: "",
3256
3384
  issuingAuthority: "",
3257
- issueDate: Timestamp8.now(),
3385
+ issueDate: Timestamp9.now(),
3258
3386
  verificationStatus: "pending"
3259
3387
  },
3260
3388
  isActive: true,
@@ -3294,7 +3422,7 @@ var UserService = class extends BaseService {
3294
3422
  ];
3295
3423
  const q = query4(collection4(this.db, USERS_COLLECTION), ...constraints);
3296
3424
  const querySnapshot = await getDocs4(q);
3297
- const users = querySnapshot.docs.map((doc13) => doc13.data());
3425
+ const users = querySnapshot.docs.map((doc14) => doc14.data());
3298
3426
  return Promise.all(users.map((userData) => userSchema.parse(userData)));
3299
3427
  }
3300
3428
  /**
@@ -3448,486 +3576,347 @@ var UserService = class extends BaseService {
3448
3576
  }
3449
3577
  };
3450
3578
 
3451
- // src/services/auth.service.ts
3452
- var AuthService = class extends BaseService {
3453
- constructor(db, auth, app, userService) {
3454
- super(db, auth, app);
3455
- this.googleProvider = new GoogleAuthProvider();
3456
- this.facebookProvider = new FacebookAuthProvider();
3457
- this.appleProvider = new OAuthProvider("apple.com");
3458
- if (!userService) {
3459
- userService = new UserService(db, auth, app);
3579
+ // src/services/clinic/utils/clinic-group.utils.ts
3580
+ import {
3581
+ collection as collection5,
3582
+ doc as doc7,
3583
+ getDoc as getDoc11,
3584
+ getDocs as getDocs5,
3585
+ query as query5,
3586
+ where as where5,
3587
+ updateDoc as updateDoc10,
3588
+ setDoc as setDoc9,
3589
+ Timestamp as Timestamp10
3590
+ } from "firebase/firestore";
3591
+ import { geohashForLocation as geohashForLocation2 } from "geofire-common";
3592
+ import { z as z13 } from "zod";
3593
+ function generateId() {
3594
+ const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
3595
+ const timestamp = Date.now().toString(36);
3596
+ const randomPart = Array.from(
3597
+ { length: 12 },
3598
+ () => chars.charAt(Math.floor(Math.random() * chars.length))
3599
+ ).join("");
3600
+ return `${randomPart}-${timestamp}`;
3601
+ }
3602
+ async function createClinicGroup(db, data, ownerId, isDefault = false, clinicAdminService) {
3603
+ const validatedData = createClinicGroupSchema.parse(data);
3604
+ const owner = await clinicAdminService.getClinicAdmin(ownerId);
3605
+ if (!owner) {
3606
+ throw new Error("Owner not found or is not a clinic admin");
3607
+ }
3608
+ if (validatedData.hqLocation) {
3609
+ validatedData.hqLocation.geohash = geohashForLocation2([
3610
+ validatedData.hqLocation.latitude,
3611
+ validatedData.hqLocation.longitude
3612
+ ]);
3613
+ }
3614
+ const now = Timestamp10.now();
3615
+ const groupData = {
3616
+ ...validatedData,
3617
+ id: doc7(collection5(db, CLINIC_GROUPS_COLLECTION)).id,
3618
+ description: isDefault ? void 0 : validatedData.description || void 0,
3619
+ hqLocation: {
3620
+ ...validatedData.hqLocation,
3621
+ geohash: validatedData.hqLocation.geohash || void 0
3622
+ },
3623
+ contactInfo: {
3624
+ ...validatedData.contactInfo,
3625
+ alternativePhoneNumber: isDefault ? void 0 : validatedData.contactInfo.alternativePhoneNumber || void 0,
3626
+ website: isDefault ? void 0 : validatedData.contactInfo.website || void 0
3627
+ },
3628
+ clinics: [],
3629
+ clinicsInfo: [],
3630
+ admins: [ownerId],
3631
+ adminsInfo: [],
3632
+ adminTokens: [],
3633
+ ownerId,
3634
+ createdAt: now,
3635
+ updatedAt: now,
3636
+ isActive: true
3637
+ };
3638
+ try {
3639
+ clinicGroupSchema.parse(groupData);
3640
+ await setDoc9(doc7(db, CLINIC_GROUPS_COLLECTION, groupData.id), groupData);
3641
+ await clinicAdminService.updateClinicAdmin(ownerId, {
3642
+ clinicGroupId: groupData.id,
3643
+ isGroupOwner: true
3644
+ });
3645
+ return groupData;
3646
+ } catch (error) {
3647
+ if (error instanceof z13.ZodError) {
3648
+ throw new Error("Invalid clinic group data: " + error.message);
3460
3649
  }
3461
- this.userService = userService;
3650
+ throw error;
3462
3651
  }
3463
- /**
3464
- * Registruje novog korisnika sa email-om i lozinkom
3465
- */
3466
- async signUp(email, password, initialRole = "patient" /* PATIENT */) {
3467
- const { user: firebaseUser } = await createUserWithEmailAndPassword(
3468
- this.auth,
3469
- email,
3470
- password
3471
- );
3472
- return this.userService.createUser(firebaseUser, [initialRole]);
3652
+ }
3653
+ async function getClinicGroup(db, groupId) {
3654
+ const docRef = doc7(db, CLINIC_GROUPS_COLLECTION, groupId);
3655
+ const docSnap = await getDoc11(docRef);
3656
+ if (docSnap.exists()) {
3657
+ return docSnap.data();
3473
3658
  }
3474
- /**
3475
- * Prijavljuje korisnika sa email-om i lozinkom
3476
- */
3477
- async signIn(email, password) {
3478
- const { user: firebaseUser } = await signInWithEmailAndPassword(
3479
- this.auth,
3480
- email,
3481
- password
3482
- );
3483
- return this.userService.getOrCreateUser(firebaseUser);
3659
+ return null;
3660
+ }
3661
+ async function getAllActiveGroups(db) {
3662
+ const q = query5(
3663
+ collection5(db, CLINIC_GROUPS_COLLECTION),
3664
+ where5("isActive", "==", true)
3665
+ );
3666
+ const querySnapshot = await getDocs5(q);
3667
+ return querySnapshot.docs.map((doc14) => doc14.data());
3668
+ }
3669
+ async function updateClinicGroup(db, groupId, data) {
3670
+ const group = await getClinicGroup(db, groupId);
3671
+ if (!group) {
3672
+ throw new Error("Clinic group not found");
3484
3673
  }
3485
- /**
3486
- * Prijavljuje korisnika sa Facebook-om
3487
- */
3488
- async signInWithFacebook() {
3489
- const provider = new FacebookAuthProvider();
3490
- const { user: firebaseUser } = await signInWithPopup(this.auth, provider);
3491
- return this.userService.getOrCreateUser(firebaseUser);
3674
+ const updatedData = {
3675
+ ...data,
3676
+ updatedAt: Timestamp10.now()
3677
+ };
3678
+ await updateDoc10(doc7(db, CLINIC_GROUPS_COLLECTION, groupId), updatedData);
3679
+ const updatedGroup = await getClinicGroup(db, groupId);
3680
+ if (!updatedGroup) {
3681
+ throw new Error("Failed to retrieve updated clinic group");
3492
3682
  }
3493
- /**
3494
- * Prijavljuje korisnika sa Google nalogom
3495
- */
3496
- async signInWithGoogle(initialRole = "patient" /* PATIENT */) {
3497
- const { user: firebaseUser } = await signInWithPopup(
3498
- this.auth,
3499
- this.googleProvider
3500
- );
3501
- return this.userService.getOrCreateUser(firebaseUser);
3683
+ return updatedGroup;
3684
+ }
3685
+ async function addAdminToGroup(db, groupId, adminId) {
3686
+ const group = await getClinicGroup(db, groupId);
3687
+ if (!group) {
3688
+ throw new Error("Clinic group not found");
3502
3689
  }
3503
- /**
3504
- * Prijavljuje korisnika sa Apple-om
3505
- */
3506
- async signInWithApple() {
3507
- const provider = new OAuthProvider("apple.com");
3508
- const { user: firebaseUser } = await signInWithPopup(this.auth, provider);
3509
- return this.userService.getOrCreateUser(firebaseUser);
3690
+ if (group.admins.includes(adminId)) {
3691
+ return;
3510
3692
  }
3511
- /**
3512
- * Prijavljuje korisnika anonimno
3513
- */
3514
- async signInAnonymously() {
3515
- const { user: firebaseUser } = await firebaseSignInAnonymously(this.auth);
3516
- return this.userService.getOrCreateUser(firebaseUser);
3693
+ await updateClinicGroup(db, groupId, {
3694
+ admins: [...group.admins, adminId]
3695
+ });
3696
+ }
3697
+ async function removeAdminFromGroup(db, groupId, adminId) {
3698
+ const group = await getClinicGroup(db, groupId);
3699
+ if (!group) {
3700
+ throw new Error("Clinic group not found");
3517
3701
  }
3518
- /**
3519
- * Odjavljuje trenutnog korisnika
3520
- */
3521
- async signOut() {
3522
- await firebaseSignOut(this.auth);
3702
+ if (group.ownerId === adminId) {
3703
+ throw new Error("Cannot remove the owner from the group");
3523
3704
  }
3524
- /**
3525
- * Vraća trenutno prijavljenog korisnika
3526
- */
3527
- async getCurrentUser() {
3528
- const firebaseUser = this.auth.currentUser;
3529
- if (!firebaseUser) return null;
3530
- return this.userService.getUserById(firebaseUser.uid);
3705
+ if (!group.admins.includes(adminId)) {
3706
+ return;
3531
3707
  }
3532
- /**
3533
- * Registruje callback za promene stanja autentifikacije
3534
- */
3535
- onAuthStateChange(callback) {
3536
- return onAuthStateChanged(this.auth, callback);
3708
+ await updateClinicGroup(db, groupId, {
3709
+ admins: group.admins.filter((id) => id !== adminId)
3710
+ });
3711
+ }
3712
+ async function deactivateClinicGroup(db, groupId) {
3713
+ const group = await getClinicGroup(db, groupId);
3714
+ if (!group) {
3715
+ throw new Error("Clinic group not found");
3537
3716
  }
3538
- async upgradeAnonymousUser(email, password) {
3539
- try {
3540
- await emailSchema.parseAsync(email);
3541
- await passwordSchema.parseAsync(password);
3542
- const currentUser = this.auth.currentUser;
3543
- if (!currentUser) {
3544
- throw AUTH_ERRORS.NOT_AUTHENTICATED;
3545
- }
3546
- if (!currentUser.isAnonymous) {
3547
- throw new AuthError(
3548
- "User is not anonymous",
3549
- "AUTH/NOT_ANONYMOUS_USER",
3550
- 400
3551
- );
3552
- }
3553
- const credential = EmailAuthProvider.credential(email, password);
3554
- await linkWithCredential(currentUser, credential);
3555
- return await this.userService.upgradeAnonymousUser(
3556
- currentUser.uid,
3557
- email
3558
- );
3559
- } catch (error) {
3560
- if (error instanceof z13.ZodError) {
3561
- throw AUTH_ERRORS.VALIDATION_ERROR;
3562
- }
3563
- const firebaseError = error;
3564
- if (firebaseError.code === "auth/email-already-in-use" /* EMAIL_ALREADY_IN_USE */) {
3565
- throw AUTH_ERRORS.EMAIL_ALREADY_EXISTS;
3566
- }
3567
- throw error;
3568
- }
3717
+ await updateDoc10(doc7(db, CLINIC_GROUPS_COLLECTION, groupId), {
3718
+ isActive: false,
3719
+ updatedAt: Timestamp10.now()
3720
+ });
3721
+ }
3722
+ async function createAdminToken(db, groupId, creatorAdminId, data) {
3723
+ const group = await getClinicGroup(db, groupId);
3724
+ if (!group) {
3725
+ throw new Error("Clinic group not found");
3569
3726
  }
3570
- /**
3571
- * Upgrades an anonymous user to a regular user by signing in with a Google account.
3572
- *
3573
- * @throws {AuthError} If the user is not anonymous.
3574
- * @throws {AuthError} If the user is not authenticated.
3575
- * @throws {AuthError} If the popup window is closed by the user.
3576
- * @throws {FirebaseError} If any other Firebase error occurs.
3577
- *
3578
- * @returns {Promise<User>} The upgraded user.
3579
- */
3580
- async upgradeAnonymousUserWithGoogle() {
3581
- try {
3582
- const currentUser = this.auth.currentUser;
3583
- if (!currentUser) {
3584
- throw AUTH_ERRORS.NOT_AUTHENTICATED;
3585
- }
3586
- if (!currentUser.isAnonymous) {
3587
- throw new AuthError(
3588
- "User is not anonymous",
3589
- "AUTH/NOT_ANONYMOUS_USER",
3590
- 400
3591
- );
3592
- }
3593
- const userCredential = await signInWithPopup(
3594
- this.auth,
3595
- this.googleProvider
3596
- );
3597
- if (!userCredential) throw AUTH_ERRORS.INVALID_CREDENTIAL;
3598
- return await this.userService.upgradeAnonymousUser(
3599
- currentUser.uid,
3600
- userCredential.user.email
3601
- );
3602
- } catch (error) {
3603
- const firebaseError = error;
3604
- if (firebaseError.code === "auth/popup-closed-by-user" /* POPUP_CLOSED_BY_USER */) {
3605
- throw AUTH_ERRORS.POPUP_CLOSED;
3606
- }
3607
- throw error;
3608
- }
3727
+ if (!group.admins.includes(creatorAdminId)) {
3728
+ throw new Error("Admin does not belong to this clinic group");
3609
3729
  }
3610
- async upgradeAnonymousUserWithFacebook() {
3611
- try {
3612
- const currentUser = this.auth.currentUser;
3613
- if (!currentUser) {
3614
- throw AUTH_ERRORS.NOT_AUTHENTICATED;
3615
- }
3616
- if (!currentUser.isAnonymous) {
3617
- throw new AuthError(
3618
- "User is not anonymous",
3619
- "AUTH/NOT_ANONYMOUS_USER",
3620
- 400
3621
- );
3622
- }
3623
- this.facebookProvider.addScope("email");
3624
- const userCredential = await signInWithPopup(
3625
- this.auth,
3626
- this.facebookProvider
3627
- );
3628
- if (!userCredential) throw AUTH_ERRORS.INVALID_CREDENTIAL;
3629
- return await this.userService.upgradeAnonymousUser(
3630
- currentUser.uid,
3631
- userCredential.user.email
3632
- );
3633
- } catch (error) {
3634
- const firebaseError = error;
3635
- if (firebaseError.code === "auth/popup-closed-by-user" /* POPUP_CLOSED_BY_USER */) {
3636
- throw AUTH_ERRORS.POPUP_CLOSED;
3637
- }
3638
- throw error;
3639
- }
3730
+ const now = Timestamp10.now();
3731
+ const expiresInDays = (data == null ? void 0 : data.expiresInDays) || 7;
3732
+ const expiresAt = new Timestamp10(
3733
+ now.seconds + expiresInDays * 24 * 60 * 60,
3734
+ now.nanoseconds
3735
+ );
3736
+ const token = {
3737
+ id: generateId(),
3738
+ token: generateId(),
3739
+ status: "active" /* ACTIVE */,
3740
+ createdAt: now,
3741
+ expiresAt
3742
+ };
3743
+ await updateClinicGroup(db, groupId, {
3744
+ adminTokens: [...group.adminTokens, token]
3745
+ });
3746
+ return token;
3747
+ }
3748
+ async function verifyAndUseAdminToken(db, groupId, token, userRef) {
3749
+ const group = await getClinicGroup(db, groupId);
3750
+ if (!group) {
3751
+ throw new Error("Clinic group not found");
3640
3752
  }
3641
- async upgradeAnonymousUserWithApple() {
3642
- try {
3643
- const currentUser = this.auth.currentUser;
3644
- if (!currentUser) {
3645
- throw AUTH_ERRORS.NOT_AUTHENTICATED;
3646
- }
3647
- if (!currentUser.isAnonymous) {
3648
- throw new AuthError(
3649
- "User is not anonymous",
3650
- "AUTH/NOT_ANONYMOUS_USER",
3651
- 400
3652
- );
3653
- }
3654
- this.appleProvider.addScope("email");
3655
- this.appleProvider.addScope("name");
3656
- const userCredential = await signInWithPopup(
3657
- this.auth,
3658
- this.appleProvider
3659
- );
3660
- if (!userCredential) throw AUTH_ERRORS.INVALID_CREDENTIAL;
3661
- return await this.userService.upgradeAnonymousUser(
3662
- currentUser.uid,
3663
- userCredential.user.email
3664
- );
3665
- } catch (error) {
3666
- const firebaseError = error;
3667
- if (firebaseError.code === "auth/popup-closed-by-user" /* POPUP_CLOSED_BY_USER */) {
3668
- throw AUTH_ERRORS.POPUP_CLOSED;
3669
- }
3670
- throw error;
3671
- }
3753
+ const adminToken = group.adminTokens.find((t) => t.token === token);
3754
+ if (!adminToken) {
3755
+ throw new Error("Admin token not found");
3756
+ }
3757
+ if (adminToken.status !== "active" /* ACTIVE */) {
3758
+ throw new Error("Admin token is not active");
3759
+ }
3760
+ const now = Timestamp10.now();
3761
+ if (adminToken.expiresAt.seconds < now.seconds) {
3762
+ const updatedTokens2 = group.adminTokens.map(
3763
+ (t) => t.id === adminToken.id ? { ...t, status: "expired" /* EXPIRED */ } : t
3764
+ );
3765
+ await updateClinicGroup(db, groupId, {
3766
+ adminTokens: updatedTokens2
3767
+ });
3768
+ throw new Error("Admin token has expired");
3769
+ }
3770
+ const updatedTokens = group.adminTokens.map(
3771
+ (t) => t.id === adminToken.id ? {
3772
+ ...t,
3773
+ status: "used" /* USED */,
3774
+ usedByUserRef: userRef
3775
+ } : t
3776
+ );
3777
+ await updateClinicGroup(db, groupId, {
3778
+ adminTokens: updatedTokens
3779
+ });
3780
+ return true;
3781
+ }
3782
+ async function deleteAdminToken(db, groupId, tokenId, adminId) {
3783
+ const group = await getClinicGroup(db, groupId);
3784
+ if (!group) {
3785
+ throw new Error("Clinic group not found");
3786
+ }
3787
+ if (!group.admins.includes(adminId)) {
3788
+ throw new Error("Admin does not belong to this clinic group");
3789
+ }
3790
+ const updatedTokens = group.adminTokens.filter((t) => t.id !== tokenId);
3791
+ await updateClinicGroup(db, groupId, {
3792
+ adminTokens: updatedTokens
3793
+ });
3794
+ }
3795
+ async function getActiveAdminTokens(db, groupId, adminId) {
3796
+ const group = await getClinicGroup(db, groupId);
3797
+ if (!group) {
3798
+ throw new Error("Clinic group not found");
3799
+ }
3800
+ if (!group.admins.includes(adminId)) {
3801
+ throw new Error("Admin does not belong to this clinic group");
3802
+ }
3803
+ return group.adminTokens.filter((t) => t.status === "active" /* ACTIVE */);
3804
+ }
3805
+
3806
+ // src/services/clinic/clinic-group.service.ts
3807
+ var ClinicGroupService = class extends BaseService {
3808
+ constructor(db, auth, app, clinicAdminService) {
3809
+ super(db, auth, app);
3810
+ this.clinicAdminService = clinicAdminService;
3672
3811
  }
3673
3812
  /**
3674
- * Šalje email za resetovanje lozinke korisniku
3675
- * @param email Email adresa korisnika
3676
- * @returns Promise koji se razrešava kada je email poslat
3813
+ * Kreira novu grupaciju klinika
3677
3814
  */
3678
- async sendPasswordResetEmail(email) {
3679
- try {
3680
- await emailSchema.parseAsync(email);
3681
- await sendPasswordResetEmail(this.auth, email);
3682
- } catch (error) {
3683
- if (error instanceof z13.ZodError) {
3684
- throw AUTH_ERRORS.VALIDATION_ERROR;
3685
- }
3686
- const firebaseError = error;
3687
- if (firebaseError.code === "auth/user-not-found" /* USER_NOT_FOUND */) {
3688
- throw AUTH_ERRORS.USER_NOT_FOUND;
3689
- }
3690
- throw error;
3691
- }
3815
+ async createClinicGroup(data, ownerId, isDefault = false) {
3816
+ return createClinicGroup(
3817
+ this.db,
3818
+ data,
3819
+ ownerId,
3820
+ isDefault,
3821
+ this.clinicAdminService
3822
+ );
3692
3823
  }
3693
3824
  /**
3694
- * Verifikuje kod za resetovanje lozinke iz email linka
3695
- * @param oobCode Kod iz URL-a za resetovanje lozinke
3696
- * @returns Promise koji se razrešava sa email adresom korisnika ako je kod validan
3825
+ * Dohvata grupaciju klinika po ID-u
3697
3826
  */
3698
- async verifyPasswordResetCode(oobCode) {
3699
- try {
3700
- return await verifyPasswordResetCode(this.auth, oobCode);
3701
- } catch (error) {
3702
- const firebaseError = error;
3703
- if (firebaseError.code === "auth/expired-action-code" /* EXPIRED_ACTION_CODE */) {
3704
- throw AUTH_ERRORS.EXPIRED_ACTION_CODE;
3705
- } else if (firebaseError.code === "auth/invalid-action-code" /* INVALID_ACTION_CODE */) {
3706
- throw AUTH_ERRORS.INVALID_ACTION_CODE;
3707
- }
3708
- throw error;
3709
- }
3827
+ async getClinicGroup(groupId) {
3828
+ return getClinicGroup(this.db, groupId);
3710
3829
  }
3711
3830
  /**
3712
- * Potvrđuje resetovanje lozinke i postavlja novu lozinku
3713
- * @param oobCode Kod iz URL-a za resetovanje lozinke
3714
- * @param newPassword Nova lozinka
3715
- * @returns Promise koji se razrešava kada je lozinka promenjena
3716
- */
3717
- async confirmPasswordReset(oobCode, newPassword) {
3718
- try {
3719
- await passwordSchema.parseAsync(newPassword);
3720
- await confirmPasswordReset(this.auth, oobCode, newPassword);
3721
- } catch (error) {
3722
- if (error instanceof z13.ZodError) {
3723
- throw AUTH_ERRORS.VALIDATION_ERROR;
3724
- }
3725
- const firebaseError = error;
3726
- if (firebaseError.code === "auth/expired-action-code" /* EXPIRED_ACTION_CODE */) {
3727
- throw AUTH_ERRORS.EXPIRED_ACTION_CODE;
3728
- } else if (firebaseError.code === "auth/invalid-action-code" /* INVALID_ACTION_CODE */) {
3729
- throw AUTH_ERRORS.INVALID_ACTION_CODE;
3730
- } else if (firebaseError.code === "auth/weak-password" /* WEAK_PASSWORD */) {
3731
- throw AUTH_ERRORS.WEAK_PASSWORD;
3732
- }
3733
- throw error;
3734
- }
3735
- }
3736
- };
3737
-
3738
- // src/services/notifications/notification.service.ts
3739
- import {
3740
- collection as collection5,
3741
- doc as doc7,
3742
- getDoc as getDoc11,
3743
- getDocs as getDocs5,
3744
- query as query5,
3745
- where as where5,
3746
- updateDoc as updateDoc10,
3747
- deleteDoc as deleteDoc4,
3748
- orderBy,
3749
- Timestamp as Timestamp9,
3750
- addDoc,
3751
- writeBatch as writeBatch2
3752
- } from "firebase/firestore";
3753
-
3754
- // src/types/notifications/index.ts
3755
- var NotificationType = /* @__PURE__ */ ((NotificationType3) => {
3756
- NotificationType3["PRE_REQUIREMENT"] = "preRequirement";
3757
- NotificationType3["POST_REQUIREMENT"] = "postRequirement";
3758
- NotificationType3["APPOINTMENT_REMINDER"] = "appointmentReminder";
3759
- NotificationType3["APPOINTMENT_NOTIFICATION"] = "appointmentNotification";
3760
- return NotificationType3;
3761
- })(NotificationType || {});
3762
- var NOTIFICATIONS_COLLECTION = "notifications";
3763
- var NotificationStatus = /* @__PURE__ */ ((NotificationStatus2) => {
3764
- NotificationStatus2["PENDING"] = "pending";
3765
- NotificationStatus2["SENT"] = "sent";
3766
- NotificationStatus2["FAILED"] = "failed";
3767
- NotificationStatus2["CANCELLED"] = "cancelled";
3768
- NotificationStatus2["PARTIAL_SUCCESS"] = "partialSuccess";
3769
- return NotificationStatus2;
3770
- })(NotificationStatus || {});
3771
-
3772
- // src/services/notifications/notification.service.ts
3773
- var NotificationService = class extends BaseService {
3774
- /**
3775
- * Kreira novu notifikaciju
3831
+ * Dohvata sve aktivne grupacije klinika
3776
3832
  */
3777
- async createNotification(notification) {
3778
- const notificationsRef = collection5(this.db, NOTIFICATIONS_COLLECTION);
3779
- const now = Timestamp9.now();
3780
- const notificationData = {
3781
- ...notification,
3782
- createdAt: now,
3783
- updatedAt: now,
3784
- status: "pending" /* PENDING */,
3785
- isRead: false,
3786
- userRole: notification.userRole || "patient" /* PATIENT */
3787
- };
3788
- const docRef = await addDoc(notificationsRef, notificationData);
3789
- return {
3790
- ...notificationData,
3791
- id: docRef.id
3792
- };
3833
+ async getAllActiveGroups() {
3834
+ return getAllActiveGroups(this.db);
3793
3835
  }
3794
3836
  /**
3795
- * Dohvata notifikaciju po ID-u
3837
+ * Ažurira grupaciju klinika
3796
3838
  */
3797
- async getNotification(notificationId) {
3798
- const notificationRef = doc7(
3799
- this.db,
3800
- NOTIFICATIONS_COLLECTION,
3801
- notificationId
3802
- );
3803
- const notificationDoc = await getDoc11(notificationRef);
3804
- if (!notificationDoc.exists()) {
3805
- return null;
3806
- }
3807
- return {
3808
- id: notificationDoc.id,
3809
- ...notificationDoc.data()
3810
- };
3839
+ async updateClinicGroup(groupId, data) {
3840
+ return updateClinicGroup(this.db, groupId, data);
3811
3841
  }
3812
3842
  /**
3813
- * Dohvata sve notifikacije za korisnika
3843
+ * Dodaje admina u grupaciju
3814
3844
  */
3815
- async getUserNotifications(userId) {
3816
- const q = query5(
3817
- collection5(this.db, NOTIFICATIONS_COLLECTION),
3818
- where5("userId", "==", userId),
3819
- orderBy("notificationTime", "desc")
3820
- );
3821
- const querySnapshot = await getDocs5(q);
3822
- return querySnapshot.docs.map((doc13) => ({
3823
- id: doc13.id,
3824
- ...doc13.data()
3825
- }));
3845
+ async addAdminToGroup(groupId, adminId) {
3846
+ return addAdminToGroup(this.db, groupId, adminId);
3826
3847
  }
3827
3848
  /**
3828
- * Dohvata nepročitane notifikacije za korisnika
3849
+ * Uklanja admina iz grupacije
3829
3850
  */
3830
- async getUnreadNotifications(userId) {
3831
- const q = query5(
3832
- collection5(this.db, NOTIFICATIONS_COLLECTION),
3833
- where5("userId", "==", userId),
3834
- where5("isRead", "==", false),
3835
- orderBy("notificationTime", "desc")
3836
- );
3837
- const querySnapshot = await getDocs5(q);
3838
- return querySnapshot.docs.map((doc13) => ({
3839
- id: doc13.id,
3840
- ...doc13.data()
3841
- }));
3851
+ async removeAdminFromGroup(groupId, adminId) {
3852
+ return removeAdminFromGroup(this.db, groupId, adminId);
3842
3853
  }
3843
3854
  /**
3844
- * Označava notifikaciju kao pročitanu
3855
+ * Deaktivira grupaciju klinika
3845
3856
  */
3846
- async markAsRead(notificationId) {
3847
- const notificationRef = doc7(
3848
- this.db,
3849
- NOTIFICATIONS_COLLECTION,
3850
- notificationId
3851
- );
3852
- await updateDoc10(notificationRef, {
3853
- isRead: true,
3854
- updatedAt: Timestamp9.now()
3855
- });
3857
+ async deactivateClinicGroup(groupId) {
3858
+ return deactivateClinicGroup(this.db, groupId);
3856
3859
  }
3857
3860
  /**
3858
- * Označava sve notifikacije korisnika kao pročitane
3861
+ * Sets up additional clinic group information after initial creation
3862
+ *
3863
+ * @param groupId - The ID of the clinic group to set up
3864
+ * @param setupData - The setup data for the clinic group
3865
+ * @returns The updated clinic group
3859
3866
  */
3860
- async markAllAsRead(userId) {
3861
- const notifications = await this.getUnreadNotifications(userId);
3862
- const batch = writeBatch2(this.db);
3863
- notifications.forEach((notification) => {
3864
- const notificationRef = doc7(
3865
- this.db,
3866
- NOTIFICATIONS_COLLECTION,
3867
- notification.id
3868
- );
3869
- batch.update(notificationRef, {
3870
- isRead: true,
3871
- updatedAt: Timestamp9.now()
3872
- });
3873
- });
3874
- await batch.commit();
3867
+ async setupClinicGroup(groupId, setupData) {
3868
+ const clinicGroup = await this.getClinicGroup(groupId);
3869
+ if (!clinicGroup) {
3870
+ throw new Error(`Clinic group with ID ${groupId} not found`);
3871
+ }
3872
+ const updateData = {
3873
+ languages: setupData.languages,
3874
+ practiceType: setupData.practiceType,
3875
+ description: setupData.description,
3876
+ logo: setupData.logo,
3877
+ calendarSyncEnabled: setupData.calendarSyncEnabled,
3878
+ autoConfirmAppointments: setupData.autoConfirmAppointments
3879
+ };
3880
+ return this.updateClinicGroup(groupId, updateData);
3875
3881
  }
3876
3882
  /**
3877
- * Ažurira status notifikacije
3883
+ * Kreira admin token za grupaciju
3878
3884
  */
3879
- async updateNotificationStatus(notificationId, status) {
3880
- const notificationRef = doc7(
3885
+ async createAdminToken(groupId, creatorAdminId, data) {
3886
+ return createAdminToken(
3881
3887
  this.db,
3882
- NOTIFICATIONS_COLLECTION,
3883
- notificationId
3888
+ groupId,
3889
+ creatorAdminId,
3890
+ data
3884
3891
  );
3885
- await updateDoc10(notificationRef, {
3886
- status,
3887
- updatedAt: Timestamp9.now()
3888
- });
3889
3892
  }
3890
3893
  /**
3891
- * Briše notifikaciju
3894
+ * Verifikuje i koristi admin token
3892
3895
  */
3893
- async deleteNotification(notificationId) {
3894
- const notificationRef = doc7(
3896
+ async verifyAndUseAdminToken(groupId, token, userRef) {
3897
+ return verifyAndUseAdminToken(
3895
3898
  this.db,
3896
- NOTIFICATIONS_COLLECTION,
3897
- notificationId
3899
+ groupId,
3900
+ token,
3901
+ userRef
3898
3902
  );
3899
- await deleteDoc4(notificationRef);
3900
3903
  }
3901
3904
  /**
3902
- * Dohvata notifikacije po tipu
3905
+ * Briše admin token
3903
3906
  */
3904
- async getNotificationsByType(userId, type) {
3905
- const q = query5(
3906
- collection5(this.db, NOTIFICATIONS_COLLECTION),
3907
- where5("userId", "==", userId),
3908
- where5("notificationType", "==", type),
3909
- orderBy("notificationTime", "desc")
3907
+ async deleteAdminToken(groupId, tokenId, adminId) {
3908
+ return deleteAdminToken(
3909
+ this.db,
3910
+ groupId,
3911
+ tokenId,
3912
+ adminId
3910
3913
  );
3911
- const querySnapshot = await getDocs5(q);
3912
- return querySnapshot.docs.map((doc13) => ({
3913
- id: doc13.id,
3914
- ...doc13.data()
3915
- }));
3916
3914
  }
3917
3915
  /**
3918
- * Dohvata notifikacije za određeni termin
3916
+ * Dohvata aktivne admin tokene
3919
3917
  */
3920
- async getAppointmentNotifications(appointmentId) {
3921
- const q = query5(
3922
- collection5(this.db, NOTIFICATIONS_COLLECTION),
3923
- where5("appointmentId", "==", appointmentId),
3924
- orderBy("notificationTime", "desc")
3925
- );
3926
- const querySnapshot = await getDocs5(q);
3927
- return querySnapshot.docs.map((doc13) => ({
3928
- id: doc13.id,
3929
- ...doc13.data()
3930
- }));
3918
+ async getActiveAdminTokens(groupId, adminId) {
3919
+ return getActiveAdminTokens(this.db, groupId, adminId);
3931
3920
  }
3932
3921
  };
3933
3922
 
@@ -3940,10 +3929,10 @@ import {
3940
3929
  query as query6,
3941
3930
  where as where6,
3942
3931
  updateDoc as updateDoc11,
3943
- setDoc as setDoc9,
3944
- Timestamp as Timestamp10
3932
+ setDoc as setDoc10,
3933
+ Timestamp as Timestamp11
3945
3934
  } from "firebase/firestore";
3946
- import { geohashForLocation as geohashForLocation2 } from "geofire-common";
3935
+ import { geohashForLocation as geohashForLocation3 } from "geofire-common";
3947
3936
  import { z as z14 } from "zod";
3948
3937
  async function createClinic(db, data, creatorAdminId, clinicGroupService, clinicAdminService) {
3949
3938
  const validatedData = createClinicSchema.parse(data);
@@ -3961,12 +3950,12 @@ async function createClinic(db, data, creatorAdminId, clinicGroupService, clinic
3961
3950
  throw new Error("Clinic group not found");
3962
3951
  }
3963
3952
  if (validatedData.location) {
3964
- validatedData.location.geohash = geohashForLocation2([
3953
+ validatedData.location.geohash = geohashForLocation3([
3965
3954
  validatedData.location.latitude,
3966
3955
  validatedData.location.longitude
3967
3956
  ]);
3968
3957
  }
3969
- const now = Timestamp10.now();
3958
+ const now = Timestamp11.now();
3970
3959
  const clinicData = {
3971
3960
  ...validatedData,
3972
3961
  id: doc8(collection6(db, CLINICS_COLLECTION)).id,
@@ -3998,7 +3987,7 @@ async function createClinic(db, data, creatorAdminId, clinicGroupService, clinic
3998
3987
  };
3999
3988
  try {
4000
3989
  clinicSchema.parse(clinicData);
4001
- await setDoc9(doc8(db, CLINICS_COLLECTION, clinicData.id), clinicData);
3990
+ await setDoc10(doc8(db, CLINICS_COLLECTION, clinicData.id), clinicData);
4002
3991
  await clinicGroupService.updateClinicGroup(validatedData.clinicGroupId, {
4003
3992
  clinics: [...group.clinics, clinicData.id]
4004
3993
  });
@@ -4026,7 +4015,7 @@ async function getClinicsByGroup(db, groupId) {
4026
4015
  where6("isActive", "==", true)
4027
4016
  );
4028
4017
  const querySnapshot = await getDocs6(q);
4029
- return querySnapshot.docs.map((doc13) => doc13.data());
4018
+ return querySnapshot.docs.map((doc14) => doc14.data());
4030
4019
  }
4031
4020
  async function updateClinic(db, clinicId, data, adminId, clinicAdminService) {
4032
4021
  const clinic = await getClinic(db, clinicId);
@@ -4042,7 +4031,7 @@ async function updateClinic(db, clinicId, data, adminId, clinicAdminService) {
4042
4031
  }
4043
4032
  const updatedData = {
4044
4033
  ...data,
4045
- updatedAt: Timestamp10.now()
4034
+ updatedAt: Timestamp11.now()
4046
4035
  };
4047
4036
  await updateDoc11(doc8(db, CLINICS_COLLECTION, clinicId), updatedData);
4048
4037
  const updatedClinic = await getClinic(db, clinicId);
@@ -4065,7 +4054,7 @@ async function deactivateClinic(db, clinicId, adminId, clinicAdminService) {
4065
4054
  }
4066
4055
  await updateDoc11(doc8(db, CLINICS_COLLECTION, clinicId), {
4067
4056
  isActive: false,
4068
- updatedAt: Timestamp10.now()
4057
+ updatedAt: Timestamp11.now()
4069
4058
  });
4070
4059
  }
4071
4060
  async function getClinicsByAdmin(db, adminId, options = {}, clinicAdminService, clinicGroupService) {
@@ -4089,7 +4078,7 @@ async function getClinicsByAdmin(db, adminId, options = {}, clinicAdminService,
4089
4078
  }
4090
4079
  const q = query6(collection6(db, CLINICS_COLLECTION), ...constraints);
4091
4080
  const querySnapshot = await getDocs6(q);
4092
- return querySnapshot.docs.map((doc13) => doc13.data());
4081
+ return querySnapshot.docs.map((doc14) => doc14.data());
4093
4082
  }
4094
4083
  async function getActiveClinicsByAdmin(db, adminId, clinicAdminService, clinicGroupService) {
4095
4084
  return getClinicsByAdmin(
@@ -4105,15 +4094,15 @@ async function getActiveClinicsByAdmin(db, adminId, clinicAdminService, clinicGr
4105
4094
  import {
4106
4095
  collection as collection7,
4107
4096
  doc as doc9,
4108
- setDoc as setDoc10,
4109
- Timestamp as Timestamp11
4097
+ setDoc as setDoc11,
4098
+ Timestamp as Timestamp12
4110
4099
  } from "firebase/firestore";
4111
4100
  async function addReview(db, clinicId, review) {
4112
4101
  const clinic = await getClinic(db, clinicId);
4113
4102
  if (!clinic) {
4114
4103
  throw new Error("Clinic not found");
4115
4104
  }
4116
- const now = Timestamp11.now();
4105
+ const now = Timestamp12.now();
4117
4106
  const reviewData = {
4118
4107
  id: doc9(collection7(db, "clinic_reviews")).id,
4119
4108
  clinicId,
@@ -4125,7 +4114,7 @@ async function addReview(db, clinicId, review) {
4125
4114
  isVerified: false
4126
4115
  };
4127
4116
  clinicReviewSchema.parse(reviewData);
4128
- await setDoc10(doc9(db, "clinic_reviews", reviewData.id), reviewData);
4117
+ await setDoc11(doc9(db, "clinic_reviews", reviewData.id), reviewData);
4129
4118
  const newRating = clinic.rating ? {
4130
4119
  average: (clinic.rating.average * clinic.rating.count + review.rating) / (clinic.rating.count + 1),
4131
4120
  count: clinic.rating.count + 1
@@ -4244,8 +4233,8 @@ async function findClinicsInRadius(db, center, radiusInKm, filters) {
4244
4233
  }
4245
4234
  const q = query7(collection8(db, CLINICS_COLLECTION), ...constraints);
4246
4235
  const querySnapshot = await getDocs7(q);
4247
- for (const doc13 of querySnapshot.docs) {
4248
- const clinic = doc13.data();
4236
+ for (const doc14 of querySnapshot.docs) {
4237
+ const clinic = doc14.data();
4249
4238
  const distance = distanceBetween(
4250
4239
  [center.latitude, center.longitude],
4251
4240
  [clinic.location.latitude, clinic.location.longitude]
@@ -4387,341 +4376,660 @@ var ClinicService = class extends BaseService {
4387
4376
  this.clinicGroupService
4388
4377
  );
4389
4378
  }
4379
+ /**
4380
+ * Creates a new clinic branch for a clinic group
4381
+ *
4382
+ * @param clinicGroupId - The ID of the clinic group
4383
+ * @param setupData - The setup data for the clinic branch
4384
+ * @param adminId - The ID of the admin creating the branch
4385
+ * @returns The created clinic
4386
+ */
4387
+ async createClinicBranch(clinicGroupId, setupData, adminId) {
4388
+ const clinicGroup = await this.clinicGroupService.getClinicGroup(
4389
+ clinicGroupId
4390
+ );
4391
+ if (!clinicGroup) {
4392
+ throw new Error(`Clinic group with ID ${clinicGroupId} not found`);
4393
+ }
4394
+ const createClinicData = {
4395
+ clinicGroupId,
4396
+ name: setupData.name,
4397
+ description: setupData.description,
4398
+ location: setupData.location,
4399
+ contactInfo: setupData.contactInfo,
4400
+ workingHours: setupData.workingHours,
4401
+ tags: setupData.tags,
4402
+ photos: setupData.photos,
4403
+ photosWithTags: setupData.photosWithTags,
4404
+ doctors: [],
4405
+ services: [],
4406
+ admins: [adminId],
4407
+ isActive: true,
4408
+ isVerified: false,
4409
+ logo: setupData.logo,
4410
+ featuredPhotos: setupData.featuredPhotos || []
4411
+ };
4412
+ const clinic = await this.createClinic(createClinicData, adminId);
4413
+ return clinic;
4414
+ }
4390
4415
  };
4391
4416
 
4392
- // src/services/clinic/utils/clinic-group.utils.ts
4393
- import {
4394
- collection as collection9,
4395
- doc as doc10,
4396
- getDoc as getDoc13,
4397
- getDocs as getDocs8,
4398
- query as query8,
4399
- where as where8,
4400
- updateDoc as updateDoc12,
4401
- setDoc as setDoc11,
4402
- Timestamp as Timestamp12
4403
- } from "firebase/firestore";
4404
- import { geohashForLocation as geohashForLocation3 } from "geofire-common";
4405
- import { z as z15 } from "zod";
4406
- function generateId() {
4407
- const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
4408
- const timestamp = Date.now().toString(36);
4409
- const randomPart = Array.from(
4410
- { length: 12 },
4411
- () => chars.charAt(Math.floor(Math.random() * chars.length))
4412
- ).join("");
4413
- return `${randomPart}-${timestamp}`;
4414
- }
4415
- async function createClinicGroup(db, data, ownerId, isDefault = false, clinicAdminService) {
4416
- const validatedData = createClinicGroupSchema.parse(data);
4417
- const owner = await clinicAdminService.getClinicAdmin(ownerId);
4418
- if (!owner) {
4419
- throw new Error("Owner not found or is not a clinic admin");
4417
+ // src/services/auth.service.ts
4418
+ var AuthService = class extends BaseService {
4419
+ constructor(db, auth, app, userService) {
4420
+ super(db, auth, app);
4421
+ this.googleProvider = new GoogleAuthProvider();
4422
+ this.facebookProvider = new FacebookAuthProvider();
4423
+ this.appleProvider = new OAuthProvider("apple.com");
4424
+ if (!userService) {
4425
+ userService = new UserService(db, auth, app);
4426
+ }
4427
+ this.userService = userService;
4420
4428
  }
4421
- if (validatedData.hqLocation) {
4422
- validatedData.hqLocation.geohash = geohashForLocation3([
4423
- validatedData.hqLocation.latitude,
4424
- validatedData.hqLocation.longitude
4425
- ]);
4429
+ /**
4430
+ * Registruje novog korisnika sa email-om i lozinkom
4431
+ */
4432
+ async signUp(email, password, initialRole = "patient" /* PATIENT */) {
4433
+ const { user: firebaseUser } = await createUserWithEmailAndPassword(
4434
+ this.auth,
4435
+ email,
4436
+ password
4437
+ );
4438
+ return this.userService.createUser(firebaseUser, [initialRole]);
4426
4439
  }
4427
- const now = Timestamp12.now();
4428
- const groupData = {
4429
- ...validatedData,
4430
- id: doc10(collection9(db, CLINIC_GROUPS_COLLECTION)).id,
4431
- description: isDefault ? void 0 : validatedData.description || void 0,
4432
- hqLocation: {
4433
- ...validatedData.hqLocation,
4434
- geohash: validatedData.hqLocation.geohash || void 0
4435
- },
4436
- contactInfo: {
4437
- ...validatedData.contactInfo,
4438
- alternativePhoneNumber: isDefault ? void 0 : validatedData.contactInfo.alternativePhoneNumber || void 0,
4439
- website: isDefault ? void 0 : validatedData.contactInfo.website || void 0
4440
- },
4441
- clinics: [],
4442
- clinicsInfo: [],
4443
- admins: [ownerId],
4444
- adminsInfo: [],
4445
- adminTokens: [],
4446
- ownerId,
4447
- createdAt: now,
4448
- updatedAt: now,
4449
- isActive: true
4450
- };
4451
- try {
4452
- clinicGroupSchema.parse(groupData);
4453
- await setDoc11(doc10(db, CLINIC_GROUPS_COLLECTION, groupData.id), groupData);
4454
- await clinicAdminService.updateClinicAdmin(ownerId, {
4455
- clinicGroupId: groupData.id,
4456
- isGroupOwner: true
4457
- });
4458
- return groupData;
4459
- } catch (error) {
4460
- if (error instanceof z15.ZodError) {
4461
- throw new Error("Invalid clinic group data: " + error.message);
4440
+ /**
4441
+ * Registers a new clinic admin user with email and password
4442
+ * Can either create a new clinic group or join an existing one with a token
4443
+ *
4444
+ * @param data - Clinic admin signup data
4445
+ * @returns The created user
4446
+ */
4447
+ async signUpClinicAdmin(data) {
4448
+ try {
4449
+ await clinicAdminSignupSchema.parseAsync(data);
4450
+ const { user: firebaseUser } = await createUserWithEmailAndPassword(
4451
+ this.auth,
4452
+ data.email,
4453
+ data.password
4454
+ );
4455
+ const user = await this.userService.createUser(
4456
+ firebaseUser,
4457
+ ["clinic_admin" /* CLINIC_ADMIN */],
4458
+ {
4459
+ skipProfileCreation: true
4460
+ }
4461
+ );
4462
+ const contactPerson = {
4463
+ firstName: data.firstName,
4464
+ lastName: data.lastName,
4465
+ title: data.title,
4466
+ email: data.email,
4467
+ phoneNumber: data.phoneNumber
4468
+ };
4469
+ const clinicAdminService = new ClinicAdminService(
4470
+ this.db,
4471
+ this.auth,
4472
+ this.app
4473
+ );
4474
+ const clinicGroupService = new ClinicGroupService(
4475
+ this.db,
4476
+ this.auth,
4477
+ this.app,
4478
+ clinicAdminService
4479
+ );
4480
+ const clinicService = new ClinicService(
4481
+ this.db,
4482
+ this.auth,
4483
+ this.app,
4484
+ clinicGroupService,
4485
+ clinicAdminService
4486
+ );
4487
+ clinicAdminService.setServices(clinicGroupService, clinicService);
4488
+ if (data.isCreatingNewGroup) {
4489
+ if (!data.clinicGroupData) {
4490
+ throw new Error(
4491
+ "Clinic group data is required when creating a new group"
4492
+ );
4493
+ }
4494
+ const createClinicGroupData = {
4495
+ name: data.clinicGroupData.name,
4496
+ hqLocation: data.clinicGroupData.hqLocation,
4497
+ contactInfo: data.clinicGroupData.contactInfo,
4498
+ contactPerson,
4499
+ ownerId: firebaseUser.uid,
4500
+ isActive: true,
4501
+ logo: data.clinicGroupData.logo,
4502
+ subscriptionModel: data.clinicGroupData.subscriptionModel || "no_subscription" /* NO_SUBSCRIPTION */
4503
+ };
4504
+ await clinicGroupService.createClinicGroup(
4505
+ createClinicGroupData,
4506
+ firebaseUser.uid,
4507
+ true
4508
+ );
4509
+ } else {
4510
+ if (!data.inviteToken) {
4511
+ throw new Error(
4512
+ "Invite token is required when joining an existing group"
4513
+ );
4514
+ }
4515
+ const groupsRef = collection9(this.db, CLINIC_GROUPS_COLLECTION);
4516
+ const q = query8(groupsRef);
4517
+ const querySnapshot = await getDocs8(q);
4518
+ let foundGroup = null;
4519
+ let foundToken = null;
4520
+ for (const docSnapshot of querySnapshot.docs) {
4521
+ const group = docSnapshot.data();
4522
+ const token = group.adminTokens.find(
4523
+ (t) => t.token === data.inviteToken && t.status === "active" /* ACTIVE */ && new Date(t.expiresAt.toDate()) > /* @__PURE__ */ new Date()
4524
+ );
4525
+ if (token) {
4526
+ foundGroup = group;
4527
+ foundToken = token;
4528
+ break;
4529
+ }
4530
+ }
4531
+ if (!foundGroup || !foundToken) {
4532
+ throw new Error("Invalid or expired invite token");
4533
+ }
4534
+ const createClinicAdminData = {
4535
+ userRef: firebaseUser.uid,
4536
+ clinicGroupId: foundGroup.id,
4537
+ isGroupOwner: false,
4538
+ clinicsManaged: [],
4539
+ contactInfo: contactPerson,
4540
+ roleTitle: data.title,
4541
+ isActive: true
4542
+ };
4543
+ await clinicAdminService.createClinicAdmin(createClinicAdminData);
4544
+ await clinicGroupService.verifyAndUseAdminToken(
4545
+ foundGroup.id,
4546
+ data.inviteToken,
4547
+ firebaseUser.uid
4548
+ );
4549
+ }
4550
+ return user;
4551
+ } catch (error) {
4552
+ if (error instanceof z15.ZodError) {
4553
+ throw AUTH_ERRORS.VALIDATION_ERROR;
4554
+ }
4555
+ const firebaseError = error;
4556
+ if (firebaseError.code === "auth/email-already-in-use" /* EMAIL_ALREADY_IN_USE */) {
4557
+ throw AUTH_ERRORS.EMAIL_ALREADY_EXISTS;
4558
+ }
4559
+ throw error;
4462
4560
  }
4463
- throw error;
4464
4561
  }
4465
- }
4466
- async function getClinicGroup(db, groupId) {
4467
- const docRef = doc10(db, CLINIC_GROUPS_COLLECTION, groupId);
4468
- const docSnap = await getDoc13(docRef);
4469
- if (docSnap.exists()) {
4470
- return docSnap.data();
4471
- }
4472
- return null;
4473
- }
4474
- async function getAllActiveGroups(db) {
4475
- const q = query8(
4476
- collection9(db, CLINIC_GROUPS_COLLECTION),
4477
- where8("isActive", "==", true)
4478
- );
4479
- const querySnapshot = await getDocs8(q);
4480
- return querySnapshot.docs.map((doc13) => doc13.data());
4481
- }
4482
- async function updateClinicGroup(db, groupId, data) {
4483
- const group = await getClinicGroup(db, groupId);
4484
- if (!group) {
4485
- throw new Error("Clinic group not found");
4486
- }
4487
- const updatedData = {
4488
- ...data,
4489
- updatedAt: Timestamp12.now()
4490
- };
4491
- await updateDoc12(doc10(db, CLINIC_GROUPS_COLLECTION, groupId), updatedData);
4492
- const updatedGroup = await getClinicGroup(db, groupId);
4493
- if (!updatedGroup) {
4494
- throw new Error("Failed to retrieve updated clinic group");
4495
- }
4496
- return updatedGroup;
4497
- }
4498
- async function addAdminToGroup(db, groupId, adminId) {
4499
- const group = await getClinicGroup(db, groupId);
4500
- if (!group) {
4501
- throw new Error("Clinic group not found");
4502
- }
4503
- if (group.admins.includes(adminId)) {
4504
- return;
4505
- }
4506
- await updateClinicGroup(db, groupId, {
4507
- admins: [...group.admins, adminId]
4508
- });
4509
- }
4510
- async function removeAdminFromGroup(db, groupId, adminId) {
4511
- const group = await getClinicGroup(db, groupId);
4512
- if (!group) {
4513
- throw new Error("Clinic group not found");
4514
- }
4515
- if (group.ownerId === adminId) {
4516
- throw new Error("Cannot remove the owner from the group");
4517
- }
4518
- if (!group.admins.includes(adminId)) {
4519
- return;
4562
+ /**
4563
+ * Prijavljuje korisnika sa email-om i lozinkom
4564
+ */
4565
+ async signIn(email, password) {
4566
+ const { user: firebaseUser } = await signInWithEmailAndPassword(
4567
+ this.auth,
4568
+ email,
4569
+ password
4570
+ );
4571
+ return this.userService.getOrCreateUser(firebaseUser);
4520
4572
  }
4521
- await updateClinicGroup(db, groupId, {
4522
- admins: group.admins.filter((id) => id !== adminId)
4523
- });
4524
- }
4525
- async function deactivateClinicGroup(db, groupId) {
4526
- const group = await getClinicGroup(db, groupId);
4527
- if (!group) {
4528
- throw new Error("Clinic group not found");
4573
+ /**
4574
+ * Prijavljuje korisnika sa Facebook-om
4575
+ */
4576
+ async signInWithFacebook() {
4577
+ const provider = new FacebookAuthProvider();
4578
+ const { user: firebaseUser } = await signInWithPopup(this.auth, provider);
4579
+ return this.userService.getOrCreateUser(firebaseUser);
4529
4580
  }
4530
- await updateDoc12(doc10(db, CLINIC_GROUPS_COLLECTION, groupId), {
4531
- isActive: false,
4532
- updatedAt: Timestamp12.now()
4533
- });
4534
- }
4535
- async function createAdminToken(db, groupId, creatorAdminId, data) {
4536
- const group = await getClinicGroup(db, groupId);
4537
- if (!group) {
4538
- throw new Error("Clinic group not found");
4581
+ /**
4582
+ * Prijavljuje korisnika sa Google nalogom
4583
+ */
4584
+ async signInWithGoogle(initialRole = "patient" /* PATIENT */) {
4585
+ const { user: firebaseUser } = await signInWithPopup(
4586
+ this.auth,
4587
+ this.googleProvider
4588
+ );
4589
+ return this.userService.getOrCreateUser(firebaseUser);
4539
4590
  }
4540
- if (!group.admins.includes(creatorAdminId)) {
4541
- throw new Error("Admin does not belong to this clinic group");
4591
+ /**
4592
+ * Prijavljuje korisnika sa Apple-om
4593
+ */
4594
+ async signInWithApple() {
4595
+ const provider = new OAuthProvider("apple.com");
4596
+ const { user: firebaseUser } = await signInWithPopup(this.auth, provider);
4597
+ return this.userService.getOrCreateUser(firebaseUser);
4542
4598
  }
4543
- const now = Timestamp12.now();
4544
- const expiresInDays = (data == null ? void 0 : data.expiresInDays) || 7;
4545
- const expiresAt = new Timestamp12(
4546
- now.seconds + expiresInDays * 24 * 60 * 60,
4547
- now.nanoseconds
4548
- );
4549
- const token = {
4550
- id: generateId(),
4551
- token: generateId(),
4552
- status: "active" /* ACTIVE */,
4553
- createdAt: now,
4554
- expiresAt
4555
- };
4556
- await updateClinicGroup(db, groupId, {
4557
- adminTokens: [...group.adminTokens, token]
4558
- });
4559
- return token;
4560
- }
4561
- async function verifyAndUseAdminToken(db, groupId, token, userRef) {
4562
- const group = await getClinicGroup(db, groupId);
4563
- if (!group) {
4564
- throw new Error("Clinic group not found");
4599
+ /**
4600
+ * Prijavljuje korisnika anonimno
4601
+ */
4602
+ async signInAnonymously() {
4603
+ const { user: firebaseUser } = await firebaseSignInAnonymously(this.auth);
4604
+ return this.userService.getOrCreateUser(firebaseUser);
4565
4605
  }
4566
- const adminToken = group.adminTokens.find((t) => t.token === token);
4567
- if (!adminToken) {
4568
- throw new Error("Admin token not found");
4606
+ /**
4607
+ * Odjavljuje trenutnog korisnika
4608
+ */
4609
+ async signOut() {
4610
+ await firebaseSignOut(this.auth);
4569
4611
  }
4570
- if (adminToken.status !== "active" /* ACTIVE */) {
4571
- throw new Error("Admin token is not active");
4612
+ /**
4613
+ * Vraća trenutno prijavljenog korisnika
4614
+ */
4615
+ async getCurrentUser() {
4616
+ const firebaseUser = this.auth.currentUser;
4617
+ if (!firebaseUser) return null;
4618
+ return this.userService.getUserById(firebaseUser.uid);
4572
4619
  }
4573
- const now = Timestamp12.now();
4574
- if (adminToken.expiresAt.seconds < now.seconds) {
4575
- const updatedTokens2 = group.adminTokens.map(
4576
- (t) => t.id === adminToken.id ? { ...t, status: "expired" /* EXPIRED */ } : t
4577
- );
4578
- await updateClinicGroup(db, groupId, {
4579
- adminTokens: updatedTokens2
4580
- });
4581
- throw new Error("Admin token has expired");
4620
+ /**
4621
+ * Registruje callback za promene stanja autentifikacije
4622
+ */
4623
+ onAuthStateChange(callback) {
4624
+ return onAuthStateChanged(this.auth, callback);
4582
4625
  }
4583
- const updatedTokens = group.adminTokens.map(
4584
- (t) => t.id === adminToken.id ? {
4585
- ...t,
4586
- status: "used" /* USED */,
4587
- usedByUserRef: userRef
4588
- } : t
4589
- );
4590
- await updateClinicGroup(db, groupId, {
4591
- adminTokens: updatedTokens
4592
- });
4593
- return true;
4594
- }
4595
- async function deleteAdminToken(db, groupId, tokenId, adminId) {
4596
- const group = await getClinicGroup(db, groupId);
4597
- if (!group) {
4598
- throw new Error("Clinic group not found");
4626
+ async upgradeAnonymousUser(email, password) {
4627
+ try {
4628
+ await emailSchema.parseAsync(email);
4629
+ await passwordSchema.parseAsync(password);
4630
+ const currentUser = this.auth.currentUser;
4631
+ if (!currentUser) {
4632
+ throw AUTH_ERRORS.NOT_AUTHENTICATED;
4633
+ }
4634
+ if (!currentUser.isAnonymous) {
4635
+ throw new AuthError(
4636
+ "User is not anonymous",
4637
+ "AUTH/NOT_ANONYMOUS_USER",
4638
+ 400
4639
+ );
4640
+ }
4641
+ const credential = EmailAuthProvider.credential(email, password);
4642
+ await linkWithCredential(currentUser, credential);
4643
+ return await this.userService.upgradeAnonymousUser(
4644
+ currentUser.uid,
4645
+ email
4646
+ );
4647
+ } catch (error) {
4648
+ if (error instanceof z15.ZodError) {
4649
+ throw AUTH_ERRORS.VALIDATION_ERROR;
4650
+ }
4651
+ const firebaseError = error;
4652
+ if (firebaseError.code === "auth/email-already-in-use" /* EMAIL_ALREADY_IN_USE */) {
4653
+ throw AUTH_ERRORS.EMAIL_ALREADY_EXISTS;
4654
+ }
4655
+ throw error;
4656
+ }
4599
4657
  }
4600
- if (!group.admins.includes(adminId)) {
4601
- throw new Error("Admin does not belong to this clinic group");
4658
+ /**
4659
+ * Upgrades an anonymous user to a regular user by signing in with a Google account.
4660
+ *
4661
+ * @throws {AuthError} If the user is not anonymous.
4662
+ * @throws {AuthError} If the user is not authenticated.
4663
+ * @throws {AuthError} If the popup window is closed by the user.
4664
+ * @throws {FirebaseError} If any other Firebase error occurs.
4665
+ *
4666
+ * @returns {Promise<User>} The upgraded user.
4667
+ */
4668
+ async upgradeAnonymousUserWithGoogle() {
4669
+ try {
4670
+ const currentUser = this.auth.currentUser;
4671
+ if (!currentUser) {
4672
+ throw AUTH_ERRORS.NOT_AUTHENTICATED;
4673
+ }
4674
+ if (!currentUser.isAnonymous) {
4675
+ throw new AuthError(
4676
+ "User is not anonymous",
4677
+ "AUTH/NOT_ANONYMOUS_USER",
4678
+ 400
4679
+ );
4680
+ }
4681
+ const userCredential = await signInWithPopup(
4682
+ this.auth,
4683
+ this.googleProvider
4684
+ );
4685
+ if (!userCredential) throw AUTH_ERRORS.INVALID_CREDENTIAL;
4686
+ return await this.userService.upgradeAnonymousUser(
4687
+ currentUser.uid,
4688
+ userCredential.user.email
4689
+ );
4690
+ } catch (error) {
4691
+ const firebaseError = error;
4692
+ if (firebaseError.code === "auth/popup-closed-by-user" /* POPUP_CLOSED_BY_USER */) {
4693
+ throw AUTH_ERRORS.POPUP_CLOSED;
4694
+ }
4695
+ throw error;
4696
+ }
4602
4697
  }
4603
- const updatedTokens = group.adminTokens.filter((t) => t.id !== tokenId);
4604
- await updateClinicGroup(db, groupId, {
4605
- adminTokens: updatedTokens
4606
- });
4607
- }
4608
- async function getActiveAdminTokens(db, groupId, adminId) {
4609
- const group = await getClinicGroup(db, groupId);
4610
- if (!group) {
4611
- throw new Error("Clinic group not found");
4698
+ async upgradeAnonymousUserWithFacebook() {
4699
+ try {
4700
+ const currentUser = this.auth.currentUser;
4701
+ if (!currentUser) {
4702
+ throw AUTH_ERRORS.NOT_AUTHENTICATED;
4703
+ }
4704
+ if (!currentUser.isAnonymous) {
4705
+ throw new AuthError(
4706
+ "User is not anonymous",
4707
+ "AUTH/NOT_ANONYMOUS_USER",
4708
+ 400
4709
+ );
4710
+ }
4711
+ this.facebookProvider.addScope("email");
4712
+ const userCredential = await signInWithPopup(
4713
+ this.auth,
4714
+ this.facebookProvider
4715
+ );
4716
+ if (!userCredential) throw AUTH_ERRORS.INVALID_CREDENTIAL;
4717
+ return await this.userService.upgradeAnonymousUser(
4718
+ currentUser.uid,
4719
+ userCredential.user.email
4720
+ );
4721
+ } catch (error) {
4722
+ const firebaseError = error;
4723
+ if (firebaseError.code === "auth/popup-closed-by-user" /* POPUP_CLOSED_BY_USER */) {
4724
+ throw AUTH_ERRORS.POPUP_CLOSED;
4725
+ }
4726
+ throw error;
4727
+ }
4612
4728
  }
4613
- if (!group.admins.includes(adminId)) {
4614
- throw new Error("Admin does not belong to this clinic group");
4729
+ async upgradeAnonymousUserWithApple() {
4730
+ try {
4731
+ const currentUser = this.auth.currentUser;
4732
+ if (!currentUser) {
4733
+ throw AUTH_ERRORS.NOT_AUTHENTICATED;
4734
+ }
4735
+ if (!currentUser.isAnonymous) {
4736
+ throw new AuthError(
4737
+ "User is not anonymous",
4738
+ "AUTH/NOT_ANONYMOUS_USER",
4739
+ 400
4740
+ );
4741
+ }
4742
+ this.appleProvider.addScope("email");
4743
+ this.appleProvider.addScope("name");
4744
+ const userCredential = await signInWithPopup(
4745
+ this.auth,
4746
+ this.appleProvider
4747
+ );
4748
+ if (!userCredential) throw AUTH_ERRORS.INVALID_CREDENTIAL;
4749
+ return await this.userService.upgradeAnonymousUser(
4750
+ currentUser.uid,
4751
+ userCredential.user.email
4752
+ );
4753
+ } catch (error) {
4754
+ const firebaseError = error;
4755
+ if (firebaseError.code === "auth/popup-closed-by-user" /* POPUP_CLOSED_BY_USER */) {
4756
+ throw AUTH_ERRORS.POPUP_CLOSED;
4757
+ }
4758
+ throw error;
4759
+ }
4615
4760
  }
4616
- return group.adminTokens.filter((t) => t.status === "active" /* ACTIVE */);
4617
- }
4618
-
4619
- // src/services/clinic/clinic-group.service.ts
4620
- var ClinicGroupService = class extends BaseService {
4621
- constructor(db, auth, app, clinicAdminService) {
4622
- super(db, auth, app);
4623
- this.clinicAdminService = clinicAdminService;
4761
+ /**
4762
+ * Šalje email za resetovanje lozinke korisniku
4763
+ * @param email Email adresa korisnika
4764
+ * @returns Promise koji se razrešava kada je email poslat
4765
+ */
4766
+ async sendPasswordResetEmail(email) {
4767
+ try {
4768
+ await emailSchema.parseAsync(email);
4769
+ await sendPasswordResetEmail(this.auth, email);
4770
+ } catch (error) {
4771
+ if (error instanceof z15.ZodError) {
4772
+ throw AUTH_ERRORS.VALIDATION_ERROR;
4773
+ }
4774
+ const firebaseError = error;
4775
+ if (firebaseError.code === "auth/user-not-found" /* USER_NOT_FOUND */) {
4776
+ throw AUTH_ERRORS.USER_NOT_FOUND;
4777
+ }
4778
+ throw error;
4779
+ }
4624
4780
  }
4625
4781
  /**
4626
- * Kreira novu grupaciju klinika
4782
+ * Verifikuje kod za resetovanje lozinke iz email linka
4783
+ * @param oobCode Kod iz URL-a za resetovanje lozinke
4784
+ * @returns Promise koji se razrešava sa email adresom korisnika ako je kod validan
4627
4785
  */
4628
- async createClinicGroup(data, ownerId, isDefault = false) {
4629
- return createClinicGroup(
4630
- this.db,
4631
- data,
4632
- ownerId,
4633
- isDefault,
4634
- this.clinicAdminService
4635
- );
4786
+ async verifyPasswordResetCode(oobCode) {
4787
+ try {
4788
+ return await verifyPasswordResetCode(this.auth, oobCode);
4789
+ } catch (error) {
4790
+ const firebaseError = error;
4791
+ if (firebaseError.code === "auth/expired-action-code" /* EXPIRED_ACTION_CODE */) {
4792
+ throw AUTH_ERRORS.EXPIRED_ACTION_CODE;
4793
+ } else if (firebaseError.code === "auth/invalid-action-code" /* INVALID_ACTION_CODE */) {
4794
+ throw AUTH_ERRORS.INVALID_ACTION_CODE;
4795
+ }
4796
+ throw error;
4797
+ }
4636
4798
  }
4637
4799
  /**
4638
- * Dohvata grupaciju klinika po ID-u
4800
+ * Potvrđuje resetovanje lozinke i postavlja novu lozinku
4801
+ * @param oobCode Kod iz URL-a za resetovanje lozinke
4802
+ * @param newPassword Nova lozinka
4803
+ * @returns Promise koji se razrešava kada je lozinka promenjena
4639
4804
  */
4640
- async getClinicGroup(groupId) {
4641
- return getClinicGroup(this.db, groupId);
4805
+ async confirmPasswordReset(oobCode, newPassword) {
4806
+ try {
4807
+ await passwordSchema.parseAsync(newPassword);
4808
+ await confirmPasswordReset(this.auth, oobCode, newPassword);
4809
+ } catch (error) {
4810
+ if (error instanceof z15.ZodError) {
4811
+ throw AUTH_ERRORS.VALIDATION_ERROR;
4812
+ }
4813
+ const firebaseError = error;
4814
+ if (firebaseError.code === "auth/expired-action-code" /* EXPIRED_ACTION_CODE */) {
4815
+ throw AUTH_ERRORS.EXPIRED_ACTION_CODE;
4816
+ } else if (firebaseError.code === "auth/invalid-action-code" /* INVALID_ACTION_CODE */) {
4817
+ throw AUTH_ERRORS.INVALID_ACTION_CODE;
4818
+ } else if (firebaseError.code === "auth/weak-password" /* WEAK_PASSWORD */) {
4819
+ throw AUTH_ERRORS.WEAK_PASSWORD;
4820
+ }
4821
+ throw error;
4822
+ }
4642
4823
  }
4824
+ };
4825
+
4826
+ // src/services/notifications/notification.service.ts
4827
+ import {
4828
+ collection as collection10,
4829
+ doc as doc11,
4830
+ getDoc as getDoc14,
4831
+ getDocs as getDocs9,
4832
+ query as query9,
4833
+ where as where9,
4834
+ updateDoc as updateDoc13,
4835
+ deleteDoc as deleteDoc6,
4836
+ orderBy,
4837
+ Timestamp as Timestamp14,
4838
+ addDoc,
4839
+ writeBatch as writeBatch2
4840
+ } from "firebase/firestore";
4841
+
4842
+ // src/types/notifications/index.ts
4843
+ var NotificationType = /* @__PURE__ */ ((NotificationType3) => {
4844
+ NotificationType3["PRE_REQUIREMENT"] = "preRequirement";
4845
+ NotificationType3["POST_REQUIREMENT"] = "postRequirement";
4846
+ NotificationType3["APPOINTMENT_REMINDER"] = "appointmentReminder";
4847
+ NotificationType3["APPOINTMENT_NOTIFICATION"] = "appointmentNotification";
4848
+ return NotificationType3;
4849
+ })(NotificationType || {});
4850
+ var NOTIFICATIONS_COLLECTION = "notifications";
4851
+ var NotificationStatus = /* @__PURE__ */ ((NotificationStatus2) => {
4852
+ NotificationStatus2["PENDING"] = "pending";
4853
+ NotificationStatus2["SENT"] = "sent";
4854
+ NotificationStatus2["FAILED"] = "failed";
4855
+ NotificationStatus2["CANCELLED"] = "cancelled";
4856
+ NotificationStatus2["PARTIAL_SUCCESS"] = "partialSuccess";
4857
+ return NotificationStatus2;
4858
+ })(NotificationStatus || {});
4859
+
4860
+ // src/services/notifications/notification.service.ts
4861
+ var NotificationService = class extends BaseService {
4643
4862
  /**
4644
- * Dohvata sve aktivne grupacije klinika
4863
+ * Kreira novu notifikaciju
4645
4864
  */
4646
- async getAllActiveGroups() {
4647
- return getAllActiveGroups(this.db);
4865
+ async createNotification(notification) {
4866
+ const notificationsRef = collection10(this.db, NOTIFICATIONS_COLLECTION);
4867
+ const now = Timestamp14.now();
4868
+ const notificationData = {
4869
+ ...notification,
4870
+ createdAt: now,
4871
+ updatedAt: now,
4872
+ status: "pending" /* PENDING */,
4873
+ isRead: false,
4874
+ userRole: notification.userRole || "patient" /* PATIENT */
4875
+ };
4876
+ const docRef = await addDoc(notificationsRef, notificationData);
4877
+ return {
4878
+ ...notificationData,
4879
+ id: docRef.id
4880
+ };
4648
4881
  }
4649
4882
  /**
4650
- * Ažurira grupaciju klinika
4883
+ * Dohvata notifikaciju po ID-u
4651
4884
  */
4652
- async updateClinicGroup(groupId, data) {
4653
- return updateClinicGroup(this.db, groupId, data);
4885
+ async getNotification(notificationId) {
4886
+ const notificationRef = doc11(
4887
+ this.db,
4888
+ NOTIFICATIONS_COLLECTION,
4889
+ notificationId
4890
+ );
4891
+ const notificationDoc = await getDoc14(notificationRef);
4892
+ if (!notificationDoc.exists()) {
4893
+ return null;
4894
+ }
4895
+ return {
4896
+ id: notificationDoc.id,
4897
+ ...notificationDoc.data()
4898
+ };
4654
4899
  }
4655
4900
  /**
4656
- * Dodaje admina u grupaciju
4901
+ * Dohvata sve notifikacije za korisnika
4657
4902
  */
4658
- async addAdminToGroup(groupId, adminId) {
4659
- return addAdminToGroup(this.db, groupId, adminId);
4903
+ async getUserNotifications(userId) {
4904
+ const q = query9(
4905
+ collection10(this.db, NOTIFICATIONS_COLLECTION),
4906
+ where9("userId", "==", userId),
4907
+ orderBy("notificationTime", "desc")
4908
+ );
4909
+ const querySnapshot = await getDocs9(q);
4910
+ return querySnapshot.docs.map((doc14) => ({
4911
+ id: doc14.id,
4912
+ ...doc14.data()
4913
+ }));
4660
4914
  }
4661
4915
  /**
4662
- * Uklanja admina iz grupacije
4916
+ * Dohvata nepročitane notifikacije za korisnika
4663
4917
  */
4664
- async removeAdminFromGroup(groupId, adminId) {
4665
- return removeAdminFromGroup(this.db, groupId, adminId);
4918
+ async getUnreadNotifications(userId) {
4919
+ const q = query9(
4920
+ collection10(this.db, NOTIFICATIONS_COLLECTION),
4921
+ where9("userId", "==", userId),
4922
+ where9("isRead", "==", false),
4923
+ orderBy("notificationTime", "desc")
4924
+ );
4925
+ const querySnapshot = await getDocs9(q);
4926
+ return querySnapshot.docs.map((doc14) => ({
4927
+ id: doc14.id,
4928
+ ...doc14.data()
4929
+ }));
4666
4930
  }
4667
4931
  /**
4668
- * Deaktivira grupaciju klinika
4932
+ * Označava notifikaciju kao pročitanu
4669
4933
  */
4670
- async deactivateClinicGroup(groupId) {
4671
- return deactivateClinicGroup(this.db, groupId);
4934
+ async markAsRead(notificationId) {
4935
+ const notificationRef = doc11(
4936
+ this.db,
4937
+ NOTIFICATIONS_COLLECTION,
4938
+ notificationId
4939
+ );
4940
+ await updateDoc13(notificationRef, {
4941
+ isRead: true,
4942
+ updatedAt: Timestamp14.now()
4943
+ });
4672
4944
  }
4673
4945
  /**
4674
- * Kreira admin token za grupaciju
4946
+ * Označava sve notifikacije korisnika kao pročitane
4675
4947
  */
4676
- async createAdminToken(groupId, creatorAdminId, data) {
4677
- return createAdminToken(
4948
+ async markAllAsRead(userId) {
4949
+ const notifications = await this.getUnreadNotifications(userId);
4950
+ const batch = writeBatch2(this.db);
4951
+ notifications.forEach((notification) => {
4952
+ const notificationRef = doc11(
4953
+ this.db,
4954
+ NOTIFICATIONS_COLLECTION,
4955
+ notification.id
4956
+ );
4957
+ batch.update(notificationRef, {
4958
+ isRead: true,
4959
+ updatedAt: Timestamp14.now()
4960
+ });
4961
+ });
4962
+ await batch.commit();
4963
+ }
4964
+ /**
4965
+ * Ažurira status notifikacije
4966
+ */
4967
+ async updateNotificationStatus(notificationId, status) {
4968
+ const notificationRef = doc11(
4678
4969
  this.db,
4679
- groupId,
4680
- creatorAdminId,
4681
- data
4970
+ NOTIFICATIONS_COLLECTION,
4971
+ notificationId
4682
4972
  );
4973
+ await updateDoc13(notificationRef, {
4974
+ status,
4975
+ updatedAt: Timestamp14.now()
4976
+ });
4683
4977
  }
4684
4978
  /**
4685
- * Verifikuje i koristi admin token
4979
+ * Briše notifikaciju
4686
4980
  */
4687
- async verifyAndUseAdminToken(groupId, token, userRef) {
4688
- return verifyAndUseAdminToken(
4981
+ async deleteNotification(notificationId) {
4982
+ const notificationRef = doc11(
4689
4983
  this.db,
4690
- groupId,
4691
- token,
4692
- userRef
4984
+ NOTIFICATIONS_COLLECTION,
4985
+ notificationId
4693
4986
  );
4987
+ await deleteDoc6(notificationRef);
4694
4988
  }
4695
4989
  /**
4696
- * Briše admin token
4990
+ * Dohvata notifikacije po tipu
4697
4991
  */
4698
- async deleteAdminToken(groupId, tokenId, adminId) {
4699
- return deleteAdminToken(
4700
- this.db,
4701
- groupId,
4702
- tokenId,
4703
- adminId
4992
+ async getNotificationsByType(userId, type) {
4993
+ const q = query9(
4994
+ collection10(this.db, NOTIFICATIONS_COLLECTION),
4995
+ where9("userId", "==", userId),
4996
+ where9("notificationType", "==", type),
4997
+ orderBy("notificationTime", "desc")
4704
4998
  );
4999
+ const querySnapshot = await getDocs9(q);
5000
+ return querySnapshot.docs.map((doc14) => ({
5001
+ id: doc14.id,
5002
+ ...doc14.data()
5003
+ }));
4705
5004
  }
4706
5005
  /**
4707
- * Dohvata aktivne admin tokene
5006
+ * Dohvata notifikacije za određeni termin
4708
5007
  */
4709
- async getActiveAdminTokens(groupId, adminId) {
4710
- return getActiveAdminTokens(this.db, groupId, adminId);
5008
+ async getAppointmentNotifications(appointmentId) {
5009
+ const q = query9(
5010
+ collection10(this.db, NOTIFICATIONS_COLLECTION),
5011
+ where9("appointmentId", "==", appointmentId),
5012
+ orderBy("notificationTime", "desc")
5013
+ );
5014
+ const querySnapshot = await getDocs9(q);
5015
+ return querySnapshot.docs.map((doc14) => ({
5016
+ id: doc14.id,
5017
+ ...doc14.data()
5018
+ }));
4711
5019
  }
4712
5020
  };
4713
5021
 
4714
5022
  // src/services/documentation-templates/documentation-template.service.ts
4715
5023
  import {
4716
- collection as collection10,
4717
- doc as doc11,
4718
- getDoc as getDoc14,
4719
- getDocs as getDocs9,
4720
- setDoc as setDoc12,
4721
- updateDoc as updateDoc13,
5024
+ collection as collection11,
5025
+ doc as doc12,
5026
+ getDoc as getDoc15,
5027
+ getDocs as getDocs10,
5028
+ setDoc as setDoc13,
5029
+ updateDoc as updateDoc14,
4722
5030
  deleteDoc as deleteDoc7,
4723
- query as query9,
4724
- where as where9,
5031
+ query as query10,
5032
+ where as where10,
4725
5033
  orderBy as orderBy2,
4726
5034
  limit,
4727
5035
  startAfter
@@ -4729,7 +5037,7 @@ import {
4729
5037
  var DocumentationTemplateService = class extends BaseService {
4730
5038
  constructor() {
4731
5039
  super(...arguments);
4732
- this.collectionRef = collection10(
5040
+ this.collectionRef = collection11(
4733
5041
  this.db,
4734
5042
  DOCUMENTATION_TEMPLATES_COLLECTION
4735
5043
  );
@@ -4760,8 +5068,8 @@ var DocumentationTemplateService = class extends BaseService {
4760
5068
  isActive: true,
4761
5069
  tags: validatedData.tags || []
4762
5070
  };
4763
- const docRef = doc11(this.collectionRef, templateId);
4764
- await setDoc12(docRef, template);
5071
+ const docRef = doc12(this.collectionRef, templateId);
5072
+ await setDoc13(docRef, template);
4765
5073
  return template;
4766
5074
  }
4767
5075
  /**
@@ -4770,8 +5078,8 @@ var DocumentationTemplateService = class extends BaseService {
4770
5078
  * @returns The template or null if not found
4771
5079
  */
4772
5080
  async getTemplateById(templateId) {
4773
- const docRef = doc11(this.collectionRef, templateId);
4774
- const docSnap = await getDoc14(docRef);
5081
+ const docRef = doc12(this.collectionRef, templateId);
5082
+ const docSnap = await getDoc15(docRef);
4775
5083
  if (!docSnap.exists()) {
4776
5084
  return null;
4777
5085
  }
@@ -4802,8 +5110,8 @@ var DocumentationTemplateService = class extends BaseService {
4802
5110
  updatedAt: Date.now(),
4803
5111
  version: template.version + 1
4804
5112
  };
4805
- const docRef = doc11(this.collectionRef, templateId);
4806
- await updateDoc13(docRef, updateData);
5113
+ const docRef = doc12(this.collectionRef, templateId);
5114
+ await updateDoc14(docRef, updateData);
4807
5115
  return {
4808
5116
  ...template,
4809
5117
  ...updateData
@@ -4814,7 +5122,7 @@ var DocumentationTemplateService = class extends BaseService {
4814
5122
  * @param templateId - ID of the template to delete
4815
5123
  */
4816
5124
  async deleteTemplate(templateId) {
4817
- const docRef = doc11(this.collectionRef, templateId);
5125
+ const docRef = doc12(this.collectionRef, templateId);
4818
5126
  await deleteDoc7(docRef);
4819
5127
  }
4820
5128
  /**
@@ -4824,21 +5132,21 @@ var DocumentationTemplateService = class extends BaseService {
4824
5132
  * @returns Array of templates and the last document for pagination
4825
5133
  */
4826
5134
  async getActiveTemplates(pageSize = 20, lastDoc) {
4827
- let q = query9(
5135
+ let q = query10(
4828
5136
  this.collectionRef,
4829
- where9("isActive", "==", true),
5137
+ where10("isActive", "==", true),
4830
5138
  orderBy2("updatedAt", "desc"),
4831
5139
  limit(pageSize)
4832
5140
  );
4833
5141
  if (lastDoc) {
4834
- q = query9(q, startAfter(lastDoc));
5142
+ q = query10(q, startAfter(lastDoc));
4835
5143
  }
4836
- const querySnapshot = await getDocs9(q);
5144
+ const querySnapshot = await getDocs10(q);
4837
5145
  const templates = [];
4838
5146
  let lastVisible = null;
4839
- querySnapshot.forEach((doc13) => {
4840
- templates.push(doc13.data());
4841
- lastVisible = doc13;
5147
+ querySnapshot.forEach((doc14) => {
5148
+ templates.push(doc14.data());
5149
+ lastVisible = doc14;
4842
5150
  });
4843
5151
  return {
4844
5152
  templates,
@@ -4853,22 +5161,22 @@ var DocumentationTemplateService = class extends BaseService {
4853
5161
  * @returns Array of templates and the last document for pagination
4854
5162
  */
4855
5163
  async getTemplatesByTags(tags, pageSize = 20, lastDoc) {
4856
- let q = query9(
5164
+ let q = query10(
4857
5165
  this.collectionRef,
4858
- where9("isActive", "==", true),
4859
- where9("tags", "array-contains-any", tags),
5166
+ where10("isActive", "==", true),
5167
+ where10("tags", "array-contains-any", tags),
4860
5168
  orderBy2("updatedAt", "desc"),
4861
5169
  limit(pageSize)
4862
5170
  );
4863
5171
  if (lastDoc) {
4864
- q = query9(q, startAfter(lastDoc));
5172
+ q = query10(q, startAfter(lastDoc));
4865
5173
  }
4866
- const querySnapshot = await getDocs9(q);
5174
+ const querySnapshot = await getDocs10(q);
4867
5175
  const templates = [];
4868
5176
  let lastVisible = null;
4869
- querySnapshot.forEach((doc13) => {
4870
- templates.push(doc13.data());
4871
- lastVisible = doc13;
5177
+ querySnapshot.forEach((doc14) => {
5178
+ templates.push(doc14.data());
5179
+ lastVisible = doc14;
4872
5180
  });
4873
5181
  return {
4874
5182
  templates,
@@ -4883,21 +5191,21 @@ var DocumentationTemplateService = class extends BaseService {
4883
5191
  * @returns Array of templates and the last document for pagination
4884
5192
  */
4885
5193
  async getTemplatesByCreator(userId, pageSize = 20, lastDoc) {
4886
- let q = query9(
5194
+ let q = query10(
4887
5195
  this.collectionRef,
4888
- where9("createdBy", "==", userId),
5196
+ where10("createdBy", "==", userId),
4889
5197
  orderBy2("updatedAt", "desc"),
4890
5198
  limit(pageSize)
4891
5199
  );
4892
5200
  if (lastDoc) {
4893
- q = query9(q, startAfter(lastDoc));
5201
+ q = query10(q, startAfter(lastDoc));
4894
5202
  }
4895
- const querySnapshot = await getDocs9(q);
5203
+ const querySnapshot = await getDocs10(q);
4896
5204
  const templates = [];
4897
5205
  let lastVisible = null;
4898
- querySnapshot.forEach((doc13) => {
4899
- templates.push(doc13.data());
4900
- lastVisible = doc13;
5206
+ querySnapshot.forEach((doc14) => {
5207
+ templates.push(doc14.data());
5208
+ lastVisible = doc14;
4901
5209
  });
4902
5210
  return {
4903
5211
  templates,
@@ -4908,14 +5216,14 @@ var DocumentationTemplateService = class extends BaseService {
4908
5216
 
4909
5217
  // src/services/documentation-templates/filled-document.service.ts
4910
5218
  import {
4911
- collection as collection11,
4912
- doc as doc12,
4913
- getDoc as getDoc15,
4914
- getDocs as getDocs10,
4915
- setDoc as setDoc13,
4916
- updateDoc as updateDoc14,
4917
- query as query10,
4918
- where as where10,
5219
+ collection as collection12,
5220
+ doc as doc13,
5221
+ getDoc as getDoc16,
5222
+ getDocs as getDocs11,
5223
+ setDoc as setDoc14,
5224
+ updateDoc as updateDoc15,
5225
+ query as query11,
5226
+ where as where11,
4919
5227
  orderBy as orderBy3,
4920
5228
  limit as limit2,
4921
5229
  startAfter as startAfter2
@@ -4923,7 +5231,7 @@ import {
4923
5231
  var FilledDocumentService = class extends BaseService {
4924
5232
  constructor(...args) {
4925
5233
  super(...args);
4926
- this.collectionRef = collection11(
5234
+ this.collectionRef = collection12(
4927
5235
  this.db,
4928
5236
  FILLED_DOCUMENTS_COLLECTION
4929
5237
  );
@@ -4956,8 +5264,8 @@ var FilledDocumentService = class extends BaseService {
4956
5264
  values: {},
4957
5265
  status: "draft" /* DRAFT */
4958
5266
  };
4959
- const docRef = doc12(this.collectionRef, documentId);
4960
- await setDoc13(docRef, filledDocument);
5267
+ const docRef = doc13(this.collectionRef, documentId);
5268
+ await setDoc14(docRef, filledDocument);
4961
5269
  return filledDocument;
4962
5270
  }
4963
5271
  /**
@@ -4966,8 +5274,8 @@ var FilledDocumentService = class extends BaseService {
4966
5274
  * @returns The filled document or null if not found
4967
5275
  */
4968
5276
  async getFilledDocumentById(documentId) {
4969
- const docRef = doc12(this.collectionRef, documentId);
4970
- const docSnap = await getDoc15(docRef);
5277
+ const docRef = doc13(this.collectionRef, documentId);
5278
+ const docSnap = await getDoc16(docRef);
4971
5279
  if (!docSnap.exists()) {
4972
5280
  return null;
4973
5281
  }
@@ -4995,8 +5303,8 @@ var FilledDocumentService = class extends BaseService {
4995
5303
  if (status) {
4996
5304
  updateData.status = status;
4997
5305
  }
4998
- const docRef = doc12(this.collectionRef, documentId);
4999
- await updateDoc14(docRef, updateData);
5306
+ const docRef = doc13(this.collectionRef, documentId);
5307
+ await updateDoc15(docRef, updateData);
5000
5308
  return {
5001
5309
  ...filledDocument,
5002
5310
  ...updateData
@@ -5010,21 +5318,21 @@ var FilledDocumentService = class extends BaseService {
5010
5318
  * @returns Array of filled documents and the last document for pagination
5011
5319
  */
5012
5320
  async getFilledDocumentsByPatient(patientId, pageSize = 20, lastDoc) {
5013
- let q = query10(
5321
+ let q = query11(
5014
5322
  this.collectionRef,
5015
- where10("patientId", "==", patientId),
5323
+ where11("patientId", "==", patientId),
5016
5324
  orderBy3("updatedAt", "desc"),
5017
5325
  limit2(pageSize)
5018
5326
  );
5019
5327
  if (lastDoc) {
5020
- q = query10(q, startAfter2(lastDoc));
5328
+ q = query11(q, startAfter2(lastDoc));
5021
5329
  }
5022
- const querySnapshot = await getDocs10(q);
5330
+ const querySnapshot = await getDocs11(q);
5023
5331
  const documents = [];
5024
5332
  let lastVisible = null;
5025
- querySnapshot.forEach((doc13) => {
5026
- documents.push(doc13.data());
5027
- lastVisible = doc13;
5333
+ querySnapshot.forEach((doc14) => {
5334
+ documents.push(doc14.data());
5335
+ lastVisible = doc14;
5028
5336
  });
5029
5337
  return {
5030
5338
  documents,
@@ -5039,21 +5347,21 @@ var FilledDocumentService = class extends BaseService {
5039
5347
  * @returns Array of filled documents and the last document for pagination
5040
5348
  */
5041
5349
  async getFilledDocumentsByPractitioner(practitionerId, pageSize = 20, lastDoc) {
5042
- let q = query10(
5350
+ let q = query11(
5043
5351
  this.collectionRef,
5044
- where10("practitionerId", "==", practitionerId),
5352
+ where11("practitionerId", "==", practitionerId),
5045
5353
  orderBy3("updatedAt", "desc"),
5046
5354
  limit2(pageSize)
5047
5355
  );
5048
5356
  if (lastDoc) {
5049
- q = query10(q, startAfter2(lastDoc));
5357
+ q = query11(q, startAfter2(lastDoc));
5050
5358
  }
5051
- const querySnapshot = await getDocs10(q);
5359
+ const querySnapshot = await getDocs11(q);
5052
5360
  const documents = [];
5053
5361
  let lastVisible = null;
5054
- querySnapshot.forEach((doc13) => {
5055
- documents.push(doc13.data());
5056
- lastVisible = doc13;
5362
+ querySnapshot.forEach((doc14) => {
5363
+ documents.push(doc14.data());
5364
+ lastVisible = doc14;
5057
5365
  });
5058
5366
  return {
5059
5367
  documents,
@@ -5068,21 +5376,21 @@ var FilledDocumentService = class extends BaseService {
5068
5376
  * @returns Array of filled documents and the last document for pagination
5069
5377
  */
5070
5378
  async getFilledDocumentsByClinic(clinicId, pageSize = 20, lastDoc) {
5071
- let q = query10(
5379
+ let q = query11(
5072
5380
  this.collectionRef,
5073
- where10("clinicId", "==", clinicId),
5381
+ where11("clinicId", "==", clinicId),
5074
5382
  orderBy3("updatedAt", "desc"),
5075
5383
  limit2(pageSize)
5076
5384
  );
5077
5385
  if (lastDoc) {
5078
- q = query10(q, startAfter2(lastDoc));
5386
+ q = query11(q, startAfter2(lastDoc));
5079
5387
  }
5080
- const querySnapshot = await getDocs10(q);
5388
+ const querySnapshot = await getDocs11(q);
5081
5389
  const documents = [];
5082
5390
  let lastVisible = null;
5083
- querySnapshot.forEach((doc13) => {
5084
- documents.push(doc13.data());
5085
- lastVisible = doc13;
5391
+ querySnapshot.forEach((doc14) => {
5392
+ documents.push(doc14.data());
5393
+ lastVisible = doc14;
5086
5394
  });
5087
5395
  return {
5088
5396
  documents,
@@ -5097,21 +5405,21 @@ var FilledDocumentService = class extends BaseService {
5097
5405
  * @returns Array of filled documents and the last document for pagination
5098
5406
  */
5099
5407
  async getFilledDocumentsByTemplate(templateId, pageSize = 20, lastDoc) {
5100
- let q = query10(
5408
+ let q = query11(
5101
5409
  this.collectionRef,
5102
- where10("templateId", "==", templateId),
5410
+ where11("templateId", "==", templateId),
5103
5411
  orderBy3("updatedAt", "desc"),
5104
5412
  limit2(pageSize)
5105
5413
  );
5106
5414
  if (lastDoc) {
5107
- q = query10(q, startAfter2(lastDoc));
5415
+ q = query11(q, startAfter2(lastDoc));
5108
5416
  }
5109
- const querySnapshot = await getDocs10(q);
5417
+ const querySnapshot = await getDocs11(q);
5110
5418
  const documents = [];
5111
5419
  let lastVisible = null;
5112
- querySnapshot.forEach((doc13) => {
5113
- documents.push(doc13.data());
5114
- lastVisible = doc13;
5420
+ querySnapshot.forEach((doc14) => {
5421
+ documents.push(doc14.data());
5422
+ lastVisible = doc14;
5115
5423
  });
5116
5424
  return {
5117
5425
  documents,
@@ -5126,21 +5434,21 @@ var FilledDocumentService = class extends BaseService {
5126
5434
  * @returns Array of filled documents and the last document for pagination
5127
5435
  */
5128
5436
  async getFilledDocumentsByStatus(status, pageSize = 20, lastDoc) {
5129
- let q = query10(
5437
+ let q = query11(
5130
5438
  this.collectionRef,
5131
- where10("status", "==", status),
5439
+ where11("status", "==", status),
5132
5440
  orderBy3("updatedAt", "desc"),
5133
5441
  limit2(pageSize)
5134
5442
  );
5135
5443
  if (lastDoc) {
5136
- q = query10(q, startAfter2(lastDoc));
5444
+ q = query11(q, startAfter2(lastDoc));
5137
5445
  }
5138
- const querySnapshot = await getDocs10(q);
5446
+ const querySnapshot = await getDocs11(q);
5139
5447
  const documents = [];
5140
5448
  let lastVisible = null;
5141
- querySnapshot.forEach((doc13) => {
5142
- documents.push(doc13.data());
5143
- lastVisible = doc13;
5449
+ querySnapshot.forEach((doc14) => {
5450
+ documents.push(doc14.data());
5451
+ lastVisible = doc14;
5144
5452
  });
5145
5453
  return {
5146
5454
  documents,
@@ -5265,8 +5573,11 @@ export {
5265
5573
  blockingConditionSchema,
5266
5574
  clinicAdminOptionsSchema,
5267
5575
  clinicAdminSchema,
5576
+ clinicAdminSignupSchema,
5577
+ clinicBranchSetupSchema,
5268
5578
  clinicContactInfoSchema,
5269
5579
  clinicGroupSchema,
5580
+ clinicGroupSetupSchema,
5270
5581
  clinicInfoSchema,
5271
5582
  clinicLocationSchema,
5272
5583
  clinicReviewSchema,
@@ -5321,6 +5632,9 @@ export {
5321
5632
  timestampSchema,
5322
5633
  updateAllergySchema,
5323
5634
  updateBlockingConditionSchema,
5635
+ updateClinicAdminSchema,
5636
+ updateClinicGroupSchema,
5637
+ updateClinicSchema,
5324
5638
  updateContraindicationSchema,
5325
5639
  updateDocumentTemplateSchema,
5326
5640
  updateMedicationSchema,