@blackcode_sa/metaestetics-api 1.4.2 → 1.4.3
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 +2841 -407
- package/dist/index.d.ts +2841 -407
- package/dist/index.js +1279 -970
- package/dist/index.mjs +1200 -893
- package/package.json +1 -1
- package/src/services/auth.service.ts +173 -0
- package/src/services/clinic/clinic-group.service.ts +34 -0
- package/src/services/clinic/clinic.service.ts +53 -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);
|
|
@@ -3245,7 +3370,7 @@ var UserService = class extends BaseService {
|
|
|
3245
3370
|
email: "",
|
|
3246
3371
|
phoneNumber: "",
|
|
3247
3372
|
title: "",
|
|
3248
|
-
dateOfBirth:
|
|
3373
|
+
dateOfBirth: Timestamp9.now(),
|
|
3249
3374
|
gender: "other",
|
|
3250
3375
|
languages: ["Serbian"]
|
|
3251
3376
|
},
|
|
@@ -3254,7 +3379,7 @@ var UserService = class extends BaseService {
|
|
|
3254
3379
|
specialties: [],
|
|
3255
3380
|
licenseNumber: "",
|
|
3256
3381
|
issuingAuthority: "",
|
|
3257
|
-
issueDate:
|
|
3382
|
+
issueDate: Timestamp9.now(),
|
|
3258
3383
|
verificationStatus: "pending"
|
|
3259
3384
|
},
|
|
3260
3385
|
isActive: true,
|
|
@@ -3294,7 +3419,7 @@ var UserService = class extends BaseService {
|
|
|
3294
3419
|
];
|
|
3295
3420
|
const q = query4(collection4(this.db, USERS_COLLECTION), ...constraints);
|
|
3296
3421
|
const querySnapshot = await getDocs4(q);
|
|
3297
|
-
const users = querySnapshot.docs.map((
|
|
3422
|
+
const users = querySnapshot.docs.map((doc14) => doc14.data());
|
|
3298
3423
|
return Promise.all(users.map((userData) => userSchema.parse(userData)));
|
|
3299
3424
|
}
|
|
3300
3425
|
/**
|
|
@@ -3448,486 +3573,347 @@ var UserService = class extends BaseService {
|
|
|
3448
3573
|
}
|
|
3449
3574
|
};
|
|
3450
3575
|
|
|
3451
|
-
// src/services/
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3576
|
+
// src/services/clinic/utils/clinic-group.utils.ts
|
|
3577
|
+
import {
|
|
3578
|
+
collection as collection5,
|
|
3579
|
+
doc as doc7,
|
|
3580
|
+
getDoc as getDoc11,
|
|
3581
|
+
getDocs as getDocs5,
|
|
3582
|
+
query as query5,
|
|
3583
|
+
where as where5,
|
|
3584
|
+
updateDoc as updateDoc10,
|
|
3585
|
+
setDoc as setDoc9,
|
|
3586
|
+
Timestamp as Timestamp10
|
|
3587
|
+
} from "firebase/firestore";
|
|
3588
|
+
import { geohashForLocation as geohashForLocation2 } from "geofire-common";
|
|
3589
|
+
import { z as z13 } from "zod";
|
|
3590
|
+
function generateId() {
|
|
3591
|
+
const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
|
3592
|
+
const timestamp = Date.now().toString(36);
|
|
3593
|
+
const randomPart = Array.from(
|
|
3594
|
+
{ length: 12 },
|
|
3595
|
+
() => chars.charAt(Math.floor(Math.random() * chars.length))
|
|
3596
|
+
).join("");
|
|
3597
|
+
return `${randomPart}-${timestamp}`;
|
|
3598
|
+
}
|
|
3599
|
+
async function createClinicGroup(db, data, ownerId, isDefault = false, clinicAdminService) {
|
|
3600
|
+
const validatedData = createClinicGroupSchema.parse(data);
|
|
3601
|
+
const owner = await clinicAdminService.getClinicAdmin(ownerId);
|
|
3602
|
+
if (!owner) {
|
|
3603
|
+
throw new Error("Owner not found or is not a clinic admin");
|
|
3604
|
+
}
|
|
3605
|
+
if (validatedData.hqLocation) {
|
|
3606
|
+
validatedData.hqLocation.geohash = geohashForLocation2([
|
|
3607
|
+
validatedData.hqLocation.latitude,
|
|
3608
|
+
validatedData.hqLocation.longitude
|
|
3609
|
+
]);
|
|
3610
|
+
}
|
|
3611
|
+
const now = Timestamp10.now();
|
|
3612
|
+
const groupData = {
|
|
3613
|
+
...validatedData,
|
|
3614
|
+
id: doc7(collection5(db, CLINIC_GROUPS_COLLECTION)).id,
|
|
3615
|
+
description: isDefault ? void 0 : validatedData.description || void 0,
|
|
3616
|
+
hqLocation: {
|
|
3617
|
+
...validatedData.hqLocation,
|
|
3618
|
+
geohash: validatedData.hqLocation.geohash || void 0
|
|
3619
|
+
},
|
|
3620
|
+
contactInfo: {
|
|
3621
|
+
...validatedData.contactInfo,
|
|
3622
|
+
alternativePhoneNumber: isDefault ? void 0 : validatedData.contactInfo.alternativePhoneNumber || void 0,
|
|
3623
|
+
website: isDefault ? void 0 : validatedData.contactInfo.website || void 0
|
|
3624
|
+
},
|
|
3625
|
+
clinics: [],
|
|
3626
|
+
clinicsInfo: [],
|
|
3627
|
+
admins: [ownerId],
|
|
3628
|
+
adminsInfo: [],
|
|
3629
|
+
adminTokens: [],
|
|
3630
|
+
ownerId,
|
|
3631
|
+
createdAt: now,
|
|
3632
|
+
updatedAt: now,
|
|
3633
|
+
isActive: true
|
|
3634
|
+
};
|
|
3635
|
+
try {
|
|
3636
|
+
clinicGroupSchema.parse(groupData);
|
|
3637
|
+
await setDoc9(doc7(db, CLINIC_GROUPS_COLLECTION, groupData.id), groupData);
|
|
3638
|
+
await clinicAdminService.updateClinicAdmin(ownerId, {
|
|
3639
|
+
clinicGroupId: groupData.id,
|
|
3640
|
+
isGroupOwner: true
|
|
3641
|
+
});
|
|
3642
|
+
return groupData;
|
|
3643
|
+
} catch (error) {
|
|
3644
|
+
if (error instanceof z13.ZodError) {
|
|
3645
|
+
throw new Error("Invalid clinic group data: " + error.message);
|
|
3460
3646
|
}
|
|
3461
|
-
|
|
3647
|
+
throw error;
|
|
3462
3648
|
}
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
email,
|
|
3470
|
-
password
|
|
3471
|
-
);
|
|
3472
|
-
return this.userService.createUser(firebaseUser, [initialRole]);
|
|
3649
|
+
}
|
|
3650
|
+
async function getClinicGroup(db, groupId) {
|
|
3651
|
+
const docRef = doc7(db, CLINIC_GROUPS_COLLECTION, groupId);
|
|
3652
|
+
const docSnap = await getDoc11(docRef);
|
|
3653
|
+
if (docSnap.exists()) {
|
|
3654
|
+
return docSnap.data();
|
|
3473
3655
|
}
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3656
|
+
return null;
|
|
3657
|
+
}
|
|
3658
|
+
async function getAllActiveGroups(db) {
|
|
3659
|
+
const q = query5(
|
|
3660
|
+
collection5(db, CLINIC_GROUPS_COLLECTION),
|
|
3661
|
+
where5("isActive", "==", true)
|
|
3662
|
+
);
|
|
3663
|
+
const querySnapshot = await getDocs5(q);
|
|
3664
|
+
return querySnapshot.docs.map((doc14) => doc14.data());
|
|
3665
|
+
}
|
|
3666
|
+
async function updateClinicGroup(db, groupId, data) {
|
|
3667
|
+
const group = await getClinicGroup(db, groupId);
|
|
3668
|
+
if (!group) {
|
|
3669
|
+
throw new Error("Clinic group not found");
|
|
3484
3670
|
}
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
|
|
3491
|
-
|
|
3671
|
+
const updatedData = {
|
|
3672
|
+
...data,
|
|
3673
|
+
updatedAt: Timestamp10.now()
|
|
3674
|
+
};
|
|
3675
|
+
await updateDoc10(doc7(db, CLINIC_GROUPS_COLLECTION, groupId), updatedData);
|
|
3676
|
+
const updatedGroup = await getClinicGroup(db, groupId);
|
|
3677
|
+
if (!updatedGroup) {
|
|
3678
|
+
throw new Error("Failed to retrieve updated clinic group");
|
|
3492
3679
|
}
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
this.googleProvider
|
|
3500
|
-
);
|
|
3501
|
-
return this.userService.getOrCreateUser(firebaseUser);
|
|
3680
|
+
return updatedGroup;
|
|
3681
|
+
}
|
|
3682
|
+
async function addAdminToGroup(db, groupId, adminId) {
|
|
3683
|
+
const group = await getClinicGroup(db, groupId);
|
|
3684
|
+
if (!group) {
|
|
3685
|
+
throw new Error("Clinic group not found");
|
|
3502
3686
|
}
|
|
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);
|
|
3687
|
+
if (group.admins.includes(adminId)) {
|
|
3688
|
+
return;
|
|
3510
3689
|
}
|
|
3511
|
-
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
|
|
3515
|
-
|
|
3516
|
-
|
|
3690
|
+
await updateClinicGroup(db, groupId, {
|
|
3691
|
+
admins: [...group.admins, adminId]
|
|
3692
|
+
});
|
|
3693
|
+
}
|
|
3694
|
+
async function removeAdminFromGroup(db, groupId, adminId) {
|
|
3695
|
+
const group = await getClinicGroup(db, groupId);
|
|
3696
|
+
if (!group) {
|
|
3697
|
+
throw new Error("Clinic group not found");
|
|
3517
3698
|
}
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
*/
|
|
3521
|
-
async signOut() {
|
|
3522
|
-
await firebaseSignOut(this.auth);
|
|
3699
|
+
if (group.ownerId === adminId) {
|
|
3700
|
+
throw new Error("Cannot remove the owner from the group");
|
|
3523
3701
|
}
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
*/
|
|
3527
|
-
async getCurrentUser() {
|
|
3528
|
-
const firebaseUser = this.auth.currentUser;
|
|
3529
|
-
if (!firebaseUser) return null;
|
|
3530
|
-
return this.userService.getUserById(firebaseUser.uid);
|
|
3702
|
+
if (!group.admins.includes(adminId)) {
|
|
3703
|
+
return;
|
|
3531
3704
|
}
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3705
|
+
await updateClinicGroup(db, groupId, {
|
|
3706
|
+
admins: group.admins.filter((id) => id !== adminId)
|
|
3707
|
+
});
|
|
3708
|
+
}
|
|
3709
|
+
async function deactivateClinicGroup(db, groupId) {
|
|
3710
|
+
const group = await getClinicGroup(db, groupId);
|
|
3711
|
+
if (!group) {
|
|
3712
|
+
throw new Error("Clinic group not found");
|
|
3537
3713
|
}
|
|
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
|
-
}
|
|
3714
|
+
await updateDoc10(doc7(db, CLINIC_GROUPS_COLLECTION, groupId), {
|
|
3715
|
+
isActive: false,
|
|
3716
|
+
updatedAt: Timestamp10.now()
|
|
3717
|
+
});
|
|
3718
|
+
}
|
|
3719
|
+
async function createAdminToken(db, groupId, creatorAdminId, data) {
|
|
3720
|
+
const group = await getClinicGroup(db, groupId);
|
|
3721
|
+
if (!group) {
|
|
3722
|
+
throw new Error("Clinic group not found");
|
|
3569
3723
|
}
|
|
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
|
-
}
|
|
3724
|
+
if (!group.admins.includes(creatorAdminId)) {
|
|
3725
|
+
throw new Error("Admin does not belong to this clinic group");
|
|
3609
3726
|
}
|
|
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
|
-
}
|
|
3727
|
+
const now = Timestamp10.now();
|
|
3728
|
+
const expiresInDays = (data == null ? void 0 : data.expiresInDays) || 7;
|
|
3729
|
+
const expiresAt = new Timestamp10(
|
|
3730
|
+
now.seconds + expiresInDays * 24 * 60 * 60,
|
|
3731
|
+
now.nanoseconds
|
|
3732
|
+
);
|
|
3733
|
+
const token = {
|
|
3734
|
+
id: generateId(),
|
|
3735
|
+
token: generateId(),
|
|
3736
|
+
status: "active" /* ACTIVE */,
|
|
3737
|
+
createdAt: now,
|
|
3738
|
+
expiresAt
|
|
3739
|
+
};
|
|
3740
|
+
await updateClinicGroup(db, groupId, {
|
|
3741
|
+
adminTokens: [...group.adminTokens, token]
|
|
3742
|
+
});
|
|
3743
|
+
return token;
|
|
3744
|
+
}
|
|
3745
|
+
async function verifyAndUseAdminToken(db, groupId, token, userRef) {
|
|
3746
|
+
const group = await getClinicGroup(db, groupId);
|
|
3747
|
+
if (!group) {
|
|
3748
|
+
throw new Error("Clinic group not found");
|
|
3640
3749
|
}
|
|
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
|
-
|
|
3750
|
+
const adminToken = group.adminTokens.find((t) => t.token === token);
|
|
3751
|
+
if (!adminToken) {
|
|
3752
|
+
throw new Error("Admin token not found");
|
|
3753
|
+
}
|
|
3754
|
+
if (adminToken.status !== "active" /* ACTIVE */) {
|
|
3755
|
+
throw new Error("Admin token is not active");
|
|
3756
|
+
}
|
|
3757
|
+
const now = Timestamp10.now();
|
|
3758
|
+
if (adminToken.expiresAt.seconds < now.seconds) {
|
|
3759
|
+
const updatedTokens2 = group.adminTokens.map(
|
|
3760
|
+
(t) => t.id === adminToken.id ? { ...t, status: "expired" /* EXPIRED */ } : t
|
|
3761
|
+
);
|
|
3762
|
+
await updateClinicGroup(db, groupId, {
|
|
3763
|
+
adminTokens: updatedTokens2
|
|
3764
|
+
});
|
|
3765
|
+
throw new Error("Admin token has expired");
|
|
3766
|
+
}
|
|
3767
|
+
const updatedTokens = group.adminTokens.map(
|
|
3768
|
+
(t) => t.id === adminToken.id ? {
|
|
3769
|
+
...t,
|
|
3770
|
+
status: "used" /* USED */,
|
|
3771
|
+
usedByUserRef: userRef
|
|
3772
|
+
} : t
|
|
3773
|
+
);
|
|
3774
|
+
await updateClinicGroup(db, groupId, {
|
|
3775
|
+
adminTokens: updatedTokens
|
|
3776
|
+
});
|
|
3777
|
+
return true;
|
|
3778
|
+
}
|
|
3779
|
+
async function deleteAdminToken(db, groupId, tokenId, adminId) {
|
|
3780
|
+
const group = await getClinicGroup(db, groupId);
|
|
3781
|
+
if (!group) {
|
|
3782
|
+
throw new Error("Clinic group not found");
|
|
3783
|
+
}
|
|
3784
|
+
if (!group.admins.includes(adminId)) {
|
|
3785
|
+
throw new Error("Admin does not belong to this clinic group");
|
|
3786
|
+
}
|
|
3787
|
+
const updatedTokens = group.adminTokens.filter((t) => t.id !== tokenId);
|
|
3788
|
+
await updateClinicGroup(db, groupId, {
|
|
3789
|
+
adminTokens: updatedTokens
|
|
3790
|
+
});
|
|
3791
|
+
}
|
|
3792
|
+
async function getActiveAdminTokens(db, groupId, adminId) {
|
|
3793
|
+
const group = await getClinicGroup(db, groupId);
|
|
3794
|
+
if (!group) {
|
|
3795
|
+
throw new Error("Clinic group not found");
|
|
3796
|
+
}
|
|
3797
|
+
if (!group.admins.includes(adminId)) {
|
|
3798
|
+
throw new Error("Admin does not belong to this clinic group");
|
|
3799
|
+
}
|
|
3800
|
+
return group.adminTokens.filter((t) => t.status === "active" /* ACTIVE */);
|
|
3801
|
+
}
|
|
3802
|
+
|
|
3803
|
+
// src/services/clinic/clinic-group.service.ts
|
|
3804
|
+
var ClinicGroupService = class extends BaseService {
|
|
3805
|
+
constructor(db, auth, app, clinicAdminService) {
|
|
3806
|
+
super(db, auth, app);
|
|
3807
|
+
this.clinicAdminService = clinicAdminService;
|
|
3672
3808
|
}
|
|
3673
3809
|
/**
|
|
3674
|
-
*
|
|
3675
|
-
* @param email Email adresa korisnika
|
|
3676
|
-
* @returns Promise koji se razrešava kada je email poslat
|
|
3810
|
+
* Kreira novu grupaciju klinika
|
|
3677
3811
|
*/
|
|
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
|
-
}
|
|
3812
|
+
async createClinicGroup(data, ownerId, isDefault = false) {
|
|
3813
|
+
return createClinicGroup(
|
|
3814
|
+
this.db,
|
|
3815
|
+
data,
|
|
3816
|
+
ownerId,
|
|
3817
|
+
isDefault,
|
|
3818
|
+
this.clinicAdminService
|
|
3819
|
+
);
|
|
3692
3820
|
}
|
|
3693
3821
|
/**
|
|
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
|
|
3822
|
+
* Dohvata grupaciju klinika po ID-u
|
|
3697
3823
|
*/
|
|
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
|
-
}
|
|
3824
|
+
async getClinicGroup(groupId) {
|
|
3825
|
+
return getClinicGroup(this.db, groupId);
|
|
3710
3826
|
}
|
|
3711
3827
|
/**
|
|
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
|
|
3828
|
+
* Dohvata sve aktivne grupacije klinika
|
|
3716
3829
|
*/
|
|
3717
|
-
async
|
|
3718
|
-
|
|
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
|
|
3776
|
-
*/
|
|
3777
|
-
async createNotification(notification) {
|
|
3778
|
-
const notificationsRef = collection5(this.db, NOTIFICATIONS_COLLECTION);
|
|
3779
|
-
const now = Timestamp9.now();
|
|
3780
|
-
const notificationData = {
|
|
3781
|
-
...notification,
|
|
3782
|
-
createdAt: now,
|
|
3783
|
-
updatedAt: now,
|
|
3784
|
-
status: "pending" /* PENDING */,
|
|
3785
|
-
isRead: false,
|
|
3786
|
-
userRole: notification.userRole || "patient" /* PATIENT */
|
|
3787
|
-
};
|
|
3788
|
-
const docRef = await addDoc(notificationsRef, notificationData);
|
|
3789
|
-
return {
|
|
3790
|
-
...notificationData,
|
|
3791
|
-
id: docRef.id
|
|
3792
|
-
};
|
|
3830
|
+
async getAllActiveGroups() {
|
|
3831
|
+
return getAllActiveGroups(this.db);
|
|
3793
3832
|
}
|
|
3794
3833
|
/**
|
|
3795
|
-
*
|
|
3834
|
+
* Ažurira grupaciju klinika
|
|
3796
3835
|
*/
|
|
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
|
-
};
|
|
3836
|
+
async updateClinicGroup(groupId, data) {
|
|
3837
|
+
return updateClinicGroup(this.db, groupId, data);
|
|
3811
3838
|
}
|
|
3812
3839
|
/**
|
|
3813
|
-
*
|
|
3840
|
+
* Dodaje admina u grupaciju
|
|
3814
3841
|
*/
|
|
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
|
-
}));
|
|
3842
|
+
async addAdminToGroup(groupId, adminId) {
|
|
3843
|
+
return addAdminToGroup(this.db, groupId, adminId);
|
|
3826
3844
|
}
|
|
3827
3845
|
/**
|
|
3828
|
-
*
|
|
3846
|
+
* Uklanja admina iz grupacije
|
|
3829
3847
|
*/
|
|
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
|
-
}));
|
|
3848
|
+
async removeAdminFromGroup(groupId, adminId) {
|
|
3849
|
+
return removeAdminFromGroup(this.db, groupId, adminId);
|
|
3842
3850
|
}
|
|
3843
3851
|
/**
|
|
3844
|
-
*
|
|
3852
|
+
* Deaktivira grupaciju klinika
|
|
3845
3853
|
*/
|
|
3846
|
-
async
|
|
3847
|
-
|
|
3848
|
-
this.db,
|
|
3849
|
-
NOTIFICATIONS_COLLECTION,
|
|
3850
|
-
notificationId
|
|
3851
|
-
);
|
|
3852
|
-
await updateDoc10(notificationRef, {
|
|
3853
|
-
isRead: true,
|
|
3854
|
-
updatedAt: Timestamp9.now()
|
|
3855
|
-
});
|
|
3854
|
+
async deactivateClinicGroup(groupId) {
|
|
3855
|
+
return deactivateClinicGroup(this.db, groupId);
|
|
3856
3856
|
}
|
|
3857
3857
|
/**
|
|
3858
|
-
*
|
|
3858
|
+
* Sets up additional clinic group information after initial creation
|
|
3859
|
+
*
|
|
3860
|
+
* @param groupId - The ID of the clinic group to set up
|
|
3861
|
+
* @param setupData - The setup data for the clinic group
|
|
3862
|
+
* @returns The updated clinic group
|
|
3859
3863
|
*/
|
|
3860
|
-
async
|
|
3861
|
-
const
|
|
3862
|
-
|
|
3863
|
-
|
|
3864
|
-
|
|
3865
|
-
|
|
3866
|
-
|
|
3867
|
-
|
|
3868
|
-
|
|
3869
|
-
|
|
3870
|
-
|
|
3871
|
-
|
|
3872
|
-
|
|
3873
|
-
|
|
3874
|
-
await batch.commit();
|
|
3864
|
+
async setupClinicGroup(groupId, setupData) {
|
|
3865
|
+
const clinicGroup = await this.getClinicGroup(groupId);
|
|
3866
|
+
if (!clinicGroup) {
|
|
3867
|
+
throw new Error(`Clinic group with ID ${groupId} not found`);
|
|
3868
|
+
}
|
|
3869
|
+
const updateData = {
|
|
3870
|
+
languages: setupData.languages,
|
|
3871
|
+
practiceType: setupData.practiceType,
|
|
3872
|
+
description: setupData.description,
|
|
3873
|
+
logo: setupData.logo,
|
|
3874
|
+
calendarSyncEnabled: setupData.calendarSyncEnabled,
|
|
3875
|
+
autoConfirmAppointments: setupData.autoConfirmAppointments
|
|
3876
|
+
};
|
|
3877
|
+
return this.updateClinicGroup(groupId, updateData);
|
|
3875
3878
|
}
|
|
3876
3879
|
/**
|
|
3877
|
-
*
|
|
3880
|
+
* Kreira admin token za grupaciju
|
|
3878
3881
|
*/
|
|
3879
|
-
async
|
|
3880
|
-
|
|
3882
|
+
async createAdminToken(groupId, creatorAdminId, data) {
|
|
3883
|
+
return createAdminToken(
|
|
3881
3884
|
this.db,
|
|
3882
|
-
|
|
3883
|
-
|
|
3885
|
+
groupId,
|
|
3886
|
+
creatorAdminId,
|
|
3887
|
+
data
|
|
3884
3888
|
);
|
|
3885
|
-
await updateDoc10(notificationRef, {
|
|
3886
|
-
status,
|
|
3887
|
-
updatedAt: Timestamp9.now()
|
|
3888
|
-
});
|
|
3889
3889
|
}
|
|
3890
3890
|
/**
|
|
3891
|
-
*
|
|
3891
|
+
* Verifikuje i koristi admin token
|
|
3892
3892
|
*/
|
|
3893
|
-
async
|
|
3894
|
-
|
|
3893
|
+
async verifyAndUseAdminToken(groupId, token, userRef) {
|
|
3894
|
+
return verifyAndUseAdminToken(
|
|
3895
3895
|
this.db,
|
|
3896
|
-
|
|
3897
|
-
|
|
3896
|
+
groupId,
|
|
3897
|
+
token,
|
|
3898
|
+
userRef
|
|
3898
3899
|
);
|
|
3899
|
-
await deleteDoc4(notificationRef);
|
|
3900
3900
|
}
|
|
3901
3901
|
/**
|
|
3902
|
-
*
|
|
3902
|
+
* Briše admin token
|
|
3903
3903
|
*/
|
|
3904
|
-
async
|
|
3905
|
-
|
|
3906
|
-
|
|
3907
|
-
|
|
3908
|
-
|
|
3909
|
-
|
|
3904
|
+
async deleteAdminToken(groupId, tokenId, adminId) {
|
|
3905
|
+
return deleteAdminToken(
|
|
3906
|
+
this.db,
|
|
3907
|
+
groupId,
|
|
3908
|
+
tokenId,
|
|
3909
|
+
adminId
|
|
3910
3910
|
);
|
|
3911
|
-
const querySnapshot = await getDocs5(q);
|
|
3912
|
-
return querySnapshot.docs.map((doc13) => ({
|
|
3913
|
-
id: doc13.id,
|
|
3914
|
-
...doc13.data()
|
|
3915
|
-
}));
|
|
3916
3911
|
}
|
|
3917
3912
|
/**
|
|
3918
|
-
* Dohvata
|
|
3913
|
+
* Dohvata aktivne admin tokene
|
|
3919
3914
|
*/
|
|
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
|
-
}));
|
|
3915
|
+
async getActiveAdminTokens(groupId, adminId) {
|
|
3916
|
+
return getActiveAdminTokens(this.db, groupId, adminId);
|
|
3931
3917
|
}
|
|
3932
3918
|
};
|
|
3933
3919
|
|
|
@@ -3940,10 +3926,10 @@ import {
|
|
|
3940
3926
|
query as query6,
|
|
3941
3927
|
where as where6,
|
|
3942
3928
|
updateDoc as updateDoc11,
|
|
3943
|
-
setDoc as
|
|
3944
|
-
Timestamp as
|
|
3929
|
+
setDoc as setDoc10,
|
|
3930
|
+
Timestamp as Timestamp11
|
|
3945
3931
|
} from "firebase/firestore";
|
|
3946
|
-
import { geohashForLocation as
|
|
3932
|
+
import { geohashForLocation as geohashForLocation3 } from "geofire-common";
|
|
3947
3933
|
import { z as z14 } from "zod";
|
|
3948
3934
|
async function createClinic(db, data, creatorAdminId, clinicGroupService, clinicAdminService) {
|
|
3949
3935
|
const validatedData = createClinicSchema.parse(data);
|
|
@@ -3961,12 +3947,12 @@ async function createClinic(db, data, creatorAdminId, clinicGroupService, clinic
|
|
|
3961
3947
|
throw new Error("Clinic group not found");
|
|
3962
3948
|
}
|
|
3963
3949
|
if (validatedData.location) {
|
|
3964
|
-
validatedData.location.geohash =
|
|
3950
|
+
validatedData.location.geohash = geohashForLocation3([
|
|
3965
3951
|
validatedData.location.latitude,
|
|
3966
3952
|
validatedData.location.longitude
|
|
3967
3953
|
]);
|
|
3968
3954
|
}
|
|
3969
|
-
const now =
|
|
3955
|
+
const now = Timestamp11.now();
|
|
3970
3956
|
const clinicData = {
|
|
3971
3957
|
...validatedData,
|
|
3972
3958
|
id: doc8(collection6(db, CLINICS_COLLECTION)).id,
|
|
@@ -3998,7 +3984,7 @@ async function createClinic(db, data, creatorAdminId, clinicGroupService, clinic
|
|
|
3998
3984
|
};
|
|
3999
3985
|
try {
|
|
4000
3986
|
clinicSchema.parse(clinicData);
|
|
4001
|
-
await
|
|
3987
|
+
await setDoc10(doc8(db, CLINICS_COLLECTION, clinicData.id), clinicData);
|
|
4002
3988
|
await clinicGroupService.updateClinicGroup(validatedData.clinicGroupId, {
|
|
4003
3989
|
clinics: [...group.clinics, clinicData.id]
|
|
4004
3990
|
});
|
|
@@ -4026,7 +4012,7 @@ async function getClinicsByGroup(db, groupId) {
|
|
|
4026
4012
|
where6("isActive", "==", true)
|
|
4027
4013
|
);
|
|
4028
4014
|
const querySnapshot = await getDocs6(q);
|
|
4029
|
-
return querySnapshot.docs.map((
|
|
4015
|
+
return querySnapshot.docs.map((doc14) => doc14.data());
|
|
4030
4016
|
}
|
|
4031
4017
|
async function updateClinic(db, clinicId, data, adminId, clinicAdminService) {
|
|
4032
4018
|
const clinic = await getClinic(db, clinicId);
|
|
@@ -4042,7 +4028,7 @@ async function updateClinic(db, clinicId, data, adminId, clinicAdminService) {
|
|
|
4042
4028
|
}
|
|
4043
4029
|
const updatedData = {
|
|
4044
4030
|
...data,
|
|
4045
|
-
updatedAt:
|
|
4031
|
+
updatedAt: Timestamp11.now()
|
|
4046
4032
|
};
|
|
4047
4033
|
await updateDoc11(doc8(db, CLINICS_COLLECTION, clinicId), updatedData);
|
|
4048
4034
|
const updatedClinic = await getClinic(db, clinicId);
|
|
@@ -4065,7 +4051,7 @@ async function deactivateClinic(db, clinicId, adminId, clinicAdminService) {
|
|
|
4065
4051
|
}
|
|
4066
4052
|
await updateDoc11(doc8(db, CLINICS_COLLECTION, clinicId), {
|
|
4067
4053
|
isActive: false,
|
|
4068
|
-
updatedAt:
|
|
4054
|
+
updatedAt: Timestamp11.now()
|
|
4069
4055
|
});
|
|
4070
4056
|
}
|
|
4071
4057
|
async function getClinicsByAdmin(db, adminId, options = {}, clinicAdminService, clinicGroupService) {
|
|
@@ -4089,7 +4075,7 @@ async function getClinicsByAdmin(db, adminId, options = {}, clinicAdminService,
|
|
|
4089
4075
|
}
|
|
4090
4076
|
const q = query6(collection6(db, CLINICS_COLLECTION), ...constraints);
|
|
4091
4077
|
const querySnapshot = await getDocs6(q);
|
|
4092
|
-
return querySnapshot.docs.map((
|
|
4078
|
+
return querySnapshot.docs.map((doc14) => doc14.data());
|
|
4093
4079
|
}
|
|
4094
4080
|
async function getActiveClinicsByAdmin(db, adminId, clinicAdminService, clinicGroupService) {
|
|
4095
4081
|
return getClinicsByAdmin(
|
|
@@ -4105,15 +4091,15 @@ async function getActiveClinicsByAdmin(db, adminId, clinicAdminService, clinicGr
|
|
|
4105
4091
|
import {
|
|
4106
4092
|
collection as collection7,
|
|
4107
4093
|
doc as doc9,
|
|
4108
|
-
setDoc as
|
|
4109
|
-
Timestamp as
|
|
4094
|
+
setDoc as setDoc11,
|
|
4095
|
+
Timestamp as Timestamp12
|
|
4110
4096
|
} from "firebase/firestore";
|
|
4111
4097
|
async function addReview(db, clinicId, review) {
|
|
4112
4098
|
const clinic = await getClinic(db, clinicId);
|
|
4113
4099
|
if (!clinic) {
|
|
4114
4100
|
throw new Error("Clinic not found");
|
|
4115
4101
|
}
|
|
4116
|
-
const now =
|
|
4102
|
+
const now = Timestamp12.now();
|
|
4117
4103
|
const reviewData = {
|
|
4118
4104
|
id: doc9(collection7(db, "clinic_reviews")).id,
|
|
4119
4105
|
clinicId,
|
|
@@ -4125,7 +4111,7 @@ async function addReview(db, clinicId, review) {
|
|
|
4125
4111
|
isVerified: false
|
|
4126
4112
|
};
|
|
4127
4113
|
clinicReviewSchema.parse(reviewData);
|
|
4128
|
-
await
|
|
4114
|
+
await setDoc11(doc9(db, "clinic_reviews", reviewData.id), reviewData);
|
|
4129
4115
|
const newRating = clinic.rating ? {
|
|
4130
4116
|
average: (clinic.rating.average * clinic.rating.count + review.rating) / (clinic.rating.count + 1),
|
|
4131
4117
|
count: clinic.rating.count + 1
|
|
@@ -4244,8 +4230,8 @@ async function findClinicsInRadius(db, center, radiusInKm, filters) {
|
|
|
4244
4230
|
}
|
|
4245
4231
|
const q = query7(collection8(db, CLINICS_COLLECTION), ...constraints);
|
|
4246
4232
|
const querySnapshot = await getDocs7(q);
|
|
4247
|
-
for (const
|
|
4248
|
-
const clinic =
|
|
4233
|
+
for (const doc14 of querySnapshot.docs) {
|
|
4234
|
+
const clinic = doc14.data();
|
|
4249
4235
|
const distance = distanceBetween(
|
|
4250
4236
|
[center.latitude, center.longitude],
|
|
4251
4237
|
[clinic.location.latitude, clinic.location.longitude]
|
|
@@ -4387,341 +4373,656 @@ var ClinicService = class extends BaseService {
|
|
|
4387
4373
|
this.clinicGroupService
|
|
4388
4374
|
);
|
|
4389
4375
|
}
|
|
4376
|
+
/**
|
|
4377
|
+
* Creates a new clinic branch for a clinic group
|
|
4378
|
+
*
|
|
4379
|
+
* @param clinicGroupId - The ID of the clinic group
|
|
4380
|
+
* @param setupData - The setup data for the clinic branch
|
|
4381
|
+
* @param adminId - The ID of the admin creating the branch
|
|
4382
|
+
* @returns The created clinic
|
|
4383
|
+
*/
|
|
4384
|
+
async createClinicBranch(clinicGroupId, setupData, adminId) {
|
|
4385
|
+
const clinicGroup = await this.clinicGroupService.getClinicGroup(
|
|
4386
|
+
clinicGroupId
|
|
4387
|
+
);
|
|
4388
|
+
if (!clinicGroup) {
|
|
4389
|
+
throw new Error(`Clinic group with ID ${clinicGroupId} not found`);
|
|
4390
|
+
}
|
|
4391
|
+
const createClinicData = {
|
|
4392
|
+
clinicGroupId,
|
|
4393
|
+
name: setupData.name,
|
|
4394
|
+
description: setupData.description,
|
|
4395
|
+
location: setupData.location,
|
|
4396
|
+
contactInfo: setupData.contactInfo,
|
|
4397
|
+
workingHours: setupData.workingHours,
|
|
4398
|
+
tags: setupData.tags,
|
|
4399
|
+
photos: setupData.photos,
|
|
4400
|
+
photosWithTags: setupData.photosWithTags,
|
|
4401
|
+
doctors: [],
|
|
4402
|
+
services: [],
|
|
4403
|
+
admins: [adminId],
|
|
4404
|
+
isActive: true,
|
|
4405
|
+
isVerified: false,
|
|
4406
|
+
logo: setupData.logo,
|
|
4407
|
+
featuredPhotos: setupData.featuredPhotos || []
|
|
4408
|
+
};
|
|
4409
|
+
const clinic = await this.createClinic(createClinicData, adminId);
|
|
4410
|
+
return clinic;
|
|
4411
|
+
}
|
|
4390
4412
|
};
|
|
4391
4413
|
|
|
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");
|
|
4414
|
+
// src/services/auth.service.ts
|
|
4415
|
+
var AuthService = class extends BaseService {
|
|
4416
|
+
constructor(db, auth, app, userService) {
|
|
4417
|
+
super(db, auth, app);
|
|
4418
|
+
this.googleProvider = new GoogleAuthProvider();
|
|
4419
|
+
this.facebookProvider = new FacebookAuthProvider();
|
|
4420
|
+
this.appleProvider = new OAuthProvider("apple.com");
|
|
4421
|
+
if (!userService) {
|
|
4422
|
+
userService = new UserService(db, auth, app);
|
|
4423
|
+
}
|
|
4424
|
+
this.userService = userService;
|
|
4420
4425
|
}
|
|
4421
|
-
|
|
4422
|
-
|
|
4423
|
-
|
|
4424
|
-
|
|
4425
|
-
|
|
4426
|
+
/**
|
|
4427
|
+
* Registruje novog korisnika sa email-om i lozinkom
|
|
4428
|
+
*/
|
|
4429
|
+
async signUp(email, password, initialRole = "patient" /* PATIENT */) {
|
|
4430
|
+
const { user: firebaseUser } = await createUserWithEmailAndPassword(
|
|
4431
|
+
this.auth,
|
|
4432
|
+
email,
|
|
4433
|
+
password
|
|
4434
|
+
);
|
|
4435
|
+
return this.userService.createUser(firebaseUser, [initialRole]);
|
|
4426
4436
|
}
|
|
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
|
-
|
|
4437
|
+
/**
|
|
4438
|
+
* Registers a new clinic admin user with email and password
|
|
4439
|
+
* Can either create a new clinic group or join an existing one with a token
|
|
4440
|
+
*
|
|
4441
|
+
* @param data - Clinic admin signup data
|
|
4442
|
+
* @returns The created user
|
|
4443
|
+
*/
|
|
4444
|
+
async signUpClinicAdmin(data) {
|
|
4445
|
+
try {
|
|
4446
|
+
await clinicAdminSignupSchema.parseAsync(data);
|
|
4447
|
+
const { user: firebaseUser } = await createUserWithEmailAndPassword(
|
|
4448
|
+
this.auth,
|
|
4449
|
+
data.email,
|
|
4450
|
+
data.password
|
|
4451
|
+
);
|
|
4452
|
+
const user = await this.userService.createUser(firebaseUser, [
|
|
4453
|
+
"clinic_admin" /* CLINIC_ADMIN */
|
|
4454
|
+
]);
|
|
4455
|
+
const contactPerson = {
|
|
4456
|
+
firstName: data.firstName,
|
|
4457
|
+
lastName: data.lastName,
|
|
4458
|
+
title: data.title,
|
|
4459
|
+
email: data.email,
|
|
4460
|
+
phoneNumber: data.phoneNumber
|
|
4461
|
+
};
|
|
4462
|
+
const clinicAdminService = new ClinicAdminService(
|
|
4463
|
+
this.db,
|
|
4464
|
+
this.auth,
|
|
4465
|
+
this.app
|
|
4466
|
+
);
|
|
4467
|
+
const clinicGroupService = new ClinicGroupService(
|
|
4468
|
+
this.db,
|
|
4469
|
+
this.auth,
|
|
4470
|
+
this.app,
|
|
4471
|
+
clinicAdminService
|
|
4472
|
+
);
|
|
4473
|
+
const clinicService = new ClinicService(
|
|
4474
|
+
this.db,
|
|
4475
|
+
this.auth,
|
|
4476
|
+
this.app,
|
|
4477
|
+
clinicGroupService,
|
|
4478
|
+
clinicAdminService
|
|
4479
|
+
);
|
|
4480
|
+
clinicAdminService.setServices(clinicGroupService, clinicService);
|
|
4481
|
+
if (data.isCreatingNewGroup) {
|
|
4482
|
+
if (!data.clinicGroupData) {
|
|
4483
|
+
throw new Error(
|
|
4484
|
+
"Clinic group data is required when creating a new group"
|
|
4485
|
+
);
|
|
4486
|
+
}
|
|
4487
|
+
const createClinicGroupData = {
|
|
4488
|
+
name: data.clinicGroupData.name,
|
|
4489
|
+
hqLocation: data.clinicGroupData.hqLocation,
|
|
4490
|
+
contactInfo: data.clinicGroupData.contactInfo,
|
|
4491
|
+
contactPerson,
|
|
4492
|
+
ownerId: firebaseUser.uid,
|
|
4493
|
+
isActive: true,
|
|
4494
|
+
logo: data.clinicGroupData.logo,
|
|
4495
|
+
subscriptionModel: data.clinicGroupData.subscriptionModel || "no_subscription" /* NO_SUBSCRIPTION */
|
|
4496
|
+
};
|
|
4497
|
+
await clinicGroupService.createClinicGroup(
|
|
4498
|
+
createClinicGroupData,
|
|
4499
|
+
firebaseUser.uid,
|
|
4500
|
+
true
|
|
4501
|
+
);
|
|
4502
|
+
} else {
|
|
4503
|
+
if (!data.inviteToken) {
|
|
4504
|
+
throw new Error(
|
|
4505
|
+
"Invite token is required when joining an existing group"
|
|
4506
|
+
);
|
|
4507
|
+
}
|
|
4508
|
+
const groupsRef = collection9(this.db, CLINIC_GROUPS_COLLECTION);
|
|
4509
|
+
const q = query8(groupsRef);
|
|
4510
|
+
const querySnapshot = await getDocs8(q);
|
|
4511
|
+
let foundGroup = null;
|
|
4512
|
+
let foundToken = null;
|
|
4513
|
+
for (const docSnapshot of querySnapshot.docs) {
|
|
4514
|
+
const group = docSnapshot.data();
|
|
4515
|
+
const token = group.adminTokens.find(
|
|
4516
|
+
(t) => t.token === data.inviteToken && t.status === "active" /* ACTIVE */ && new Date(t.expiresAt.toDate()) > /* @__PURE__ */ new Date()
|
|
4517
|
+
);
|
|
4518
|
+
if (token) {
|
|
4519
|
+
foundGroup = group;
|
|
4520
|
+
foundToken = token;
|
|
4521
|
+
break;
|
|
4522
|
+
}
|
|
4523
|
+
}
|
|
4524
|
+
if (!foundGroup || !foundToken) {
|
|
4525
|
+
throw new Error("Invalid or expired invite token");
|
|
4526
|
+
}
|
|
4527
|
+
const createClinicAdminData = {
|
|
4528
|
+
userRef: firebaseUser.uid,
|
|
4529
|
+
clinicGroupId: foundGroup.id,
|
|
4530
|
+
isGroupOwner: false,
|
|
4531
|
+
clinicsManaged: [],
|
|
4532
|
+
contactInfo: contactPerson,
|
|
4533
|
+
roleTitle: data.title,
|
|
4534
|
+
isActive: true
|
|
4535
|
+
};
|
|
4536
|
+
await clinicAdminService.createClinicAdmin(createClinicAdminData);
|
|
4537
|
+
await clinicGroupService.verifyAndUseAdminToken(
|
|
4538
|
+
foundGroup.id,
|
|
4539
|
+
data.inviteToken,
|
|
4540
|
+
firebaseUser.uid
|
|
4541
|
+
);
|
|
4542
|
+
}
|
|
4543
|
+
return user;
|
|
4544
|
+
} catch (error) {
|
|
4545
|
+
if (error instanceof z15.ZodError) {
|
|
4546
|
+
throw AUTH_ERRORS.VALIDATION_ERROR;
|
|
4547
|
+
}
|
|
4548
|
+
const firebaseError = error;
|
|
4549
|
+
if (firebaseError.code === "auth/email-already-in-use" /* EMAIL_ALREADY_IN_USE */) {
|
|
4550
|
+
throw AUTH_ERRORS.EMAIL_ALREADY_EXISTS;
|
|
4551
|
+
}
|
|
4552
|
+
throw error;
|
|
4462
4553
|
}
|
|
4463
|
-
throw error;
|
|
4464
|
-
}
|
|
4465
|
-
}
|
|
4466
|
-
async function getClinicGroup(db, groupId) {
|
|
4467
|
-
const docRef = doc10(db, CLINIC_GROUPS_COLLECTION, groupId);
|
|
4468
|
-
const docSnap = await getDoc13(docRef);
|
|
4469
|
-
if (docSnap.exists()) {
|
|
4470
|
-
return docSnap.data();
|
|
4471
|
-
}
|
|
4472
|
-
return null;
|
|
4473
|
-
}
|
|
4474
|
-
async function getAllActiveGroups(db) {
|
|
4475
|
-
const q = query8(
|
|
4476
|
-
collection9(db, CLINIC_GROUPS_COLLECTION),
|
|
4477
|
-
where8("isActive", "==", true)
|
|
4478
|
-
);
|
|
4479
|
-
const querySnapshot = await getDocs8(q);
|
|
4480
|
-
return querySnapshot.docs.map((doc13) => doc13.data());
|
|
4481
|
-
}
|
|
4482
|
-
async function updateClinicGroup(db, groupId, data) {
|
|
4483
|
-
const group = await getClinicGroup(db, groupId);
|
|
4484
|
-
if (!group) {
|
|
4485
|
-
throw new Error("Clinic group not found");
|
|
4486
|
-
}
|
|
4487
|
-
const updatedData = {
|
|
4488
|
-
...data,
|
|
4489
|
-
updatedAt: Timestamp12.now()
|
|
4490
|
-
};
|
|
4491
|
-
await updateDoc12(doc10(db, CLINIC_GROUPS_COLLECTION, groupId), updatedData);
|
|
4492
|
-
const updatedGroup = await getClinicGroup(db, groupId);
|
|
4493
|
-
if (!updatedGroup) {
|
|
4494
|
-
throw new Error("Failed to retrieve updated clinic group");
|
|
4495
|
-
}
|
|
4496
|
-
return updatedGroup;
|
|
4497
|
-
}
|
|
4498
|
-
async function addAdminToGroup(db, groupId, adminId) {
|
|
4499
|
-
const group = await getClinicGroup(db, groupId);
|
|
4500
|
-
if (!group) {
|
|
4501
|
-
throw new Error("Clinic group not found");
|
|
4502
|
-
}
|
|
4503
|
-
if (group.admins.includes(adminId)) {
|
|
4504
|
-
return;
|
|
4505
|
-
}
|
|
4506
|
-
await updateClinicGroup(db, groupId, {
|
|
4507
|
-
admins: [...group.admins, adminId]
|
|
4508
|
-
});
|
|
4509
|
-
}
|
|
4510
|
-
async function removeAdminFromGroup(db, groupId, adminId) {
|
|
4511
|
-
const group = await getClinicGroup(db, groupId);
|
|
4512
|
-
if (!group) {
|
|
4513
|
-
throw new Error("Clinic group not found");
|
|
4514
4554
|
}
|
|
4515
|
-
|
|
4516
|
-
|
|
4517
|
-
|
|
4518
|
-
|
|
4519
|
-
|
|
4555
|
+
/**
|
|
4556
|
+
* Prijavljuje korisnika sa email-om i lozinkom
|
|
4557
|
+
*/
|
|
4558
|
+
async signIn(email, password) {
|
|
4559
|
+
const { user: firebaseUser } = await signInWithEmailAndPassword(
|
|
4560
|
+
this.auth,
|
|
4561
|
+
email,
|
|
4562
|
+
password
|
|
4563
|
+
);
|
|
4564
|
+
return this.userService.getOrCreateUser(firebaseUser);
|
|
4520
4565
|
}
|
|
4521
|
-
|
|
4522
|
-
|
|
4523
|
-
|
|
4524
|
-
|
|
4525
|
-
|
|
4526
|
-
|
|
4527
|
-
|
|
4528
|
-
throw new Error("Clinic group not found");
|
|
4566
|
+
/**
|
|
4567
|
+
* Prijavljuje korisnika sa Facebook-om
|
|
4568
|
+
*/
|
|
4569
|
+
async signInWithFacebook() {
|
|
4570
|
+
const provider = new FacebookAuthProvider();
|
|
4571
|
+
const { user: firebaseUser } = await signInWithPopup(this.auth, provider);
|
|
4572
|
+
return this.userService.getOrCreateUser(firebaseUser);
|
|
4529
4573
|
}
|
|
4530
|
-
|
|
4531
|
-
|
|
4532
|
-
|
|
4533
|
-
|
|
4534
|
-
}
|
|
4535
|
-
|
|
4536
|
-
|
|
4537
|
-
|
|
4538
|
-
|
|
4574
|
+
/**
|
|
4575
|
+
* Prijavljuje korisnika sa Google nalogom
|
|
4576
|
+
*/
|
|
4577
|
+
async signInWithGoogle(initialRole = "patient" /* PATIENT */) {
|
|
4578
|
+
const { user: firebaseUser } = await signInWithPopup(
|
|
4579
|
+
this.auth,
|
|
4580
|
+
this.googleProvider
|
|
4581
|
+
);
|
|
4582
|
+
return this.userService.getOrCreateUser(firebaseUser);
|
|
4539
4583
|
}
|
|
4540
|
-
|
|
4541
|
-
|
|
4584
|
+
/**
|
|
4585
|
+
* Prijavljuje korisnika sa Apple-om
|
|
4586
|
+
*/
|
|
4587
|
+
async signInWithApple() {
|
|
4588
|
+
const provider = new OAuthProvider("apple.com");
|
|
4589
|
+
const { user: firebaseUser } = await signInWithPopup(this.auth, provider);
|
|
4590
|
+
return this.userService.getOrCreateUser(firebaseUser);
|
|
4542
4591
|
}
|
|
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");
|
|
4592
|
+
/**
|
|
4593
|
+
* Prijavljuje korisnika anonimno
|
|
4594
|
+
*/
|
|
4595
|
+
async signInAnonymously() {
|
|
4596
|
+
const { user: firebaseUser } = await firebaseSignInAnonymously(this.auth);
|
|
4597
|
+
return this.userService.getOrCreateUser(firebaseUser);
|
|
4565
4598
|
}
|
|
4566
|
-
|
|
4567
|
-
|
|
4568
|
-
|
|
4599
|
+
/**
|
|
4600
|
+
* Odjavljuje trenutnog korisnika
|
|
4601
|
+
*/
|
|
4602
|
+
async signOut() {
|
|
4603
|
+
await firebaseSignOut(this.auth);
|
|
4569
4604
|
}
|
|
4570
|
-
|
|
4571
|
-
|
|
4605
|
+
/**
|
|
4606
|
+
* Vraća trenutno prijavljenog korisnika
|
|
4607
|
+
*/
|
|
4608
|
+
async getCurrentUser() {
|
|
4609
|
+
const firebaseUser = this.auth.currentUser;
|
|
4610
|
+
if (!firebaseUser) return null;
|
|
4611
|
+
return this.userService.getUserById(firebaseUser.uid);
|
|
4572
4612
|
}
|
|
4573
|
-
|
|
4574
|
-
|
|
4575
|
-
|
|
4576
|
-
|
|
4577
|
-
);
|
|
4578
|
-
await updateClinicGroup(db, groupId, {
|
|
4579
|
-
adminTokens: updatedTokens2
|
|
4580
|
-
});
|
|
4581
|
-
throw new Error("Admin token has expired");
|
|
4613
|
+
/**
|
|
4614
|
+
* Registruje callback za promene stanja autentifikacije
|
|
4615
|
+
*/
|
|
4616
|
+
onAuthStateChange(callback) {
|
|
4617
|
+
return onAuthStateChanged(this.auth, callback);
|
|
4582
4618
|
}
|
|
4583
|
-
|
|
4584
|
-
|
|
4585
|
-
|
|
4586
|
-
|
|
4587
|
-
|
|
4588
|
-
|
|
4589
|
-
|
|
4590
|
-
|
|
4591
|
-
|
|
4592
|
-
|
|
4593
|
-
|
|
4594
|
-
|
|
4595
|
-
|
|
4596
|
-
|
|
4597
|
-
|
|
4598
|
-
|
|
4619
|
+
async upgradeAnonymousUser(email, password) {
|
|
4620
|
+
try {
|
|
4621
|
+
await emailSchema.parseAsync(email);
|
|
4622
|
+
await passwordSchema.parseAsync(password);
|
|
4623
|
+
const currentUser = this.auth.currentUser;
|
|
4624
|
+
if (!currentUser) {
|
|
4625
|
+
throw AUTH_ERRORS.NOT_AUTHENTICATED;
|
|
4626
|
+
}
|
|
4627
|
+
if (!currentUser.isAnonymous) {
|
|
4628
|
+
throw new AuthError(
|
|
4629
|
+
"User is not anonymous",
|
|
4630
|
+
"AUTH/NOT_ANONYMOUS_USER",
|
|
4631
|
+
400
|
|
4632
|
+
);
|
|
4633
|
+
}
|
|
4634
|
+
const credential = EmailAuthProvider.credential(email, password);
|
|
4635
|
+
await linkWithCredential(currentUser, credential);
|
|
4636
|
+
return await this.userService.upgradeAnonymousUser(
|
|
4637
|
+
currentUser.uid,
|
|
4638
|
+
email
|
|
4639
|
+
);
|
|
4640
|
+
} catch (error) {
|
|
4641
|
+
if (error instanceof z15.ZodError) {
|
|
4642
|
+
throw AUTH_ERRORS.VALIDATION_ERROR;
|
|
4643
|
+
}
|
|
4644
|
+
const firebaseError = error;
|
|
4645
|
+
if (firebaseError.code === "auth/email-already-in-use" /* EMAIL_ALREADY_IN_USE */) {
|
|
4646
|
+
throw AUTH_ERRORS.EMAIL_ALREADY_EXISTS;
|
|
4647
|
+
}
|
|
4648
|
+
throw error;
|
|
4649
|
+
}
|
|
4599
4650
|
}
|
|
4600
|
-
|
|
4601
|
-
|
|
4651
|
+
/**
|
|
4652
|
+
* Upgrades an anonymous user to a regular user by signing in with a Google account.
|
|
4653
|
+
*
|
|
4654
|
+
* @throws {AuthError} If the user is not anonymous.
|
|
4655
|
+
* @throws {AuthError} If the user is not authenticated.
|
|
4656
|
+
* @throws {AuthError} If the popup window is closed by the user.
|
|
4657
|
+
* @throws {FirebaseError} If any other Firebase error occurs.
|
|
4658
|
+
*
|
|
4659
|
+
* @returns {Promise<User>} The upgraded user.
|
|
4660
|
+
*/
|
|
4661
|
+
async upgradeAnonymousUserWithGoogle() {
|
|
4662
|
+
try {
|
|
4663
|
+
const currentUser = this.auth.currentUser;
|
|
4664
|
+
if (!currentUser) {
|
|
4665
|
+
throw AUTH_ERRORS.NOT_AUTHENTICATED;
|
|
4666
|
+
}
|
|
4667
|
+
if (!currentUser.isAnonymous) {
|
|
4668
|
+
throw new AuthError(
|
|
4669
|
+
"User is not anonymous",
|
|
4670
|
+
"AUTH/NOT_ANONYMOUS_USER",
|
|
4671
|
+
400
|
|
4672
|
+
);
|
|
4673
|
+
}
|
|
4674
|
+
const userCredential = await signInWithPopup(
|
|
4675
|
+
this.auth,
|
|
4676
|
+
this.googleProvider
|
|
4677
|
+
);
|
|
4678
|
+
if (!userCredential) throw AUTH_ERRORS.INVALID_CREDENTIAL;
|
|
4679
|
+
return await this.userService.upgradeAnonymousUser(
|
|
4680
|
+
currentUser.uid,
|
|
4681
|
+
userCredential.user.email
|
|
4682
|
+
);
|
|
4683
|
+
} catch (error) {
|
|
4684
|
+
const firebaseError = error;
|
|
4685
|
+
if (firebaseError.code === "auth/popup-closed-by-user" /* POPUP_CLOSED_BY_USER */) {
|
|
4686
|
+
throw AUTH_ERRORS.POPUP_CLOSED;
|
|
4687
|
+
}
|
|
4688
|
+
throw error;
|
|
4689
|
+
}
|
|
4602
4690
|
}
|
|
4603
|
-
|
|
4604
|
-
|
|
4605
|
-
|
|
4606
|
-
|
|
4607
|
-
|
|
4608
|
-
|
|
4609
|
-
|
|
4610
|
-
|
|
4611
|
-
|
|
4691
|
+
async upgradeAnonymousUserWithFacebook() {
|
|
4692
|
+
try {
|
|
4693
|
+
const currentUser = this.auth.currentUser;
|
|
4694
|
+
if (!currentUser) {
|
|
4695
|
+
throw AUTH_ERRORS.NOT_AUTHENTICATED;
|
|
4696
|
+
}
|
|
4697
|
+
if (!currentUser.isAnonymous) {
|
|
4698
|
+
throw new AuthError(
|
|
4699
|
+
"User is not anonymous",
|
|
4700
|
+
"AUTH/NOT_ANONYMOUS_USER",
|
|
4701
|
+
400
|
|
4702
|
+
);
|
|
4703
|
+
}
|
|
4704
|
+
this.facebookProvider.addScope("email");
|
|
4705
|
+
const userCredential = await signInWithPopup(
|
|
4706
|
+
this.auth,
|
|
4707
|
+
this.facebookProvider
|
|
4708
|
+
);
|
|
4709
|
+
if (!userCredential) throw AUTH_ERRORS.INVALID_CREDENTIAL;
|
|
4710
|
+
return await this.userService.upgradeAnonymousUser(
|
|
4711
|
+
currentUser.uid,
|
|
4712
|
+
userCredential.user.email
|
|
4713
|
+
);
|
|
4714
|
+
} catch (error) {
|
|
4715
|
+
const firebaseError = error;
|
|
4716
|
+
if (firebaseError.code === "auth/popup-closed-by-user" /* POPUP_CLOSED_BY_USER */) {
|
|
4717
|
+
throw AUTH_ERRORS.POPUP_CLOSED;
|
|
4718
|
+
}
|
|
4719
|
+
throw error;
|
|
4720
|
+
}
|
|
4612
4721
|
}
|
|
4613
|
-
|
|
4614
|
-
|
|
4722
|
+
async upgradeAnonymousUserWithApple() {
|
|
4723
|
+
try {
|
|
4724
|
+
const currentUser = this.auth.currentUser;
|
|
4725
|
+
if (!currentUser) {
|
|
4726
|
+
throw AUTH_ERRORS.NOT_AUTHENTICATED;
|
|
4727
|
+
}
|
|
4728
|
+
if (!currentUser.isAnonymous) {
|
|
4729
|
+
throw new AuthError(
|
|
4730
|
+
"User is not anonymous",
|
|
4731
|
+
"AUTH/NOT_ANONYMOUS_USER",
|
|
4732
|
+
400
|
|
4733
|
+
);
|
|
4734
|
+
}
|
|
4735
|
+
this.appleProvider.addScope("email");
|
|
4736
|
+
this.appleProvider.addScope("name");
|
|
4737
|
+
const userCredential = await signInWithPopup(
|
|
4738
|
+
this.auth,
|
|
4739
|
+
this.appleProvider
|
|
4740
|
+
);
|
|
4741
|
+
if (!userCredential) throw AUTH_ERRORS.INVALID_CREDENTIAL;
|
|
4742
|
+
return await this.userService.upgradeAnonymousUser(
|
|
4743
|
+
currentUser.uid,
|
|
4744
|
+
userCredential.user.email
|
|
4745
|
+
);
|
|
4746
|
+
} catch (error) {
|
|
4747
|
+
const firebaseError = error;
|
|
4748
|
+
if (firebaseError.code === "auth/popup-closed-by-user" /* POPUP_CLOSED_BY_USER */) {
|
|
4749
|
+
throw AUTH_ERRORS.POPUP_CLOSED;
|
|
4750
|
+
}
|
|
4751
|
+
throw error;
|
|
4752
|
+
}
|
|
4615
4753
|
}
|
|
4616
|
-
|
|
4617
|
-
|
|
4618
|
-
|
|
4619
|
-
|
|
4620
|
-
|
|
4621
|
-
|
|
4622
|
-
|
|
4623
|
-
|
|
4754
|
+
/**
|
|
4755
|
+
* Šalje email za resetovanje lozinke korisniku
|
|
4756
|
+
* @param email Email adresa korisnika
|
|
4757
|
+
* @returns Promise koji se razrešava kada je email poslat
|
|
4758
|
+
*/
|
|
4759
|
+
async sendPasswordResetEmail(email) {
|
|
4760
|
+
try {
|
|
4761
|
+
await emailSchema.parseAsync(email);
|
|
4762
|
+
await sendPasswordResetEmail(this.auth, email);
|
|
4763
|
+
} catch (error) {
|
|
4764
|
+
if (error instanceof z15.ZodError) {
|
|
4765
|
+
throw AUTH_ERRORS.VALIDATION_ERROR;
|
|
4766
|
+
}
|
|
4767
|
+
const firebaseError = error;
|
|
4768
|
+
if (firebaseError.code === "auth/user-not-found" /* USER_NOT_FOUND */) {
|
|
4769
|
+
throw AUTH_ERRORS.USER_NOT_FOUND;
|
|
4770
|
+
}
|
|
4771
|
+
throw error;
|
|
4772
|
+
}
|
|
4624
4773
|
}
|
|
4625
4774
|
/**
|
|
4626
|
-
*
|
|
4775
|
+
* Verifikuje kod za resetovanje lozinke iz email linka
|
|
4776
|
+
* @param oobCode Kod iz URL-a za resetovanje lozinke
|
|
4777
|
+
* @returns Promise koji se razrešava sa email adresom korisnika ako je kod validan
|
|
4627
4778
|
*/
|
|
4628
|
-
async
|
|
4629
|
-
|
|
4630
|
-
this.
|
|
4631
|
-
|
|
4632
|
-
|
|
4633
|
-
|
|
4634
|
-
|
|
4635
|
-
|
|
4779
|
+
async verifyPasswordResetCode(oobCode) {
|
|
4780
|
+
try {
|
|
4781
|
+
return await verifyPasswordResetCode(this.auth, oobCode);
|
|
4782
|
+
} catch (error) {
|
|
4783
|
+
const firebaseError = error;
|
|
4784
|
+
if (firebaseError.code === "auth/expired-action-code" /* EXPIRED_ACTION_CODE */) {
|
|
4785
|
+
throw AUTH_ERRORS.EXPIRED_ACTION_CODE;
|
|
4786
|
+
} else if (firebaseError.code === "auth/invalid-action-code" /* INVALID_ACTION_CODE */) {
|
|
4787
|
+
throw AUTH_ERRORS.INVALID_ACTION_CODE;
|
|
4788
|
+
}
|
|
4789
|
+
throw error;
|
|
4790
|
+
}
|
|
4636
4791
|
}
|
|
4637
4792
|
/**
|
|
4638
|
-
*
|
|
4793
|
+
* Potvrđuje resetovanje lozinke i postavlja novu lozinku
|
|
4794
|
+
* @param oobCode Kod iz URL-a za resetovanje lozinke
|
|
4795
|
+
* @param newPassword Nova lozinka
|
|
4796
|
+
* @returns Promise koji se razrešava kada je lozinka promenjena
|
|
4639
4797
|
*/
|
|
4640
|
-
async
|
|
4641
|
-
|
|
4798
|
+
async confirmPasswordReset(oobCode, newPassword) {
|
|
4799
|
+
try {
|
|
4800
|
+
await passwordSchema.parseAsync(newPassword);
|
|
4801
|
+
await confirmPasswordReset(this.auth, oobCode, newPassword);
|
|
4802
|
+
} catch (error) {
|
|
4803
|
+
if (error instanceof z15.ZodError) {
|
|
4804
|
+
throw AUTH_ERRORS.VALIDATION_ERROR;
|
|
4805
|
+
}
|
|
4806
|
+
const firebaseError = error;
|
|
4807
|
+
if (firebaseError.code === "auth/expired-action-code" /* EXPIRED_ACTION_CODE */) {
|
|
4808
|
+
throw AUTH_ERRORS.EXPIRED_ACTION_CODE;
|
|
4809
|
+
} else if (firebaseError.code === "auth/invalid-action-code" /* INVALID_ACTION_CODE */) {
|
|
4810
|
+
throw AUTH_ERRORS.INVALID_ACTION_CODE;
|
|
4811
|
+
} else if (firebaseError.code === "auth/weak-password" /* WEAK_PASSWORD */) {
|
|
4812
|
+
throw AUTH_ERRORS.WEAK_PASSWORD;
|
|
4813
|
+
}
|
|
4814
|
+
throw error;
|
|
4815
|
+
}
|
|
4642
4816
|
}
|
|
4817
|
+
};
|
|
4818
|
+
|
|
4819
|
+
// src/services/notifications/notification.service.ts
|
|
4820
|
+
import {
|
|
4821
|
+
collection as collection10,
|
|
4822
|
+
doc as doc11,
|
|
4823
|
+
getDoc as getDoc14,
|
|
4824
|
+
getDocs as getDocs9,
|
|
4825
|
+
query as query9,
|
|
4826
|
+
where as where9,
|
|
4827
|
+
updateDoc as updateDoc13,
|
|
4828
|
+
deleteDoc as deleteDoc6,
|
|
4829
|
+
orderBy,
|
|
4830
|
+
Timestamp as Timestamp14,
|
|
4831
|
+
addDoc,
|
|
4832
|
+
writeBatch as writeBatch2
|
|
4833
|
+
} from "firebase/firestore";
|
|
4834
|
+
|
|
4835
|
+
// src/types/notifications/index.ts
|
|
4836
|
+
var NotificationType = /* @__PURE__ */ ((NotificationType3) => {
|
|
4837
|
+
NotificationType3["PRE_REQUIREMENT"] = "preRequirement";
|
|
4838
|
+
NotificationType3["POST_REQUIREMENT"] = "postRequirement";
|
|
4839
|
+
NotificationType3["APPOINTMENT_REMINDER"] = "appointmentReminder";
|
|
4840
|
+
NotificationType3["APPOINTMENT_NOTIFICATION"] = "appointmentNotification";
|
|
4841
|
+
return NotificationType3;
|
|
4842
|
+
})(NotificationType || {});
|
|
4843
|
+
var NOTIFICATIONS_COLLECTION = "notifications";
|
|
4844
|
+
var NotificationStatus = /* @__PURE__ */ ((NotificationStatus2) => {
|
|
4845
|
+
NotificationStatus2["PENDING"] = "pending";
|
|
4846
|
+
NotificationStatus2["SENT"] = "sent";
|
|
4847
|
+
NotificationStatus2["FAILED"] = "failed";
|
|
4848
|
+
NotificationStatus2["CANCELLED"] = "cancelled";
|
|
4849
|
+
NotificationStatus2["PARTIAL_SUCCESS"] = "partialSuccess";
|
|
4850
|
+
return NotificationStatus2;
|
|
4851
|
+
})(NotificationStatus || {});
|
|
4852
|
+
|
|
4853
|
+
// src/services/notifications/notification.service.ts
|
|
4854
|
+
var NotificationService = class extends BaseService {
|
|
4643
4855
|
/**
|
|
4644
|
-
*
|
|
4856
|
+
* Kreira novu notifikaciju
|
|
4645
4857
|
*/
|
|
4646
|
-
async
|
|
4647
|
-
|
|
4858
|
+
async createNotification(notification) {
|
|
4859
|
+
const notificationsRef = collection10(this.db, NOTIFICATIONS_COLLECTION);
|
|
4860
|
+
const now = Timestamp14.now();
|
|
4861
|
+
const notificationData = {
|
|
4862
|
+
...notification,
|
|
4863
|
+
createdAt: now,
|
|
4864
|
+
updatedAt: now,
|
|
4865
|
+
status: "pending" /* PENDING */,
|
|
4866
|
+
isRead: false,
|
|
4867
|
+
userRole: notification.userRole || "patient" /* PATIENT */
|
|
4868
|
+
};
|
|
4869
|
+
const docRef = await addDoc(notificationsRef, notificationData);
|
|
4870
|
+
return {
|
|
4871
|
+
...notificationData,
|
|
4872
|
+
id: docRef.id
|
|
4873
|
+
};
|
|
4648
4874
|
}
|
|
4649
4875
|
/**
|
|
4650
|
-
*
|
|
4876
|
+
* Dohvata notifikaciju po ID-u
|
|
4651
4877
|
*/
|
|
4652
|
-
async
|
|
4653
|
-
|
|
4878
|
+
async getNotification(notificationId) {
|
|
4879
|
+
const notificationRef = doc11(
|
|
4880
|
+
this.db,
|
|
4881
|
+
NOTIFICATIONS_COLLECTION,
|
|
4882
|
+
notificationId
|
|
4883
|
+
);
|
|
4884
|
+
const notificationDoc = await getDoc14(notificationRef);
|
|
4885
|
+
if (!notificationDoc.exists()) {
|
|
4886
|
+
return null;
|
|
4887
|
+
}
|
|
4888
|
+
return {
|
|
4889
|
+
id: notificationDoc.id,
|
|
4890
|
+
...notificationDoc.data()
|
|
4891
|
+
};
|
|
4654
4892
|
}
|
|
4655
4893
|
/**
|
|
4656
|
-
*
|
|
4894
|
+
* Dohvata sve notifikacije za korisnika
|
|
4657
4895
|
*/
|
|
4658
|
-
async
|
|
4659
|
-
|
|
4896
|
+
async getUserNotifications(userId) {
|
|
4897
|
+
const q = query9(
|
|
4898
|
+
collection10(this.db, NOTIFICATIONS_COLLECTION),
|
|
4899
|
+
where9("userId", "==", userId),
|
|
4900
|
+
orderBy("notificationTime", "desc")
|
|
4901
|
+
);
|
|
4902
|
+
const querySnapshot = await getDocs9(q);
|
|
4903
|
+
return querySnapshot.docs.map((doc14) => ({
|
|
4904
|
+
id: doc14.id,
|
|
4905
|
+
...doc14.data()
|
|
4906
|
+
}));
|
|
4660
4907
|
}
|
|
4661
4908
|
/**
|
|
4662
|
-
*
|
|
4909
|
+
* Dohvata nepročitane notifikacije za korisnika
|
|
4663
4910
|
*/
|
|
4664
|
-
async
|
|
4665
|
-
|
|
4911
|
+
async getUnreadNotifications(userId) {
|
|
4912
|
+
const q = query9(
|
|
4913
|
+
collection10(this.db, NOTIFICATIONS_COLLECTION),
|
|
4914
|
+
where9("userId", "==", userId),
|
|
4915
|
+
where9("isRead", "==", false),
|
|
4916
|
+
orderBy("notificationTime", "desc")
|
|
4917
|
+
);
|
|
4918
|
+
const querySnapshot = await getDocs9(q);
|
|
4919
|
+
return querySnapshot.docs.map((doc14) => ({
|
|
4920
|
+
id: doc14.id,
|
|
4921
|
+
...doc14.data()
|
|
4922
|
+
}));
|
|
4666
4923
|
}
|
|
4667
4924
|
/**
|
|
4668
|
-
*
|
|
4925
|
+
* Označava notifikaciju kao pročitanu
|
|
4669
4926
|
*/
|
|
4670
|
-
async
|
|
4671
|
-
|
|
4927
|
+
async markAsRead(notificationId) {
|
|
4928
|
+
const notificationRef = doc11(
|
|
4929
|
+
this.db,
|
|
4930
|
+
NOTIFICATIONS_COLLECTION,
|
|
4931
|
+
notificationId
|
|
4932
|
+
);
|
|
4933
|
+
await updateDoc13(notificationRef, {
|
|
4934
|
+
isRead: true,
|
|
4935
|
+
updatedAt: Timestamp14.now()
|
|
4936
|
+
});
|
|
4672
4937
|
}
|
|
4673
4938
|
/**
|
|
4674
|
-
*
|
|
4939
|
+
* Označava sve notifikacije korisnika kao pročitane
|
|
4675
4940
|
*/
|
|
4676
|
-
async
|
|
4677
|
-
|
|
4941
|
+
async markAllAsRead(userId) {
|
|
4942
|
+
const notifications = await this.getUnreadNotifications(userId);
|
|
4943
|
+
const batch = writeBatch2(this.db);
|
|
4944
|
+
notifications.forEach((notification) => {
|
|
4945
|
+
const notificationRef = doc11(
|
|
4946
|
+
this.db,
|
|
4947
|
+
NOTIFICATIONS_COLLECTION,
|
|
4948
|
+
notification.id
|
|
4949
|
+
);
|
|
4950
|
+
batch.update(notificationRef, {
|
|
4951
|
+
isRead: true,
|
|
4952
|
+
updatedAt: Timestamp14.now()
|
|
4953
|
+
});
|
|
4954
|
+
});
|
|
4955
|
+
await batch.commit();
|
|
4956
|
+
}
|
|
4957
|
+
/**
|
|
4958
|
+
* Ažurira status notifikacije
|
|
4959
|
+
*/
|
|
4960
|
+
async updateNotificationStatus(notificationId, status) {
|
|
4961
|
+
const notificationRef = doc11(
|
|
4678
4962
|
this.db,
|
|
4679
|
-
|
|
4680
|
-
|
|
4681
|
-
data
|
|
4963
|
+
NOTIFICATIONS_COLLECTION,
|
|
4964
|
+
notificationId
|
|
4682
4965
|
);
|
|
4966
|
+
await updateDoc13(notificationRef, {
|
|
4967
|
+
status,
|
|
4968
|
+
updatedAt: Timestamp14.now()
|
|
4969
|
+
});
|
|
4683
4970
|
}
|
|
4684
4971
|
/**
|
|
4685
|
-
*
|
|
4972
|
+
* Briše notifikaciju
|
|
4686
4973
|
*/
|
|
4687
|
-
async
|
|
4688
|
-
|
|
4974
|
+
async deleteNotification(notificationId) {
|
|
4975
|
+
const notificationRef = doc11(
|
|
4689
4976
|
this.db,
|
|
4690
|
-
|
|
4691
|
-
|
|
4692
|
-
userRef
|
|
4977
|
+
NOTIFICATIONS_COLLECTION,
|
|
4978
|
+
notificationId
|
|
4693
4979
|
);
|
|
4980
|
+
await deleteDoc6(notificationRef);
|
|
4694
4981
|
}
|
|
4695
4982
|
/**
|
|
4696
|
-
*
|
|
4983
|
+
* Dohvata notifikacije po tipu
|
|
4697
4984
|
*/
|
|
4698
|
-
async
|
|
4699
|
-
|
|
4700
|
-
this.db,
|
|
4701
|
-
|
|
4702
|
-
|
|
4703
|
-
|
|
4985
|
+
async getNotificationsByType(userId, type) {
|
|
4986
|
+
const q = query9(
|
|
4987
|
+
collection10(this.db, NOTIFICATIONS_COLLECTION),
|
|
4988
|
+
where9("userId", "==", userId),
|
|
4989
|
+
where9("notificationType", "==", type),
|
|
4990
|
+
orderBy("notificationTime", "desc")
|
|
4704
4991
|
);
|
|
4992
|
+
const querySnapshot = await getDocs9(q);
|
|
4993
|
+
return querySnapshot.docs.map((doc14) => ({
|
|
4994
|
+
id: doc14.id,
|
|
4995
|
+
...doc14.data()
|
|
4996
|
+
}));
|
|
4705
4997
|
}
|
|
4706
4998
|
/**
|
|
4707
|
-
* Dohvata
|
|
4999
|
+
* Dohvata notifikacije za određeni termin
|
|
4708
5000
|
*/
|
|
4709
|
-
async
|
|
4710
|
-
|
|
5001
|
+
async getAppointmentNotifications(appointmentId) {
|
|
5002
|
+
const q = query9(
|
|
5003
|
+
collection10(this.db, NOTIFICATIONS_COLLECTION),
|
|
5004
|
+
where9("appointmentId", "==", appointmentId),
|
|
5005
|
+
orderBy("notificationTime", "desc")
|
|
5006
|
+
);
|
|
5007
|
+
const querySnapshot = await getDocs9(q);
|
|
5008
|
+
return querySnapshot.docs.map((doc14) => ({
|
|
5009
|
+
id: doc14.id,
|
|
5010
|
+
...doc14.data()
|
|
5011
|
+
}));
|
|
4711
5012
|
}
|
|
4712
5013
|
};
|
|
4713
5014
|
|
|
4714
5015
|
// src/services/documentation-templates/documentation-template.service.ts
|
|
4715
5016
|
import {
|
|
4716
|
-
collection as
|
|
4717
|
-
doc as
|
|
4718
|
-
getDoc as
|
|
4719
|
-
getDocs as
|
|
4720
|
-
setDoc as
|
|
4721
|
-
updateDoc as
|
|
5017
|
+
collection as collection11,
|
|
5018
|
+
doc as doc12,
|
|
5019
|
+
getDoc as getDoc15,
|
|
5020
|
+
getDocs as getDocs10,
|
|
5021
|
+
setDoc as setDoc13,
|
|
5022
|
+
updateDoc as updateDoc14,
|
|
4722
5023
|
deleteDoc as deleteDoc7,
|
|
4723
|
-
query as
|
|
4724
|
-
where as
|
|
5024
|
+
query as query10,
|
|
5025
|
+
where as where10,
|
|
4725
5026
|
orderBy as orderBy2,
|
|
4726
5027
|
limit,
|
|
4727
5028
|
startAfter
|
|
@@ -4729,7 +5030,7 @@ import {
|
|
|
4729
5030
|
var DocumentationTemplateService = class extends BaseService {
|
|
4730
5031
|
constructor() {
|
|
4731
5032
|
super(...arguments);
|
|
4732
|
-
this.collectionRef =
|
|
5033
|
+
this.collectionRef = collection11(
|
|
4733
5034
|
this.db,
|
|
4734
5035
|
DOCUMENTATION_TEMPLATES_COLLECTION
|
|
4735
5036
|
);
|
|
@@ -4760,8 +5061,8 @@ var DocumentationTemplateService = class extends BaseService {
|
|
|
4760
5061
|
isActive: true,
|
|
4761
5062
|
tags: validatedData.tags || []
|
|
4762
5063
|
};
|
|
4763
|
-
const docRef =
|
|
4764
|
-
await
|
|
5064
|
+
const docRef = doc12(this.collectionRef, templateId);
|
|
5065
|
+
await setDoc13(docRef, template);
|
|
4765
5066
|
return template;
|
|
4766
5067
|
}
|
|
4767
5068
|
/**
|
|
@@ -4770,8 +5071,8 @@ var DocumentationTemplateService = class extends BaseService {
|
|
|
4770
5071
|
* @returns The template or null if not found
|
|
4771
5072
|
*/
|
|
4772
5073
|
async getTemplateById(templateId) {
|
|
4773
|
-
const docRef =
|
|
4774
|
-
const docSnap = await
|
|
5074
|
+
const docRef = doc12(this.collectionRef, templateId);
|
|
5075
|
+
const docSnap = await getDoc15(docRef);
|
|
4775
5076
|
if (!docSnap.exists()) {
|
|
4776
5077
|
return null;
|
|
4777
5078
|
}
|
|
@@ -4802,8 +5103,8 @@ var DocumentationTemplateService = class extends BaseService {
|
|
|
4802
5103
|
updatedAt: Date.now(),
|
|
4803
5104
|
version: template.version + 1
|
|
4804
5105
|
};
|
|
4805
|
-
const docRef =
|
|
4806
|
-
await
|
|
5106
|
+
const docRef = doc12(this.collectionRef, templateId);
|
|
5107
|
+
await updateDoc14(docRef, updateData);
|
|
4807
5108
|
return {
|
|
4808
5109
|
...template,
|
|
4809
5110
|
...updateData
|
|
@@ -4814,7 +5115,7 @@ var DocumentationTemplateService = class extends BaseService {
|
|
|
4814
5115
|
* @param templateId - ID of the template to delete
|
|
4815
5116
|
*/
|
|
4816
5117
|
async deleteTemplate(templateId) {
|
|
4817
|
-
const docRef =
|
|
5118
|
+
const docRef = doc12(this.collectionRef, templateId);
|
|
4818
5119
|
await deleteDoc7(docRef);
|
|
4819
5120
|
}
|
|
4820
5121
|
/**
|
|
@@ -4824,21 +5125,21 @@ var DocumentationTemplateService = class extends BaseService {
|
|
|
4824
5125
|
* @returns Array of templates and the last document for pagination
|
|
4825
5126
|
*/
|
|
4826
5127
|
async getActiveTemplates(pageSize = 20, lastDoc) {
|
|
4827
|
-
let q =
|
|
5128
|
+
let q = query10(
|
|
4828
5129
|
this.collectionRef,
|
|
4829
|
-
|
|
5130
|
+
where10("isActive", "==", true),
|
|
4830
5131
|
orderBy2("updatedAt", "desc"),
|
|
4831
5132
|
limit(pageSize)
|
|
4832
5133
|
);
|
|
4833
5134
|
if (lastDoc) {
|
|
4834
|
-
q =
|
|
5135
|
+
q = query10(q, startAfter(lastDoc));
|
|
4835
5136
|
}
|
|
4836
|
-
const querySnapshot = await
|
|
5137
|
+
const querySnapshot = await getDocs10(q);
|
|
4837
5138
|
const templates = [];
|
|
4838
5139
|
let lastVisible = null;
|
|
4839
|
-
querySnapshot.forEach((
|
|
4840
|
-
templates.push(
|
|
4841
|
-
lastVisible =
|
|
5140
|
+
querySnapshot.forEach((doc14) => {
|
|
5141
|
+
templates.push(doc14.data());
|
|
5142
|
+
lastVisible = doc14;
|
|
4842
5143
|
});
|
|
4843
5144
|
return {
|
|
4844
5145
|
templates,
|
|
@@ -4853,22 +5154,22 @@ var DocumentationTemplateService = class extends BaseService {
|
|
|
4853
5154
|
* @returns Array of templates and the last document for pagination
|
|
4854
5155
|
*/
|
|
4855
5156
|
async getTemplatesByTags(tags, pageSize = 20, lastDoc) {
|
|
4856
|
-
let q =
|
|
5157
|
+
let q = query10(
|
|
4857
5158
|
this.collectionRef,
|
|
4858
|
-
|
|
4859
|
-
|
|
5159
|
+
where10("isActive", "==", true),
|
|
5160
|
+
where10("tags", "array-contains-any", tags),
|
|
4860
5161
|
orderBy2("updatedAt", "desc"),
|
|
4861
5162
|
limit(pageSize)
|
|
4862
5163
|
);
|
|
4863
5164
|
if (lastDoc) {
|
|
4864
|
-
q =
|
|
5165
|
+
q = query10(q, startAfter(lastDoc));
|
|
4865
5166
|
}
|
|
4866
|
-
const querySnapshot = await
|
|
5167
|
+
const querySnapshot = await getDocs10(q);
|
|
4867
5168
|
const templates = [];
|
|
4868
5169
|
let lastVisible = null;
|
|
4869
|
-
querySnapshot.forEach((
|
|
4870
|
-
templates.push(
|
|
4871
|
-
lastVisible =
|
|
5170
|
+
querySnapshot.forEach((doc14) => {
|
|
5171
|
+
templates.push(doc14.data());
|
|
5172
|
+
lastVisible = doc14;
|
|
4872
5173
|
});
|
|
4873
5174
|
return {
|
|
4874
5175
|
templates,
|
|
@@ -4883,21 +5184,21 @@ var DocumentationTemplateService = class extends BaseService {
|
|
|
4883
5184
|
* @returns Array of templates and the last document for pagination
|
|
4884
5185
|
*/
|
|
4885
5186
|
async getTemplatesByCreator(userId, pageSize = 20, lastDoc) {
|
|
4886
|
-
let q =
|
|
5187
|
+
let q = query10(
|
|
4887
5188
|
this.collectionRef,
|
|
4888
|
-
|
|
5189
|
+
where10("createdBy", "==", userId),
|
|
4889
5190
|
orderBy2("updatedAt", "desc"),
|
|
4890
5191
|
limit(pageSize)
|
|
4891
5192
|
);
|
|
4892
5193
|
if (lastDoc) {
|
|
4893
|
-
q =
|
|
5194
|
+
q = query10(q, startAfter(lastDoc));
|
|
4894
5195
|
}
|
|
4895
|
-
const querySnapshot = await
|
|
5196
|
+
const querySnapshot = await getDocs10(q);
|
|
4896
5197
|
const templates = [];
|
|
4897
5198
|
let lastVisible = null;
|
|
4898
|
-
querySnapshot.forEach((
|
|
4899
|
-
templates.push(
|
|
4900
|
-
lastVisible =
|
|
5199
|
+
querySnapshot.forEach((doc14) => {
|
|
5200
|
+
templates.push(doc14.data());
|
|
5201
|
+
lastVisible = doc14;
|
|
4901
5202
|
});
|
|
4902
5203
|
return {
|
|
4903
5204
|
templates,
|
|
@@ -4908,14 +5209,14 @@ var DocumentationTemplateService = class extends BaseService {
|
|
|
4908
5209
|
|
|
4909
5210
|
// src/services/documentation-templates/filled-document.service.ts
|
|
4910
5211
|
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
|
|
5212
|
+
collection as collection12,
|
|
5213
|
+
doc as doc13,
|
|
5214
|
+
getDoc as getDoc16,
|
|
5215
|
+
getDocs as getDocs11,
|
|
5216
|
+
setDoc as setDoc14,
|
|
5217
|
+
updateDoc as updateDoc15,
|
|
5218
|
+
query as query11,
|
|
5219
|
+
where as where11,
|
|
4919
5220
|
orderBy as orderBy3,
|
|
4920
5221
|
limit as limit2,
|
|
4921
5222
|
startAfter as startAfter2
|
|
@@ -4923,7 +5224,7 @@ import {
|
|
|
4923
5224
|
var FilledDocumentService = class extends BaseService {
|
|
4924
5225
|
constructor(...args) {
|
|
4925
5226
|
super(...args);
|
|
4926
|
-
this.collectionRef =
|
|
5227
|
+
this.collectionRef = collection12(
|
|
4927
5228
|
this.db,
|
|
4928
5229
|
FILLED_DOCUMENTS_COLLECTION
|
|
4929
5230
|
);
|
|
@@ -4956,8 +5257,8 @@ var FilledDocumentService = class extends BaseService {
|
|
|
4956
5257
|
values: {},
|
|
4957
5258
|
status: "draft" /* DRAFT */
|
|
4958
5259
|
};
|
|
4959
|
-
const docRef =
|
|
4960
|
-
await
|
|
5260
|
+
const docRef = doc13(this.collectionRef, documentId);
|
|
5261
|
+
await setDoc14(docRef, filledDocument);
|
|
4961
5262
|
return filledDocument;
|
|
4962
5263
|
}
|
|
4963
5264
|
/**
|
|
@@ -4966,8 +5267,8 @@ var FilledDocumentService = class extends BaseService {
|
|
|
4966
5267
|
* @returns The filled document or null if not found
|
|
4967
5268
|
*/
|
|
4968
5269
|
async getFilledDocumentById(documentId) {
|
|
4969
|
-
const docRef =
|
|
4970
|
-
const docSnap = await
|
|
5270
|
+
const docRef = doc13(this.collectionRef, documentId);
|
|
5271
|
+
const docSnap = await getDoc16(docRef);
|
|
4971
5272
|
if (!docSnap.exists()) {
|
|
4972
5273
|
return null;
|
|
4973
5274
|
}
|
|
@@ -4995,8 +5296,8 @@ var FilledDocumentService = class extends BaseService {
|
|
|
4995
5296
|
if (status) {
|
|
4996
5297
|
updateData.status = status;
|
|
4997
5298
|
}
|
|
4998
|
-
const docRef =
|
|
4999
|
-
await
|
|
5299
|
+
const docRef = doc13(this.collectionRef, documentId);
|
|
5300
|
+
await updateDoc15(docRef, updateData);
|
|
5000
5301
|
return {
|
|
5001
5302
|
...filledDocument,
|
|
5002
5303
|
...updateData
|
|
@@ -5010,21 +5311,21 @@ var FilledDocumentService = class extends BaseService {
|
|
|
5010
5311
|
* @returns Array of filled documents and the last document for pagination
|
|
5011
5312
|
*/
|
|
5012
5313
|
async getFilledDocumentsByPatient(patientId, pageSize = 20, lastDoc) {
|
|
5013
|
-
let q =
|
|
5314
|
+
let q = query11(
|
|
5014
5315
|
this.collectionRef,
|
|
5015
|
-
|
|
5316
|
+
where11("patientId", "==", patientId),
|
|
5016
5317
|
orderBy3("updatedAt", "desc"),
|
|
5017
5318
|
limit2(pageSize)
|
|
5018
5319
|
);
|
|
5019
5320
|
if (lastDoc) {
|
|
5020
|
-
q =
|
|
5321
|
+
q = query11(q, startAfter2(lastDoc));
|
|
5021
5322
|
}
|
|
5022
|
-
const querySnapshot = await
|
|
5323
|
+
const querySnapshot = await getDocs11(q);
|
|
5023
5324
|
const documents = [];
|
|
5024
5325
|
let lastVisible = null;
|
|
5025
|
-
querySnapshot.forEach((
|
|
5026
|
-
documents.push(
|
|
5027
|
-
lastVisible =
|
|
5326
|
+
querySnapshot.forEach((doc14) => {
|
|
5327
|
+
documents.push(doc14.data());
|
|
5328
|
+
lastVisible = doc14;
|
|
5028
5329
|
});
|
|
5029
5330
|
return {
|
|
5030
5331
|
documents,
|
|
@@ -5039,21 +5340,21 @@ var FilledDocumentService = class extends BaseService {
|
|
|
5039
5340
|
* @returns Array of filled documents and the last document for pagination
|
|
5040
5341
|
*/
|
|
5041
5342
|
async getFilledDocumentsByPractitioner(practitionerId, pageSize = 20, lastDoc) {
|
|
5042
|
-
let q =
|
|
5343
|
+
let q = query11(
|
|
5043
5344
|
this.collectionRef,
|
|
5044
|
-
|
|
5345
|
+
where11("practitionerId", "==", practitionerId),
|
|
5045
5346
|
orderBy3("updatedAt", "desc"),
|
|
5046
5347
|
limit2(pageSize)
|
|
5047
5348
|
);
|
|
5048
5349
|
if (lastDoc) {
|
|
5049
|
-
q =
|
|
5350
|
+
q = query11(q, startAfter2(lastDoc));
|
|
5050
5351
|
}
|
|
5051
|
-
const querySnapshot = await
|
|
5352
|
+
const querySnapshot = await getDocs11(q);
|
|
5052
5353
|
const documents = [];
|
|
5053
5354
|
let lastVisible = null;
|
|
5054
|
-
querySnapshot.forEach((
|
|
5055
|
-
documents.push(
|
|
5056
|
-
lastVisible =
|
|
5355
|
+
querySnapshot.forEach((doc14) => {
|
|
5356
|
+
documents.push(doc14.data());
|
|
5357
|
+
lastVisible = doc14;
|
|
5057
5358
|
});
|
|
5058
5359
|
return {
|
|
5059
5360
|
documents,
|
|
@@ -5068,21 +5369,21 @@ var FilledDocumentService = class extends BaseService {
|
|
|
5068
5369
|
* @returns Array of filled documents and the last document for pagination
|
|
5069
5370
|
*/
|
|
5070
5371
|
async getFilledDocumentsByClinic(clinicId, pageSize = 20, lastDoc) {
|
|
5071
|
-
let q =
|
|
5372
|
+
let q = query11(
|
|
5072
5373
|
this.collectionRef,
|
|
5073
|
-
|
|
5374
|
+
where11("clinicId", "==", clinicId),
|
|
5074
5375
|
orderBy3("updatedAt", "desc"),
|
|
5075
5376
|
limit2(pageSize)
|
|
5076
5377
|
);
|
|
5077
5378
|
if (lastDoc) {
|
|
5078
|
-
q =
|
|
5379
|
+
q = query11(q, startAfter2(lastDoc));
|
|
5079
5380
|
}
|
|
5080
|
-
const querySnapshot = await
|
|
5381
|
+
const querySnapshot = await getDocs11(q);
|
|
5081
5382
|
const documents = [];
|
|
5082
5383
|
let lastVisible = null;
|
|
5083
|
-
querySnapshot.forEach((
|
|
5084
|
-
documents.push(
|
|
5085
|
-
lastVisible =
|
|
5384
|
+
querySnapshot.forEach((doc14) => {
|
|
5385
|
+
documents.push(doc14.data());
|
|
5386
|
+
lastVisible = doc14;
|
|
5086
5387
|
});
|
|
5087
5388
|
return {
|
|
5088
5389
|
documents,
|
|
@@ -5097,21 +5398,21 @@ var FilledDocumentService = class extends BaseService {
|
|
|
5097
5398
|
* @returns Array of filled documents and the last document for pagination
|
|
5098
5399
|
*/
|
|
5099
5400
|
async getFilledDocumentsByTemplate(templateId, pageSize = 20, lastDoc) {
|
|
5100
|
-
let q =
|
|
5401
|
+
let q = query11(
|
|
5101
5402
|
this.collectionRef,
|
|
5102
|
-
|
|
5403
|
+
where11("templateId", "==", templateId),
|
|
5103
5404
|
orderBy3("updatedAt", "desc"),
|
|
5104
5405
|
limit2(pageSize)
|
|
5105
5406
|
);
|
|
5106
5407
|
if (lastDoc) {
|
|
5107
|
-
q =
|
|
5408
|
+
q = query11(q, startAfter2(lastDoc));
|
|
5108
5409
|
}
|
|
5109
|
-
const querySnapshot = await
|
|
5410
|
+
const querySnapshot = await getDocs11(q);
|
|
5110
5411
|
const documents = [];
|
|
5111
5412
|
let lastVisible = null;
|
|
5112
|
-
querySnapshot.forEach((
|
|
5113
|
-
documents.push(
|
|
5114
|
-
lastVisible =
|
|
5413
|
+
querySnapshot.forEach((doc14) => {
|
|
5414
|
+
documents.push(doc14.data());
|
|
5415
|
+
lastVisible = doc14;
|
|
5115
5416
|
});
|
|
5116
5417
|
return {
|
|
5117
5418
|
documents,
|
|
@@ -5126,21 +5427,21 @@ var FilledDocumentService = class extends BaseService {
|
|
|
5126
5427
|
* @returns Array of filled documents and the last document for pagination
|
|
5127
5428
|
*/
|
|
5128
5429
|
async getFilledDocumentsByStatus(status, pageSize = 20, lastDoc) {
|
|
5129
|
-
let q =
|
|
5430
|
+
let q = query11(
|
|
5130
5431
|
this.collectionRef,
|
|
5131
|
-
|
|
5432
|
+
where11("status", "==", status),
|
|
5132
5433
|
orderBy3("updatedAt", "desc"),
|
|
5133
5434
|
limit2(pageSize)
|
|
5134
5435
|
);
|
|
5135
5436
|
if (lastDoc) {
|
|
5136
|
-
q =
|
|
5437
|
+
q = query11(q, startAfter2(lastDoc));
|
|
5137
5438
|
}
|
|
5138
|
-
const querySnapshot = await
|
|
5439
|
+
const querySnapshot = await getDocs11(q);
|
|
5139
5440
|
const documents = [];
|
|
5140
5441
|
let lastVisible = null;
|
|
5141
|
-
querySnapshot.forEach((
|
|
5142
|
-
documents.push(
|
|
5143
|
-
lastVisible =
|
|
5442
|
+
querySnapshot.forEach((doc14) => {
|
|
5443
|
+
documents.push(doc14.data());
|
|
5444
|
+
lastVisible = doc14;
|
|
5144
5445
|
});
|
|
5145
5446
|
return {
|
|
5146
5447
|
documents,
|
|
@@ -5265,8 +5566,11 @@ export {
|
|
|
5265
5566
|
blockingConditionSchema,
|
|
5266
5567
|
clinicAdminOptionsSchema,
|
|
5267
5568
|
clinicAdminSchema,
|
|
5569
|
+
clinicAdminSignupSchema,
|
|
5570
|
+
clinicBranchSetupSchema,
|
|
5268
5571
|
clinicContactInfoSchema,
|
|
5269
5572
|
clinicGroupSchema,
|
|
5573
|
+
clinicGroupSetupSchema,
|
|
5270
5574
|
clinicInfoSchema,
|
|
5271
5575
|
clinicLocationSchema,
|
|
5272
5576
|
clinicReviewSchema,
|
|
@@ -5321,6 +5625,9 @@ export {
|
|
|
5321
5625
|
timestampSchema,
|
|
5322
5626
|
updateAllergySchema,
|
|
5323
5627
|
updateBlockingConditionSchema,
|
|
5628
|
+
updateClinicAdminSchema,
|
|
5629
|
+
updateClinicGroupSchema,
|
|
5630
|
+
updateClinicSchema,
|
|
5324
5631
|
updateContraindicationSchema,
|
|
5325
5632
|
updateDocumentTemplateSchema,
|
|
5326
5633
|
updateMedicationSchema,
|