@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.d.mts +2842 -407
- package/dist/index.d.ts +2842 -407
- package/dist/index.js +1285 -969
- package/dist/index.mjs +1207 -893
- package/package.json +1 -1
- package/src/services/auth.service.ts +177 -0
- package/src/services/clinic/clinic-group.service.ts +34 -0
- package/src/services/clinic/clinic.service.ts +53 -0
- package/src/services/user.service.ts +8 -0
- package/src/types/clinic/index.ts +165 -100
- package/src/types/clinic/preferences.types.ts +101 -0
- package/src/validations/clinic.schema.ts +162 -34
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
|
|
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
|
|
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
|
|
1275
|
-
if (!
|
|
1276
|
-
const medicalInfo =
|
|
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
|
|
1293
|
-
if (!
|
|
1294
|
-
const medicalInfo =
|
|
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
|
|
1320
|
-
if (!
|
|
1321
|
-
const medicalInfo =
|
|
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
|
|
1338
|
-
if (!
|
|
1339
|
-
const medicalInfo =
|
|
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
|
|
1365
|
-
if (!
|
|
1366
|
-
const medicalInfo =
|
|
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
|
|
1383
|
-
if (!
|
|
1384
|
-
const medicalInfo =
|
|
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
|
|
1410
|
-
if (!
|
|
1411
|
-
const medicalInfo =
|
|
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
|
|
1428
|
-
if (!
|
|
1429
|
-
const medicalInfo =
|
|
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
|
|
2091
|
+
Timestamp as Timestamp6,
|
|
2087
2092
|
serverTimestamp as serverTimestamp8
|
|
2088
2093
|
} from "firebase/firestore";
|
|
2089
2094
|
|
|
2090
|
-
// src/types/clinic/
|
|
2091
|
-
var
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
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.
|
|
2335
|
+
createdAt: z9.instanceof(Date).or(z9.instanceof(Timestamp5)),
|
|
2284
2336
|
// Timestamp
|
|
2285
|
-
updatedAt: z9.
|
|
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.
|
|
2298
|
-
updatedAt: z9.
|
|
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.
|
|
2358
|
+
createdAt: z9.instanceof(Date).or(z9.instanceof(Timestamp5)),
|
|
2307
2359
|
// Timestamp
|
|
2308
|
-
expiresAt: z9.
|
|
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.
|
|
2379
|
+
createdAt: z9.instanceof(Date).or(z9.instanceof(Timestamp5)),
|
|
2328
2380
|
// Timestamp
|
|
2329
|
-
updatedAt: z9.
|
|
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.
|
|
2397
|
+
createdAt: z9.instanceof(Date).or(z9.instanceof(Timestamp5)),
|
|
2340
2398
|
// Timestamp
|
|
2341
|
-
updatedAt: z9.
|
|
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.
|
|
2431
|
+
createdAt: z9.instanceof(Date).or(z9.instanceof(Timestamp5)),
|
|
2368
2432
|
// Timestamp
|
|
2369
|
-
updatedAt: z9.
|
|
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().
|
|
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().
|
|
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:
|
|
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:
|
|
2485
|
-
updatedAt:
|
|
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((
|
|
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
|
|
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
|
|
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(
|
|
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(
|
|
2854
|
-
expiryDate: z10.instanceof(
|
|
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(
|
|
2872
|
-
updatedAt: z10.instanceof(
|
|
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(
|
|
2882
|
-
updatedAt: z10.instanceof(
|
|
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(
|
|
2891
|
-
updatedAt: z10.instanceof(
|
|
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(
|
|
2902
|
-
updatedAt: z10.instanceof(
|
|
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:
|
|
2966
|
-
updatedAt:
|
|
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((
|
|
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:
|
|
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:
|
|
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:
|
|
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((
|
|
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/
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
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
|
-
|
|
3650
|
+
throw error;
|
|
3462
3651
|
}
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
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
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
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
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
|
|
3491
|
-
|
|
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
|
-
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3513
|
-
|
|
3514
|
-
|
|
3515
|
-
|
|
3516
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
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
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
|
|
3631
|
-
|
|
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
|
-
|
|
3642
|
-
|
|
3643
|
-
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
|
|
3657
|
-
|
|
3658
|
-
|
|
3659
|
-
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
|
|
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
|
-
*
|
|
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
|
|
3679
|
-
|
|
3680
|
-
|
|
3681
|
-
|
|
3682
|
-
|
|
3683
|
-
|
|
3684
|
-
|
|
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
|
-
*
|
|
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
|
|
3699
|
-
|
|
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
|
-
*
|
|
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
|
|
3778
|
-
|
|
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
|
-
*
|
|
3837
|
+
* Ažurira grupaciju klinika
|
|
3796
3838
|
*/
|
|
3797
|
-
async
|
|
3798
|
-
|
|
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
|
-
*
|
|
3843
|
+
* Dodaje admina u grupaciju
|
|
3814
3844
|
*/
|
|
3815
|
-
async
|
|
3816
|
-
|
|
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
|
-
*
|
|
3849
|
+
* Uklanja admina iz grupacije
|
|
3829
3850
|
*/
|
|
3830
|
-
async
|
|
3831
|
-
|
|
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
|
-
*
|
|
3855
|
+
* Deaktivira grupaciju klinika
|
|
3845
3856
|
*/
|
|
3846
|
-
async
|
|
3847
|
-
|
|
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
|
-
*
|
|
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
|
|
3861
|
-
const
|
|
3862
|
-
|
|
3863
|
-
|
|
3864
|
-
|
|
3865
|
-
|
|
3866
|
-
|
|
3867
|
-
|
|
3868
|
-
|
|
3869
|
-
|
|
3870
|
-
|
|
3871
|
-
|
|
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
|
-
*
|
|
3883
|
+
* Kreira admin token za grupaciju
|
|
3878
3884
|
*/
|
|
3879
|
-
async
|
|
3880
|
-
|
|
3885
|
+
async createAdminToken(groupId, creatorAdminId, data) {
|
|
3886
|
+
return createAdminToken(
|
|
3881
3887
|
this.db,
|
|
3882
|
-
|
|
3883
|
-
|
|
3888
|
+
groupId,
|
|
3889
|
+
creatorAdminId,
|
|
3890
|
+
data
|
|
3884
3891
|
);
|
|
3885
|
-
await updateDoc10(notificationRef, {
|
|
3886
|
-
status,
|
|
3887
|
-
updatedAt: Timestamp9.now()
|
|
3888
|
-
});
|
|
3889
3892
|
}
|
|
3890
3893
|
/**
|
|
3891
|
-
*
|
|
3894
|
+
* Verifikuje i koristi admin token
|
|
3892
3895
|
*/
|
|
3893
|
-
async
|
|
3894
|
-
|
|
3896
|
+
async verifyAndUseAdminToken(groupId, token, userRef) {
|
|
3897
|
+
return verifyAndUseAdminToken(
|
|
3895
3898
|
this.db,
|
|
3896
|
-
|
|
3897
|
-
|
|
3899
|
+
groupId,
|
|
3900
|
+
token,
|
|
3901
|
+
userRef
|
|
3898
3902
|
);
|
|
3899
|
-
await deleteDoc4(notificationRef);
|
|
3900
3903
|
}
|
|
3901
3904
|
/**
|
|
3902
|
-
*
|
|
3905
|
+
* Briše admin token
|
|
3903
3906
|
*/
|
|
3904
|
-
async
|
|
3905
|
-
|
|
3906
|
-
|
|
3907
|
-
|
|
3908
|
-
|
|
3909
|
-
|
|
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
|
|
3916
|
+
* Dohvata aktivne admin tokene
|
|
3919
3917
|
*/
|
|
3920
|
-
async
|
|
3921
|
-
|
|
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
|
|
3944
|
-
Timestamp as
|
|
3932
|
+
setDoc as setDoc10,
|
|
3933
|
+
Timestamp as Timestamp11
|
|
3945
3934
|
} from "firebase/firestore";
|
|
3946
|
-
import { geohashForLocation as
|
|
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 =
|
|
3953
|
+
validatedData.location.geohash = geohashForLocation3([
|
|
3965
3954
|
validatedData.location.latitude,
|
|
3966
3955
|
validatedData.location.longitude
|
|
3967
3956
|
]);
|
|
3968
3957
|
}
|
|
3969
|
-
const 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
|
|
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((
|
|
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:
|
|
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:
|
|
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((
|
|
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
|
|
4109
|
-
Timestamp as
|
|
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 =
|
|
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
|
|
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
|
|
4248
|
-
const clinic =
|
|
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/
|
|
4393
|
-
|
|
4394
|
-
|
|
4395
|
-
|
|
4396
|
-
|
|
4397
|
-
|
|
4398
|
-
|
|
4399
|
-
|
|
4400
|
-
|
|
4401
|
-
|
|
4402
|
-
|
|
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
|
-
|
|
4422
|
-
|
|
4423
|
-
|
|
4424
|
-
|
|
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
|
-
|
|
4428
|
-
|
|
4429
|
-
|
|
4430
|
-
|
|
4431
|
-
|
|
4432
|
-
|
|
4433
|
-
|
|
4434
|
-
|
|
4435
|
-
|
|
4436
|
-
|
|
4437
|
-
|
|
4438
|
-
|
|
4439
|
-
|
|
4440
|
-
|
|
4441
|
-
|
|
4442
|
-
|
|
4443
|
-
|
|
4444
|
-
|
|
4445
|
-
|
|
4446
|
-
|
|
4447
|
-
|
|
4448
|
-
|
|
4449
|
-
|
|
4450
|
-
|
|
4451
|
-
|
|
4452
|
-
|
|
4453
|
-
|
|
4454
|
-
|
|
4455
|
-
|
|
4456
|
-
|
|
4457
|
-
|
|
4458
|
-
|
|
4459
|
-
|
|
4460
|
-
|
|
4461
|
-
|
|
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
|
-
|
|
4467
|
-
|
|
4468
|
-
|
|
4469
|
-
|
|
4470
|
-
|
|
4471
|
-
|
|
4472
|
-
|
|
4473
|
-
|
|
4474
|
-
|
|
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
|
-
|
|
4522
|
-
|
|
4523
|
-
|
|
4524
|
-
|
|
4525
|
-
|
|
4526
|
-
|
|
4527
|
-
|
|
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
|
-
|
|
4531
|
-
|
|
4532
|
-
|
|
4533
|
-
|
|
4534
|
-
}
|
|
4535
|
-
|
|
4536
|
-
|
|
4537
|
-
|
|
4538
|
-
|
|
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
|
-
|
|
4541
|
-
|
|
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
|
-
|
|
4544
|
-
|
|
4545
|
-
|
|
4546
|
-
|
|
4547
|
-
|
|
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
|
-
|
|
4567
|
-
|
|
4568
|
-
|
|
4606
|
+
/**
|
|
4607
|
+
* Odjavljuje trenutnog korisnika
|
|
4608
|
+
*/
|
|
4609
|
+
async signOut() {
|
|
4610
|
+
await firebaseSignOut(this.auth);
|
|
4569
4611
|
}
|
|
4570
|
-
|
|
4571
|
-
|
|
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
|
-
|
|
4574
|
-
|
|
4575
|
-
|
|
4576
|
-
|
|
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
|
-
|
|
4584
|
-
|
|
4585
|
-
|
|
4586
|
-
|
|
4587
|
-
|
|
4588
|
-
|
|
4589
|
-
|
|
4590
|
-
|
|
4591
|
-
|
|
4592
|
-
|
|
4593
|
-
|
|
4594
|
-
|
|
4595
|
-
|
|
4596
|
-
|
|
4597
|
-
|
|
4598
|
-
|
|
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
|
-
|
|
4601
|
-
|
|
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
|
-
|
|
4604
|
-
|
|
4605
|
-
|
|
4606
|
-
|
|
4607
|
-
|
|
4608
|
-
|
|
4609
|
-
|
|
4610
|
-
|
|
4611
|
-
|
|
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
|
-
|
|
4614
|
-
|
|
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
|
-
|
|
4617
|
-
|
|
4618
|
-
|
|
4619
|
-
|
|
4620
|
-
|
|
4621
|
-
|
|
4622
|
-
|
|
4623
|
-
|
|
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
|
-
*
|
|
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
|
|
4629
|
-
|
|
4630
|
-
this.
|
|
4631
|
-
|
|
4632
|
-
|
|
4633
|
-
|
|
4634
|
-
|
|
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
|
-
*
|
|
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
|
|
4641
|
-
|
|
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
|
-
*
|
|
4863
|
+
* Kreira novu notifikaciju
|
|
4645
4864
|
*/
|
|
4646
|
-
async
|
|
4647
|
-
|
|
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
|
-
*
|
|
4883
|
+
* Dohvata notifikaciju po ID-u
|
|
4651
4884
|
*/
|
|
4652
|
-
async
|
|
4653
|
-
|
|
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
|
-
*
|
|
4901
|
+
* Dohvata sve notifikacije za korisnika
|
|
4657
4902
|
*/
|
|
4658
|
-
async
|
|
4659
|
-
|
|
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
|
-
*
|
|
4916
|
+
* Dohvata nepročitane notifikacije za korisnika
|
|
4663
4917
|
*/
|
|
4664
|
-
async
|
|
4665
|
-
|
|
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
|
-
*
|
|
4932
|
+
* Označava notifikaciju kao pročitanu
|
|
4669
4933
|
*/
|
|
4670
|
-
async
|
|
4671
|
-
|
|
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
|
-
*
|
|
4946
|
+
* Označava sve notifikacije korisnika kao pročitane
|
|
4675
4947
|
*/
|
|
4676
|
-
async
|
|
4677
|
-
|
|
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
|
-
|
|
4680
|
-
|
|
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
|
-
*
|
|
4979
|
+
* Briše notifikaciju
|
|
4686
4980
|
*/
|
|
4687
|
-
async
|
|
4688
|
-
|
|
4981
|
+
async deleteNotification(notificationId) {
|
|
4982
|
+
const notificationRef = doc11(
|
|
4689
4983
|
this.db,
|
|
4690
|
-
|
|
4691
|
-
|
|
4692
|
-
userRef
|
|
4984
|
+
NOTIFICATIONS_COLLECTION,
|
|
4985
|
+
notificationId
|
|
4693
4986
|
);
|
|
4987
|
+
await deleteDoc6(notificationRef);
|
|
4694
4988
|
}
|
|
4695
4989
|
/**
|
|
4696
|
-
*
|
|
4990
|
+
* Dohvata notifikacije po tipu
|
|
4697
4991
|
*/
|
|
4698
|
-
async
|
|
4699
|
-
|
|
4700
|
-
this.db,
|
|
4701
|
-
|
|
4702
|
-
|
|
4703
|
-
|
|
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
|
|
5006
|
+
* Dohvata notifikacije za određeni termin
|
|
4708
5007
|
*/
|
|
4709
|
-
async
|
|
4710
|
-
|
|
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
|
|
4717
|
-
doc as
|
|
4718
|
-
getDoc as
|
|
4719
|
-
getDocs as
|
|
4720
|
-
setDoc as
|
|
4721
|
-
updateDoc as
|
|
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
|
|
4724
|
-
where as
|
|
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 =
|
|
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 =
|
|
4764
|
-
await
|
|
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 =
|
|
4774
|
-
const docSnap = await
|
|
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 =
|
|
4806
|
-
await
|
|
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 =
|
|
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 =
|
|
5135
|
+
let q = query10(
|
|
4828
5136
|
this.collectionRef,
|
|
4829
|
-
|
|
5137
|
+
where10("isActive", "==", true),
|
|
4830
5138
|
orderBy2("updatedAt", "desc"),
|
|
4831
5139
|
limit(pageSize)
|
|
4832
5140
|
);
|
|
4833
5141
|
if (lastDoc) {
|
|
4834
|
-
q =
|
|
5142
|
+
q = query10(q, startAfter(lastDoc));
|
|
4835
5143
|
}
|
|
4836
|
-
const querySnapshot = await
|
|
5144
|
+
const querySnapshot = await getDocs10(q);
|
|
4837
5145
|
const templates = [];
|
|
4838
5146
|
let lastVisible = null;
|
|
4839
|
-
querySnapshot.forEach((
|
|
4840
|
-
templates.push(
|
|
4841
|
-
lastVisible =
|
|
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 =
|
|
5164
|
+
let q = query10(
|
|
4857
5165
|
this.collectionRef,
|
|
4858
|
-
|
|
4859
|
-
|
|
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 =
|
|
5172
|
+
q = query10(q, startAfter(lastDoc));
|
|
4865
5173
|
}
|
|
4866
|
-
const querySnapshot = await
|
|
5174
|
+
const querySnapshot = await getDocs10(q);
|
|
4867
5175
|
const templates = [];
|
|
4868
5176
|
let lastVisible = null;
|
|
4869
|
-
querySnapshot.forEach((
|
|
4870
|
-
templates.push(
|
|
4871
|
-
lastVisible =
|
|
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 =
|
|
5194
|
+
let q = query10(
|
|
4887
5195
|
this.collectionRef,
|
|
4888
|
-
|
|
5196
|
+
where10("createdBy", "==", userId),
|
|
4889
5197
|
orderBy2("updatedAt", "desc"),
|
|
4890
5198
|
limit(pageSize)
|
|
4891
5199
|
);
|
|
4892
5200
|
if (lastDoc) {
|
|
4893
|
-
q =
|
|
5201
|
+
q = query10(q, startAfter(lastDoc));
|
|
4894
5202
|
}
|
|
4895
|
-
const querySnapshot = await
|
|
5203
|
+
const querySnapshot = await getDocs10(q);
|
|
4896
5204
|
const templates = [];
|
|
4897
5205
|
let lastVisible = null;
|
|
4898
|
-
querySnapshot.forEach((
|
|
4899
|
-
templates.push(
|
|
4900
|
-
lastVisible =
|
|
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
|
|
4912
|
-
doc as
|
|
4913
|
-
getDoc as
|
|
4914
|
-
getDocs as
|
|
4915
|
-
setDoc as
|
|
4916
|
-
updateDoc as
|
|
4917
|
-
query as
|
|
4918
|
-
where as
|
|
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 =
|
|
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 =
|
|
4960
|
-
await
|
|
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 =
|
|
4970
|
-
const docSnap = await
|
|
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 =
|
|
4999
|
-
await
|
|
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 =
|
|
5321
|
+
let q = query11(
|
|
5014
5322
|
this.collectionRef,
|
|
5015
|
-
|
|
5323
|
+
where11("patientId", "==", patientId),
|
|
5016
5324
|
orderBy3("updatedAt", "desc"),
|
|
5017
5325
|
limit2(pageSize)
|
|
5018
5326
|
);
|
|
5019
5327
|
if (lastDoc) {
|
|
5020
|
-
q =
|
|
5328
|
+
q = query11(q, startAfter2(lastDoc));
|
|
5021
5329
|
}
|
|
5022
|
-
const querySnapshot = await
|
|
5330
|
+
const querySnapshot = await getDocs11(q);
|
|
5023
5331
|
const documents = [];
|
|
5024
5332
|
let lastVisible = null;
|
|
5025
|
-
querySnapshot.forEach((
|
|
5026
|
-
documents.push(
|
|
5027
|
-
lastVisible =
|
|
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 =
|
|
5350
|
+
let q = query11(
|
|
5043
5351
|
this.collectionRef,
|
|
5044
|
-
|
|
5352
|
+
where11("practitionerId", "==", practitionerId),
|
|
5045
5353
|
orderBy3("updatedAt", "desc"),
|
|
5046
5354
|
limit2(pageSize)
|
|
5047
5355
|
);
|
|
5048
5356
|
if (lastDoc) {
|
|
5049
|
-
q =
|
|
5357
|
+
q = query11(q, startAfter2(lastDoc));
|
|
5050
5358
|
}
|
|
5051
|
-
const querySnapshot = await
|
|
5359
|
+
const querySnapshot = await getDocs11(q);
|
|
5052
5360
|
const documents = [];
|
|
5053
5361
|
let lastVisible = null;
|
|
5054
|
-
querySnapshot.forEach((
|
|
5055
|
-
documents.push(
|
|
5056
|
-
lastVisible =
|
|
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 =
|
|
5379
|
+
let q = query11(
|
|
5072
5380
|
this.collectionRef,
|
|
5073
|
-
|
|
5381
|
+
where11("clinicId", "==", clinicId),
|
|
5074
5382
|
orderBy3("updatedAt", "desc"),
|
|
5075
5383
|
limit2(pageSize)
|
|
5076
5384
|
);
|
|
5077
5385
|
if (lastDoc) {
|
|
5078
|
-
q =
|
|
5386
|
+
q = query11(q, startAfter2(lastDoc));
|
|
5079
5387
|
}
|
|
5080
|
-
const querySnapshot = await
|
|
5388
|
+
const querySnapshot = await getDocs11(q);
|
|
5081
5389
|
const documents = [];
|
|
5082
5390
|
let lastVisible = null;
|
|
5083
|
-
querySnapshot.forEach((
|
|
5084
|
-
documents.push(
|
|
5085
|
-
lastVisible =
|
|
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 =
|
|
5408
|
+
let q = query11(
|
|
5101
5409
|
this.collectionRef,
|
|
5102
|
-
|
|
5410
|
+
where11("templateId", "==", templateId),
|
|
5103
5411
|
orderBy3("updatedAt", "desc"),
|
|
5104
5412
|
limit2(pageSize)
|
|
5105
5413
|
);
|
|
5106
5414
|
if (lastDoc) {
|
|
5107
|
-
q =
|
|
5415
|
+
q = query11(q, startAfter2(lastDoc));
|
|
5108
5416
|
}
|
|
5109
|
-
const querySnapshot = await
|
|
5417
|
+
const querySnapshot = await getDocs11(q);
|
|
5110
5418
|
const documents = [];
|
|
5111
5419
|
let lastVisible = null;
|
|
5112
|
-
querySnapshot.forEach((
|
|
5113
|
-
documents.push(
|
|
5114
|
-
lastVisible =
|
|
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 =
|
|
5437
|
+
let q = query11(
|
|
5130
5438
|
this.collectionRef,
|
|
5131
|
-
|
|
5439
|
+
where11("status", "==", status),
|
|
5132
5440
|
orderBy3("updatedAt", "desc"),
|
|
5133
5441
|
limit2(pageSize)
|
|
5134
5442
|
);
|
|
5135
5443
|
if (lastDoc) {
|
|
5136
|
-
q =
|
|
5444
|
+
q = query11(q, startAfter2(lastDoc));
|
|
5137
5445
|
}
|
|
5138
|
-
const querySnapshot = await
|
|
5446
|
+
const querySnapshot = await getDocs11(q);
|
|
5139
5447
|
const documents = [];
|
|
5140
5448
|
let lastVisible = null;
|
|
5141
|
-
querySnapshot.forEach((
|
|
5142
|
-
documents.push(
|
|
5143
|
-
lastVisible =
|
|
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,
|