@blackcode_sa/metaestetics-api 1.11.3 → 1.12.1
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/admin/index.d.mts +378 -334
- package/dist/admin/index.d.ts +378 -334
- package/dist/backoffice/index.d.mts +1198 -430
- package/dist/backoffice/index.d.ts +1198 -430
- package/dist/backoffice/index.js +1128 -245
- package/dist/backoffice/index.mjs +1119 -209
- package/dist/index.d.mts +4478 -4031
- package/dist/index.d.ts +4478 -4031
- package/dist/index.js +1974 -757
- package/dist/index.mjs +1735 -490
- package/package.json +1 -1
- package/src/backoffice/expo-safe/index.ts +4 -0
- package/src/backoffice/services/README.md +40 -0
- package/src/backoffice/services/brand.service.ts +85 -6
- package/src/backoffice/services/category.service.ts +92 -10
- package/src/backoffice/services/constants.service.ts +308 -0
- package/src/backoffice/services/documentation-template.service.ts +56 -2
- package/src/backoffice/services/index.ts +1 -0
- package/src/backoffice/services/product.service.ts +126 -5
- package/src/backoffice/services/requirement.service.ts +13 -0
- package/src/backoffice/services/subcategory.service.ts +184 -13
- package/src/backoffice/services/technology.service.ts +344 -129
- package/src/backoffice/types/admin-constants.types.ts +69 -0
- package/src/backoffice/types/brand.types.ts +1 -0
- package/src/backoffice/types/index.ts +2 -0
- package/src/backoffice/types/procedure-product.types.ts +38 -0
- package/src/backoffice/types/product.types.ts +31 -4
- package/src/backoffice/types/static/contraindication.types.ts +1 -0
- package/src/backoffice/types/static/treatment-benefit.types.ts +1 -0
- package/src/backoffice/types/technology.types.ts +113 -4
- package/src/backoffice/validations/schemas.ts +35 -9
- package/src/services/appointment/appointment.service.ts +0 -5
- package/src/services/appointment/utils/appointment.utils.ts +124 -113
- package/src/services/base.service.ts +10 -3
- package/src/services/documentation-templates/documentation-template.service.ts +116 -0
- package/src/services/media/media.service.ts +2 -2
- package/src/services/practitioner/practitioner.service.ts +201 -83
- package/src/services/procedure/README.md +76 -1
- package/src/services/procedure/procedure.service.ts +538 -235
- package/src/types/appointment/index.ts +2 -3
- package/src/types/clinic/index.ts +1 -6
- package/src/types/patient/medical-info.types.ts +3 -3
- package/src/types/procedure/index.ts +39 -20
- package/src/validations/clinic.schema.ts +1 -6
- package/src/validations/patient/medical-info.schema.ts +7 -2
- package/src/validations/procedure-product.schema.ts +41 -0
- package/src/validations/procedure.schema.ts +59 -8
- package/src/backoffice/services/__tests__/brand.service.test.ts +0 -196
- package/src/backoffice/services/__tests__/category.service.test.ts +0 -201
- package/src/backoffice/services/__tests__/product.service.test.ts +0 -358
- package/src/backoffice/services/__tests__/requirement.service.test.ts +0 -226
- package/src/backoffice/services/__tests__/subcategory.service.test.ts +0 -181
- package/src/backoffice/services/__tests__/technology.service.test.ts +0 -1097
package/dist/index.mjs
CHANGED
|
@@ -17,11 +17,13 @@ import { getFunctions } from "firebase/functions";
|
|
|
17
17
|
// src/services/base.service.ts
|
|
18
18
|
import { getStorage } from "firebase/storage";
|
|
19
19
|
var BaseService = class {
|
|
20
|
-
constructor(db, auth, app) {
|
|
20
|
+
constructor(db, auth, app, storage) {
|
|
21
21
|
this.db = db;
|
|
22
22
|
this.auth = auth;
|
|
23
23
|
this.app = app;
|
|
24
|
-
|
|
24
|
+
if (app) {
|
|
25
|
+
this.storage = storage || getStorage(app);
|
|
26
|
+
}
|
|
25
27
|
}
|
|
26
28
|
/**
|
|
27
29
|
* Generiše jedinstveni ID za dokumente
|
|
@@ -53,13 +55,13 @@ var AppointmentStatus = /* @__PURE__ */ ((AppointmentStatus2) => {
|
|
|
53
55
|
AppointmentStatus2["RESCHEDULED_BY_CLINIC"] = "rescheduled_by_clinic";
|
|
54
56
|
return AppointmentStatus2;
|
|
55
57
|
})(AppointmentStatus || {});
|
|
56
|
-
var PaymentStatus = /* @__PURE__ */ ((
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
return
|
|
58
|
+
var PaymentStatus = /* @__PURE__ */ ((PaymentStatus4) => {
|
|
59
|
+
PaymentStatus4["UNPAID"] = "unpaid";
|
|
60
|
+
PaymentStatus4["PAID"] = "paid";
|
|
61
|
+
PaymentStatus4["PARTIALLY_PAID"] = "partially_paid";
|
|
62
|
+
PaymentStatus4["REFUNDED"] = "refunded";
|
|
63
|
+
PaymentStatus4["NOT_APPLICABLE"] = "not_applicable";
|
|
64
|
+
return PaymentStatus4;
|
|
63
65
|
})(PaymentStatus || {});
|
|
64
66
|
var MediaType = /* @__PURE__ */ ((MediaType2) => {
|
|
65
67
|
MediaType2["BEFORE_PHOTO"] = "before_photo";
|
|
@@ -582,7 +584,6 @@ import {
|
|
|
582
584
|
getDocs,
|
|
583
585
|
query,
|
|
584
586
|
where,
|
|
585
|
-
setDoc,
|
|
586
587
|
updateDoc,
|
|
587
588
|
serverTimestamp,
|
|
588
589
|
Timestamp,
|
|
@@ -819,44 +820,48 @@ async function updateAppointmentUtil(db, appointmentId, data) {
|
|
|
819
820
|
const validPreReqIds = currentAppointment.preProcedureRequirements.map(
|
|
820
821
|
(req) => req.id
|
|
821
822
|
);
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
if (invalidPreReqIds.length > 0) {
|
|
826
|
-
throw new Error(
|
|
827
|
-
`Invalid pre-requirement IDs: ${invalidPreReqIds.join(", ")}`
|
|
823
|
+
if (Array.isArray(data.completedPreRequirements)) {
|
|
824
|
+
const invalidPreReqIds = data.completedPreRequirements.filter(
|
|
825
|
+
(id) => !validPreReqIds.includes(id)
|
|
828
826
|
);
|
|
827
|
+
if (invalidPreReqIds.length > 0) {
|
|
828
|
+
throw new Error(
|
|
829
|
+
`Invalid pre-requirement IDs: ${invalidPreReqIds.join(", ")}`
|
|
830
|
+
);
|
|
831
|
+
}
|
|
832
|
+
completedPreRequirements = [
|
|
833
|
+
.../* @__PURE__ */ new Set([
|
|
834
|
+
...completedPreRequirements,
|
|
835
|
+
...data.completedPreRequirements
|
|
836
|
+
])
|
|
837
|
+
];
|
|
829
838
|
}
|
|
830
|
-
completedPreRequirements = [
|
|
831
|
-
.../* @__PURE__ */ new Set([
|
|
832
|
-
...completedPreRequirements,
|
|
833
|
-
...data.completedPreRequirements
|
|
834
|
-
])
|
|
835
|
-
];
|
|
836
839
|
}
|
|
837
840
|
if (data.completedPostRequirements) {
|
|
838
841
|
const validPostReqIds = currentAppointment.postProcedureRequirements.map(
|
|
839
842
|
(req) => req.id
|
|
840
843
|
);
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
if (invalidPostReqIds.length > 0) {
|
|
845
|
-
throw new Error(
|
|
846
|
-
`Invalid post-requirement IDs: ${invalidPostReqIds.join(", ")}`
|
|
844
|
+
if (Array.isArray(data.completedPostRequirements)) {
|
|
845
|
+
const invalidPostReqIds = data.completedPostRequirements.filter(
|
|
846
|
+
(id) => !validPostReqIds.includes(id)
|
|
847
847
|
);
|
|
848
|
+
if (invalidPostReqIds.length > 0) {
|
|
849
|
+
throw new Error(
|
|
850
|
+
`Invalid post-requirement IDs: ${invalidPostReqIds.join(", ")}`
|
|
851
|
+
);
|
|
852
|
+
}
|
|
853
|
+
completedPostRequirements = [
|
|
854
|
+
.../* @__PURE__ */ new Set([
|
|
855
|
+
...completedPostRequirements,
|
|
856
|
+
...data.completedPostRequirements
|
|
857
|
+
])
|
|
858
|
+
];
|
|
848
859
|
}
|
|
849
|
-
completedPostRequirements = [
|
|
850
|
-
.../* @__PURE__ */ new Set([
|
|
851
|
-
...completedPostRequirements,
|
|
852
|
-
...data.completedPostRequirements
|
|
853
|
-
])
|
|
854
|
-
];
|
|
855
860
|
}
|
|
856
861
|
const updateData = {
|
|
857
862
|
...data,
|
|
858
|
-
completedPreRequirements,
|
|
859
|
-
completedPostRequirements,
|
|
863
|
+
completedPreRequirements: Array.isArray(data.completedPreRequirements) ? completedPreRequirements : data.completedPreRequirements,
|
|
864
|
+
completedPostRequirements: Array.isArray(data.completedPostRequirements) ? completedPostRequirements : data.completedPostRequirements,
|
|
860
865
|
updatedAt: serverTimestamp()
|
|
861
866
|
};
|
|
862
867
|
Object.keys(updateData).forEach((key) => {
|
|
@@ -906,7 +911,7 @@ async function updateCalendarEventStatus(db, calendarEventId, appointmentStatus)
|
|
|
906
911
|
case "canceled_clinic" /* CANCELED_CLINIC */:
|
|
907
912
|
calendarStatus = "canceled";
|
|
908
913
|
break;
|
|
909
|
-
case
|
|
914
|
+
case "rescheduled_by_clinic" /* RESCHEDULED_BY_CLINIC */:
|
|
910
915
|
calendarStatus = "rescheduled";
|
|
911
916
|
break;
|
|
912
917
|
case "completed" /* COMPLETED */:
|
|
@@ -980,7 +985,7 @@ async function searchAppointmentsUtil(db, params) {
|
|
|
980
985
|
const q = query(collection(db, APPOINTMENTS_COLLECTION), ...constraints);
|
|
981
986
|
const querySnapshot = await getDocs(q);
|
|
982
987
|
const appointments = querySnapshot.docs.map(
|
|
983
|
-
(
|
|
988
|
+
(doc38) => doc38.data()
|
|
984
989
|
);
|
|
985
990
|
const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
986
991
|
return { appointments, lastDoc };
|
|
@@ -1787,7 +1792,7 @@ var AppointmentService = class extends BaseService {
|
|
|
1787
1792
|
);
|
|
1788
1793
|
const querySnapshot = await getDocs2(q);
|
|
1789
1794
|
const appointments = querySnapshot.docs.map(
|
|
1790
|
-
(
|
|
1795
|
+
(doc38) => doc38.data()
|
|
1791
1796
|
);
|
|
1792
1797
|
const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
1793
1798
|
console.log(
|
|
@@ -1860,7 +1865,7 @@ var AppointmentService = class extends BaseService {
|
|
|
1860
1865
|
);
|
|
1861
1866
|
const querySnapshot = await getDocs2(q);
|
|
1862
1867
|
const appointments = querySnapshot.docs.map(
|
|
1863
|
-
(
|
|
1868
|
+
(doc38) => doc38.data()
|
|
1864
1869
|
);
|
|
1865
1870
|
const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
1866
1871
|
console.log(
|
|
@@ -2412,8 +2417,8 @@ var MediaAccessLevel = /* @__PURE__ */ ((MediaAccessLevel2) => {
|
|
|
2412
2417
|
})(MediaAccessLevel || {});
|
|
2413
2418
|
var MEDIA_METADATA_COLLECTION = "media_metadata";
|
|
2414
2419
|
var MediaService = class extends BaseService {
|
|
2415
|
-
constructor(
|
|
2416
|
-
super(
|
|
2420
|
+
constructor(...args) {
|
|
2421
|
+
super(...args);
|
|
2417
2422
|
}
|
|
2418
2423
|
/**
|
|
2419
2424
|
* Upload a media file, store its metadata, and return the metadata including the URL.
|
|
@@ -2673,7 +2678,7 @@ var MediaService = class extends BaseService {
|
|
|
2673
2678
|
try {
|
|
2674
2679
|
const querySnapshot = await getDocs3(finalQuery);
|
|
2675
2680
|
const mediaList = querySnapshot.docs.map(
|
|
2676
|
-
(
|
|
2681
|
+
(doc38) => doc38.data()
|
|
2677
2682
|
);
|
|
2678
2683
|
console.log(`[MediaService] Found ${mediaList.length} media items.`);
|
|
2679
2684
|
return mediaList;
|
|
@@ -2736,8 +2741,8 @@ var getPatientsByClinicUtil = async (db, clinicId, options) => {
|
|
|
2736
2741
|
}
|
|
2737
2742
|
const patientsSnapshot = await getDocs4(q);
|
|
2738
2743
|
const patients = [];
|
|
2739
|
-
patientsSnapshot.forEach((
|
|
2740
|
-
patients.push(
|
|
2744
|
+
patientsSnapshot.forEach((doc38) => {
|
|
2745
|
+
patients.push(doc38.data());
|
|
2741
2746
|
});
|
|
2742
2747
|
console.log(
|
|
2743
2748
|
`[getPatientsByClinicUtil] Found ${patients.length} patients for clinic ID: ${clinicId}`
|
|
@@ -2799,23 +2804,6 @@ var BlockingCondition = /* @__PURE__ */ ((BlockingCondition2) => {
|
|
|
2799
2804
|
return BlockingCondition2;
|
|
2800
2805
|
})(BlockingCondition || {});
|
|
2801
2806
|
|
|
2802
|
-
// src/backoffice/types/static/contraindication.types.ts
|
|
2803
|
-
var Contraindication = /* @__PURE__ */ ((Contraindication2) => {
|
|
2804
|
-
Contraindication2["SENSITIVE_SKIN"] = "sensitive_skin";
|
|
2805
|
-
Contraindication2["RECENT_TANNING"] = "recent_tanning";
|
|
2806
|
-
Contraindication2["RECENT_BOTOX"] = "recent_botox";
|
|
2807
|
-
Contraindication2["RECENT_FILLERS"] = "recent_fillers";
|
|
2808
|
-
Contraindication2["SKIN_ALLERGIES"] = "skin_allergies";
|
|
2809
|
-
Contraindication2["MEDICATIONS"] = "medications";
|
|
2810
|
-
Contraindication2["RECENT_CHEMICAL_PEEL"] = "recent_chemical_peel";
|
|
2811
|
-
Contraindication2["RECENT_LASER"] = "recent_laser";
|
|
2812
|
-
Contraindication2["SKIN_INFLAMMATION"] = "skin_inflammation";
|
|
2813
|
-
Contraindication2["OPEN_WOUNDS"] = "open_wounds";
|
|
2814
|
-
Contraindication2["HERPES_SIMPLEX"] = "herpes_simplex";
|
|
2815
|
-
Contraindication2["COLD_SORES"] = "cold_sores";
|
|
2816
|
-
return Contraindication2;
|
|
2817
|
-
})(Contraindication || {});
|
|
2818
|
-
|
|
2819
2807
|
// src/validations/common.schema.ts
|
|
2820
2808
|
import { z as z5 } from "zod";
|
|
2821
2809
|
import { Timestamp as Timestamp4 } from "firebase/firestore";
|
|
@@ -2870,8 +2858,13 @@ var blockingConditionSchema = z6.object({
|
|
|
2870
2858
|
notes: z6.string().optional().nullable(),
|
|
2871
2859
|
isActive: z6.boolean()
|
|
2872
2860
|
});
|
|
2861
|
+
var contraindicationDynamicSchema = z6.object({
|
|
2862
|
+
id: z6.string(),
|
|
2863
|
+
name: z6.string(),
|
|
2864
|
+
description: z6.string().optional()
|
|
2865
|
+
});
|
|
2873
2866
|
var contraindicationSchema = z6.object({
|
|
2874
|
-
condition:
|
|
2867
|
+
condition: contraindicationDynamicSchema,
|
|
2875
2868
|
lastOccurrence: timestampSchema,
|
|
2876
2869
|
frequency: z6.enum(["rare", "occasional", "frequent"]),
|
|
2877
2870
|
notes: z6.string().optional().nullable(),
|
|
@@ -3111,8 +3104,8 @@ var getPatientsByPractitionerUtil = async (db, practitionerId, options) => {
|
|
|
3111
3104
|
}
|
|
3112
3105
|
const patientsSnapshot = await getDocs5(q);
|
|
3113
3106
|
const patients = [];
|
|
3114
|
-
patientsSnapshot.forEach((
|
|
3115
|
-
patients.push(
|
|
3107
|
+
patientsSnapshot.forEach((doc38) => {
|
|
3108
|
+
patients.push(doc38.data());
|
|
3116
3109
|
});
|
|
3117
3110
|
console.log(
|
|
3118
3111
|
`[getPatientsByPractitionerUtil] Found ${patients.length} patients for practitioner ID: ${practitionerId}`
|
|
@@ -3891,7 +3884,7 @@ async function getClinicAdminsByGroup(db, clinicGroupId) {
|
|
|
3891
3884
|
where6("clinicGroupId", "==", clinicGroupId)
|
|
3892
3885
|
);
|
|
3893
3886
|
const querySnapshot = await getDocs6(q);
|
|
3894
|
-
return querySnapshot.docs.map((
|
|
3887
|
+
return querySnapshot.docs.map((doc38) => doc38.data());
|
|
3895
3888
|
}
|
|
3896
3889
|
async function updateClinicAdmin(db, adminId, data) {
|
|
3897
3890
|
const admin = await getClinicAdmin(db, adminId);
|
|
@@ -4572,9 +4565,9 @@ var updateAllergyUtil = async (db, patientId, data, requesterId, requesterRoles)
|
|
|
4572
4565
|
};
|
|
4573
4566
|
var removeAllergyUtil = async (db, patientId, allergyIndex, requesterId, requesterRoles) => {
|
|
4574
4567
|
await checkMedicalAccessUtil(db, patientId, requesterId, requesterRoles);
|
|
4575
|
-
const
|
|
4576
|
-
if (!
|
|
4577
|
-
const medicalInfo =
|
|
4568
|
+
const doc38 = await getDoc10(getMedicalInfoDocRef(db, patientId));
|
|
4569
|
+
if (!doc38.exists()) throw new Error("Medical info not found");
|
|
4570
|
+
const medicalInfo = doc38.data();
|
|
4578
4571
|
if (allergyIndex >= medicalInfo.allergies.length) {
|
|
4579
4572
|
throw new Error("Invalid allergy index");
|
|
4580
4573
|
}
|
|
@@ -4601,9 +4594,9 @@ var updateBlockingConditionUtil = async (db, patientId, data, requesterId, reque
|
|
|
4601
4594
|
await checkMedicalAccessUtil(db, patientId, requesterId, requesterRoles);
|
|
4602
4595
|
const validatedData = updateBlockingConditionSchema.parse(data);
|
|
4603
4596
|
const { conditionIndex, ...updateData } = validatedData;
|
|
4604
|
-
const
|
|
4605
|
-
if (!
|
|
4606
|
-
const medicalInfo =
|
|
4597
|
+
const doc38 = await getDoc10(getMedicalInfoDocRef(db, patientId));
|
|
4598
|
+
if (!doc38.exists()) throw new Error("Medical info not found");
|
|
4599
|
+
const medicalInfo = doc38.data();
|
|
4607
4600
|
if (conditionIndex >= medicalInfo.blockingConditions.length) {
|
|
4608
4601
|
throw new Error("Invalid blocking condition index");
|
|
4609
4602
|
}
|
|
@@ -4620,9 +4613,9 @@ var updateBlockingConditionUtil = async (db, patientId, data, requesterId, reque
|
|
|
4620
4613
|
};
|
|
4621
4614
|
var removeBlockingConditionUtil = async (db, patientId, conditionIndex, requesterId, requesterRoles) => {
|
|
4622
4615
|
await checkMedicalAccessUtil(db, patientId, requesterId, requesterRoles);
|
|
4623
|
-
const
|
|
4624
|
-
if (!
|
|
4625
|
-
const medicalInfo =
|
|
4616
|
+
const doc38 = await getDoc10(getMedicalInfoDocRef(db, patientId));
|
|
4617
|
+
if (!doc38.exists()) throw new Error("Medical info not found");
|
|
4618
|
+
const medicalInfo = doc38.data();
|
|
4626
4619
|
if (conditionIndex >= medicalInfo.blockingConditions.length) {
|
|
4627
4620
|
throw new Error("Invalid blocking condition index");
|
|
4628
4621
|
}
|
|
@@ -4649,9 +4642,9 @@ var updateContraindicationUtil = async (db, patientId, data, requesterId, reques
|
|
|
4649
4642
|
await checkMedicalAccessUtil(db, patientId, requesterId, requesterRoles);
|
|
4650
4643
|
const validatedData = updateContraindicationSchema.parse(data);
|
|
4651
4644
|
const { contraindicationIndex, ...updateData } = validatedData;
|
|
4652
|
-
const
|
|
4653
|
-
if (!
|
|
4654
|
-
const medicalInfo =
|
|
4645
|
+
const doc38 = await getDoc10(getMedicalInfoDocRef(db, patientId));
|
|
4646
|
+
if (!doc38.exists()) throw new Error("Medical info not found");
|
|
4647
|
+
const medicalInfo = doc38.data();
|
|
4655
4648
|
if (contraindicationIndex >= medicalInfo.contraindications.length) {
|
|
4656
4649
|
throw new Error("Invalid contraindication index");
|
|
4657
4650
|
}
|
|
@@ -4668,9 +4661,9 @@ var updateContraindicationUtil = async (db, patientId, data, requesterId, reques
|
|
|
4668
4661
|
};
|
|
4669
4662
|
var removeContraindicationUtil = async (db, patientId, contraindicationIndex, requesterId, requesterRoles) => {
|
|
4670
4663
|
await checkMedicalAccessUtil(db, patientId, requesterId, requesterRoles);
|
|
4671
|
-
const
|
|
4672
|
-
if (!
|
|
4673
|
-
const medicalInfo =
|
|
4664
|
+
const doc38 = await getDoc10(getMedicalInfoDocRef(db, patientId));
|
|
4665
|
+
if (!doc38.exists()) throw new Error("Medical info not found");
|
|
4666
|
+
const medicalInfo = doc38.data();
|
|
4674
4667
|
if (contraindicationIndex >= medicalInfo.contraindications.length) {
|
|
4675
4668
|
throw new Error("Invalid contraindication index");
|
|
4676
4669
|
}
|
|
@@ -4697,9 +4690,9 @@ var updateMedicationUtil = async (db, patientId, data, requesterId, requesterRol
|
|
|
4697
4690
|
await checkMedicalAccessUtil(db, patientId, requesterId, requesterRoles);
|
|
4698
4691
|
const validatedData = updateMedicationSchema.parse(data);
|
|
4699
4692
|
const { medicationIndex, ...updateData } = validatedData;
|
|
4700
|
-
const
|
|
4701
|
-
if (!
|
|
4702
|
-
const medicalInfo =
|
|
4693
|
+
const doc38 = await getDoc10(getMedicalInfoDocRef(db, patientId));
|
|
4694
|
+
if (!doc38.exists()) throw new Error("Medical info not found");
|
|
4695
|
+
const medicalInfo = doc38.data();
|
|
4703
4696
|
if (medicationIndex >= medicalInfo.currentMedications.length) {
|
|
4704
4697
|
throw new Error("Invalid medication index");
|
|
4705
4698
|
}
|
|
@@ -4716,9 +4709,9 @@ var updateMedicationUtil = async (db, patientId, data, requesterId, requesterRol
|
|
|
4716
4709
|
};
|
|
4717
4710
|
var removeMedicationUtil = async (db, patientId, medicationIndex, requesterId, requesterRoles) => {
|
|
4718
4711
|
await checkMedicalAccessUtil(db, patientId, requesterId, requesterRoles);
|
|
4719
|
-
const
|
|
4720
|
-
if (!
|
|
4721
|
-
const medicalInfo =
|
|
4712
|
+
const doc38 = await getDoc10(getMedicalInfoDocRef(db, patientId));
|
|
4713
|
+
if (!doc38.exists()) throw new Error("Medical info not found");
|
|
4714
|
+
const medicalInfo = doc38.data();
|
|
4722
4715
|
if (medicationIndex >= medicalInfo.currentMedications.length) {
|
|
4723
4716
|
throw new Error("Invalid medication index");
|
|
4724
4717
|
}
|
|
@@ -5021,7 +5014,7 @@ var searchPatientsUtil = async (db, params, requester) => {
|
|
|
5021
5014
|
const finalQuery = query8(patientsCollectionRef, ...constraints);
|
|
5022
5015
|
const querySnapshot = await getDocs8(finalQuery);
|
|
5023
5016
|
const patients = querySnapshot.docs.map(
|
|
5024
|
-
(
|
|
5017
|
+
(doc38) => doc38.data()
|
|
5025
5018
|
);
|
|
5026
5019
|
console.log(
|
|
5027
5020
|
`[searchPatientsUtil] Found ${patients.length} patients matching criteria.`
|
|
@@ -5053,8 +5046,8 @@ var getAllPatientsUtil = async (db, options) => {
|
|
|
5053
5046
|
}
|
|
5054
5047
|
const patientsSnapshot = await getDocs8(q);
|
|
5055
5048
|
const patients = [];
|
|
5056
|
-
patientsSnapshot.forEach((
|
|
5057
|
-
patients.push(
|
|
5049
|
+
patientsSnapshot.forEach((doc38) => {
|
|
5050
|
+
patients.push(doc38.data());
|
|
5058
5051
|
});
|
|
5059
5052
|
console.log(`[getAllPatientsUtil] Found ${patients.length} patients`);
|
|
5060
5053
|
return patients;
|
|
@@ -5187,7 +5180,7 @@ var getActiveInviteTokensByClinicUtil = async (db, clinicId) => {
|
|
|
5187
5180
|
if (querySnapshot.empty) {
|
|
5188
5181
|
return [];
|
|
5189
5182
|
}
|
|
5190
|
-
return querySnapshot.docs.map((
|
|
5183
|
+
return querySnapshot.docs.map((doc38) => doc38.data());
|
|
5191
5184
|
};
|
|
5192
5185
|
var getActiveInviteTokensByPatientUtil = async (db, patientId) => {
|
|
5193
5186
|
const tokensRef = collection9(
|
|
@@ -5205,7 +5198,7 @@ var getActiveInviteTokensByPatientUtil = async (db, patientId) => {
|
|
|
5205
5198
|
if (querySnapshot.empty) {
|
|
5206
5199
|
return [];
|
|
5207
5200
|
}
|
|
5208
|
-
return querySnapshot.docs.map((
|
|
5201
|
+
return querySnapshot.docs.map((doc38) => doc38.data());
|
|
5209
5202
|
};
|
|
5210
5203
|
|
|
5211
5204
|
// src/services/patient/patient.service.ts
|
|
@@ -6494,7 +6487,7 @@ var PractitionerService = class extends BaseService {
|
|
|
6494
6487
|
where10("expiresAt", ">", Timestamp14.now())
|
|
6495
6488
|
);
|
|
6496
6489
|
const querySnapshot = await getDocs10(q);
|
|
6497
|
-
return querySnapshot.docs.map((
|
|
6490
|
+
return querySnapshot.docs.map((doc38) => doc38.data());
|
|
6498
6491
|
}
|
|
6499
6492
|
/**
|
|
6500
6493
|
* Gets a token by its string value and validates it
|
|
@@ -6604,7 +6597,7 @@ var PractitionerService = class extends BaseService {
|
|
|
6604
6597
|
where10("status", "==", "active" /* ACTIVE */)
|
|
6605
6598
|
);
|
|
6606
6599
|
const querySnapshot = await getDocs10(q);
|
|
6607
|
-
return querySnapshot.docs.map((
|
|
6600
|
+
return querySnapshot.docs.map((doc38) => doc38.data());
|
|
6608
6601
|
}
|
|
6609
6602
|
/**
|
|
6610
6603
|
* Dohvata sve zdravstvene radnike za određenu kliniku
|
|
@@ -6616,7 +6609,7 @@ var PractitionerService = class extends BaseService {
|
|
|
6616
6609
|
where10("isActive", "==", true)
|
|
6617
6610
|
);
|
|
6618
6611
|
const querySnapshot = await getDocs10(q);
|
|
6619
|
-
return querySnapshot.docs.map((
|
|
6612
|
+
return querySnapshot.docs.map((doc38) => doc38.data());
|
|
6620
6613
|
}
|
|
6621
6614
|
/**
|
|
6622
6615
|
* Dohvata sve draft zdravstvene radnike za određenu kliniku sa statusom DRAFT
|
|
@@ -6628,7 +6621,7 @@ var PractitionerService = class extends BaseService {
|
|
|
6628
6621
|
where10("status", "==", "draft" /* DRAFT */)
|
|
6629
6622
|
);
|
|
6630
6623
|
const querySnapshot = await getDocs10(q);
|
|
6631
|
-
return querySnapshot.docs.map((
|
|
6624
|
+
return querySnapshot.docs.map((doc38) => doc38.data());
|
|
6632
6625
|
}
|
|
6633
6626
|
/**
|
|
6634
6627
|
* Updates a practitioner
|
|
@@ -6646,7 +6639,9 @@ var PractitionerService = class extends BaseService {
|
|
|
6646
6639
|
throw new Error(`Practitioner ${practitionerId} not found`);
|
|
6647
6640
|
}
|
|
6648
6641
|
const currentPractitioner = practitionerDoc.data();
|
|
6649
|
-
let processedData = {
|
|
6642
|
+
let processedData = {
|
|
6643
|
+
...validData
|
|
6644
|
+
};
|
|
6650
6645
|
if (validData.basicInfo) {
|
|
6651
6646
|
processedData.basicInfo = await this.processBasicInfo(
|
|
6652
6647
|
validData.basicInfo,
|
|
@@ -6843,7 +6838,7 @@ var PractitionerService = class extends BaseService {
|
|
|
6843
6838
|
);
|
|
6844
6839
|
const querySnapshot = await getDocs10(q);
|
|
6845
6840
|
const practitioners = querySnapshot.docs.map(
|
|
6846
|
-
(
|
|
6841
|
+
(doc38) => doc38.data()
|
|
6847
6842
|
);
|
|
6848
6843
|
const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
6849
6844
|
return {
|
|
@@ -6880,7 +6875,9 @@ var PractitionerService = class extends BaseService {
|
|
|
6880
6875
|
*/
|
|
6881
6876
|
async getPractitionersByFilters(filters) {
|
|
6882
6877
|
try {
|
|
6883
|
-
console.log(
|
|
6878
|
+
console.log(
|
|
6879
|
+
"[PRACTITIONER_SERVICE] Starting practitioner filtering with fallback strategies"
|
|
6880
|
+
);
|
|
6884
6881
|
if (filters.location && filters.radiusInKm) {
|
|
6885
6882
|
console.log("[PRACTITIONER_SERVICE] Executing geo query:", {
|
|
6886
6883
|
location: filters.location,
|
|
@@ -6888,14 +6885,19 @@ var PractitionerService = class extends BaseService {
|
|
|
6888
6885
|
serviceName: "PractitionerService"
|
|
6889
6886
|
});
|
|
6890
6887
|
if (!filters.location.latitude || !filters.location.longitude) {
|
|
6891
|
-
console.warn(
|
|
6888
|
+
console.warn(
|
|
6889
|
+
"[PRACTITIONER_SERVICE] Invalid location data:",
|
|
6890
|
+
filters.location
|
|
6891
|
+
);
|
|
6892
6892
|
filters.location = void 0;
|
|
6893
6893
|
filters.radiusInKm = void 0;
|
|
6894
6894
|
}
|
|
6895
6895
|
}
|
|
6896
6896
|
if (filters.nameSearch && filters.nameSearch.trim()) {
|
|
6897
6897
|
try {
|
|
6898
|
-
console.log(
|
|
6898
|
+
console.log(
|
|
6899
|
+
"[PRACTITIONER_SERVICE] Strategy 1: Trying fullNameLower search"
|
|
6900
|
+
);
|
|
6899
6901
|
const searchTerm = filters.nameSearch.trim().toLowerCase();
|
|
6900
6902
|
const constraints = [];
|
|
6901
6903
|
if (!filters.includeDraftPractitioners) {
|
|
@@ -6915,11 +6917,18 @@ var PractitionerService = class extends BaseService {
|
|
|
6915
6917
|
}
|
|
6916
6918
|
}
|
|
6917
6919
|
constraints.push(limit7(filters.pagination || 10));
|
|
6918
|
-
const q = query10(
|
|
6920
|
+
const q = query10(
|
|
6921
|
+
collection10(this.db, PRACTITIONERS_COLLECTION),
|
|
6922
|
+
...constraints
|
|
6923
|
+
);
|
|
6919
6924
|
const querySnapshot = await getDocs10(q);
|
|
6920
|
-
const practitioners = querySnapshot.docs.map(
|
|
6925
|
+
const practitioners = querySnapshot.docs.map(
|
|
6926
|
+
(doc38) => ({ ...doc38.data(), id: doc38.id })
|
|
6927
|
+
);
|
|
6921
6928
|
const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
6922
|
-
console.log(
|
|
6929
|
+
console.log(
|
|
6930
|
+
`[PRACTITIONER_SERVICE] Strategy 1 success: ${practitioners.length} practitioners`
|
|
6931
|
+
);
|
|
6923
6932
|
if (practitioners.length < (filters.pagination || 10)) {
|
|
6924
6933
|
return { practitioners, lastDoc: null };
|
|
6925
6934
|
}
|
|
@@ -6929,7 +6938,9 @@ var PractitionerService = class extends BaseService {
|
|
|
6929
6938
|
}
|
|
6930
6939
|
}
|
|
6931
6940
|
try {
|
|
6932
|
-
console.log(
|
|
6941
|
+
console.log(
|
|
6942
|
+
"[PRACTITIONER_SERVICE] Strategy 2: Basic query with createdAt ordering"
|
|
6943
|
+
);
|
|
6933
6944
|
const constraints = [];
|
|
6934
6945
|
if (!filters.includeDraftPractitioners) {
|
|
6935
6946
|
constraints.push(where10("status", "==", "active" /* ACTIVE */));
|
|
@@ -6938,14 +6949,22 @@ var PractitionerService = class extends BaseService {
|
|
|
6938
6949
|
if (filters.certifications && filters.certifications.length > 0) {
|
|
6939
6950
|
const certificationsToMatch = filters.certifications;
|
|
6940
6951
|
constraints.push(
|
|
6941
|
-
where10(
|
|
6952
|
+
where10(
|
|
6953
|
+
"certification.specialties",
|
|
6954
|
+
"array-contains-any",
|
|
6955
|
+
certificationsToMatch
|
|
6956
|
+
)
|
|
6942
6957
|
);
|
|
6943
6958
|
}
|
|
6944
6959
|
if (filters.minRating !== void 0) {
|
|
6945
|
-
constraints.push(
|
|
6960
|
+
constraints.push(
|
|
6961
|
+
where10("reviewInfo.averageRating", ">=", filters.minRating)
|
|
6962
|
+
);
|
|
6946
6963
|
}
|
|
6947
6964
|
if (filters.maxRating !== void 0) {
|
|
6948
|
-
constraints.push(
|
|
6965
|
+
constraints.push(
|
|
6966
|
+
where10("reviewInfo.averageRating", "<=", filters.maxRating)
|
|
6967
|
+
);
|
|
6949
6968
|
}
|
|
6950
6969
|
constraints.push(orderBy4("createdAt", "desc"));
|
|
6951
6970
|
if (filters.location && filters.radiusInKm) {
|
|
@@ -6962,9 +6981,14 @@ var PractitionerService = class extends BaseService {
|
|
|
6962
6981
|
}
|
|
6963
6982
|
constraints.push(limit7(filters.pagination || 10));
|
|
6964
6983
|
}
|
|
6965
|
-
const q = query10(
|
|
6984
|
+
const q = query10(
|
|
6985
|
+
collection10(this.db, PRACTITIONERS_COLLECTION),
|
|
6986
|
+
...constraints
|
|
6987
|
+
);
|
|
6966
6988
|
const querySnapshot = await getDocs10(q);
|
|
6967
|
-
let practitioners = querySnapshot.docs.map(
|
|
6989
|
+
let practitioners = querySnapshot.docs.map(
|
|
6990
|
+
(doc38) => ({ ...doc38.data(), id: doc38.id })
|
|
6991
|
+
);
|
|
6968
6992
|
if (filters.location && filters.radiusInKm && filters.radiusInKm > 0) {
|
|
6969
6993
|
const location = filters.location;
|
|
6970
6994
|
const radiusInKm = filters.radiusInKm;
|
|
@@ -6983,7 +7007,9 @@ var PractitionerService = class extends BaseService {
|
|
|
6983
7007
|
}
|
|
6984
7008
|
practitioners = this.applyInMemoryFilters(practitioners, filters);
|
|
6985
7009
|
const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
6986
|
-
console.log(
|
|
7010
|
+
console.log(
|
|
7011
|
+
`[PRACTITIONER_SERVICE] Strategy 2 success: ${practitioners.length} practitioners`
|
|
7012
|
+
);
|
|
6987
7013
|
if (practitioners.length < (filters.pagination || 10)) {
|
|
6988
7014
|
return { practitioners, lastDoc: null };
|
|
6989
7015
|
}
|
|
@@ -6992,18 +7018,27 @@ var PractitionerService = class extends BaseService {
|
|
|
6992
7018
|
console.log("[PRACTITIONER_SERVICE] Strategy 2 failed:", error);
|
|
6993
7019
|
}
|
|
6994
7020
|
try {
|
|
6995
|
-
console.log(
|
|
7021
|
+
console.log(
|
|
7022
|
+
"[PRACTITIONER_SERVICE] Strategy 3: Minimal query fallback"
|
|
7023
|
+
);
|
|
6996
7024
|
const constraints = [
|
|
6997
7025
|
where10("isActive", "==", true),
|
|
6998
7026
|
orderBy4("createdAt", "desc"),
|
|
6999
7027
|
limit7(filters.pagination || 10)
|
|
7000
7028
|
];
|
|
7001
|
-
const q = query10(
|
|
7029
|
+
const q = query10(
|
|
7030
|
+
collection10(this.db, PRACTITIONERS_COLLECTION),
|
|
7031
|
+
...constraints
|
|
7032
|
+
);
|
|
7002
7033
|
const querySnapshot = await getDocs10(q);
|
|
7003
|
-
let practitioners = querySnapshot.docs.map(
|
|
7034
|
+
let practitioners = querySnapshot.docs.map(
|
|
7035
|
+
(doc38) => ({ ...doc38.data(), id: doc38.id })
|
|
7036
|
+
);
|
|
7004
7037
|
practitioners = this.applyInMemoryFilters(practitioners, filters);
|
|
7005
7038
|
const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
7006
|
-
console.log(
|
|
7039
|
+
console.log(
|
|
7040
|
+
`[PRACTITIONER_SERVICE] Strategy 3 success: ${practitioners.length} practitioners`
|
|
7041
|
+
);
|
|
7007
7042
|
if (practitioners.length < (filters.pagination || 10)) {
|
|
7008
7043
|
return { practitioners, lastDoc: null };
|
|
7009
7044
|
}
|
|
@@ -7012,19 +7047,28 @@ var PractitionerService = class extends BaseService {
|
|
|
7012
7047
|
console.log("[PRACTITIONER_SERVICE] Strategy 3 failed:", error);
|
|
7013
7048
|
}
|
|
7014
7049
|
try {
|
|
7015
|
-
console.log(
|
|
7050
|
+
console.log(
|
|
7051
|
+
"[PRACTITIONER_SERVICE] Strategy 4: Client-side filtering fallback"
|
|
7052
|
+
);
|
|
7016
7053
|
const constraints = [
|
|
7017
7054
|
where10("isActive", "==", true),
|
|
7018
7055
|
where10("status", "==", "active" /* ACTIVE */),
|
|
7019
7056
|
orderBy4("createdAt", "desc"),
|
|
7020
7057
|
limit7(filters.pagination || 10)
|
|
7021
7058
|
];
|
|
7022
|
-
const q = query10(
|
|
7059
|
+
const q = query10(
|
|
7060
|
+
collection10(this.db, PRACTITIONERS_COLLECTION),
|
|
7061
|
+
...constraints
|
|
7062
|
+
);
|
|
7023
7063
|
const querySnapshot = await getDocs10(q);
|
|
7024
|
-
let practitioners = querySnapshot.docs.map(
|
|
7064
|
+
let practitioners = querySnapshot.docs.map(
|
|
7065
|
+
(doc38) => ({ ...doc38.data(), id: doc38.id })
|
|
7066
|
+
);
|
|
7025
7067
|
practitioners = this.applyInMemoryFilters(practitioners, filters);
|
|
7026
7068
|
const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
7027
|
-
console.log(
|
|
7069
|
+
console.log(
|
|
7070
|
+
`[PRACTITIONER_SERVICE] Strategy 4 success: ${practitioners.length} practitioners`
|
|
7071
|
+
);
|
|
7028
7072
|
if (practitioners.length < (filters.pagination || 10)) {
|
|
7029
7073
|
return { practitioners, lastDoc: null };
|
|
7030
7074
|
}
|
|
@@ -7032,10 +7076,15 @@ var PractitionerService = class extends BaseService {
|
|
|
7032
7076
|
} catch (error) {
|
|
7033
7077
|
console.log("[PRACTITIONER_SERVICE] Strategy 4 failed:", error);
|
|
7034
7078
|
}
|
|
7035
|
-
console.log(
|
|
7079
|
+
console.log(
|
|
7080
|
+
"[PRACTITIONER_SERVICE] All strategies failed, returning empty result"
|
|
7081
|
+
);
|
|
7036
7082
|
return { practitioners: [], lastDoc: null };
|
|
7037
7083
|
} catch (error) {
|
|
7038
|
-
console.error(
|
|
7084
|
+
console.error(
|
|
7085
|
+
"[PRACTITIONER_SERVICE] Error filtering practitioners:",
|
|
7086
|
+
error
|
|
7087
|
+
);
|
|
7039
7088
|
return { practitioners: [], lastDoc: null };
|
|
7040
7089
|
}
|
|
7041
7090
|
}
|
|
@@ -7055,63 +7104,93 @@ var PractitionerService = class extends BaseService {
|
|
|
7055
7104
|
const fullNameLower = practitioner.fullNameLower || "";
|
|
7056
7105
|
return firstName.includes(searchTerm) || lastName.includes(searchTerm) || fullName.includes(searchTerm) || fullNameLower.includes(searchTerm);
|
|
7057
7106
|
});
|
|
7058
|
-
console.log(
|
|
7107
|
+
console.log(
|
|
7108
|
+
`[PRACTITIONER_SERVICE] Applied name filter, results: ${filteredPractitioners.length}`
|
|
7109
|
+
);
|
|
7059
7110
|
}
|
|
7060
7111
|
if (filters.certifications && filters.certifications.length > 0) {
|
|
7061
7112
|
const certificationsToMatch = filters.certifications;
|
|
7062
7113
|
filteredPractitioners = filteredPractitioners.filter((practitioner) => {
|
|
7063
7114
|
var _a;
|
|
7064
7115
|
const practitionerCerts = ((_a = practitioner.certification) == null ? void 0 : _a.specialties) || [];
|
|
7065
|
-
return certificationsToMatch.some(
|
|
7116
|
+
return certificationsToMatch.some(
|
|
7117
|
+
(cert) => practitionerCerts.includes(cert)
|
|
7118
|
+
);
|
|
7066
7119
|
});
|
|
7067
|
-
console.log(
|
|
7120
|
+
console.log(
|
|
7121
|
+
`[PRACTITIONER_SERVICE] Applied certifications filter, results: ${filteredPractitioners.length}`
|
|
7122
|
+
);
|
|
7068
7123
|
}
|
|
7069
7124
|
if (filters.specialties && filters.specialties.length > 0) {
|
|
7070
7125
|
const specialtiesToMatch = filters.specialties;
|
|
7071
7126
|
filteredPractitioners = filteredPractitioners.filter((practitioner) => {
|
|
7072
7127
|
var _a;
|
|
7073
7128
|
const practitionerSpecs = ((_a = practitioner.certification) == null ? void 0 : _a.specialties) || [];
|
|
7074
|
-
return specialtiesToMatch.some(
|
|
7129
|
+
return specialtiesToMatch.some(
|
|
7130
|
+
(spec) => practitionerSpecs.includes(spec)
|
|
7131
|
+
);
|
|
7075
7132
|
});
|
|
7076
|
-
console.log(
|
|
7133
|
+
console.log(
|
|
7134
|
+
`[PRACTITIONER_SERVICE] Applied specialties filter, results: ${filteredPractitioners.length}`
|
|
7135
|
+
);
|
|
7077
7136
|
}
|
|
7078
7137
|
if (filters.minRating !== void 0 || filters.maxRating !== void 0) {
|
|
7079
7138
|
filteredPractitioners = filteredPractitioners.filter((practitioner) => {
|
|
7080
7139
|
var _a;
|
|
7081
7140
|
const rating = ((_a = practitioner.reviewInfo) == null ? void 0 : _a.averageRating) || 0;
|
|
7082
|
-
if (filters.minRating !== void 0 && rating < filters.minRating)
|
|
7083
|
-
|
|
7141
|
+
if (filters.minRating !== void 0 && rating < filters.minRating)
|
|
7142
|
+
return false;
|
|
7143
|
+
if (filters.maxRating !== void 0 && rating > filters.maxRating)
|
|
7144
|
+
return false;
|
|
7084
7145
|
return true;
|
|
7085
7146
|
});
|
|
7086
|
-
console.log(
|
|
7147
|
+
console.log(
|
|
7148
|
+
`[PRACTITIONER_SERVICE] Applied rating filter, results: ${filteredPractitioners.length}`
|
|
7149
|
+
);
|
|
7087
7150
|
}
|
|
7088
7151
|
if (filters.procedureFamily) {
|
|
7089
7152
|
filteredPractitioners = filteredPractitioners.filter((practitioner) => {
|
|
7090
7153
|
const proceduresInfo = practitioner.proceduresInfo || [];
|
|
7091
|
-
return proceduresInfo.some(
|
|
7154
|
+
return proceduresInfo.some(
|
|
7155
|
+
(proc) => proc.family === filters.procedureFamily
|
|
7156
|
+
);
|
|
7092
7157
|
});
|
|
7093
|
-
console.log(
|
|
7158
|
+
console.log(
|
|
7159
|
+
`[PRACTITIONER_SERVICE] Applied procedure family filter, results: ${filteredPractitioners.length}`
|
|
7160
|
+
);
|
|
7094
7161
|
}
|
|
7095
7162
|
if (filters.procedureCategory) {
|
|
7096
7163
|
filteredPractitioners = filteredPractitioners.filter((practitioner) => {
|
|
7097
7164
|
const proceduresInfo = practitioner.proceduresInfo || [];
|
|
7098
|
-
return proceduresInfo.some(
|
|
7165
|
+
return proceduresInfo.some(
|
|
7166
|
+
(proc) => proc.categoryName === filters.procedureCategory
|
|
7167
|
+
);
|
|
7099
7168
|
});
|
|
7100
|
-
console.log(
|
|
7169
|
+
console.log(
|
|
7170
|
+
`[PRACTITIONER_SERVICE] Applied procedure category filter, results: ${filteredPractitioners.length}`
|
|
7171
|
+
);
|
|
7101
7172
|
}
|
|
7102
7173
|
if (filters.procedureSubcategory) {
|
|
7103
7174
|
filteredPractitioners = filteredPractitioners.filter((practitioner) => {
|
|
7104
7175
|
const proceduresInfo = practitioner.proceduresInfo || [];
|
|
7105
|
-
return proceduresInfo.some(
|
|
7176
|
+
return proceduresInfo.some(
|
|
7177
|
+
(proc) => proc.subcategoryName === filters.procedureSubcategory
|
|
7178
|
+
);
|
|
7106
7179
|
});
|
|
7107
|
-
console.log(
|
|
7180
|
+
console.log(
|
|
7181
|
+
`[PRACTITIONER_SERVICE] Applied procedure subcategory filter, results: ${filteredPractitioners.length}`
|
|
7182
|
+
);
|
|
7108
7183
|
}
|
|
7109
7184
|
if (filters.procedureTechnology) {
|
|
7110
7185
|
filteredPractitioners = filteredPractitioners.filter((practitioner) => {
|
|
7111
7186
|
const proceduresInfo = practitioner.proceduresInfo || [];
|
|
7112
|
-
return proceduresInfo.some(
|
|
7187
|
+
return proceduresInfo.some(
|
|
7188
|
+
(proc) => proc.technologyName === filters.procedureTechnology
|
|
7189
|
+
);
|
|
7113
7190
|
});
|
|
7114
|
-
console.log(
|
|
7191
|
+
console.log(
|
|
7192
|
+
`[PRACTITIONER_SERVICE] Applied procedure technology filter, results: ${filteredPractitioners.length}`
|
|
7193
|
+
);
|
|
7115
7194
|
}
|
|
7116
7195
|
if (filters.location && filters.radiusInKm && filters.radiusInKm > 0) {
|
|
7117
7196
|
const location = filters.location;
|
|
@@ -7127,7 +7206,9 @@ var PractitionerService = class extends BaseService {
|
|
|
7127
7206
|
return distanceInKm <= radiusInKm;
|
|
7128
7207
|
});
|
|
7129
7208
|
});
|
|
7130
|
-
console.log(
|
|
7209
|
+
console.log(
|
|
7210
|
+
`[PRACTITIONER_SERVICE] Applied geo filter, results: ${filteredPractitioners.length}`
|
|
7211
|
+
);
|
|
7131
7212
|
}
|
|
7132
7213
|
return filteredPractitioners;
|
|
7133
7214
|
}
|
|
@@ -7192,6 +7273,15 @@ var PractitionerService = class extends BaseService {
|
|
|
7192
7273
|
price: 0,
|
|
7193
7274
|
currency: "EUR" /* EUR */,
|
|
7194
7275
|
pricingMeasure: "per_session" /* PER_SESSION */,
|
|
7276
|
+
productsMetadata: [
|
|
7277
|
+
{
|
|
7278
|
+
productId: "free-consultation-product",
|
|
7279
|
+
price: 0,
|
|
7280
|
+
currency: "EUR" /* EUR */,
|
|
7281
|
+
pricingMeasure: "per_session" /* PER_SESSION */,
|
|
7282
|
+
isDefault: true
|
|
7283
|
+
}
|
|
7284
|
+
],
|
|
7195
7285
|
duration: 30,
|
|
7196
7286
|
// 30 minutes consultation
|
|
7197
7287
|
practitionerId,
|
|
@@ -7536,7 +7626,7 @@ var UserService = class extends BaseService {
|
|
|
7536
7626
|
];
|
|
7537
7627
|
const q = query11(collection11(this.db, USERS_COLLECTION), ...constraints);
|
|
7538
7628
|
const querySnapshot = await getDocs11(q);
|
|
7539
|
-
const users = querySnapshot.docs.map((
|
|
7629
|
+
const users = querySnapshot.docs.map((doc38) => doc38.data());
|
|
7540
7630
|
return users.map((userData) => userSchema.parse(userData));
|
|
7541
7631
|
}
|
|
7542
7632
|
/**
|
|
@@ -7916,7 +8006,7 @@ async function getAllActiveGroups(db) {
|
|
|
7916
8006
|
where12("isActive", "==", true)
|
|
7917
8007
|
);
|
|
7918
8008
|
const querySnapshot = await getDocs12(q);
|
|
7919
|
-
return querySnapshot.docs.map((
|
|
8009
|
+
return querySnapshot.docs.map((doc38) => doc38.data());
|
|
7920
8010
|
}
|
|
7921
8011
|
async function updateClinicGroup(db, groupId, data, app) {
|
|
7922
8012
|
console.log("[CLINIC_GROUP] Updating clinic group", { groupId });
|
|
@@ -8384,7 +8474,7 @@ async function getClinicsByGroup(db, groupId) {
|
|
|
8384
8474
|
where13("isActive", "==", true)
|
|
8385
8475
|
);
|
|
8386
8476
|
const querySnapshot = await getDocs13(q);
|
|
8387
|
-
return querySnapshot.docs.map((
|
|
8477
|
+
return querySnapshot.docs.map((doc38) => doc38.data());
|
|
8388
8478
|
}
|
|
8389
8479
|
async function updateClinic(db, clinicId, data, adminId, clinicAdminService, app) {
|
|
8390
8480
|
console.log("[CLINIC] Starting clinic update", { clinicId, adminId });
|
|
@@ -8578,7 +8668,7 @@ async function getClinicsByAdmin(db, adminId, options = {}, clinicAdminService,
|
|
|
8578
8668
|
}
|
|
8579
8669
|
const q = query13(collection13(db, CLINICS_COLLECTION), ...constraints);
|
|
8580
8670
|
const querySnapshot = await getDocs13(q);
|
|
8581
|
-
return querySnapshot.docs.map((
|
|
8671
|
+
return querySnapshot.docs.map((doc38) => doc38.data());
|
|
8582
8672
|
}
|
|
8583
8673
|
async function getActiveClinicsByAdmin(db, adminId, clinicAdminService, clinicGroupService) {
|
|
8584
8674
|
return getClinicsByAdmin(
|
|
@@ -8623,11 +8713,11 @@ async function getAllClinics(db, pagination, lastDoc) {
|
|
|
8623
8713
|
}
|
|
8624
8714
|
const clinicsSnapshot = await getDocs13(clinicsQuery);
|
|
8625
8715
|
const lastVisible = clinicsSnapshot.docs[clinicsSnapshot.docs.length - 1];
|
|
8626
|
-
const clinics = clinicsSnapshot.docs.map((
|
|
8627
|
-
const data =
|
|
8716
|
+
const clinics = clinicsSnapshot.docs.map((doc38) => {
|
|
8717
|
+
const data = doc38.data();
|
|
8628
8718
|
return {
|
|
8629
8719
|
...data,
|
|
8630
|
-
id:
|
|
8720
|
+
id: doc38.id
|
|
8631
8721
|
};
|
|
8632
8722
|
});
|
|
8633
8723
|
return {
|
|
@@ -8654,8 +8744,8 @@ async function getAllClinicsInRange(db, center, rangeInKm, pagination, lastDoc)
|
|
|
8654
8744
|
];
|
|
8655
8745
|
const q = query13(collection13(db, CLINICS_COLLECTION), ...constraints);
|
|
8656
8746
|
const querySnapshot = await getDocs13(q);
|
|
8657
|
-
for (const
|
|
8658
|
-
const clinic =
|
|
8747
|
+
for (const doc38 of querySnapshot.docs) {
|
|
8748
|
+
const clinic = doc38.data();
|
|
8659
8749
|
const distance = distanceBetween2(
|
|
8660
8750
|
[center.latitude, center.longitude],
|
|
8661
8751
|
[clinic.location.latitude, clinic.location.longitude]
|
|
@@ -8777,8 +8867,8 @@ async function findClinicsInRadius(db, center, radiusInKm, filters) {
|
|
|
8777
8867
|
}
|
|
8778
8868
|
const q = query14(collection14(db, CLINICS_COLLECTION), ...constraints);
|
|
8779
8869
|
const querySnapshot = await getDocs14(q);
|
|
8780
|
-
for (const
|
|
8781
|
-
const clinic =
|
|
8870
|
+
for (const doc38 of querySnapshot.docs) {
|
|
8871
|
+
const clinic = doc38.data();
|
|
8782
8872
|
const distance = distanceBetween3(
|
|
8783
8873
|
[center.latitude, center.longitude],
|
|
8784
8874
|
[clinic.location.latitude, clinic.location.longitude]
|
|
@@ -8907,7 +8997,7 @@ async function getClinicsByFilters(db, filters) {
|
|
|
8907
8997
|
constraints.push(limit9(filters.pagination || 5));
|
|
8908
8998
|
const q = query15(collection15(db, CLINICS_COLLECTION), ...constraints);
|
|
8909
8999
|
const querySnapshot = await getDocs15(q);
|
|
8910
|
-
let clinics = querySnapshot.docs.map((
|
|
9000
|
+
let clinics = querySnapshot.docs.map((doc38) => ({ ...doc38.data(), id: doc38.id }));
|
|
8911
9001
|
clinics = applyInMemoryFilters(clinics, filters);
|
|
8912
9002
|
const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
8913
9003
|
console.log(`[CLINIC_SERVICE] Strategy 1 success: ${clinics.length} clinics`);
|
|
@@ -8939,7 +9029,7 @@ async function getClinicsByFilters(db, filters) {
|
|
|
8939
9029
|
constraints.push(limit9(filters.pagination || 5));
|
|
8940
9030
|
const q = query15(collection15(db, CLINICS_COLLECTION), ...constraints);
|
|
8941
9031
|
const querySnapshot = await getDocs15(q);
|
|
8942
|
-
let clinics = querySnapshot.docs.map((
|
|
9032
|
+
let clinics = querySnapshot.docs.map((doc38) => ({ ...doc38.data(), id: doc38.id }));
|
|
8943
9033
|
clinics = applyInMemoryFilters(clinics, filters);
|
|
8944
9034
|
const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
8945
9035
|
console.log(`[CLINIC_SERVICE] Strategy 2 success: ${clinics.length} clinics`);
|
|
@@ -8969,7 +9059,7 @@ async function getClinicsByFilters(db, filters) {
|
|
|
8969
9059
|
constraints.push(limit9(filters.pagination || 5));
|
|
8970
9060
|
const q = query15(collection15(db, CLINICS_COLLECTION), ...constraints);
|
|
8971
9061
|
const querySnapshot = await getDocs15(q);
|
|
8972
|
-
let clinics = querySnapshot.docs.map((
|
|
9062
|
+
let clinics = querySnapshot.docs.map((doc38) => ({ ...doc38.data(), id: doc38.id }));
|
|
8973
9063
|
clinics = applyInMemoryFilters(clinics, filters);
|
|
8974
9064
|
const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
8975
9065
|
console.log(`[CLINIC_SERVICE] Strategy 3 success: ${clinics.length} clinics`);
|
|
@@ -8989,7 +9079,7 @@ async function getClinicsByFilters(db, filters) {
|
|
|
8989
9079
|
];
|
|
8990
9080
|
const q = query15(collection15(db, CLINICS_COLLECTION), ...constraints);
|
|
8991
9081
|
const querySnapshot = await getDocs15(q);
|
|
8992
|
-
let clinics = querySnapshot.docs.map((
|
|
9082
|
+
let clinics = querySnapshot.docs.map((doc38) => ({ ...doc38.data(), id: doc38.id }));
|
|
8993
9083
|
clinics = applyInMemoryFilters(clinics, filters);
|
|
8994
9084
|
const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
8995
9085
|
console.log(`[CLINIC_SERVICE] Strategy 4 success: ${clinics.length} clinics`);
|
|
@@ -9565,11 +9655,11 @@ var ClinicService = class extends BaseService {
|
|
|
9565
9655
|
async getClinicsForMap() {
|
|
9566
9656
|
const clinicsRef = collection16(this.db, CLINICS_COLLECTION);
|
|
9567
9657
|
const snapshot = await getDocs16(clinicsRef);
|
|
9568
|
-
const clinicsForMap = snapshot.docs.map((
|
|
9658
|
+
const clinicsForMap = snapshot.docs.map((doc38) => {
|
|
9569
9659
|
var _a, _b, _c;
|
|
9570
|
-
const data =
|
|
9660
|
+
const data = doc38.data();
|
|
9571
9661
|
return {
|
|
9572
|
-
id:
|
|
9662
|
+
id: doc38.id,
|
|
9573
9663
|
name: data.name,
|
|
9574
9664
|
address: ((_a = data.location) == null ? void 0 : _a.address) || "",
|
|
9575
9665
|
latitude: (_b = data.location) == null ? void 0 : _b.latitude,
|
|
@@ -10776,7 +10866,7 @@ async function updatePractitionerCalendarEventUtil(db, practitionerId, eventId,
|
|
|
10776
10866
|
}
|
|
10777
10867
|
|
|
10778
10868
|
// src/services/calendar/utils/appointment.utils.ts
|
|
10779
|
-
async function
|
|
10869
|
+
async function createAppointmentUtil(db, clinicId, practitionerId, patientId, eventData, generateId2) {
|
|
10780
10870
|
const eventId = generateId2();
|
|
10781
10871
|
const autoConfirm = await checkAutoConfirmAppointmentsUtil(db, clinicId);
|
|
10782
10872
|
const initialStatus = autoConfirm ? "confirmed" /* CONFIRMED */ : "pending" /* PENDING */;
|
|
@@ -10923,7 +11013,7 @@ async function searchCalendarEventsUtil(db, params) {
|
|
|
10923
11013
|
const finalQuery = query21(collectionRef, ...constraints);
|
|
10924
11014
|
const querySnapshot = await getDocs21(finalQuery);
|
|
10925
11015
|
const events = querySnapshot.docs.map(
|
|
10926
|
-
(
|
|
11016
|
+
(doc38) => ({ id: doc38.id, ...doc38.data() })
|
|
10927
11017
|
);
|
|
10928
11018
|
return events;
|
|
10929
11019
|
} catch (error) {
|
|
@@ -11016,7 +11106,7 @@ async function getPractitionerSyncedCalendarsUtil(db, practitionerId) {
|
|
|
11016
11106
|
);
|
|
11017
11107
|
const q = query22(calendarsRef, orderBy11("createdAt", "desc"));
|
|
11018
11108
|
const querySnapshot = await getDocs22(q);
|
|
11019
|
-
return querySnapshot.docs.map((
|
|
11109
|
+
return querySnapshot.docs.map((doc38) => doc38.data());
|
|
11020
11110
|
}
|
|
11021
11111
|
async function getPatientSyncedCalendarUtil(db, patientId, calendarId) {
|
|
11022
11112
|
const calendarRef = getPatientSyncedCalendarDocRef(db, patientId, calendarId);
|
|
@@ -11033,7 +11123,7 @@ async function getPatientSyncedCalendarsUtil(db, patientId) {
|
|
|
11033
11123
|
);
|
|
11034
11124
|
const q = query22(calendarsRef, orderBy11("createdAt", "desc"));
|
|
11035
11125
|
const querySnapshot = await getDocs22(q);
|
|
11036
|
-
return querySnapshot.docs.map((
|
|
11126
|
+
return querySnapshot.docs.map((doc38) => doc38.data());
|
|
11037
11127
|
}
|
|
11038
11128
|
async function getClinicSyncedCalendarUtil(db, clinicId, calendarId) {
|
|
11039
11129
|
const calendarRef = getClinicSyncedCalendarDocRef(db, clinicId, calendarId);
|
|
@@ -11050,7 +11140,7 @@ async function getClinicSyncedCalendarsUtil(db, clinicId) {
|
|
|
11050
11140
|
);
|
|
11051
11141
|
const q = query22(calendarsRef, orderBy11("createdAt", "desc"));
|
|
11052
11142
|
const querySnapshot = await getDocs22(q);
|
|
11053
|
-
return querySnapshot.docs.map((
|
|
11143
|
+
return querySnapshot.docs.map((doc38) => doc38.data());
|
|
11054
11144
|
}
|
|
11055
11145
|
async function updatePractitionerSyncedCalendarUtil(db, practitionerId, calendarId, updateData) {
|
|
11056
11146
|
const calendarRef = getPractitionerSyncedCalendarDocRef(
|
|
@@ -12118,7 +12208,7 @@ var CalendarServiceV2 = class extends BaseService {
|
|
|
12118
12208
|
syncStatus: "internal" /* INTERNAL */,
|
|
12119
12209
|
eventType: "appointment" /* APPOINTMENT */
|
|
12120
12210
|
};
|
|
12121
|
-
const appointment = await
|
|
12211
|
+
const appointment = await createAppointmentUtil(
|
|
12122
12212
|
this.db,
|
|
12123
12213
|
params.clinicId,
|
|
12124
12214
|
params.doctorId,
|
|
@@ -12405,9 +12495,9 @@ var CalendarServiceV2 = class extends BaseService {
|
|
|
12405
12495
|
where23("eventTime.start", "<=", Timestamp26.fromDate(endDate))
|
|
12406
12496
|
);
|
|
12407
12497
|
const eventsSnapshot = await getDocs23(q);
|
|
12408
|
-
const events = eventsSnapshot.docs.map((
|
|
12409
|
-
id:
|
|
12410
|
-
...
|
|
12498
|
+
const events = eventsSnapshot.docs.map((doc38) => ({
|
|
12499
|
+
id: doc38.id,
|
|
12500
|
+
...doc38.data()
|
|
12411
12501
|
}));
|
|
12412
12502
|
const calendars = await this.syncedCalendarsService.getPractitionerSyncedCalendars(
|
|
12413
12503
|
doctorId
|
|
@@ -13041,7 +13131,7 @@ var CalendarServiceV2 = class extends BaseService {
|
|
|
13041
13131
|
])
|
|
13042
13132
|
);
|
|
13043
13133
|
const querySnapshot = await getDocs23(q);
|
|
13044
|
-
return querySnapshot.docs.map((
|
|
13134
|
+
return querySnapshot.docs.map((doc38) => doc38.data());
|
|
13045
13135
|
}
|
|
13046
13136
|
/**
|
|
13047
13137
|
* Calculates available time slots based on working hours, schedule and existing appointments
|
|
@@ -13586,7 +13676,7 @@ var PractitionerInviteService = class extends BaseService {
|
|
|
13586
13676
|
...constraints
|
|
13587
13677
|
);
|
|
13588
13678
|
const querySnapshot = await getDocs24(q);
|
|
13589
|
-
return querySnapshot.docs.map((
|
|
13679
|
+
return querySnapshot.docs.map((doc38) => doc38.data());
|
|
13590
13680
|
} catch (error) {
|
|
13591
13681
|
console.error(
|
|
13592
13682
|
"[PractitionerInviteService] Error getting doctor invites:",
|
|
@@ -13615,7 +13705,7 @@ var PractitionerInviteService = class extends BaseService {
|
|
|
13615
13705
|
...constraints
|
|
13616
13706
|
);
|
|
13617
13707
|
const querySnapshot = await getDocs24(q);
|
|
13618
|
-
return querySnapshot.docs.map((
|
|
13708
|
+
return querySnapshot.docs.map((doc38) => doc38.data());
|
|
13619
13709
|
} catch (error) {
|
|
13620
13710
|
console.error(
|
|
13621
13711
|
"[PractitionerInviteService] Error getting clinic invites:",
|
|
@@ -13771,7 +13861,7 @@ var PractitionerInviteService = class extends BaseService {
|
|
|
13771
13861
|
);
|
|
13772
13862
|
const querySnapshot = await getDocs24(q);
|
|
13773
13863
|
let invites = querySnapshot.docs.map(
|
|
13774
|
-
(
|
|
13864
|
+
(doc38) => doc38.data()
|
|
13775
13865
|
);
|
|
13776
13866
|
if (filters.fromDate) {
|
|
13777
13867
|
invites = invites.filter(
|
|
@@ -13887,9 +13977,10 @@ import {
|
|
|
13887
13977
|
limit as limit12,
|
|
13888
13978
|
startAfter as startAfter10
|
|
13889
13979
|
} from "firebase/firestore";
|
|
13980
|
+
import { getCountFromServer } from "firebase/firestore";
|
|
13890
13981
|
var DocumentationTemplateService = class extends BaseService {
|
|
13891
|
-
constructor() {
|
|
13892
|
-
super(...
|
|
13982
|
+
constructor(...args) {
|
|
13983
|
+
super(...args);
|
|
13893
13984
|
this.collectionRef = collection25(
|
|
13894
13985
|
this.db,
|
|
13895
13986
|
DOCUMENTATION_TEMPLATES_COLLECTION
|
|
@@ -14041,8 +14132,8 @@ var DocumentationTemplateService = class extends BaseService {
|
|
|
14041
14132
|
const q = query25(versionsCollectionRef, orderBy13("version", "desc"));
|
|
14042
14133
|
const querySnapshot = await getDocs25(q);
|
|
14043
14134
|
const versions = [];
|
|
14044
|
-
querySnapshot.forEach((
|
|
14045
|
-
versions.push(
|
|
14135
|
+
querySnapshot.forEach((doc38) => {
|
|
14136
|
+
versions.push(doc38.data());
|
|
14046
14137
|
});
|
|
14047
14138
|
return versions;
|
|
14048
14139
|
}
|
|
@@ -14073,15 +14164,97 @@ var DocumentationTemplateService = class extends BaseService {
|
|
|
14073
14164
|
const querySnapshot = await getDocs25(q);
|
|
14074
14165
|
const templates = [];
|
|
14075
14166
|
let lastVisible = null;
|
|
14076
|
-
querySnapshot.forEach((
|
|
14077
|
-
templates.push(
|
|
14078
|
-
lastVisible =
|
|
14167
|
+
querySnapshot.forEach((doc38) => {
|
|
14168
|
+
templates.push(doc38.data());
|
|
14169
|
+
lastVisible = doc38;
|
|
14170
|
+
});
|
|
14171
|
+
return {
|
|
14172
|
+
templates,
|
|
14173
|
+
lastDoc: lastVisible
|
|
14174
|
+
};
|
|
14175
|
+
}
|
|
14176
|
+
/**
|
|
14177
|
+
* Get all active templates with optional filters and pagination.
|
|
14178
|
+
* @param options - Options for filtering and pagination.
|
|
14179
|
+
* @returns A promise that resolves to the templates and the last visible document.
|
|
14180
|
+
*/
|
|
14181
|
+
async getTemplates(options) {
|
|
14182
|
+
const {
|
|
14183
|
+
pageSize = 20,
|
|
14184
|
+
lastDoc,
|
|
14185
|
+
isUserForm,
|
|
14186
|
+
isRequired,
|
|
14187
|
+
sortingOrder
|
|
14188
|
+
} = options;
|
|
14189
|
+
const constraints = [
|
|
14190
|
+
where25("isActive", "==", true),
|
|
14191
|
+
orderBy13("sortingOrder", "asc"),
|
|
14192
|
+
orderBy13("title", "asc"),
|
|
14193
|
+
limit12(pageSize)
|
|
14194
|
+
];
|
|
14195
|
+
if (isUserForm !== void 0) {
|
|
14196
|
+
constraints.push(where25("isUserForm", "==", isUserForm));
|
|
14197
|
+
}
|
|
14198
|
+
if (isRequired !== void 0) {
|
|
14199
|
+
constraints.push(where25("isRequired", "==", isRequired));
|
|
14200
|
+
}
|
|
14201
|
+
if (sortingOrder !== void 0) {
|
|
14202
|
+
constraints.push(where25("sortingOrder", "==", sortingOrder));
|
|
14203
|
+
}
|
|
14204
|
+
if (lastDoc) {
|
|
14205
|
+
constraints.push(startAfter10(lastDoc));
|
|
14206
|
+
}
|
|
14207
|
+
const q = query25(this.collectionRef, ...constraints.filter((c) => c));
|
|
14208
|
+
const querySnapshot = await getDocs25(q);
|
|
14209
|
+
const templates = [];
|
|
14210
|
+
let lastVisible = null;
|
|
14211
|
+
querySnapshot.forEach((doc38) => {
|
|
14212
|
+
templates.push(doc38.data());
|
|
14213
|
+
lastVisible = doc38;
|
|
14079
14214
|
});
|
|
14080
14215
|
return {
|
|
14081
14216
|
templates,
|
|
14082
14217
|
lastDoc: lastVisible
|
|
14083
14218
|
};
|
|
14084
14219
|
}
|
|
14220
|
+
/**
|
|
14221
|
+
* Get the total count of active templates with optional filters.
|
|
14222
|
+
* @param options - Options for filtering.
|
|
14223
|
+
* @returns A promise that resolves to the total count of templates.
|
|
14224
|
+
*/
|
|
14225
|
+
async getTemplatesCount(options) {
|
|
14226
|
+
const { isUserForm, isRequired, sortingOrder } = options;
|
|
14227
|
+
const constraints = [where25("isActive", "==", true)];
|
|
14228
|
+
if (isUserForm !== void 0) {
|
|
14229
|
+
constraints.push(where25("isUserForm", "==", isUserForm));
|
|
14230
|
+
}
|
|
14231
|
+
if (isRequired !== void 0) {
|
|
14232
|
+
constraints.push(where25("isRequired", "==", isRequired));
|
|
14233
|
+
}
|
|
14234
|
+
if (sortingOrder !== void 0) {
|
|
14235
|
+
constraints.push(where25("sortingOrder", "==", sortingOrder));
|
|
14236
|
+
}
|
|
14237
|
+
const q = query25(this.collectionRef, ...constraints.filter((c) => c));
|
|
14238
|
+
const snapshot = await getCountFromServer(q);
|
|
14239
|
+
return snapshot.data().count;
|
|
14240
|
+
}
|
|
14241
|
+
/**
|
|
14242
|
+
* Get all active templates without pagination for filtering purposes.
|
|
14243
|
+
* @returns A promise that resolves to an array of all active templates.
|
|
14244
|
+
*/
|
|
14245
|
+
async getAllActiveTemplates() {
|
|
14246
|
+
const q = query25(
|
|
14247
|
+
this.collectionRef,
|
|
14248
|
+
where25("isActive", "==", true),
|
|
14249
|
+
orderBy13("title", "asc")
|
|
14250
|
+
);
|
|
14251
|
+
const querySnapshot = await getDocs25(q);
|
|
14252
|
+
const templates = [];
|
|
14253
|
+
querySnapshot.forEach((doc38) => {
|
|
14254
|
+
templates.push(doc38.data());
|
|
14255
|
+
});
|
|
14256
|
+
return templates;
|
|
14257
|
+
}
|
|
14085
14258
|
/**
|
|
14086
14259
|
* Get templates by tags
|
|
14087
14260
|
* @param tags - Tags to filter by
|
|
@@ -14103,9 +14276,9 @@ var DocumentationTemplateService = class extends BaseService {
|
|
|
14103
14276
|
const querySnapshot = await getDocs25(q);
|
|
14104
14277
|
const templates = [];
|
|
14105
14278
|
let lastVisible = null;
|
|
14106
|
-
querySnapshot.forEach((
|
|
14107
|
-
templates.push(
|
|
14108
|
-
lastVisible =
|
|
14279
|
+
querySnapshot.forEach((doc38) => {
|
|
14280
|
+
templates.push(doc38.data());
|
|
14281
|
+
lastVisible = doc38;
|
|
14109
14282
|
});
|
|
14110
14283
|
return {
|
|
14111
14284
|
templates,
|
|
@@ -14132,9 +14305,9 @@ var DocumentationTemplateService = class extends BaseService {
|
|
|
14132
14305
|
const querySnapshot = await getDocs25(q);
|
|
14133
14306
|
const templates = [];
|
|
14134
14307
|
let lastVisible = null;
|
|
14135
|
-
querySnapshot.forEach((
|
|
14136
|
-
templates.push(
|
|
14137
|
-
lastVisible =
|
|
14308
|
+
querySnapshot.forEach((doc38) => {
|
|
14309
|
+
templates.push(doc38.data());
|
|
14310
|
+
lastVisible = doc38;
|
|
14138
14311
|
});
|
|
14139
14312
|
return {
|
|
14140
14313
|
templates,
|
|
@@ -14160,8 +14333,8 @@ var DocumentationTemplateService = class extends BaseService {
|
|
|
14160
14333
|
}
|
|
14161
14334
|
const querySnapshot = await getDocs25(q);
|
|
14162
14335
|
const templates = [];
|
|
14163
|
-
querySnapshot.forEach((
|
|
14164
|
-
templates.push(
|
|
14336
|
+
querySnapshot.forEach((doc38) => {
|
|
14337
|
+
templates.push(doc38.data());
|
|
14165
14338
|
});
|
|
14166
14339
|
return templates;
|
|
14167
14340
|
}
|
|
@@ -14367,9 +14540,9 @@ var FilledDocumentService = class extends BaseService {
|
|
|
14367
14540
|
const querySnapshot = await getDocs26(q);
|
|
14368
14541
|
const documents = [];
|
|
14369
14542
|
let lastVisible = null;
|
|
14370
|
-
querySnapshot.forEach((
|
|
14371
|
-
documents.push(
|
|
14372
|
-
lastVisible =
|
|
14543
|
+
querySnapshot.forEach((doc38) => {
|
|
14544
|
+
documents.push(doc38.data());
|
|
14545
|
+
lastVisible = doc38;
|
|
14373
14546
|
});
|
|
14374
14547
|
return {
|
|
14375
14548
|
documents,
|
|
@@ -14591,9 +14764,9 @@ var NotificationService = class extends BaseService {
|
|
|
14591
14764
|
orderBy15("notificationTime", "desc")
|
|
14592
14765
|
);
|
|
14593
14766
|
const querySnapshot = await getDocs27(q);
|
|
14594
|
-
return querySnapshot.docs.map((
|
|
14595
|
-
id:
|
|
14596
|
-
...
|
|
14767
|
+
return querySnapshot.docs.map((doc38) => ({
|
|
14768
|
+
id: doc38.id,
|
|
14769
|
+
...doc38.data()
|
|
14597
14770
|
}));
|
|
14598
14771
|
}
|
|
14599
14772
|
/**
|
|
@@ -14607,9 +14780,9 @@ var NotificationService = class extends BaseService {
|
|
|
14607
14780
|
orderBy15("notificationTime", "desc")
|
|
14608
14781
|
);
|
|
14609
14782
|
const querySnapshot = await getDocs27(q);
|
|
14610
|
-
return querySnapshot.docs.map((
|
|
14611
|
-
id:
|
|
14612
|
-
...
|
|
14783
|
+
return querySnapshot.docs.map((doc38) => ({
|
|
14784
|
+
id: doc38.id,
|
|
14785
|
+
...doc38.data()
|
|
14613
14786
|
}));
|
|
14614
14787
|
}
|
|
14615
14788
|
/**
|
|
@@ -14681,9 +14854,9 @@ var NotificationService = class extends BaseService {
|
|
|
14681
14854
|
orderBy15("notificationTime", "desc")
|
|
14682
14855
|
);
|
|
14683
14856
|
const querySnapshot = await getDocs27(q);
|
|
14684
|
-
return querySnapshot.docs.map((
|
|
14685
|
-
id:
|
|
14686
|
-
...
|
|
14857
|
+
return querySnapshot.docs.map((doc38) => ({
|
|
14858
|
+
id: doc38.id,
|
|
14859
|
+
...doc38.data()
|
|
14687
14860
|
}));
|
|
14688
14861
|
}
|
|
14689
14862
|
/**
|
|
@@ -14696,9 +14869,9 @@ var NotificationService = class extends BaseService {
|
|
|
14696
14869
|
orderBy15("notificationTime", "desc")
|
|
14697
14870
|
);
|
|
14698
14871
|
const querySnapshot = await getDocs27(q);
|
|
14699
|
-
return querySnapshot.docs.map((
|
|
14700
|
-
id:
|
|
14701
|
-
...
|
|
14872
|
+
return querySnapshot.docs.map((doc38) => ({
|
|
14873
|
+
id: doc38.id,
|
|
14874
|
+
...doc38.data()
|
|
14702
14875
|
}));
|
|
14703
14876
|
}
|
|
14704
14877
|
};
|
|
@@ -14905,67 +15078,141 @@ import {
|
|
|
14905
15078
|
} from "firebase/firestore";
|
|
14906
15079
|
|
|
14907
15080
|
// src/validations/procedure.schema.ts
|
|
15081
|
+
import { z as z25 } from "zod";
|
|
15082
|
+
|
|
15083
|
+
// src/validations/procedure-product.schema.ts
|
|
14908
15084
|
import { z as z24 } from "zod";
|
|
14909
|
-
var
|
|
14910
|
-
|
|
14911
|
-
|
|
14912
|
-
|
|
14913
|
-
|
|
14914
|
-
|
|
14915
|
-
|
|
14916
|
-
|
|
14917
|
-
|
|
14918
|
-
|
|
14919
|
-
price: z24.number().min(0),
|
|
15085
|
+
var procedureProductDataSchema = z24.object({
|
|
15086
|
+
/**
|
|
15087
|
+
* The ID of the product. Must be a non-empty string.
|
|
15088
|
+
* @validation
|
|
15089
|
+
*/
|
|
15090
|
+
productId: z24.string().min(1, "Product ID is required"),
|
|
15091
|
+
/**
|
|
15092
|
+
* The price of the product. Must be a non-negative number.
|
|
15093
|
+
* @validation
|
|
15094
|
+
*/
|
|
15095
|
+
price: z24.number().min(0, "Price must be a non-negative number"),
|
|
15096
|
+
/**
|
|
15097
|
+
* The currency for the price. Must be one of the values from the Currency enum.
|
|
15098
|
+
* @validation
|
|
15099
|
+
*/
|
|
14920
15100
|
currency: z24.nativeEnum(Currency),
|
|
15101
|
+
/**
|
|
15102
|
+
* The pricing measure for the product. Must be one of the values from the PricingMeasure enum.
|
|
15103
|
+
* @validation
|
|
15104
|
+
*/
|
|
14921
15105
|
pricingMeasure: z24.nativeEnum(PricingMeasure),
|
|
14922
|
-
|
|
15106
|
+
/**
|
|
15107
|
+
* Whether this is the default product for the procedure.
|
|
15108
|
+
* @validation
|
|
15109
|
+
*/
|
|
15110
|
+
isDefault: z24.boolean().optional()
|
|
15111
|
+
});
|
|
15112
|
+
|
|
15113
|
+
// src/validations/procedure.schema.ts
|
|
15114
|
+
var storedProcedureProductSchema = z25.object({
|
|
15115
|
+
/**
|
|
15116
|
+
* The full product object used in the procedure.
|
|
15117
|
+
*/
|
|
15118
|
+
product: z25.any(),
|
|
15119
|
+
// We'll validate the full product object separately
|
|
15120
|
+
/**
|
|
15121
|
+
* The price of the procedure when using this specific product.
|
|
15122
|
+
*/
|
|
15123
|
+
price: z25.number().min(0, "Price must be a non-negative number"),
|
|
15124
|
+
/**
|
|
15125
|
+
* The currency for the price of this product.
|
|
15126
|
+
*/
|
|
15127
|
+
currency: z25.nativeEnum(Currency),
|
|
15128
|
+
/**
|
|
15129
|
+
* How the price is measured (e.g., per ml, per zone).
|
|
15130
|
+
*/
|
|
15131
|
+
pricingMeasure: z25.nativeEnum(PricingMeasure),
|
|
15132
|
+
/**
|
|
15133
|
+
* Whether this is the default product for the procedure.
|
|
15134
|
+
*/
|
|
15135
|
+
isDefault: z25.boolean().optional()
|
|
15136
|
+
});
|
|
15137
|
+
var createProcedureSchema = z25.object({
|
|
15138
|
+
name: z25.string().min(1).max(200),
|
|
15139
|
+
// Optional: service will derive from name if not provided by client
|
|
15140
|
+
nameLower: z25.string().min(1).max(200).optional(),
|
|
15141
|
+
description: z25.string().min(1).max(2e3),
|
|
15142
|
+
family: z25.nativeEnum(ProcedureFamily),
|
|
15143
|
+
categoryId: z25.string().min(1),
|
|
15144
|
+
subcategoryId: z25.string().min(1),
|
|
15145
|
+
technologyId: z25.string().min(1),
|
|
15146
|
+
productId: z25.string().min(1),
|
|
15147
|
+
price: z25.number().min(0),
|
|
15148
|
+
currency: z25.nativeEnum(Currency),
|
|
15149
|
+
pricingMeasure: z25.nativeEnum(PricingMeasure),
|
|
15150
|
+
productsMetadata: z25.array(procedureProductDataSchema).min(1),
|
|
15151
|
+
duration: z25.number().min(1).max(480),
|
|
14923
15152
|
// Max 8 hours
|
|
14924
|
-
practitionerId:
|
|
14925
|
-
clinicBranchId:
|
|
14926
|
-
photos:
|
|
15153
|
+
practitionerId: z25.string().min(1),
|
|
15154
|
+
clinicBranchId: z25.string().min(1),
|
|
15155
|
+
photos: z25.array(mediaResourceSchema).optional()
|
|
14927
15156
|
});
|
|
14928
|
-
var updateProcedureSchema =
|
|
14929
|
-
name:
|
|
14930
|
-
nameLower:
|
|
14931
|
-
description:
|
|
14932
|
-
price:
|
|
14933
|
-
currency:
|
|
14934
|
-
pricingMeasure:
|
|
14935
|
-
|
|
14936
|
-
|
|
14937
|
-
|
|
14938
|
-
|
|
14939
|
-
|
|
14940
|
-
|
|
14941
|
-
|
|
14942
|
-
|
|
14943
|
-
|
|
15157
|
+
var updateProcedureSchema = z25.object({
|
|
15158
|
+
name: z25.string().min(3).max(100).optional(),
|
|
15159
|
+
nameLower: z25.string().min(1).max(200).optional(),
|
|
15160
|
+
description: z25.string().min(3).max(1e3).optional(),
|
|
15161
|
+
price: z25.number().min(0).optional(),
|
|
15162
|
+
currency: z25.nativeEnum(Currency).optional(),
|
|
15163
|
+
pricingMeasure: z25.nativeEnum(PricingMeasure).optional(),
|
|
15164
|
+
productsMetadata: z25.array(procedureProductDataSchema).min(1).optional(),
|
|
15165
|
+
duration: z25.number().min(0).optional(),
|
|
15166
|
+
isActive: z25.boolean().optional(),
|
|
15167
|
+
practitionerId: z25.string().optional(),
|
|
15168
|
+
categoryId: z25.string().optional(),
|
|
15169
|
+
subcategoryId: z25.string().optional(),
|
|
15170
|
+
technologyId: z25.string().optional(),
|
|
15171
|
+
productId: z25.string().optional(),
|
|
15172
|
+
clinicBranchId: z25.string().optional(),
|
|
15173
|
+
photos: z25.array(mediaResourceSchema).optional()
|
|
14944
15174
|
});
|
|
14945
|
-
var procedureSchema =
|
|
14946
|
-
id:
|
|
14947
|
-
|
|
14948
|
-
|
|
15175
|
+
var procedureSchema = z25.object({
|
|
15176
|
+
id: z25.string().min(1),
|
|
15177
|
+
name: z25.string().min(1).max(200),
|
|
15178
|
+
nameLower: z25.string().min(1).max(200),
|
|
15179
|
+
description: z25.string().min(1).max(2e3),
|
|
15180
|
+
family: z25.nativeEnum(ProcedureFamily),
|
|
15181
|
+
category: z25.any(),
|
|
14949
15182
|
// We'll validate the full category object separately
|
|
14950
|
-
subcategory:
|
|
15183
|
+
subcategory: z25.any(),
|
|
14951
15184
|
// We'll validate the full subcategory object separately
|
|
14952
|
-
technology:
|
|
15185
|
+
technology: z25.any(),
|
|
14953
15186
|
// We'll validate the full technology object separately
|
|
14954
|
-
product:
|
|
15187
|
+
product: z25.any(),
|
|
14955
15188
|
// We'll validate the full product object separately
|
|
14956
|
-
|
|
15189
|
+
productsMetadata: z25.array(storedProcedureProductSchema).min(1),
|
|
15190
|
+
// Use stored format schema
|
|
15191
|
+
price: z25.number().min(0),
|
|
15192
|
+
currency: z25.nativeEnum(Currency),
|
|
15193
|
+
pricingMeasure: z25.nativeEnum(PricingMeasure),
|
|
15194
|
+
duration: z25.number().min(1).max(480),
|
|
15195
|
+
practitionerId: z25.string().min(1),
|
|
15196
|
+
clinicBranchId: z25.string().min(1),
|
|
15197
|
+
photos: z25.array(z25.string()).optional(),
|
|
15198
|
+
// Stored as URL strings
|
|
15199
|
+
blockingConditions: z25.array(z25.any()),
|
|
14957
15200
|
// We'll validate blocking conditions separately
|
|
14958
|
-
contraindications:
|
|
15201
|
+
contraindications: z25.array(z25.any()),
|
|
14959
15202
|
// We'll validate contraindications separately
|
|
14960
|
-
|
|
15203
|
+
contraindicationIds: z25.array(z25.string()),
|
|
15204
|
+
// Array of IDs for efficient querying
|
|
15205
|
+
treatmentBenefits: z25.array(z25.any()),
|
|
14961
15206
|
// We'll validate treatment benefits separately
|
|
14962
|
-
|
|
15207
|
+
treatmentBenefitIds: z25.array(z25.string()),
|
|
15208
|
+
// Array of IDs for efficient querying
|
|
15209
|
+
preRequirements: z25.array(z25.any()),
|
|
14963
15210
|
// We'll validate requirements separately
|
|
14964
|
-
postRequirements:
|
|
15211
|
+
postRequirements: z25.array(z25.any()),
|
|
14965
15212
|
// We'll validate requirements separately
|
|
14966
|
-
certificationRequirement:
|
|
15213
|
+
certificationRequirement: z25.any(),
|
|
14967
15214
|
// We'll validate certification requirement separately
|
|
14968
|
-
documentationTemplates:
|
|
15215
|
+
documentationTemplates: z25.array(z25.any()),
|
|
14969
15216
|
// We'll validate documentation templates separately
|
|
14970
15217
|
clinicInfo: clinicInfoSchema,
|
|
14971
15218
|
// Clinic info validation
|
|
@@ -14973,9 +15220,9 @@ var procedureSchema = createProcedureSchema.extend({
|
|
|
14973
15220
|
// Doctor info validation
|
|
14974
15221
|
reviewInfo: procedureReviewInfoSchema,
|
|
14975
15222
|
// Procedure review info validation
|
|
14976
|
-
isActive:
|
|
14977
|
-
createdAt:
|
|
14978
|
-
updatedAt:
|
|
15223
|
+
isActive: z25.boolean(),
|
|
15224
|
+
createdAt: z25.date(),
|
|
15225
|
+
updatedAt: z25.date()
|
|
14979
15226
|
});
|
|
14980
15227
|
|
|
14981
15228
|
// src/services/procedure/procedure.service.ts
|
|
@@ -15002,7 +15249,9 @@ var ProcedureService = class extends BaseService {
|
|
|
15002
15249
|
return media;
|
|
15003
15250
|
}
|
|
15004
15251
|
if (media instanceof File || media instanceof Blob) {
|
|
15005
|
-
console.log(
|
|
15252
|
+
console.log(
|
|
15253
|
+
`[ProcedureService] Uploading ${collectionName} media for ${ownerId}`
|
|
15254
|
+
);
|
|
15006
15255
|
const metadata = await this.mediaService.uploadMedia(
|
|
15007
15256
|
media,
|
|
15008
15257
|
ownerId,
|
|
@@ -15024,41 +15273,91 @@ var ProcedureService = class extends BaseService {
|
|
|
15024
15273
|
if (!mediaArray || mediaArray.length === 0) return [];
|
|
15025
15274
|
const result = [];
|
|
15026
15275
|
for (const media of mediaArray) {
|
|
15027
|
-
const processedUrl = await this.processMedia(
|
|
15276
|
+
const processedUrl = await this.processMedia(
|
|
15277
|
+
media,
|
|
15278
|
+
ownerId,
|
|
15279
|
+
collectionName
|
|
15280
|
+
);
|
|
15028
15281
|
if (processedUrl) {
|
|
15029
15282
|
result.push(processedUrl);
|
|
15030
15283
|
}
|
|
15031
15284
|
}
|
|
15032
15285
|
return result;
|
|
15033
15286
|
}
|
|
15287
|
+
/**
|
|
15288
|
+
* Transforms validated procedure product data (with productId) to ProcedureProduct objects (with full product)
|
|
15289
|
+
* @param productsMetadata Array of validated procedure product data
|
|
15290
|
+
* @param technologyId Technology ID to fetch products from
|
|
15291
|
+
* @returns Array of ProcedureProduct objects with full product information
|
|
15292
|
+
*/
|
|
15293
|
+
async transformProductsMetadata(productsMetadata, technologyId) {
|
|
15294
|
+
const transformedProducts = [];
|
|
15295
|
+
for (const productData of productsMetadata) {
|
|
15296
|
+
const product = await this.productService.getById(
|
|
15297
|
+
technologyId,
|
|
15298
|
+
productData.productId
|
|
15299
|
+
);
|
|
15300
|
+
if (!product) {
|
|
15301
|
+
throw new Error(
|
|
15302
|
+
`Product with ID ${productData.productId} not found for technology ${technologyId}`
|
|
15303
|
+
);
|
|
15304
|
+
}
|
|
15305
|
+
transformedProducts.push({
|
|
15306
|
+
product,
|
|
15307
|
+
price: productData.price,
|
|
15308
|
+
currency: productData.currency,
|
|
15309
|
+
pricingMeasure: productData.pricingMeasure,
|
|
15310
|
+
isDefault: productData.isDefault
|
|
15311
|
+
});
|
|
15312
|
+
}
|
|
15313
|
+
return transformedProducts;
|
|
15314
|
+
}
|
|
15034
15315
|
/**
|
|
15035
15316
|
* Creates a new procedure
|
|
15036
15317
|
* @param data - The data for creating a new procedure
|
|
15037
15318
|
* @returns The created procedure
|
|
15038
15319
|
*/
|
|
15039
15320
|
async createProcedure(data) {
|
|
15040
|
-
var _a;
|
|
15321
|
+
var _a, _b, _c;
|
|
15041
15322
|
const validatedData = createProcedureSchema.parse(data);
|
|
15042
15323
|
const procedureId = this.generateId();
|
|
15043
15324
|
const [category, subcategory, technology, product] = await Promise.all([
|
|
15044
15325
|
this.categoryService.getById(validatedData.categoryId),
|
|
15045
|
-
this.subcategoryService.getById(
|
|
15326
|
+
this.subcategoryService.getById(
|
|
15327
|
+
validatedData.categoryId,
|
|
15328
|
+
validatedData.subcategoryId
|
|
15329
|
+
),
|
|
15046
15330
|
this.technologyService.getById(validatedData.technologyId),
|
|
15047
|
-
this.productService.getById(
|
|
15331
|
+
this.productService.getById(
|
|
15332
|
+
validatedData.technologyId,
|
|
15333
|
+
validatedData.productId
|
|
15334
|
+
)
|
|
15048
15335
|
]);
|
|
15049
15336
|
if (!category || !subcategory || !technology || !product) {
|
|
15050
15337
|
throw new Error("One or more required base entities not found");
|
|
15051
15338
|
}
|
|
15052
|
-
const clinicRef = doc30(
|
|
15339
|
+
const clinicRef = doc30(
|
|
15340
|
+
this.db,
|
|
15341
|
+
CLINICS_COLLECTION,
|
|
15342
|
+
validatedData.clinicBranchId
|
|
15343
|
+
);
|
|
15053
15344
|
const clinicSnapshot = await getDoc32(clinicRef);
|
|
15054
15345
|
if (!clinicSnapshot.exists()) {
|
|
15055
|
-
throw new Error(
|
|
15346
|
+
throw new Error(
|
|
15347
|
+
`Clinic with ID ${validatedData.clinicBranchId} not found`
|
|
15348
|
+
);
|
|
15056
15349
|
}
|
|
15057
15350
|
const clinic = clinicSnapshot.data();
|
|
15058
|
-
const practitionerRef = doc30(
|
|
15351
|
+
const practitionerRef = doc30(
|
|
15352
|
+
this.db,
|
|
15353
|
+
PRACTITIONERS_COLLECTION,
|
|
15354
|
+
validatedData.practitionerId
|
|
15355
|
+
);
|
|
15059
15356
|
const practitionerSnapshot = await getDoc32(practitionerRef);
|
|
15060
15357
|
if (!practitionerSnapshot.exists()) {
|
|
15061
|
-
throw new Error(
|
|
15358
|
+
throw new Error(
|
|
15359
|
+
`Practitioner with ID ${validatedData.practitionerId} not found`
|
|
15360
|
+
);
|
|
15062
15361
|
}
|
|
15063
15362
|
const practitioner = practitionerSnapshot.data();
|
|
15064
15363
|
let processedPhotos = [];
|
|
@@ -15069,6 +15368,10 @@ var ProcedureService = class extends BaseService {
|
|
|
15069
15368
|
"procedure-photos"
|
|
15070
15369
|
);
|
|
15071
15370
|
}
|
|
15371
|
+
const transformedProductsMetadata = await this.transformProductsMetadata(
|
|
15372
|
+
validatedData.productsMetadata,
|
|
15373
|
+
validatedData.technologyId
|
|
15374
|
+
);
|
|
15072
15375
|
const clinicInfo = {
|
|
15073
15376
|
id: clinicSnapshot.id,
|
|
15074
15377
|
name: clinic.name,
|
|
@@ -15097,9 +15400,12 @@ var ProcedureService = class extends BaseService {
|
|
|
15097
15400
|
subcategory,
|
|
15098
15401
|
technology,
|
|
15099
15402
|
product,
|
|
15403
|
+
productsMetadata: transformedProductsMetadata,
|
|
15100
15404
|
blockingConditions: technology.blockingConditions,
|
|
15101
15405
|
contraindications: technology.contraindications || [],
|
|
15406
|
+
contraindicationIds: ((_b = technology.contraindications) == null ? void 0 : _b.map((c) => c.id)) || [],
|
|
15102
15407
|
treatmentBenefits: technology.benefits,
|
|
15408
|
+
treatmentBenefitIds: ((_c = technology.benefits) == null ? void 0 : _c.map((b) => b.id)) || [],
|
|
15103
15409
|
preRequirements: technology.requirements.pre,
|
|
15104
15410
|
postRequirements: technology.requirements.post,
|
|
15105
15411
|
certificationRequirement: technology.certificationRequirement,
|
|
@@ -15140,7 +15446,7 @@ var ProcedureService = class extends BaseService {
|
|
|
15140
15446
|
* @returns A promise that resolves to an array of the newly created procedures.
|
|
15141
15447
|
*/
|
|
15142
15448
|
async bulkCreateProcedures(baseData, practitionerIds) {
|
|
15143
|
-
var _a;
|
|
15449
|
+
var _a, _b, _c;
|
|
15144
15450
|
if (!practitionerIds || practitionerIds.length === 0) {
|
|
15145
15451
|
throw new Error("Practitioner IDs array cannot be empty.");
|
|
15146
15452
|
}
|
|
@@ -15148,16 +15454,24 @@ var ProcedureService = class extends BaseService {
|
|
|
15148
15454
|
const validatedData = createProcedureSchema.parse(validationData);
|
|
15149
15455
|
const [category, subcategory, technology, product, clinicSnapshot] = await Promise.all([
|
|
15150
15456
|
this.categoryService.getById(validatedData.categoryId),
|
|
15151
|
-
this.subcategoryService.getById(
|
|
15457
|
+
this.subcategoryService.getById(
|
|
15458
|
+
validatedData.categoryId,
|
|
15459
|
+
validatedData.subcategoryId
|
|
15460
|
+
),
|
|
15152
15461
|
this.technologyService.getById(validatedData.technologyId),
|
|
15153
|
-
this.productService.getById(
|
|
15462
|
+
this.productService.getById(
|
|
15463
|
+
validatedData.technologyId,
|
|
15464
|
+
validatedData.productId
|
|
15465
|
+
),
|
|
15154
15466
|
getDoc32(doc30(this.db, CLINICS_COLLECTION, validatedData.clinicBranchId))
|
|
15155
15467
|
]);
|
|
15156
15468
|
if (!category || !subcategory || !technology || !product) {
|
|
15157
15469
|
throw new Error("One or more required base entities not found");
|
|
15158
15470
|
}
|
|
15159
15471
|
if (!clinicSnapshot.exists()) {
|
|
15160
|
-
throw new Error(
|
|
15472
|
+
throw new Error(
|
|
15473
|
+
`Clinic with ID ${validatedData.clinicBranchId} not found`
|
|
15474
|
+
);
|
|
15161
15475
|
}
|
|
15162
15476
|
const clinic = clinicSnapshot.data();
|
|
15163
15477
|
let processedPhotos = [];
|
|
@@ -15169,6 +15483,10 @@ var ProcedureService = class extends BaseService {
|
|
|
15169
15483
|
"procedure-photos-batch"
|
|
15170
15484
|
);
|
|
15171
15485
|
}
|
|
15486
|
+
const transformedProductsMetadata = await this.transformProductsMetadata(
|
|
15487
|
+
validatedData.productsMetadata,
|
|
15488
|
+
validatedData.technologyId
|
|
15489
|
+
);
|
|
15172
15490
|
const practitionersMap = /* @__PURE__ */ new Map();
|
|
15173
15491
|
for (let i = 0; i < practitionerIds.length; i += 30) {
|
|
15174
15492
|
const chunk = practitionerIds.slice(i, i + 30);
|
|
@@ -15177,14 +15495,18 @@ var ProcedureService = class extends BaseService {
|
|
|
15177
15495
|
where29(documentId2(), "in", chunk)
|
|
15178
15496
|
);
|
|
15179
15497
|
const practitionersSnapshot = await getDocs29(practitionersQuery);
|
|
15180
|
-
practitionersSnapshot.docs.forEach((
|
|
15181
|
-
practitionersMap.set(
|
|
15498
|
+
practitionersSnapshot.docs.forEach((doc38) => {
|
|
15499
|
+
practitionersMap.set(doc38.id, doc38.data());
|
|
15182
15500
|
});
|
|
15183
15501
|
}
|
|
15184
15502
|
if (practitionersMap.size !== practitionerIds.length) {
|
|
15185
15503
|
const foundIds = Array.from(practitionersMap.keys());
|
|
15186
|
-
const notFoundIds = practitionerIds.filter(
|
|
15187
|
-
|
|
15504
|
+
const notFoundIds = practitionerIds.filter(
|
|
15505
|
+
(id) => !foundIds.includes(id)
|
|
15506
|
+
);
|
|
15507
|
+
throw new Error(
|
|
15508
|
+
`The following practitioners were not found: ${notFoundIds.join(", ")}`
|
|
15509
|
+
);
|
|
15188
15510
|
}
|
|
15189
15511
|
const batch = writeBatch6(this.db);
|
|
15190
15512
|
const createdProcedureIds = [];
|
|
@@ -15220,9 +15542,12 @@ var ProcedureService = class extends BaseService {
|
|
|
15220
15542
|
subcategory,
|
|
15221
15543
|
technology,
|
|
15222
15544
|
product,
|
|
15545
|
+
productsMetadata: transformedProductsMetadata,
|
|
15223
15546
|
blockingConditions: technology.blockingConditions,
|
|
15224
15547
|
contraindications: technology.contraindications || [],
|
|
15548
|
+
contraindicationIds: ((_b = technology.contraindications) == null ? void 0 : _b.map((c) => c.id)) || [],
|
|
15225
15549
|
treatmentBenefits: technology.benefits,
|
|
15550
|
+
treatmentBenefitIds: ((_c = technology.benefits) == null ? void 0 : _c.map((b) => b.id)) || [],
|
|
15226
15551
|
preRequirements: technology.requirements.pre,
|
|
15227
15552
|
postRequirements: technology.requirements.post,
|
|
15228
15553
|
certificationRequirement: technology.certificationRequirement,
|
|
@@ -15252,10 +15577,13 @@ var ProcedureService = class extends BaseService {
|
|
|
15252
15577
|
const fetchedProcedures = [];
|
|
15253
15578
|
for (let i = 0; i < createdProcedureIds.length; i += 30) {
|
|
15254
15579
|
const chunk = createdProcedureIds.slice(i, i + 30);
|
|
15255
|
-
const q = query29(
|
|
15580
|
+
const q = query29(
|
|
15581
|
+
collection29(this.db, PROCEDURES_COLLECTION),
|
|
15582
|
+
where29(documentId2(), "in", chunk)
|
|
15583
|
+
);
|
|
15256
15584
|
const snapshot = await getDocs29(q);
|
|
15257
|
-
snapshot.forEach((
|
|
15258
|
-
fetchedProcedures.push(
|
|
15585
|
+
snapshot.forEach((doc38) => {
|
|
15586
|
+
fetchedProcedures.push(doc38.data());
|
|
15259
15587
|
});
|
|
15260
15588
|
}
|
|
15261
15589
|
return fetchedProcedures;
|
|
@@ -15285,7 +15613,7 @@ var ProcedureService = class extends BaseService {
|
|
|
15285
15613
|
where29("isActive", "==", true)
|
|
15286
15614
|
);
|
|
15287
15615
|
const snapshot = await getDocs29(q);
|
|
15288
|
-
return snapshot.docs.map((
|
|
15616
|
+
return snapshot.docs.map((doc38) => doc38.data());
|
|
15289
15617
|
}
|
|
15290
15618
|
/**
|
|
15291
15619
|
* Gets all procedures for a practitioner
|
|
@@ -15299,7 +15627,7 @@ var ProcedureService = class extends BaseService {
|
|
|
15299
15627
|
where29("isActive", "==", true)
|
|
15300
15628
|
);
|
|
15301
15629
|
const snapshot = await getDocs29(q);
|
|
15302
|
-
return snapshot.docs.map((
|
|
15630
|
+
return snapshot.docs.map((doc38) => doc38.data());
|
|
15303
15631
|
}
|
|
15304
15632
|
/**
|
|
15305
15633
|
* Gets all inactive procedures for a practitioner
|
|
@@ -15313,7 +15641,7 @@ var ProcedureService = class extends BaseService {
|
|
|
15313
15641
|
where29("isActive", "==", false)
|
|
15314
15642
|
);
|
|
15315
15643
|
const snapshot = await getDocs29(q);
|
|
15316
|
-
return snapshot.docs.map((
|
|
15644
|
+
return snapshot.docs.map((doc38) => doc38.data());
|
|
15317
15645
|
}
|
|
15318
15646
|
/**
|
|
15319
15647
|
* Updates a procedure
|
|
@@ -15322,7 +15650,7 @@ var ProcedureService = class extends BaseService {
|
|
|
15322
15650
|
* @returns The updated procedure
|
|
15323
15651
|
*/
|
|
15324
15652
|
async updateProcedure(id, data) {
|
|
15325
|
-
var _a;
|
|
15653
|
+
var _a, _b, _c, _d;
|
|
15326
15654
|
const validatedData = updateProcedureSchema.parse(data);
|
|
15327
15655
|
const procedureRef = doc30(this.db, PROCEDURES_COLLECTION, id);
|
|
15328
15656
|
const procedureSnapshot = await getDoc32(procedureRef);
|
|
@@ -15330,7 +15658,21 @@ var ProcedureService = class extends BaseService {
|
|
|
15330
15658
|
throw new Error(`Procedure with ID ${id} not found`);
|
|
15331
15659
|
}
|
|
15332
15660
|
const existingProcedure = procedureSnapshot.data();
|
|
15333
|
-
let updatedProcedureData = {
|
|
15661
|
+
let updatedProcedureData = {};
|
|
15662
|
+
if (validatedData.name !== void 0)
|
|
15663
|
+
updatedProcedureData.name = validatedData.name;
|
|
15664
|
+
if (validatedData.description !== void 0)
|
|
15665
|
+
updatedProcedureData.description = validatedData.description;
|
|
15666
|
+
if (validatedData.price !== void 0)
|
|
15667
|
+
updatedProcedureData.price = validatedData.price;
|
|
15668
|
+
if (validatedData.currency !== void 0)
|
|
15669
|
+
updatedProcedureData.currency = validatedData.currency;
|
|
15670
|
+
if (validatedData.pricingMeasure !== void 0)
|
|
15671
|
+
updatedProcedureData.pricingMeasure = validatedData.pricingMeasure;
|
|
15672
|
+
if (validatedData.duration !== void 0)
|
|
15673
|
+
updatedProcedureData.duration = validatedData.duration;
|
|
15674
|
+
if (validatedData.isActive !== void 0)
|
|
15675
|
+
updatedProcedureData.isActive = validatedData.isActive;
|
|
15334
15676
|
let practitionerChanged = false;
|
|
15335
15677
|
let clinicChanged = false;
|
|
15336
15678
|
const oldPractitionerId = existingProcedure.practitionerId;
|
|
@@ -15344,6 +15686,18 @@ var ProcedureService = class extends BaseService {
|
|
|
15344
15686
|
"procedure-photos"
|
|
15345
15687
|
);
|
|
15346
15688
|
}
|
|
15689
|
+
if (validatedData.productsMetadata !== void 0) {
|
|
15690
|
+
const technologyId = (_a = validatedData.technologyId) != null ? _a : existingProcedure.technology.id;
|
|
15691
|
+
if (!technologyId) {
|
|
15692
|
+
throw new Error(
|
|
15693
|
+
"Technology ID is required for updating products metadata"
|
|
15694
|
+
);
|
|
15695
|
+
}
|
|
15696
|
+
updatedProcedureData.productsMetadata = await this.transformProductsMetadata(
|
|
15697
|
+
validatedData.productsMetadata,
|
|
15698
|
+
technologyId
|
|
15699
|
+
);
|
|
15700
|
+
}
|
|
15347
15701
|
if (validatedData.practitionerId && validatedData.practitionerId !== oldPractitionerId) {
|
|
15348
15702
|
practitionerChanged = true;
|
|
15349
15703
|
const newPractitionerRef = doc30(
|
|
@@ -15353,7 +15707,9 @@ var ProcedureService = class extends BaseService {
|
|
|
15353
15707
|
);
|
|
15354
15708
|
const newPractitionerSnap = await getDoc32(newPractitionerRef);
|
|
15355
15709
|
if (!newPractitionerSnap.exists())
|
|
15356
|
-
throw new Error(
|
|
15710
|
+
throw new Error(
|
|
15711
|
+
`New Practitioner ${validatedData.practitionerId} not found`
|
|
15712
|
+
);
|
|
15357
15713
|
newPractitioner = newPractitionerSnap.data();
|
|
15358
15714
|
updatedProcedureData.doctorInfo = {
|
|
15359
15715
|
id: newPractitioner.id,
|
|
@@ -15361,13 +15717,17 @@ var ProcedureService = class extends BaseService {
|
|
|
15361
15717
|
description: newPractitioner.basicInfo.bio || "",
|
|
15362
15718
|
photo: typeof newPractitioner.basicInfo.profileImageUrl === "string" ? newPractitioner.basicInfo.profileImageUrl : "",
|
|
15363
15719
|
// Default to empty string if not a processed URL
|
|
15364
|
-
rating: ((
|
|
15720
|
+
rating: ((_b = newPractitioner.reviewInfo) == null ? void 0 : _b.averageRating) || 0,
|
|
15365
15721
|
services: newPractitioner.procedures || []
|
|
15366
15722
|
};
|
|
15367
15723
|
}
|
|
15368
15724
|
if (validatedData.clinicBranchId && validatedData.clinicBranchId !== oldClinicId) {
|
|
15369
15725
|
clinicChanged = true;
|
|
15370
|
-
const newClinicRef = doc30(
|
|
15726
|
+
const newClinicRef = doc30(
|
|
15727
|
+
this.db,
|
|
15728
|
+
CLINICS_COLLECTION,
|
|
15729
|
+
validatedData.clinicBranchId
|
|
15730
|
+
);
|
|
15371
15731
|
const newClinicSnap = await getDoc32(newClinicRef);
|
|
15372
15732
|
if (!newClinicSnap.exists())
|
|
15373
15733
|
throw new Error(`New Clinic ${validatedData.clinicBranchId} not found`);
|
|
@@ -15386,8 +15746,11 @@ var ProcedureService = class extends BaseService {
|
|
|
15386
15746
|
updatedProcedureData.nameLower = validatedData.name.toLowerCase();
|
|
15387
15747
|
}
|
|
15388
15748
|
if (validatedData.categoryId) {
|
|
15389
|
-
const category = await this.categoryService.getById(
|
|
15390
|
-
|
|
15749
|
+
const category = await this.categoryService.getById(
|
|
15750
|
+
validatedData.categoryId
|
|
15751
|
+
);
|
|
15752
|
+
if (!category)
|
|
15753
|
+
throw new Error(`Category ${validatedData.categoryId} not found`);
|
|
15391
15754
|
updatedProcedureData.category = category;
|
|
15392
15755
|
finalCategoryId = category.id;
|
|
15393
15756
|
}
|
|
@@ -15402,23 +15765,34 @@ var ProcedureService = class extends BaseService {
|
|
|
15402
15765
|
);
|
|
15403
15766
|
updatedProcedureData.subcategory = subcategory;
|
|
15404
15767
|
} else if (validatedData.subcategoryId) {
|
|
15405
|
-
console.warn(
|
|
15768
|
+
console.warn(
|
|
15769
|
+
"Attempted to update subcategory without a valid categoryId"
|
|
15770
|
+
);
|
|
15406
15771
|
}
|
|
15407
15772
|
let finalTechnologyId = existingProcedure.technology.id;
|
|
15408
15773
|
if (validatedData.technologyId) {
|
|
15409
|
-
const technology = await this.technologyService.getById(
|
|
15410
|
-
|
|
15774
|
+
const technology = await this.technologyService.getById(
|
|
15775
|
+
validatedData.technologyId
|
|
15776
|
+
);
|
|
15777
|
+
if (!technology)
|
|
15778
|
+
throw new Error(`Technology ${validatedData.technologyId} not found`);
|
|
15411
15779
|
updatedProcedureData.technology = technology;
|
|
15412
15780
|
finalTechnologyId = technology.id;
|
|
15413
15781
|
updatedProcedureData.blockingConditions = technology.blockingConditions;
|
|
15782
|
+
updatedProcedureData.contraindications = technology.contraindications || [];
|
|
15783
|
+
updatedProcedureData.contraindicationIds = ((_c = technology.contraindications) == null ? void 0 : _c.map((c) => c.id)) || [];
|
|
15414
15784
|
updatedProcedureData.treatmentBenefits = technology.benefits;
|
|
15785
|
+
updatedProcedureData.treatmentBenefitIds = ((_d = technology.benefits) == null ? void 0 : _d.map((b) => b.id)) || [];
|
|
15415
15786
|
updatedProcedureData.preRequirements = technology.requirements.pre;
|
|
15416
15787
|
updatedProcedureData.postRequirements = technology.requirements.post;
|
|
15417
15788
|
updatedProcedureData.certificationRequirement = technology.certificationRequirement;
|
|
15418
15789
|
updatedProcedureData.documentationTemplates = technology.documentationTemplates || [];
|
|
15419
15790
|
}
|
|
15420
15791
|
if (validatedData.productId && finalTechnologyId) {
|
|
15421
|
-
const product = await this.productService.getById(
|
|
15792
|
+
const product = await this.productService.getById(
|
|
15793
|
+
finalTechnologyId,
|
|
15794
|
+
validatedData.productId
|
|
15795
|
+
);
|
|
15422
15796
|
if (!product)
|
|
15423
15797
|
throw new Error(
|
|
15424
15798
|
`Product ${validatedData.productId} not found for technology ${finalTechnologyId}`
|
|
@@ -15490,28 +15864,32 @@ var ProcedureService = class extends BaseService {
|
|
|
15490
15864
|
const proceduresCollection = collection29(this.db, PROCEDURES_COLLECTION);
|
|
15491
15865
|
let proceduresQuery = query29(proceduresCollection);
|
|
15492
15866
|
if (pagination && pagination > 0) {
|
|
15493
|
-
const { limit:
|
|
15867
|
+
const { limit: limit21, startAfter: startAfter19 } = await import("firebase/firestore");
|
|
15494
15868
|
if (lastDoc) {
|
|
15495
15869
|
proceduresQuery = query29(
|
|
15496
15870
|
proceduresCollection,
|
|
15497
15871
|
orderBy17("name"),
|
|
15498
15872
|
// Use imported orderBy
|
|
15499
|
-
|
|
15500
|
-
|
|
15873
|
+
startAfter19(lastDoc),
|
|
15874
|
+
limit21(pagination)
|
|
15501
15875
|
);
|
|
15502
15876
|
} else {
|
|
15503
|
-
proceduresQuery = query29(
|
|
15877
|
+
proceduresQuery = query29(
|
|
15878
|
+
proceduresCollection,
|
|
15879
|
+
orderBy17("name"),
|
|
15880
|
+
limit21(pagination)
|
|
15881
|
+
);
|
|
15504
15882
|
}
|
|
15505
15883
|
} else {
|
|
15506
15884
|
proceduresQuery = query29(proceduresCollection, orderBy17("name"));
|
|
15507
15885
|
}
|
|
15508
15886
|
const proceduresSnapshot = await getDocs29(proceduresQuery);
|
|
15509
15887
|
const lastVisible = proceduresSnapshot.docs[proceduresSnapshot.docs.length - 1];
|
|
15510
|
-
const procedures = proceduresSnapshot.docs.map((
|
|
15511
|
-
const data =
|
|
15888
|
+
const procedures = proceduresSnapshot.docs.map((doc38) => {
|
|
15889
|
+
const data = doc38.data();
|
|
15512
15890
|
return {
|
|
15513
15891
|
...data,
|
|
15514
|
-
id:
|
|
15892
|
+
id: doc38.id
|
|
15515
15893
|
// Ensure ID is present
|
|
15516
15894
|
};
|
|
15517
15895
|
});
|
|
@@ -15531,7 +15909,7 @@ var ProcedureService = class extends BaseService {
|
|
|
15531
15909
|
*
|
|
15532
15910
|
* @param filters - Various filters to apply
|
|
15533
15911
|
* @param filters.nameSearch - Optional search text for procedure name
|
|
15534
|
-
* @param filters.
|
|
15912
|
+
* @param filters.treatmentBenefitIds - Optional array of treatment benefits to filter by
|
|
15535
15913
|
* @param filters.procedureFamily - Optional procedure family to filter by
|
|
15536
15914
|
* @param filters.procedureCategory - Optional procedure category to filter by
|
|
15537
15915
|
* @param filters.procedureSubcategory - Optional procedure subcategory to filter by
|
|
@@ -15549,7 +15927,9 @@ var ProcedureService = class extends BaseService {
|
|
|
15549
15927
|
*/
|
|
15550
15928
|
async getProceduresByFilters(filters) {
|
|
15551
15929
|
try {
|
|
15552
|
-
console.log(
|
|
15930
|
+
console.log(
|
|
15931
|
+
"[PROCEDURE_SERVICE] Starting procedure filtering with multiple strategies"
|
|
15932
|
+
);
|
|
15553
15933
|
if (filters.location && filters.radiusInKm) {
|
|
15554
15934
|
console.log("[PROCEDURE_SERVICE] Executing geo query:", {
|
|
15555
15935
|
location: filters.location,
|
|
@@ -15557,7 +15937,10 @@ var ProcedureService = class extends BaseService {
|
|
|
15557
15937
|
serviceName: "ProcedureService"
|
|
15558
15938
|
});
|
|
15559
15939
|
if (!filters.location.latitude || !filters.location.longitude) {
|
|
15560
|
-
console.warn(
|
|
15940
|
+
console.warn(
|
|
15941
|
+
"[PROCEDURE_SERVICE] Invalid location data:",
|
|
15942
|
+
filters.location
|
|
15943
|
+
);
|
|
15561
15944
|
filters.location = void 0;
|
|
15562
15945
|
filters.radiusInKm = void 0;
|
|
15563
15946
|
}
|
|
@@ -15577,13 +15960,19 @@ var ProcedureService = class extends BaseService {
|
|
|
15577
15960
|
constraints.push(where29("family", "==", filters.procedureFamily));
|
|
15578
15961
|
}
|
|
15579
15962
|
if (filters.procedureCategory) {
|
|
15580
|
-
constraints.push(
|
|
15963
|
+
constraints.push(
|
|
15964
|
+
where29("category.id", "==", filters.procedureCategory)
|
|
15965
|
+
);
|
|
15581
15966
|
}
|
|
15582
15967
|
if (filters.procedureSubcategory) {
|
|
15583
|
-
constraints.push(
|
|
15968
|
+
constraints.push(
|
|
15969
|
+
where29("subcategory.id", "==", filters.procedureSubcategory)
|
|
15970
|
+
);
|
|
15584
15971
|
}
|
|
15585
15972
|
if (filters.procedureTechnology) {
|
|
15586
|
-
constraints.push(
|
|
15973
|
+
constraints.push(
|
|
15974
|
+
where29("technology.id", "==", filters.procedureTechnology)
|
|
15975
|
+
);
|
|
15587
15976
|
}
|
|
15588
15977
|
if (filters.minPrice !== void 0) {
|
|
15589
15978
|
constraints.push(where29("price", ">=", filters.minPrice));
|
|
@@ -15592,20 +15981,32 @@ var ProcedureService = class extends BaseService {
|
|
|
15592
15981
|
constraints.push(where29("price", "<=", filters.maxPrice));
|
|
15593
15982
|
}
|
|
15594
15983
|
if (filters.minRating !== void 0) {
|
|
15595
|
-
constraints.push(
|
|
15984
|
+
constraints.push(
|
|
15985
|
+
where29("reviewInfo.averageRating", ">=", filters.minRating)
|
|
15986
|
+
);
|
|
15596
15987
|
}
|
|
15597
15988
|
if (filters.maxRating !== void 0) {
|
|
15598
|
-
constraints.push(
|
|
15989
|
+
constraints.push(
|
|
15990
|
+
where29("reviewInfo.averageRating", "<=", filters.maxRating)
|
|
15991
|
+
);
|
|
15599
15992
|
}
|
|
15600
15993
|
if (filters.treatmentBenefits && filters.treatmentBenefits.length > 0) {
|
|
15601
|
-
const
|
|
15602
|
-
constraints.push(
|
|
15994
|
+
const benefitIdsToMatch = filters.treatmentBenefits;
|
|
15995
|
+
constraints.push(
|
|
15996
|
+
where29(
|
|
15997
|
+
"treatmentBenefitIds",
|
|
15998
|
+
"array-contains-any",
|
|
15999
|
+
benefitIdsToMatch
|
|
16000
|
+
)
|
|
16001
|
+
);
|
|
15603
16002
|
}
|
|
15604
16003
|
return constraints;
|
|
15605
16004
|
};
|
|
15606
16005
|
if (filters.nameSearch && filters.nameSearch.trim()) {
|
|
15607
16006
|
try {
|
|
15608
|
-
console.log(
|
|
16007
|
+
console.log(
|
|
16008
|
+
"[PROCEDURE_SERVICE] Strategy 1: Trying nameLower search"
|
|
16009
|
+
);
|
|
15609
16010
|
const searchTerm = filters.nameSearch.trim().toLowerCase();
|
|
15610
16011
|
const constraints = getBaseConstraints();
|
|
15611
16012
|
constraints.push(where29("nameLower", ">=", searchTerm));
|
|
@@ -15621,13 +16022,18 @@ var ProcedureService = class extends BaseService {
|
|
|
15621
16022
|
}
|
|
15622
16023
|
}
|
|
15623
16024
|
constraints.push(limit15(filters.pagination || 10));
|
|
15624
|
-
const q = query29(
|
|
16025
|
+
const q = query29(
|
|
16026
|
+
collection29(this.db, PROCEDURES_COLLECTION),
|
|
16027
|
+
...constraints
|
|
16028
|
+
);
|
|
15625
16029
|
const querySnapshot = await getDocs29(q);
|
|
15626
16030
|
const procedures = querySnapshot.docs.map(
|
|
15627
|
-
(
|
|
16031
|
+
(doc38) => ({ ...doc38.data(), id: doc38.id })
|
|
15628
16032
|
);
|
|
15629
16033
|
const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
15630
|
-
console.log(
|
|
16034
|
+
console.log(
|
|
16035
|
+
`[PROCEDURE_SERVICE] Strategy 1 success: ${procedures.length} procedures`
|
|
16036
|
+
);
|
|
15631
16037
|
if (procedures.length < (filters.pagination || 10)) {
|
|
15632
16038
|
return { procedures, lastDoc: null };
|
|
15633
16039
|
}
|
|
@@ -15638,7 +16044,9 @@ var ProcedureService = class extends BaseService {
|
|
|
15638
16044
|
}
|
|
15639
16045
|
if (filters.nameSearch && filters.nameSearch.trim()) {
|
|
15640
16046
|
try {
|
|
15641
|
-
console.log(
|
|
16047
|
+
console.log(
|
|
16048
|
+
"[PROCEDURE_SERVICE] Strategy 2: Trying name field search"
|
|
16049
|
+
);
|
|
15642
16050
|
const searchTerm = filters.nameSearch.trim().toLowerCase();
|
|
15643
16051
|
const constraints = getBaseConstraints();
|
|
15644
16052
|
constraints.push(where29("name", ">=", searchTerm));
|
|
@@ -15654,13 +16062,18 @@ var ProcedureService = class extends BaseService {
|
|
|
15654
16062
|
}
|
|
15655
16063
|
}
|
|
15656
16064
|
constraints.push(limit15(filters.pagination || 10));
|
|
15657
|
-
const q = query29(
|
|
16065
|
+
const q = query29(
|
|
16066
|
+
collection29(this.db, PROCEDURES_COLLECTION),
|
|
16067
|
+
...constraints
|
|
16068
|
+
);
|
|
15658
16069
|
const querySnapshot = await getDocs29(q);
|
|
15659
16070
|
const procedures = querySnapshot.docs.map(
|
|
15660
|
-
(
|
|
16071
|
+
(doc38) => ({ ...doc38.data(), id: doc38.id })
|
|
15661
16072
|
);
|
|
15662
16073
|
const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
15663
|
-
console.log(
|
|
16074
|
+
console.log(
|
|
16075
|
+
`[PROCEDURE_SERVICE] Strategy 2 success: ${procedures.length} procedures`
|
|
16076
|
+
);
|
|
15664
16077
|
if (procedures.length < (filters.pagination || 10)) {
|
|
15665
16078
|
return { procedures, lastDoc: null };
|
|
15666
16079
|
}
|
|
@@ -15685,14 +16098,19 @@ var ProcedureService = class extends BaseService {
|
|
|
15685
16098
|
}
|
|
15686
16099
|
}
|
|
15687
16100
|
constraints.push(limit15(filters.pagination || 10));
|
|
15688
|
-
const q = query29(
|
|
16101
|
+
const q = query29(
|
|
16102
|
+
collection29(this.db, PROCEDURES_COLLECTION),
|
|
16103
|
+
...constraints
|
|
16104
|
+
);
|
|
15689
16105
|
const querySnapshot = await getDocs29(q);
|
|
15690
16106
|
let procedures = querySnapshot.docs.map(
|
|
15691
|
-
(
|
|
16107
|
+
(doc38) => ({ ...doc38.data(), id: doc38.id })
|
|
15692
16108
|
);
|
|
15693
16109
|
procedures = this.applyInMemoryFilters(procedures, filters);
|
|
15694
16110
|
const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
15695
|
-
console.log(
|
|
16111
|
+
console.log(
|
|
16112
|
+
`[PROCEDURE_SERVICE] Strategy 3 success: ${procedures.length} procedures`
|
|
16113
|
+
);
|
|
15696
16114
|
if (procedures.length < (filters.pagination || 10)) {
|
|
15697
16115
|
return { procedures, lastDoc: null };
|
|
15698
16116
|
}
|
|
@@ -15707,14 +16125,19 @@ var ProcedureService = class extends BaseService {
|
|
|
15707
16125
|
orderBy17("createdAt", "desc"),
|
|
15708
16126
|
limit15(filters.pagination || 10)
|
|
15709
16127
|
];
|
|
15710
|
-
const q = query29(
|
|
16128
|
+
const q = query29(
|
|
16129
|
+
collection29(this.db, PROCEDURES_COLLECTION),
|
|
16130
|
+
...constraints
|
|
16131
|
+
);
|
|
15711
16132
|
const querySnapshot = await getDocs29(q);
|
|
15712
16133
|
let procedures = querySnapshot.docs.map(
|
|
15713
|
-
(
|
|
16134
|
+
(doc38) => ({ ...doc38.data(), id: doc38.id })
|
|
15714
16135
|
);
|
|
15715
16136
|
procedures = this.applyInMemoryFilters(procedures, filters);
|
|
15716
16137
|
const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
15717
|
-
console.log(
|
|
16138
|
+
console.log(
|
|
16139
|
+
`[PROCEDURE_SERVICE] Strategy 4 success: ${procedures.length} procedures`
|
|
16140
|
+
);
|
|
15718
16141
|
if (procedures.length < (filters.pagination || 10)) {
|
|
15719
16142
|
return { procedures, lastDoc: null };
|
|
15720
16143
|
}
|
|
@@ -15722,7 +16145,9 @@ var ProcedureService = class extends BaseService {
|
|
|
15722
16145
|
} catch (error) {
|
|
15723
16146
|
console.log("[PROCEDURE_SERVICE] Strategy 4 failed:", error);
|
|
15724
16147
|
}
|
|
15725
|
-
console.log(
|
|
16148
|
+
console.log(
|
|
16149
|
+
"[PROCEDURE_SERVICE] All strategies failed, returning empty result"
|
|
16150
|
+
);
|
|
15726
16151
|
return { procedures: [], lastDoc: null };
|
|
15727
16152
|
} catch (error) {
|
|
15728
16153
|
console.error("[PROCEDURE_SERVICE] Error filtering procedures:", error);
|
|
@@ -15742,13 +16167,17 @@ var ProcedureService = class extends BaseService {
|
|
|
15742
16167
|
const nameLower = procedure.nameLower || "";
|
|
15743
16168
|
return name.includes(searchTerm) || nameLower.includes(searchTerm);
|
|
15744
16169
|
});
|
|
15745
|
-
console.log(
|
|
16170
|
+
console.log(
|
|
16171
|
+
`[PROCEDURE_SERVICE] Applied name filter, results: ${filteredProcedures.length}`
|
|
16172
|
+
);
|
|
15746
16173
|
}
|
|
15747
16174
|
if (filters.minPrice !== void 0 || filters.maxPrice !== void 0) {
|
|
15748
16175
|
filteredProcedures = filteredProcedures.filter((procedure) => {
|
|
15749
16176
|
const price = procedure.price || 0;
|
|
15750
|
-
if (filters.minPrice !== void 0 && price < filters.minPrice)
|
|
15751
|
-
|
|
16177
|
+
if (filters.minPrice !== void 0 && price < filters.minPrice)
|
|
16178
|
+
return false;
|
|
16179
|
+
if (filters.maxPrice !== void 0 && price > filters.maxPrice)
|
|
16180
|
+
return false;
|
|
15752
16181
|
return true;
|
|
15753
16182
|
});
|
|
15754
16183
|
console.log(
|
|
@@ -15759,8 +16188,10 @@ var ProcedureService = class extends BaseService {
|
|
|
15759
16188
|
filteredProcedures = filteredProcedures.filter((procedure) => {
|
|
15760
16189
|
var _a;
|
|
15761
16190
|
const rating = ((_a = procedure.reviewInfo) == null ? void 0 : _a.averageRating) || 0;
|
|
15762
|
-
if (filters.minRating !== void 0 && rating < filters.minRating)
|
|
15763
|
-
|
|
16191
|
+
if (filters.minRating !== void 0 && rating < filters.minRating)
|
|
16192
|
+
return false;
|
|
16193
|
+
if (filters.maxRating !== void 0 && rating > filters.maxRating)
|
|
16194
|
+
return false;
|
|
15764
16195
|
return true;
|
|
15765
16196
|
});
|
|
15766
16197
|
console.log(
|
|
@@ -15768,10 +16199,12 @@ var ProcedureService = class extends BaseService {
|
|
|
15768
16199
|
);
|
|
15769
16200
|
}
|
|
15770
16201
|
if (filters.treatmentBenefits && filters.treatmentBenefits.length > 0) {
|
|
15771
|
-
const
|
|
16202
|
+
const benefitIdsToMatch = filters.treatmentBenefits;
|
|
15772
16203
|
filteredProcedures = filteredProcedures.filter((procedure) => {
|
|
15773
|
-
const
|
|
15774
|
-
return
|
|
16204
|
+
const procedureBenefitIds = procedure.treatmentBenefitIds || [];
|
|
16205
|
+
return benefitIdsToMatch.some(
|
|
16206
|
+
(benefitId) => procedureBenefitIds.includes(benefitId)
|
|
16207
|
+
);
|
|
15775
16208
|
});
|
|
15776
16209
|
console.log(
|
|
15777
16210
|
`[PROCEDURE_SERVICE] Applied benefits filter, results: ${filteredProcedures.length}`
|
|
@@ -15834,8 +16267,12 @@ var ProcedureService = class extends BaseService {
|
|
|
15834
16267
|
procedure.distance = distance;
|
|
15835
16268
|
return distance <= radiusInKm;
|
|
15836
16269
|
});
|
|
15837
|
-
console.log(
|
|
15838
|
-
|
|
16270
|
+
console.log(
|
|
16271
|
+
`[PROCEDURE_SERVICE] Applied geo filter, results: ${filteredProcedures.length}`
|
|
16272
|
+
);
|
|
16273
|
+
filteredProcedures.sort(
|
|
16274
|
+
(a, b) => (a.distance || 0) - (b.distance || 0)
|
|
16275
|
+
);
|
|
15839
16276
|
}
|
|
15840
16277
|
return filteredProcedures;
|
|
15841
16278
|
}
|
|
@@ -15847,19 +16284,30 @@ var ProcedureService = class extends BaseService {
|
|
|
15847
16284
|
if (!location || !radiusInKm) {
|
|
15848
16285
|
return Promise.resolve({ procedures: [], lastDoc: null });
|
|
15849
16286
|
}
|
|
15850
|
-
const bounds = geohashQueryBounds5(
|
|
16287
|
+
const bounds = geohashQueryBounds5(
|
|
16288
|
+
[location.latitude, location.longitude],
|
|
16289
|
+
radiusInKm * 1e3
|
|
16290
|
+
);
|
|
15851
16291
|
const fetches = bounds.map((b) => {
|
|
15852
16292
|
const constraints = [
|
|
15853
16293
|
where29("clinicInfo.location.geohash", ">=", b[0]),
|
|
15854
16294
|
where29("clinicInfo.location.geohash", "<=", b[1]),
|
|
15855
|
-
where29(
|
|
16295
|
+
where29(
|
|
16296
|
+
"isActive",
|
|
16297
|
+
"==",
|
|
16298
|
+
filters.isActive !== void 0 ? filters.isActive : true
|
|
16299
|
+
)
|
|
15856
16300
|
];
|
|
15857
|
-
return getDocs29(
|
|
16301
|
+
return getDocs29(
|
|
16302
|
+
query29(collection29(this.db, PROCEDURES_COLLECTION), ...constraints)
|
|
16303
|
+
);
|
|
15858
16304
|
});
|
|
15859
16305
|
return Promise.all(fetches).then((snaps) => {
|
|
15860
16306
|
const collected = [];
|
|
15861
16307
|
snaps.forEach((snap) => {
|
|
15862
|
-
snap.docs.forEach(
|
|
16308
|
+
snap.docs.forEach(
|
|
16309
|
+
(d) => collected.push({ ...d.data(), id: d.id })
|
|
16310
|
+
);
|
|
15863
16311
|
});
|
|
15864
16312
|
const uniqueMap = /* @__PURE__ */ new Map();
|
|
15865
16313
|
for (const p of collected) {
|
|
@@ -15870,7 +16318,9 @@ var ProcedureService = class extends BaseService {
|
|
|
15870
16318
|
const pageSize = filters.pagination || 10;
|
|
15871
16319
|
let startIndex = 0;
|
|
15872
16320
|
if (filters.lastDoc && typeof filters.lastDoc === "object" && filters.lastDoc.id) {
|
|
15873
|
-
const idx = procedures.findIndex(
|
|
16321
|
+
const idx = procedures.findIndex(
|
|
16322
|
+
(p) => p.id === filters.lastDoc.id
|
|
16323
|
+
);
|
|
15874
16324
|
if (idx >= 0) startIndex = idx + 1;
|
|
15875
16325
|
}
|
|
15876
16326
|
const page = procedures.slice(startIndex, startIndex + pageSize);
|
|
@@ -15895,7 +16345,7 @@ var ProcedureService = class extends BaseService {
|
|
|
15895
16345
|
* @returns The created procedure
|
|
15896
16346
|
*/
|
|
15897
16347
|
async createConsultationProcedure(data) {
|
|
15898
|
-
var _a;
|
|
16348
|
+
var _a, _b, _c;
|
|
15899
16349
|
const procedureId = this.generateId();
|
|
15900
16350
|
const [category, subcategory, technology] = await Promise.all([
|
|
15901
16351
|
this.categoryService.getById(data.categoryId),
|
|
@@ -15911,7 +16361,11 @@ var ProcedureService = class extends BaseService {
|
|
|
15911
16361
|
throw new Error(`Clinic with ID ${data.clinicBranchId} not found`);
|
|
15912
16362
|
}
|
|
15913
16363
|
const clinic = clinicSnapshot.data();
|
|
15914
|
-
const practitionerRef = doc30(
|
|
16364
|
+
const practitionerRef = doc30(
|
|
16365
|
+
this.db,
|
|
16366
|
+
PRACTITIONERS_COLLECTION,
|
|
16367
|
+
data.practitionerId
|
|
16368
|
+
);
|
|
15915
16369
|
const practitionerSnapshot = await getDoc32(practitionerRef);
|
|
15916
16370
|
if (!practitionerSnapshot.exists()) {
|
|
15917
16371
|
throw new Error(`Practitioner with ID ${data.practitionerId} not found`);
|
|
@@ -15919,8 +16373,16 @@ var ProcedureService = class extends BaseService {
|
|
|
15919
16373
|
const practitioner = practitionerSnapshot.data();
|
|
15920
16374
|
let processedPhotos = [];
|
|
15921
16375
|
if (data.photos && data.photos.length > 0) {
|
|
15922
|
-
processedPhotos = await this.processMediaArray(
|
|
16376
|
+
processedPhotos = await this.processMediaArray(
|
|
16377
|
+
data.photos,
|
|
16378
|
+
procedureId,
|
|
16379
|
+
"procedure-photos"
|
|
16380
|
+
);
|
|
15923
16381
|
}
|
|
16382
|
+
const transformedProductsMetadata = await this.transformProductsMetadata(
|
|
16383
|
+
data.productsMetadata,
|
|
16384
|
+
data.technologyId
|
|
16385
|
+
);
|
|
15924
16386
|
const clinicInfo = {
|
|
15925
16387
|
id: clinicSnapshot.id,
|
|
15926
16388
|
name: clinic.name,
|
|
@@ -15945,6 +16407,8 @@ var ProcedureService = class extends BaseService {
|
|
|
15945
16407
|
brandName: "Consultation",
|
|
15946
16408
|
technologyId: data.technologyId,
|
|
15947
16409
|
technologyName: technology.name,
|
|
16410
|
+
categoryId: technology.categoryId,
|
|
16411
|
+
subcategoryId: technology.subcategoryId,
|
|
15948
16412
|
isActive: true,
|
|
15949
16413
|
createdAt: /* @__PURE__ */ new Date(),
|
|
15950
16414
|
updatedAt: /* @__PURE__ */ new Date()
|
|
@@ -15959,9 +16423,12 @@ var ProcedureService = class extends BaseService {
|
|
|
15959
16423
|
technology,
|
|
15960
16424
|
product: consultationProduct,
|
|
15961
16425
|
// Use placeholder product
|
|
16426
|
+
productsMetadata: transformedProductsMetadata,
|
|
15962
16427
|
blockingConditions: technology.blockingConditions,
|
|
15963
16428
|
contraindications: technology.contraindications || [],
|
|
16429
|
+
contraindicationIds: ((_b = technology.contraindications) == null ? void 0 : _b.map((c) => c.id)) || [],
|
|
15964
16430
|
treatmentBenefits: technology.benefits,
|
|
16431
|
+
treatmentBenefitIds: ((_c = technology.benefits) == null ? void 0 : _c.map((b) => b.id)) || [],
|
|
15965
16432
|
preRequirements: technology.requirements.pre,
|
|
15966
16433
|
postRequirements: technology.requirements.post,
|
|
15967
16434
|
certificationRequirement: technology.certificationRequirement,
|
|
@@ -15997,11 +16464,11 @@ var ProcedureService = class extends BaseService {
|
|
|
15997
16464
|
async getProceduresForMap() {
|
|
15998
16465
|
const proceduresRef = collection29(this.db, PROCEDURES_COLLECTION);
|
|
15999
16466
|
const snapshot = await getDocs29(proceduresRef);
|
|
16000
|
-
const proceduresForMap = snapshot.docs.map((
|
|
16467
|
+
const proceduresForMap = snapshot.docs.map((doc38) => {
|
|
16001
16468
|
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
16002
|
-
const data =
|
|
16469
|
+
const data = doc38.data();
|
|
16003
16470
|
return {
|
|
16004
|
-
id:
|
|
16471
|
+
id: doc38.id,
|
|
16005
16472
|
name: data.name,
|
|
16006
16473
|
clinicId: (_a = data.clinicInfo) == null ? void 0 : _a.id,
|
|
16007
16474
|
clinicName: (_b = data.clinicInfo) == null ? void 0 : _b.name,
|
|
@@ -16026,7 +16493,7 @@ import {
|
|
|
16026
16493
|
deleteDoc as deleteDoc19,
|
|
16027
16494
|
serverTimestamp as serverTimestamp26
|
|
16028
16495
|
} from "firebase/firestore";
|
|
16029
|
-
import { z as
|
|
16496
|
+
import { z as z26 } from "zod";
|
|
16030
16497
|
var ReviewService = class extends BaseService {
|
|
16031
16498
|
constructor(db, auth, app) {
|
|
16032
16499
|
super(db, auth, app);
|
|
@@ -16113,7 +16580,7 @@ var ReviewService = class extends BaseService {
|
|
|
16113
16580
|
});
|
|
16114
16581
|
return review;
|
|
16115
16582
|
} catch (error) {
|
|
16116
|
-
if (error instanceof
|
|
16583
|
+
if (error instanceof z26.ZodError) {
|
|
16117
16584
|
throw new Error(`Invalid review data: ${error.message}`);
|
|
16118
16585
|
}
|
|
16119
16586
|
throw error;
|
|
@@ -16143,7 +16610,7 @@ var ReviewService = class extends BaseService {
|
|
|
16143
16610
|
where30("patientId", "==", patientId)
|
|
16144
16611
|
);
|
|
16145
16612
|
const snapshot = await getDocs30(q);
|
|
16146
|
-
return snapshot.docs.map((
|
|
16613
|
+
return snapshot.docs.map((doc38) => doc38.data());
|
|
16147
16614
|
}
|
|
16148
16615
|
/**
|
|
16149
16616
|
* Gets all reviews for a specific clinic
|
|
@@ -16156,7 +16623,7 @@ var ReviewService = class extends BaseService {
|
|
|
16156
16623
|
where30("clinicReview.clinicId", "==", clinicId)
|
|
16157
16624
|
);
|
|
16158
16625
|
const snapshot = await getDocs30(q);
|
|
16159
|
-
return snapshot.docs.map((
|
|
16626
|
+
return snapshot.docs.map((doc38) => doc38.data());
|
|
16160
16627
|
}
|
|
16161
16628
|
/**
|
|
16162
16629
|
* Gets all reviews for a specific practitioner
|
|
@@ -16169,7 +16636,7 @@ var ReviewService = class extends BaseService {
|
|
|
16169
16636
|
where30("practitionerReview.practitionerId", "==", practitionerId)
|
|
16170
16637
|
);
|
|
16171
16638
|
const snapshot = await getDocs30(q);
|
|
16172
|
-
return snapshot.docs.map((
|
|
16639
|
+
return snapshot.docs.map((doc38) => doc38.data());
|
|
16173
16640
|
}
|
|
16174
16641
|
/**
|
|
16175
16642
|
* Gets all reviews for a specific procedure
|
|
@@ -16182,7 +16649,7 @@ var ReviewService = class extends BaseService {
|
|
|
16182
16649
|
where30("procedureReview.procedureId", "==", procedureId)
|
|
16183
16650
|
);
|
|
16184
16651
|
const snapshot = await getDocs30(q);
|
|
16185
|
-
return snapshot.docs.map((
|
|
16652
|
+
return snapshot.docs.map((doc38) => doc38.data());
|
|
16186
16653
|
}
|
|
16187
16654
|
/**
|
|
16188
16655
|
* Gets all reviews for a specific appointment
|
|
@@ -16288,7 +16755,11 @@ import {
|
|
|
16288
16755
|
getDocs as getDocs31,
|
|
16289
16756
|
query as query31,
|
|
16290
16757
|
updateDoc as updateDoc30,
|
|
16291
|
-
where as where31
|
|
16758
|
+
where as where31,
|
|
16759
|
+
limit as limit16,
|
|
16760
|
+
orderBy as orderBy18,
|
|
16761
|
+
startAfter as startAfter14,
|
|
16762
|
+
getCountFromServer as getCountFromServer2
|
|
16292
16763
|
} from "firebase/firestore";
|
|
16293
16764
|
|
|
16294
16765
|
// src/backoffice/types/brand.types.ts
|
|
@@ -16309,6 +16780,7 @@ var BrandService = class extends BaseService {
|
|
|
16309
16780
|
const now = /* @__PURE__ */ new Date();
|
|
16310
16781
|
const newBrand = {
|
|
16311
16782
|
...brand,
|
|
16783
|
+
name_lowercase: brand.name.toLowerCase(),
|
|
16312
16784
|
createdAt: now,
|
|
16313
16785
|
updatedAt: now,
|
|
16314
16786
|
isActive: true
|
|
@@ -16317,15 +16789,69 @@ var BrandService = class extends BaseService {
|
|
|
16317
16789
|
return { id: docRef.id, ...newBrand };
|
|
16318
16790
|
}
|
|
16319
16791
|
/**
|
|
16320
|
-
* Gets
|
|
16792
|
+
* Gets a paginated list of active brands, optionally filtered by name.
|
|
16793
|
+
* @param rowsPerPage - The number of brands to fetch.
|
|
16794
|
+
* @param searchTerm - An optional string to filter brand names by (starts-with search).
|
|
16795
|
+
* @param lastVisible - An optional document snapshot to use as a cursor for pagination.
|
|
16321
16796
|
*/
|
|
16322
|
-
async getAll() {
|
|
16323
|
-
const
|
|
16797
|
+
async getAll(rowsPerPage, searchTerm, lastVisible) {
|
|
16798
|
+
const constraints = [
|
|
16799
|
+
where31("isActive", "==", true),
|
|
16800
|
+
orderBy18("name_lowercase")
|
|
16801
|
+
];
|
|
16802
|
+
if (searchTerm) {
|
|
16803
|
+
const lowercasedSearchTerm = searchTerm.toLowerCase();
|
|
16804
|
+
constraints.push(where31("name_lowercase", ">=", lowercasedSearchTerm));
|
|
16805
|
+
constraints.push(
|
|
16806
|
+
where31("name_lowercase", "<=", lowercasedSearchTerm + "\uF8FF")
|
|
16807
|
+
);
|
|
16808
|
+
}
|
|
16809
|
+
if (lastVisible) {
|
|
16810
|
+
constraints.push(startAfter14(lastVisible));
|
|
16811
|
+
}
|
|
16812
|
+
constraints.push(limit16(rowsPerPage));
|
|
16813
|
+
const q = query31(this.getBrandsRef(), ...constraints);
|
|
16814
|
+
const snapshot = await getDocs31(q);
|
|
16815
|
+
const brands = snapshot.docs.map(
|
|
16816
|
+
(doc38) => ({
|
|
16817
|
+
id: doc38.id,
|
|
16818
|
+
...doc38.data()
|
|
16819
|
+
})
|
|
16820
|
+
);
|
|
16821
|
+
const newLastVisible = snapshot.docs[snapshot.docs.length - 1];
|
|
16822
|
+
return { brands, lastVisible: newLastVisible };
|
|
16823
|
+
}
|
|
16824
|
+
/**
|
|
16825
|
+
* Gets the total count of active brands, optionally filtered by name.
|
|
16826
|
+
* @param searchTerm - An optional string to filter brand names by (starts-with search).
|
|
16827
|
+
*/
|
|
16828
|
+
async getBrandsCount(searchTerm) {
|
|
16829
|
+
const constraints = [where31("isActive", "==", true)];
|
|
16830
|
+
if (searchTerm) {
|
|
16831
|
+
const lowercasedSearchTerm = searchTerm.toLowerCase();
|
|
16832
|
+
constraints.push(where31("name_lowercase", ">=", lowercasedSearchTerm));
|
|
16833
|
+
constraints.push(
|
|
16834
|
+
where31("name_lowercase", "<=", lowercasedSearchTerm + "\uF8FF")
|
|
16835
|
+
);
|
|
16836
|
+
}
|
|
16837
|
+
const q = query31(this.getBrandsRef(), ...constraints);
|
|
16838
|
+
const snapshot = await getCountFromServer2(q);
|
|
16839
|
+
return snapshot.data().count;
|
|
16840
|
+
}
|
|
16841
|
+
/**
|
|
16842
|
+
* Gets all active brands for filter dropdowns (not paginated).
|
|
16843
|
+
*/
|
|
16844
|
+
async getAllForFilter() {
|
|
16845
|
+
const q = query31(
|
|
16846
|
+
this.getBrandsRef(),
|
|
16847
|
+
where31("isActive", "==", true),
|
|
16848
|
+
orderBy18("name")
|
|
16849
|
+
);
|
|
16324
16850
|
const snapshot = await getDocs31(q);
|
|
16325
16851
|
return snapshot.docs.map(
|
|
16326
|
-
(
|
|
16327
|
-
id:
|
|
16328
|
-
...
|
|
16852
|
+
(doc38) => ({
|
|
16853
|
+
id: doc38.id,
|
|
16854
|
+
...doc38.data()
|
|
16329
16855
|
})
|
|
16330
16856
|
);
|
|
16331
16857
|
}
|
|
@@ -16337,6 +16863,9 @@ var BrandService = class extends BaseService {
|
|
|
16337
16863
|
...brand,
|
|
16338
16864
|
updatedAt: /* @__PURE__ */ new Date()
|
|
16339
16865
|
};
|
|
16866
|
+
if (brand.name) {
|
|
16867
|
+
updateData.name_lowercase = brand.name.toLowerCase();
|
|
16868
|
+
}
|
|
16340
16869
|
const docRef = doc32(this.getBrandsRef(), brandId);
|
|
16341
16870
|
await updateDoc30(docRef, updateData);
|
|
16342
16871
|
return this.getById(brandId);
|
|
@@ -16368,9 +16897,13 @@ import {
|
|
|
16368
16897
|
addDoc as addDoc4,
|
|
16369
16898
|
collection as collection32,
|
|
16370
16899
|
doc as doc33,
|
|
16900
|
+
getCountFromServer as getCountFromServer3,
|
|
16371
16901
|
getDoc as getDoc35,
|
|
16372
16902
|
getDocs as getDocs32,
|
|
16903
|
+
limit as limit17,
|
|
16904
|
+
orderBy as orderBy19,
|
|
16373
16905
|
query as query32,
|
|
16906
|
+
startAfter as startAfter15,
|
|
16374
16907
|
updateDoc as updateDoc31,
|
|
16375
16908
|
where as where32
|
|
16376
16909
|
} from "firebase/firestore";
|
|
@@ -16403,37 +16936,87 @@ var CategoryService = class extends BaseService {
|
|
|
16403
16936
|
return { id: docRef.id, ...newCategory };
|
|
16404
16937
|
}
|
|
16405
16938
|
/**
|
|
16406
|
-
*
|
|
16407
|
-
* @
|
|
16939
|
+
* Returns counts of categories for each family.
|
|
16940
|
+
* @param active - Whether to count active or inactive categories.
|
|
16941
|
+
* @returns A record mapping family to category count.
|
|
16408
16942
|
*/
|
|
16409
|
-
async
|
|
16943
|
+
async getCategoryCounts(active = true) {
|
|
16944
|
+
const counts = {};
|
|
16945
|
+
const families = Object.values(ProcedureFamily);
|
|
16946
|
+
for (const family of families) {
|
|
16947
|
+
const q = query32(
|
|
16948
|
+
this.categoriesRef,
|
|
16949
|
+
where32("family", "==", family),
|
|
16950
|
+
where32("isActive", "==", active)
|
|
16951
|
+
);
|
|
16952
|
+
const snapshot = await getCountFromServer3(q);
|
|
16953
|
+
counts[family] = snapshot.data().count;
|
|
16954
|
+
}
|
|
16955
|
+
return counts;
|
|
16956
|
+
}
|
|
16957
|
+
/**
|
|
16958
|
+
* Vraća sve kategorije za potrebe filtera (bez paginacije)
|
|
16959
|
+
* @returns Lista svih aktivnih kategorija
|
|
16960
|
+
*/
|
|
16961
|
+
async getAllForFilter() {
|
|
16410
16962
|
const q = query32(this.categoriesRef, where32("isActive", "==", true));
|
|
16411
16963
|
const snapshot = await getDocs32(q);
|
|
16412
16964
|
return snapshot.docs.map(
|
|
16413
|
-
(
|
|
16414
|
-
id:
|
|
16415
|
-
...
|
|
16965
|
+
(doc38) => ({
|
|
16966
|
+
id: doc38.id,
|
|
16967
|
+
...doc38.data()
|
|
16968
|
+
})
|
|
16969
|
+
);
|
|
16970
|
+
}
|
|
16971
|
+
/**
|
|
16972
|
+
* Vraća sve kategorije sa paginacijom
|
|
16973
|
+
* @param options - Pagination and filter options
|
|
16974
|
+
* @returns Lista kategorija i poslednji vidljiv dokument
|
|
16975
|
+
*/
|
|
16976
|
+
async getAll(options = {}) {
|
|
16977
|
+
const { active = true, limit: queryLimit = 10, lastVisible } = options;
|
|
16978
|
+
const constraints = [
|
|
16979
|
+
where32("isActive", "==", active),
|
|
16980
|
+
orderBy19("name"),
|
|
16981
|
+
queryLimit ? limit17(queryLimit) : void 0,
|
|
16982
|
+
lastVisible ? startAfter15(lastVisible) : void 0
|
|
16983
|
+
].filter((c) => !!c);
|
|
16984
|
+
const q = query32(this.categoriesRef, ...constraints);
|
|
16985
|
+
const snapshot = await getDocs32(q);
|
|
16986
|
+
const categories = snapshot.docs.map(
|
|
16987
|
+
(doc38) => ({
|
|
16988
|
+
id: doc38.id,
|
|
16989
|
+
...doc38.data()
|
|
16416
16990
|
})
|
|
16417
16991
|
);
|
|
16992
|
+
const newLastVisible = snapshot.docs[snapshot.docs.length - 1];
|
|
16993
|
+
return { categories, lastVisible: newLastVisible };
|
|
16418
16994
|
}
|
|
16419
16995
|
/**
|
|
16420
|
-
* Vraća sve aktivne kategorije za određenu familiju procedura
|
|
16996
|
+
* Vraća sve aktivne kategorije za određenu familiju procedura sa paginacijom
|
|
16421
16997
|
* @param family - Familija procedura (aesthetics/surgery)
|
|
16998
|
+
* @param options - Pagination options
|
|
16422
16999
|
* @returns Lista kategorija koje pripadaju traženoj familiji
|
|
16423
17000
|
*/
|
|
16424
|
-
async getAllByFamily(family) {
|
|
16425
|
-
const
|
|
16426
|
-
|
|
17001
|
+
async getAllByFamily(family, options = {}) {
|
|
17002
|
+
const { active = true, limit: queryLimit = 10, lastVisible } = options;
|
|
17003
|
+
const constraints = [
|
|
16427
17004
|
where32("family", "==", family),
|
|
16428
|
-
where32("isActive", "==",
|
|
16429
|
-
|
|
17005
|
+
where32("isActive", "==", active),
|
|
17006
|
+
orderBy19("name"),
|
|
17007
|
+
queryLimit ? limit17(queryLimit) : void 0,
|
|
17008
|
+
lastVisible ? startAfter15(lastVisible) : void 0
|
|
17009
|
+
].filter((c) => !!c);
|
|
17010
|
+
const q = query32(this.categoriesRef, ...constraints);
|
|
16430
17011
|
const snapshot = await getDocs32(q);
|
|
16431
|
-
|
|
16432
|
-
(
|
|
16433
|
-
id:
|
|
16434
|
-
...
|
|
17012
|
+
const categories = snapshot.docs.map(
|
|
17013
|
+
(doc38) => ({
|
|
17014
|
+
id: doc38.id,
|
|
17015
|
+
...doc38.data()
|
|
16435
17016
|
})
|
|
16436
17017
|
);
|
|
17018
|
+
const newLastVisible = snapshot.docs[snapshot.docs.length - 1];
|
|
17019
|
+
return { categories, lastVisible: newLastVisible };
|
|
16437
17020
|
}
|
|
16438
17021
|
/**
|
|
16439
17022
|
* Ažurira postojeću kategoriju
|
|
@@ -16457,6 +17040,13 @@ var CategoryService = class extends BaseService {
|
|
|
16457
17040
|
async delete(id) {
|
|
16458
17041
|
await this.update(id, { isActive: false });
|
|
16459
17042
|
}
|
|
17043
|
+
/**
|
|
17044
|
+
* Reactivates a category by setting its isActive flag to true.
|
|
17045
|
+
* @param id - The ID of the category to reactivate.
|
|
17046
|
+
*/
|
|
17047
|
+
async reactivate(id) {
|
|
17048
|
+
await this.update(id, { isActive: true });
|
|
17049
|
+
}
|
|
16460
17050
|
/**
|
|
16461
17051
|
* Vraća kategoriju po ID-u
|
|
16462
17052
|
* @param id - ID tražene kategorije
|
|
@@ -16477,10 +17067,17 @@ var CategoryService = class extends BaseService {
|
|
|
16477
17067
|
import {
|
|
16478
17068
|
addDoc as addDoc5,
|
|
16479
17069
|
collection as collection33,
|
|
17070
|
+
collectionGroup as collectionGroup2,
|
|
17071
|
+
deleteDoc as deleteDoc20,
|
|
16480
17072
|
doc as doc34,
|
|
17073
|
+
getCountFromServer as getCountFromServer4,
|
|
16481
17074
|
getDoc as getDoc36,
|
|
16482
17075
|
getDocs as getDocs33,
|
|
17076
|
+
limit as limit18,
|
|
17077
|
+
orderBy as orderBy20,
|
|
16483
17078
|
query as query33,
|
|
17079
|
+
setDoc as setDoc28,
|
|
17080
|
+
startAfter as startAfter16,
|
|
16484
17081
|
updateDoc as updateDoc32,
|
|
16485
17082
|
where as where33
|
|
16486
17083
|
} from "firebase/firestore";
|
|
@@ -16524,20 +17121,110 @@ var SubcategoryService = class extends BaseService {
|
|
|
16524
17121
|
return { id: docRef.id, ...newSubcategory };
|
|
16525
17122
|
}
|
|
16526
17123
|
/**
|
|
16527
|
-
*
|
|
17124
|
+
* Returns counts of subcategories for all categories.
|
|
17125
|
+
* @param active - Whether to count active or inactive subcategories.
|
|
17126
|
+
* @returns A record mapping category ID to subcategory count.
|
|
17127
|
+
*/
|
|
17128
|
+
async getSubcategoryCounts(active = true) {
|
|
17129
|
+
const categoriesRef = collection33(this.db, CATEGORIES_COLLECTION);
|
|
17130
|
+
const categoriesSnapshot = await getDocs33(categoriesRef);
|
|
17131
|
+
const counts = {};
|
|
17132
|
+
for (const categoryDoc of categoriesSnapshot.docs) {
|
|
17133
|
+
const categoryId = categoryDoc.id;
|
|
17134
|
+
const subcategoriesRef = this.getSubcategoriesRef(categoryId);
|
|
17135
|
+
const q = query33(subcategoriesRef, where33("isActive", "==", active));
|
|
17136
|
+
const snapshot = await getCountFromServer4(q);
|
|
17137
|
+
counts[categoryId] = snapshot.data().count;
|
|
17138
|
+
}
|
|
17139
|
+
return counts;
|
|
17140
|
+
}
|
|
17141
|
+
/**
|
|
17142
|
+
* Vraća sve aktivne podkategorije za određenu kategoriju sa paginacijom
|
|
16528
17143
|
* @param categoryId - ID kategorije čije podkategorije tražimo
|
|
16529
|
-
* @
|
|
17144
|
+
* @param options - Pagination options
|
|
17145
|
+
* @returns Lista aktivnih podkategorija i poslednji vidljiv dokument
|
|
16530
17146
|
*/
|
|
16531
|
-
async getAllByCategoryId(categoryId) {
|
|
17147
|
+
async getAllByCategoryId(categoryId, options = {}) {
|
|
17148
|
+
const { active = true, limit: queryLimit = 10, lastVisible } = options;
|
|
17149
|
+
const constraints = [
|
|
17150
|
+
where33("isActive", "==", active),
|
|
17151
|
+
orderBy20("name"),
|
|
17152
|
+
queryLimit ? limit18(queryLimit) : void 0,
|
|
17153
|
+
lastVisible ? startAfter16(lastVisible) : void 0
|
|
17154
|
+
].filter((c) => !!c);
|
|
17155
|
+
const q = query33(this.getSubcategoriesRef(categoryId), ...constraints);
|
|
17156
|
+
const querySnapshot = await getDocs33(q);
|
|
17157
|
+
const subcategories = querySnapshot.docs.map(
|
|
17158
|
+
(doc38) => ({
|
|
17159
|
+
id: doc38.id,
|
|
17160
|
+
...doc38.data()
|
|
17161
|
+
})
|
|
17162
|
+
);
|
|
17163
|
+
const newLastVisible = querySnapshot.docs[querySnapshot.docs.length - 1];
|
|
17164
|
+
return { subcategories, lastVisible: newLastVisible };
|
|
17165
|
+
}
|
|
17166
|
+
/**
|
|
17167
|
+
* Vraća sve podkategorije sa paginacijom koristeći collection group query.
|
|
17168
|
+
* NOTE: This query requires a composite index in Firestore on the 'subcategories' collection group.
|
|
17169
|
+
* The index should be on 'isActive' (ascending) and 'name' (ascending).
|
|
17170
|
+
* Firestore will provide a link to create this index in the console error if it's missing.
|
|
17171
|
+
* @param options - Pagination options
|
|
17172
|
+
* @returns Lista podkategorija i poslednji vidljiv dokument
|
|
17173
|
+
*/
|
|
17174
|
+
async getAll(options = {}) {
|
|
17175
|
+
const { active = true, limit: queryLimit = 10, lastVisible } = options;
|
|
17176
|
+
const constraints = [
|
|
17177
|
+
where33("isActive", "==", active),
|
|
17178
|
+
orderBy20("name"),
|
|
17179
|
+
queryLimit ? limit18(queryLimit) : void 0,
|
|
17180
|
+
lastVisible ? startAfter16(lastVisible) : void 0
|
|
17181
|
+
].filter((c) => !!c);
|
|
17182
|
+
const q = query33(
|
|
17183
|
+
collectionGroup2(this.db, SUBCATEGORIES_COLLECTION),
|
|
17184
|
+
...constraints
|
|
17185
|
+
);
|
|
17186
|
+
const querySnapshot = await getDocs33(q);
|
|
17187
|
+
const subcategories = querySnapshot.docs.map(
|
|
17188
|
+
(doc38) => ({
|
|
17189
|
+
id: doc38.id,
|
|
17190
|
+
...doc38.data()
|
|
17191
|
+
})
|
|
17192
|
+
);
|
|
17193
|
+
const newLastVisible = querySnapshot.docs[querySnapshot.docs.length - 1];
|
|
17194
|
+
return { subcategories, lastVisible: newLastVisible };
|
|
17195
|
+
}
|
|
17196
|
+
/**
|
|
17197
|
+
* Vraća sve subkategorije za određenu kategoriju za potrebe filtera (bez paginacije)
|
|
17198
|
+
* @param categoryId - ID kategorije čije subkategorije tražimo
|
|
17199
|
+
* @returns Lista svih aktivnih subkategorija
|
|
17200
|
+
*/
|
|
17201
|
+
async getAllForFilterByCategoryId(categoryId) {
|
|
16532
17202
|
const q = query33(
|
|
16533
17203
|
this.getSubcategoriesRef(categoryId),
|
|
16534
17204
|
where33("isActive", "==", true)
|
|
16535
17205
|
);
|
|
16536
|
-
const
|
|
16537
|
-
return
|
|
16538
|
-
(
|
|
16539
|
-
id:
|
|
16540
|
-
...
|
|
17206
|
+
const querySnapshot = await getDocs33(q);
|
|
17207
|
+
return querySnapshot.docs.map(
|
|
17208
|
+
(doc38) => ({
|
|
17209
|
+
id: doc38.id,
|
|
17210
|
+
...doc38.data()
|
|
17211
|
+
})
|
|
17212
|
+
);
|
|
17213
|
+
}
|
|
17214
|
+
/**
|
|
17215
|
+
* Vraća sve subkategorije za potrebe filtera (bez paginacije)
|
|
17216
|
+
* @returns Lista svih aktivnih subkategorija
|
|
17217
|
+
*/
|
|
17218
|
+
async getAllForFilter() {
|
|
17219
|
+
const q = query33(
|
|
17220
|
+
collectionGroup2(this.db, SUBCATEGORIES_COLLECTION),
|
|
17221
|
+
where33("isActive", "==", true)
|
|
17222
|
+
);
|
|
17223
|
+
const querySnapshot = await getDocs33(q);
|
|
17224
|
+
return querySnapshot.docs.map(
|
|
17225
|
+
(doc38) => ({
|
|
17226
|
+
id: doc38.id,
|
|
17227
|
+
...doc38.data()
|
|
16541
17228
|
})
|
|
16542
17229
|
);
|
|
16543
17230
|
}
|
|
@@ -16549,13 +17236,42 @@ var SubcategoryService = class extends BaseService {
|
|
|
16549
17236
|
* @returns Ažurirana podkategorija
|
|
16550
17237
|
*/
|
|
16551
17238
|
async update(categoryId, subcategoryId, subcategory) {
|
|
16552
|
-
const
|
|
16553
|
-
|
|
16554
|
-
|
|
16555
|
-
|
|
16556
|
-
|
|
16557
|
-
|
|
16558
|
-
|
|
17239
|
+
const newCategoryId = subcategory.categoryId;
|
|
17240
|
+
if (newCategoryId && newCategoryId !== categoryId) {
|
|
17241
|
+
const oldDocRef = doc34(
|
|
17242
|
+
this.getSubcategoriesRef(categoryId),
|
|
17243
|
+
subcategoryId
|
|
17244
|
+
);
|
|
17245
|
+
const docSnap = await getDoc36(oldDocRef);
|
|
17246
|
+
if (!docSnap.exists()) {
|
|
17247
|
+
throw new Error("Subcategory to update does not exist.");
|
|
17248
|
+
}
|
|
17249
|
+
const existingData = docSnap.data();
|
|
17250
|
+
const newData = {
|
|
17251
|
+
...existingData,
|
|
17252
|
+
...subcategory,
|
|
17253
|
+
categoryId: newCategoryId,
|
|
17254
|
+
// Ensure categoryId is updated
|
|
17255
|
+
createdAt: existingData.createdAt,
|
|
17256
|
+
// Preserve original creation date
|
|
17257
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
17258
|
+
};
|
|
17259
|
+
const newDocRef = doc34(
|
|
17260
|
+
this.getSubcategoriesRef(newCategoryId),
|
|
17261
|
+
subcategoryId
|
|
17262
|
+
);
|
|
17263
|
+
await setDoc28(newDocRef, newData);
|
|
17264
|
+
await deleteDoc20(oldDocRef);
|
|
17265
|
+
return { id: subcategoryId, ...newData };
|
|
17266
|
+
} else {
|
|
17267
|
+
const updateData = {
|
|
17268
|
+
...subcategory,
|
|
17269
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
17270
|
+
};
|
|
17271
|
+
const docRef = doc34(this.getSubcategoriesRef(categoryId), subcategoryId);
|
|
17272
|
+
await updateDoc32(docRef, updateData);
|
|
17273
|
+
return this.getById(categoryId, subcategoryId);
|
|
17274
|
+
}
|
|
16559
17275
|
}
|
|
16560
17276
|
/**
|
|
16561
17277
|
* Soft delete podkategorije (postavlja isActive na false)
|
|
@@ -16565,6 +17281,14 @@ var SubcategoryService = class extends BaseService {
|
|
|
16565
17281
|
async delete(categoryId, subcategoryId) {
|
|
16566
17282
|
await this.update(categoryId, subcategoryId, { isActive: false });
|
|
16567
17283
|
}
|
|
17284
|
+
/**
|
|
17285
|
+
* Reactivates a subcategory by setting its isActive flag to true.
|
|
17286
|
+
* @param categoryId - The ID of the category to which the subcategory belongs.
|
|
17287
|
+
* @param subcategoryId - The ID of the subcategory to reactivate.
|
|
17288
|
+
*/
|
|
17289
|
+
async reactivate(categoryId, subcategoryId) {
|
|
17290
|
+
await this.update(categoryId, subcategoryId, { isActive: true });
|
|
17291
|
+
}
|
|
16568
17292
|
/**
|
|
16569
17293
|
* Vraća podkategoriju po ID-u
|
|
16570
17294
|
* @param categoryId - ID kategorije kojoj pripada podkategorija
|
|
@@ -16589,7 +17313,10 @@ import {
|
|
|
16589
17313
|
doc as doc35,
|
|
16590
17314
|
getDoc as getDoc37,
|
|
16591
17315
|
getDocs as getDocs34,
|
|
17316
|
+
limit as limit19,
|
|
17317
|
+
orderBy as orderBy21,
|
|
16592
17318
|
query as query34,
|
|
17319
|
+
startAfter as startAfter17,
|
|
16593
17320
|
updateDoc as updateDoc33,
|
|
16594
17321
|
where as where34,
|
|
16595
17322
|
arrayUnion as arrayUnion9,
|
|
@@ -16601,137 +17328,185 @@ var DEFAULT_CERTIFICATION_REQUIREMENT = {
|
|
|
16601
17328
|
};
|
|
16602
17329
|
var TechnologyService = class extends BaseService {
|
|
16603
17330
|
/**
|
|
16604
|
-
*
|
|
17331
|
+
* Reference to the Firestore collection of technologies.
|
|
16605
17332
|
*/
|
|
16606
|
-
|
|
17333
|
+
get technologiesRef() {
|
|
16607
17334
|
return collection34(this.db, TECHNOLOGIES_COLLECTION);
|
|
16608
17335
|
}
|
|
16609
17336
|
/**
|
|
16610
|
-
*
|
|
16611
|
-
* @param technology -
|
|
16612
|
-
* @returns
|
|
17337
|
+
* Creates a new technology.
|
|
17338
|
+
* @param technology - Data for the new technology.
|
|
17339
|
+
* @returns The created technology with its generated ID.
|
|
16613
17340
|
*/
|
|
16614
17341
|
async create(technology) {
|
|
16615
17342
|
const now = /* @__PURE__ */ new Date();
|
|
16616
17343
|
const newTechnology = {
|
|
16617
|
-
|
|
16618
|
-
|
|
16619
|
-
|
|
16620
|
-
|
|
16621
|
-
|
|
16622
|
-
|
|
16623
|
-
post: []
|
|
16624
|
-
},
|
|
17344
|
+
name: technology.name,
|
|
17345
|
+
description: technology.description,
|
|
17346
|
+
family: technology.family,
|
|
17347
|
+
categoryId: technology.categoryId,
|
|
17348
|
+
subcategoryId: technology.subcategoryId,
|
|
17349
|
+
requirements: technology.requirements || { pre: [], post: [] },
|
|
16625
17350
|
blockingConditions: technology.blockingConditions || [],
|
|
16626
17351
|
contraindications: technology.contraindications || [],
|
|
16627
17352
|
benefits: technology.benefits || [],
|
|
16628
|
-
certificationRequirement: technology.certificationRequirement || DEFAULT_CERTIFICATION_REQUIREMENT
|
|
17353
|
+
certificationRequirement: technology.certificationRequirement || DEFAULT_CERTIFICATION_REQUIREMENT,
|
|
17354
|
+
documentationTemplates: technology.documentationTemplates || [],
|
|
17355
|
+
isActive: true,
|
|
17356
|
+
createdAt: now,
|
|
17357
|
+
updatedAt: now
|
|
16629
17358
|
};
|
|
16630
|
-
|
|
17359
|
+
if (technology.technicalDetails) {
|
|
17360
|
+
newTechnology.technicalDetails = technology.technicalDetails;
|
|
17361
|
+
}
|
|
17362
|
+
const docRef = await addDoc6(this.technologiesRef, newTechnology);
|
|
16631
17363
|
return { id: docRef.id, ...newTechnology };
|
|
16632
17364
|
}
|
|
16633
17365
|
/**
|
|
16634
|
-
*
|
|
16635
|
-
* @
|
|
17366
|
+
* Returns counts of technologies for each subcategory.
|
|
17367
|
+
* @param active - Whether to count active or inactive technologies.
|
|
17368
|
+
* @returns A record mapping subcategory ID to technology count.
|
|
16636
17369
|
*/
|
|
16637
|
-
async
|
|
16638
|
-
const q = query34(this.
|
|
17370
|
+
async getTechnologyCounts(active = true) {
|
|
17371
|
+
const q = query34(this.technologiesRef, where34("isActive", "==", active));
|
|
16639
17372
|
const snapshot = await getDocs34(q);
|
|
16640
|
-
|
|
16641
|
-
|
|
16642
|
-
|
|
16643
|
-
|
|
16644
|
-
|
|
16645
|
-
|
|
17373
|
+
const counts = {};
|
|
17374
|
+
snapshot.docs.forEach((doc38) => {
|
|
17375
|
+
const tech = doc38.data();
|
|
17376
|
+
counts[tech.subcategoryId] = (counts[tech.subcategoryId] || 0) + 1;
|
|
17377
|
+
});
|
|
17378
|
+
return counts;
|
|
16646
17379
|
}
|
|
16647
17380
|
/**
|
|
16648
|
-
*
|
|
16649
|
-
* @param
|
|
16650
|
-
* @returns
|
|
17381
|
+
* Returns counts of technologies for each category.
|
|
17382
|
+
* @param active - Whether to count active or inactive technologies.
|
|
17383
|
+
* @returns A record mapping category ID to technology count.
|
|
16651
17384
|
*/
|
|
16652
|
-
async
|
|
16653
|
-
const q = query34(
|
|
16654
|
-
this.getTechnologiesRef(),
|
|
16655
|
-
where34("isActive", "==", true),
|
|
16656
|
-
where34("family", "==", family)
|
|
16657
|
-
);
|
|
17385
|
+
async getTechnologyCountsByCategory(active = true) {
|
|
17386
|
+
const q = query34(this.technologiesRef, where34("isActive", "==", active));
|
|
16658
17387
|
const snapshot = await getDocs34(q);
|
|
16659
|
-
|
|
16660
|
-
|
|
16661
|
-
|
|
16662
|
-
|
|
17388
|
+
const counts = {};
|
|
17389
|
+
snapshot.docs.forEach((doc38) => {
|
|
17390
|
+
const tech = doc38.data();
|
|
17391
|
+
counts[tech.categoryId] = (counts[tech.categoryId] || 0) + 1;
|
|
17392
|
+
});
|
|
17393
|
+
return counts;
|
|
17394
|
+
}
|
|
17395
|
+
/**
|
|
17396
|
+
* Returns all technologies with pagination.
|
|
17397
|
+
* @param options - Pagination and filter options.
|
|
17398
|
+
* @returns A list of technologies and the last visible document.
|
|
17399
|
+
*/
|
|
17400
|
+
async getAll(options = {}) {
|
|
17401
|
+
const { active = true, limit: queryLimit = 10, lastVisible } = options;
|
|
17402
|
+
const constraints = [
|
|
17403
|
+
where34("isActive", "==", active),
|
|
17404
|
+
orderBy21("name"),
|
|
17405
|
+
queryLimit ? limit19(queryLimit) : void 0,
|
|
17406
|
+
lastVisible ? startAfter17(lastVisible) : void 0
|
|
17407
|
+
].filter((c) => !!c);
|
|
17408
|
+
const q = query34(this.technologiesRef, ...constraints);
|
|
17409
|
+
const snapshot = await getDocs34(q);
|
|
17410
|
+
const technologies = snapshot.docs.map(
|
|
17411
|
+
(doc38) => ({
|
|
17412
|
+
id: doc38.id,
|
|
17413
|
+
...doc38.data()
|
|
16663
17414
|
})
|
|
16664
17415
|
);
|
|
17416
|
+
const newLastVisible = snapshot.docs[snapshot.docs.length - 1];
|
|
17417
|
+
return { technologies, lastVisible: newLastVisible };
|
|
16665
17418
|
}
|
|
16666
17419
|
/**
|
|
16667
|
-
*
|
|
16668
|
-
* @param categoryId - ID
|
|
16669
|
-
* @
|
|
17420
|
+
* Returns all technologies for a specific category with pagination.
|
|
17421
|
+
* @param categoryId - The ID of the category.
|
|
17422
|
+
* @param options - Pagination options.
|
|
17423
|
+
* @returns A list of technologies for the specified category.
|
|
16670
17424
|
*/
|
|
16671
|
-
async getAllByCategoryId(categoryId) {
|
|
16672
|
-
const
|
|
16673
|
-
|
|
16674
|
-
where34("
|
|
16675
|
-
where34("
|
|
16676
|
-
|
|
17425
|
+
async getAllByCategoryId(categoryId, options = {}) {
|
|
17426
|
+
const { active = true, limit: queryLimit = 10, lastVisible } = options;
|
|
17427
|
+
const constraints = [
|
|
17428
|
+
where34("categoryId", "==", categoryId),
|
|
17429
|
+
where34("isActive", "==", active),
|
|
17430
|
+
orderBy21("name"),
|
|
17431
|
+
queryLimit ? limit19(queryLimit) : void 0,
|
|
17432
|
+
lastVisible ? startAfter17(lastVisible) : void 0
|
|
17433
|
+
].filter((c) => !!c);
|
|
17434
|
+
const q = query34(this.technologiesRef, ...constraints);
|
|
16677
17435
|
const snapshot = await getDocs34(q);
|
|
16678
|
-
|
|
16679
|
-
(
|
|
16680
|
-
id:
|
|
16681
|
-
...
|
|
17436
|
+
const technologies = snapshot.docs.map(
|
|
17437
|
+
(doc38) => ({
|
|
17438
|
+
id: doc38.id,
|
|
17439
|
+
...doc38.data()
|
|
16682
17440
|
})
|
|
16683
17441
|
);
|
|
17442
|
+
const newLastVisible = snapshot.docs[snapshot.docs.length - 1];
|
|
17443
|
+
return { technologies, lastVisible: newLastVisible };
|
|
16684
17444
|
}
|
|
16685
17445
|
/**
|
|
16686
|
-
*
|
|
16687
|
-
* @param subcategoryId - ID
|
|
16688
|
-
* @
|
|
17446
|
+
* Returns all technologies for a specific subcategory with pagination.
|
|
17447
|
+
* @param subcategoryId - The ID of the subcategory.
|
|
17448
|
+
* @param options - Pagination options.
|
|
17449
|
+
* @returns A list of technologies for the specified subcategory.
|
|
16689
17450
|
*/
|
|
16690
|
-
async getAllBySubcategoryId(subcategoryId) {
|
|
16691
|
-
const
|
|
16692
|
-
|
|
16693
|
-
where34("
|
|
16694
|
-
where34("
|
|
16695
|
-
|
|
17451
|
+
async getAllBySubcategoryId(subcategoryId, options = {}) {
|
|
17452
|
+
const { active = true, limit: queryLimit = 10, lastVisible } = options;
|
|
17453
|
+
const constraints = [
|
|
17454
|
+
where34("subcategoryId", "==", subcategoryId),
|
|
17455
|
+
where34("isActive", "==", active),
|
|
17456
|
+
orderBy21("name"),
|
|
17457
|
+
queryLimit ? limit19(queryLimit) : void 0,
|
|
17458
|
+
lastVisible ? startAfter17(lastVisible) : void 0
|
|
17459
|
+
].filter((c) => !!c);
|
|
17460
|
+
const q = query34(this.technologiesRef, ...constraints);
|
|
16696
17461
|
const snapshot = await getDocs34(q);
|
|
16697
|
-
|
|
16698
|
-
(
|
|
16699
|
-
id:
|
|
16700
|
-
...
|
|
17462
|
+
const technologies = snapshot.docs.map(
|
|
17463
|
+
(doc38) => ({
|
|
17464
|
+
id: doc38.id,
|
|
17465
|
+
...doc38.data()
|
|
16701
17466
|
})
|
|
16702
17467
|
);
|
|
17468
|
+
const newLastVisible = snapshot.docs[snapshot.docs.length - 1];
|
|
17469
|
+
return { technologies, lastVisible: newLastVisible };
|
|
16703
17470
|
}
|
|
16704
17471
|
/**
|
|
16705
|
-
*
|
|
16706
|
-
* @param
|
|
16707
|
-
* @param technology -
|
|
16708
|
-
* @returns
|
|
17472
|
+
* Updates an existing technology.
|
|
17473
|
+
* @param id - The ID of the technology to update.
|
|
17474
|
+
* @param technology - New data for the technology.
|
|
17475
|
+
* @returns The updated technology.
|
|
16709
17476
|
*/
|
|
16710
|
-
async update(
|
|
16711
|
-
const updateData = {
|
|
16712
|
-
|
|
16713
|
-
|
|
16714
|
-
|
|
16715
|
-
|
|
17477
|
+
async update(id, technology) {
|
|
17478
|
+
const updateData = { ...technology };
|
|
17479
|
+
Object.keys(updateData).forEach((key) => {
|
|
17480
|
+
if (updateData[key] === void 0) {
|
|
17481
|
+
delete updateData[key];
|
|
17482
|
+
}
|
|
17483
|
+
});
|
|
17484
|
+
updateData.updatedAt = /* @__PURE__ */ new Date();
|
|
17485
|
+
const docRef = doc35(this.technologiesRef, id);
|
|
16716
17486
|
await updateDoc33(docRef, updateData);
|
|
16717
|
-
return this.getById(
|
|
17487
|
+
return this.getById(id);
|
|
16718
17488
|
}
|
|
16719
17489
|
/**
|
|
16720
|
-
* Soft
|
|
16721
|
-
* @param
|
|
17490
|
+
* Soft deletes a technology.
|
|
17491
|
+
* @param id - The ID of the technology to delete.
|
|
16722
17492
|
*/
|
|
16723
|
-
async delete(
|
|
16724
|
-
await this.update(
|
|
16725
|
-
|
|
16726
|
-
|
|
17493
|
+
async delete(id) {
|
|
17494
|
+
await this.update(id, { isActive: false });
|
|
17495
|
+
}
|
|
17496
|
+
/**
|
|
17497
|
+
* Reactivates a technology.
|
|
17498
|
+
* @param id - The ID of the technology to reactivate.
|
|
17499
|
+
*/
|
|
17500
|
+
async reactivate(id) {
|
|
17501
|
+
await this.update(id, { isActive: true });
|
|
16727
17502
|
}
|
|
16728
17503
|
/**
|
|
16729
|
-
*
|
|
16730
|
-
* @param
|
|
16731
|
-
* @returns
|
|
17504
|
+
* Returns a technology by its ID.
|
|
17505
|
+
* @param id - The ID of the requested technology.
|
|
17506
|
+
* @returns The technology or null if it doesn't exist.
|
|
16732
17507
|
*/
|
|
16733
|
-
async getById(
|
|
16734
|
-
const docRef = doc35(this.
|
|
17508
|
+
async getById(id) {
|
|
17509
|
+
const docRef = doc35(this.technologiesRef, id);
|
|
16735
17510
|
const docSnap = await getDoc37(docRef);
|
|
16736
17511
|
if (!docSnap.exists()) return null;
|
|
16737
17512
|
return {
|
|
@@ -16746,7 +17521,7 @@ var TechnologyService = class extends BaseService {
|
|
|
16746
17521
|
* @returns Ažurirana tehnologija sa novim zahtevom
|
|
16747
17522
|
*/
|
|
16748
17523
|
async addRequirement(technologyId, requirement) {
|
|
16749
|
-
const docRef = doc35(this.
|
|
17524
|
+
const docRef = doc35(this.technologiesRef, technologyId);
|
|
16750
17525
|
const requirementType = requirement.type === "pre" ? "requirements.pre" : "requirements.post";
|
|
16751
17526
|
await updateDoc33(docRef, {
|
|
16752
17527
|
[requirementType]: arrayUnion9(requirement),
|
|
@@ -16761,7 +17536,7 @@ var TechnologyService = class extends BaseService {
|
|
|
16761
17536
|
* @returns Ažurirana tehnologija bez uklonjenog zahteva
|
|
16762
17537
|
*/
|
|
16763
17538
|
async removeRequirement(technologyId, requirement) {
|
|
16764
|
-
const docRef = doc35(this.
|
|
17539
|
+
const docRef = doc35(this.technologiesRef, technologyId);
|
|
16765
17540
|
const requirementType = requirement.type === "pre" ? "requirements.pre" : "requirements.post";
|
|
16766
17541
|
await updateDoc33(docRef, {
|
|
16767
17542
|
[requirementType]: arrayRemove8(requirement),
|
|
@@ -16801,7 +17576,7 @@ var TechnologyService = class extends BaseService {
|
|
|
16801
17576
|
* @returns Ažurirana tehnologija
|
|
16802
17577
|
*/
|
|
16803
17578
|
async addBlockingCondition(technologyId, condition) {
|
|
16804
|
-
const docRef = doc35(this.
|
|
17579
|
+
const docRef = doc35(this.technologiesRef, technologyId);
|
|
16805
17580
|
await updateDoc33(docRef, {
|
|
16806
17581
|
blockingConditions: arrayUnion9(condition),
|
|
16807
17582
|
updatedAt: /* @__PURE__ */ new Date()
|
|
@@ -16815,7 +17590,7 @@ var TechnologyService = class extends BaseService {
|
|
|
16815
17590
|
* @returns Ažurirana tehnologija
|
|
16816
17591
|
*/
|
|
16817
17592
|
async removeBlockingCondition(technologyId, condition) {
|
|
16818
|
-
const docRef = doc35(this.
|
|
17593
|
+
const docRef = doc35(this.technologiesRef, technologyId);
|
|
16819
17594
|
await updateDoc33(docRef, {
|
|
16820
17595
|
blockingConditions: arrayRemove8(condition),
|
|
16821
17596
|
updatedAt: /* @__PURE__ */ new Date()
|
|
@@ -16829,9 +17604,17 @@ var TechnologyService = class extends BaseService {
|
|
|
16829
17604
|
* @returns Ažurirana tehnologija
|
|
16830
17605
|
*/
|
|
16831
17606
|
async addContraindication(technologyId, contraindication) {
|
|
16832
|
-
const docRef = doc35(this.
|
|
17607
|
+
const docRef = doc35(this.technologiesRef, technologyId);
|
|
17608
|
+
const technology = await this.getById(technologyId);
|
|
17609
|
+
if (!technology) {
|
|
17610
|
+
throw new Error(`Technology with id ${technologyId} not found`);
|
|
17611
|
+
}
|
|
17612
|
+
const existingContraindications = technology.contraindications || [];
|
|
17613
|
+
if (existingContraindications.some((c) => c.id === contraindication.id)) {
|
|
17614
|
+
return technology;
|
|
17615
|
+
}
|
|
16833
17616
|
await updateDoc33(docRef, {
|
|
16834
|
-
contraindications:
|
|
17617
|
+
contraindications: [...existingContraindications, contraindication],
|
|
16835
17618
|
updatedAt: /* @__PURE__ */ new Date()
|
|
16836
17619
|
});
|
|
16837
17620
|
return this.getById(technologyId);
|
|
@@ -16843,9 +17626,45 @@ var TechnologyService = class extends BaseService {
|
|
|
16843
17626
|
* @returns Ažurirana tehnologija
|
|
16844
17627
|
*/
|
|
16845
17628
|
async removeContraindication(technologyId, contraindication) {
|
|
16846
|
-
const docRef = doc35(this.
|
|
17629
|
+
const docRef = doc35(this.technologiesRef, technologyId);
|
|
17630
|
+
const technology = await this.getById(technologyId);
|
|
17631
|
+
if (!technology) {
|
|
17632
|
+
throw new Error(`Technology with id ${technologyId} not found`);
|
|
17633
|
+
}
|
|
17634
|
+
const updatedContraindications = (technology.contraindications || []).filter((c) => c.id !== contraindication.id);
|
|
16847
17635
|
await updateDoc33(docRef, {
|
|
16848
|
-
contraindications:
|
|
17636
|
+
contraindications: updatedContraindications,
|
|
17637
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
17638
|
+
});
|
|
17639
|
+
return this.getById(technologyId);
|
|
17640
|
+
}
|
|
17641
|
+
/**
|
|
17642
|
+
* Updates an existing contraindication in a technology's list.
|
|
17643
|
+
* If the contraindication does not exist, it will not be added.
|
|
17644
|
+
* @param technologyId - ID of the technology
|
|
17645
|
+
* @param contraindication - The updated contraindication object
|
|
17646
|
+
* @returns The updated technology
|
|
17647
|
+
*/
|
|
17648
|
+
async updateContraindication(technologyId, contraindication) {
|
|
17649
|
+
const docRef = doc35(this.technologiesRef, technologyId);
|
|
17650
|
+
const technology = await this.getById(technologyId);
|
|
17651
|
+
if (!technology) {
|
|
17652
|
+
throw new Error(`Technology with id ${technologyId} not found`);
|
|
17653
|
+
}
|
|
17654
|
+
const contraindications = technology.contraindications || [];
|
|
17655
|
+
const index = contraindications.findIndex(
|
|
17656
|
+
(c) => c.id === contraindication.id
|
|
17657
|
+
);
|
|
17658
|
+
if (index === -1) {
|
|
17659
|
+
console.warn(
|
|
17660
|
+
`Contraindication with id ${contraindication.id} not found for technology ${technologyId}. No update performed.`
|
|
17661
|
+
);
|
|
17662
|
+
return technology;
|
|
17663
|
+
}
|
|
17664
|
+
const updatedContraindications = [...contraindications];
|
|
17665
|
+
updatedContraindications[index] = contraindication;
|
|
17666
|
+
await updateDoc33(docRef, {
|
|
17667
|
+
contraindications: updatedContraindications,
|
|
16849
17668
|
updatedAt: /* @__PURE__ */ new Date()
|
|
16850
17669
|
});
|
|
16851
17670
|
return this.getById(technologyId);
|
|
@@ -16857,9 +17676,17 @@ var TechnologyService = class extends BaseService {
|
|
|
16857
17676
|
* @returns Ažurirana tehnologija
|
|
16858
17677
|
*/
|
|
16859
17678
|
async addBenefit(technologyId, benefit) {
|
|
16860
|
-
const docRef = doc35(this.
|
|
17679
|
+
const docRef = doc35(this.technologiesRef, technologyId);
|
|
17680
|
+
const technology = await this.getById(technologyId);
|
|
17681
|
+
if (!technology) {
|
|
17682
|
+
throw new Error(`Technology with id ${technologyId} not found`);
|
|
17683
|
+
}
|
|
17684
|
+
const existingBenefits = technology.benefits || [];
|
|
17685
|
+
if (existingBenefits.some((b) => b.id === benefit.id)) {
|
|
17686
|
+
return technology;
|
|
17687
|
+
}
|
|
16861
17688
|
await updateDoc33(docRef, {
|
|
16862
|
-
benefits:
|
|
17689
|
+
benefits: [...existingBenefits, benefit],
|
|
16863
17690
|
updatedAt: /* @__PURE__ */ new Date()
|
|
16864
17691
|
});
|
|
16865
17692
|
return this.getById(technologyId);
|
|
@@ -16871,9 +17698,45 @@ var TechnologyService = class extends BaseService {
|
|
|
16871
17698
|
* @returns Ažurirana tehnologija
|
|
16872
17699
|
*/
|
|
16873
17700
|
async removeBenefit(technologyId, benefit) {
|
|
16874
|
-
const docRef = doc35(this.
|
|
17701
|
+
const docRef = doc35(this.technologiesRef, technologyId);
|
|
17702
|
+
const technology = await this.getById(technologyId);
|
|
17703
|
+
if (!technology) {
|
|
17704
|
+
throw new Error(`Technology with id ${technologyId} not found`);
|
|
17705
|
+
}
|
|
17706
|
+
const updatedBenefits = (technology.benefits || []).filter(
|
|
17707
|
+
(b) => b.id !== benefit.id
|
|
17708
|
+
);
|
|
17709
|
+
await updateDoc33(docRef, {
|
|
17710
|
+
benefits: updatedBenefits,
|
|
17711
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
17712
|
+
});
|
|
17713
|
+
return this.getById(technologyId);
|
|
17714
|
+
}
|
|
17715
|
+
/**
|
|
17716
|
+
* Updates an existing benefit in a technology's list.
|
|
17717
|
+
* If the benefit does not exist, it will not be added.
|
|
17718
|
+
* @param technologyId - ID of the technology
|
|
17719
|
+
* @param benefit - The updated benefit object
|
|
17720
|
+
* @returns The updated technology
|
|
17721
|
+
*/
|
|
17722
|
+
async updateBenefit(technologyId, benefit) {
|
|
17723
|
+
const docRef = doc35(this.technologiesRef, technologyId);
|
|
17724
|
+
const technology = await this.getById(technologyId);
|
|
17725
|
+
if (!technology) {
|
|
17726
|
+
throw new Error(`Technology with id ${technologyId} not found`);
|
|
17727
|
+
}
|
|
17728
|
+
const benefits = technology.benefits || [];
|
|
17729
|
+
const index = benefits.findIndex((b) => b.id === benefit.id);
|
|
17730
|
+
if (index === -1) {
|
|
17731
|
+
console.warn(
|
|
17732
|
+
`Benefit with id ${benefit.id} not found for technology ${technologyId}. No update performed.`
|
|
17733
|
+
);
|
|
17734
|
+
return technology;
|
|
17735
|
+
}
|
|
17736
|
+
const updatedBenefits = [...benefits];
|
|
17737
|
+
updatedBenefits[index] = benefit;
|
|
16875
17738
|
await updateDoc33(docRef, {
|
|
16876
|
-
benefits:
|
|
17739
|
+
benefits: updatedBenefits,
|
|
16877
17740
|
updatedAt: /* @__PURE__ */ new Date()
|
|
16878
17741
|
});
|
|
16879
17742
|
return this.getById(technologyId);
|
|
@@ -16912,7 +17775,7 @@ var TechnologyService = class extends BaseService {
|
|
|
16912
17775
|
* @returns Ažurirana tehnologija
|
|
16913
17776
|
*/
|
|
16914
17777
|
async updateCertificationRequirement(technologyId, certificationRequirement) {
|
|
16915
|
-
const docRef = doc35(this.
|
|
17778
|
+
const docRef = doc35(this.technologiesRef, technologyId);
|
|
16916
17779
|
await updateDoc33(docRef, {
|
|
16917
17780
|
certificationRequirement,
|
|
16918
17781
|
updatedAt: /* @__PURE__ */ new Date()
|
|
@@ -16990,7 +17853,7 @@ var TechnologyService = class extends BaseService {
|
|
|
16990
17853
|
*/
|
|
16991
17854
|
async getAllowedTechnologies(practitioner) {
|
|
16992
17855
|
const allTechnologies = await this.getAll();
|
|
16993
|
-
const allowedTechnologies = allTechnologies.filter(
|
|
17856
|
+
const allowedTechnologies = allTechnologies.technologies.filter(
|
|
16994
17857
|
(technology) => this.validateCertification(
|
|
16995
17858
|
technology.certificationRequirement,
|
|
16996
17859
|
practitioner.certification
|
|
@@ -17010,18 +17873,61 @@ var TechnologyService = class extends BaseService {
|
|
|
17010
17873
|
subcategories
|
|
17011
17874
|
};
|
|
17012
17875
|
}
|
|
17876
|
+
/**
|
|
17877
|
+
* Gets all active technologies for a subcategory for filter dropdowns.
|
|
17878
|
+
* @param categoryId - The ID of the parent category.
|
|
17879
|
+
* @param subcategoryId - The ID of the subcategory.
|
|
17880
|
+
*/
|
|
17881
|
+
async getAllForFilterBySubcategoryId(categoryId, subcategoryId) {
|
|
17882
|
+
const q = query34(
|
|
17883
|
+
collection34(this.db, TECHNOLOGIES_COLLECTION),
|
|
17884
|
+
where34("isActive", "==", true),
|
|
17885
|
+
where34("categoryId", "==", categoryId),
|
|
17886
|
+
where34("subcategoryId", "==", subcategoryId),
|
|
17887
|
+
orderBy21("name")
|
|
17888
|
+
);
|
|
17889
|
+
const snapshot = await getDocs34(q);
|
|
17890
|
+
return snapshot.docs.map(
|
|
17891
|
+
(doc38) => ({
|
|
17892
|
+
id: doc38.id,
|
|
17893
|
+
...doc38.data()
|
|
17894
|
+
})
|
|
17895
|
+
);
|
|
17896
|
+
}
|
|
17897
|
+
/**
|
|
17898
|
+
* Gets all active technologies for filter dropdowns.
|
|
17899
|
+
*/
|
|
17900
|
+
async getAllForFilter() {
|
|
17901
|
+
const q = query34(
|
|
17902
|
+
collection34(this.db, TECHNOLOGIES_COLLECTION),
|
|
17903
|
+
where34("isActive", "==", true),
|
|
17904
|
+
orderBy21("name")
|
|
17905
|
+
);
|
|
17906
|
+
const snapshot = await getDocs34(q);
|
|
17907
|
+
return snapshot.docs.map(
|
|
17908
|
+
(doc38) => ({
|
|
17909
|
+
id: doc38.id,
|
|
17910
|
+
...doc38.data()
|
|
17911
|
+
})
|
|
17912
|
+
);
|
|
17913
|
+
}
|
|
17013
17914
|
};
|
|
17014
17915
|
|
|
17015
17916
|
// src/backoffice/services/product.service.ts
|
|
17016
17917
|
import {
|
|
17017
17918
|
addDoc as addDoc7,
|
|
17018
17919
|
collection as collection35,
|
|
17920
|
+
collectionGroup as collectionGroup3,
|
|
17019
17921
|
doc as doc36,
|
|
17020
17922
|
getDoc as getDoc38,
|
|
17021
17923
|
getDocs as getDocs35,
|
|
17022
17924
|
query as query35,
|
|
17023
17925
|
updateDoc as updateDoc34,
|
|
17024
|
-
where as where35
|
|
17926
|
+
where as where35,
|
|
17927
|
+
limit as limit20,
|
|
17928
|
+
orderBy as orderBy22,
|
|
17929
|
+
startAfter as startAfter18,
|
|
17930
|
+
getCountFromServer as getCountFromServer6
|
|
17025
17931
|
} from "firebase/firestore";
|
|
17026
17932
|
|
|
17027
17933
|
// src/backoffice/types/product.types.ts
|
|
@@ -17062,20 +17968,102 @@ var ProductService = class extends BaseService {
|
|
|
17062
17968
|
return { id: productRef.id, ...newProduct };
|
|
17063
17969
|
}
|
|
17064
17970
|
/**
|
|
17065
|
-
* Gets all products
|
|
17971
|
+
* Gets a paginated list of all products, with optional filters.
|
|
17972
|
+
* This uses a collectionGroup query to search across all technologies.
|
|
17066
17973
|
*/
|
|
17067
|
-
async
|
|
17974
|
+
async getAll(options) {
|
|
17975
|
+
const {
|
|
17976
|
+
rowsPerPage,
|
|
17977
|
+
lastVisible,
|
|
17978
|
+
categoryId,
|
|
17979
|
+
subcategoryId,
|
|
17980
|
+
technologyId
|
|
17981
|
+
} = options;
|
|
17982
|
+
const constraints = [
|
|
17983
|
+
where35("isActive", "==", true),
|
|
17984
|
+
orderBy22("name")
|
|
17985
|
+
];
|
|
17986
|
+
if (categoryId) {
|
|
17987
|
+
constraints.push(where35("categoryId", "==", categoryId));
|
|
17988
|
+
}
|
|
17989
|
+
if (subcategoryId) {
|
|
17990
|
+
constraints.push(where35("subcategoryId", "==", subcategoryId));
|
|
17991
|
+
}
|
|
17992
|
+
if (technologyId) {
|
|
17993
|
+
constraints.push(where35("technologyId", "==", technologyId));
|
|
17994
|
+
}
|
|
17995
|
+
if (lastVisible) {
|
|
17996
|
+
constraints.push(startAfter18(lastVisible));
|
|
17997
|
+
}
|
|
17998
|
+
constraints.push(limit20(rowsPerPage));
|
|
17068
17999
|
const q = query35(
|
|
17069
|
-
this.
|
|
17070
|
-
|
|
18000
|
+
collectionGroup3(this.db, PRODUCTS_COLLECTION),
|
|
18001
|
+
...constraints
|
|
17071
18002
|
);
|
|
17072
18003
|
const snapshot = await getDocs35(q);
|
|
17073
|
-
|
|
17074
|
-
(
|
|
17075
|
-
id:
|
|
17076
|
-
...
|
|
18004
|
+
const products = snapshot.docs.map(
|
|
18005
|
+
(doc38) => ({
|
|
18006
|
+
id: doc38.id,
|
|
18007
|
+
...doc38.data()
|
|
17077
18008
|
})
|
|
17078
18009
|
);
|
|
18010
|
+
const newLastVisible = snapshot.docs[snapshot.docs.length - 1];
|
|
18011
|
+
return { products, lastVisible: newLastVisible };
|
|
18012
|
+
}
|
|
18013
|
+
/**
|
|
18014
|
+
* Gets the total count of active products, with optional filters.
|
|
18015
|
+
*/
|
|
18016
|
+
async getProductsCount(options) {
|
|
18017
|
+
const { categoryId, subcategoryId, technologyId } = options;
|
|
18018
|
+
const constraints = [where35("isActive", "==", true)];
|
|
18019
|
+
if (categoryId) {
|
|
18020
|
+
constraints.push(where35("categoryId", "==", categoryId));
|
|
18021
|
+
}
|
|
18022
|
+
if (subcategoryId) {
|
|
18023
|
+
constraints.push(where35("subcategoryId", "==", subcategoryId));
|
|
18024
|
+
}
|
|
18025
|
+
if (technologyId) {
|
|
18026
|
+
constraints.push(where35("technologyId", "==", technologyId));
|
|
18027
|
+
}
|
|
18028
|
+
const q = query35(
|
|
18029
|
+
collectionGroup3(this.db, PRODUCTS_COLLECTION),
|
|
18030
|
+
...constraints
|
|
18031
|
+
);
|
|
18032
|
+
const snapshot = await getCountFromServer6(q);
|
|
18033
|
+
return snapshot.data().count;
|
|
18034
|
+
}
|
|
18035
|
+
/**
|
|
18036
|
+
* Gets counts of active products grouped by category, subcategory, and technology.
|
|
18037
|
+
* This uses a single collectionGroup query for efficiency.
|
|
18038
|
+
*/
|
|
18039
|
+
async getProductCounts() {
|
|
18040
|
+
const q = query35(
|
|
18041
|
+
collectionGroup3(this.db, PRODUCTS_COLLECTION),
|
|
18042
|
+
where35("isActive", "==", true)
|
|
18043
|
+
);
|
|
18044
|
+
const snapshot = await getDocs35(q);
|
|
18045
|
+
const counts = {
|
|
18046
|
+
byCategory: {},
|
|
18047
|
+
bySubcategory: {},
|
|
18048
|
+
byTechnology: {}
|
|
18049
|
+
};
|
|
18050
|
+
if (snapshot.empty) {
|
|
18051
|
+
return counts;
|
|
18052
|
+
}
|
|
18053
|
+
snapshot.docs.forEach((doc38) => {
|
|
18054
|
+
const product = doc38.data();
|
|
18055
|
+
const { categoryId, subcategoryId, technologyId } = product;
|
|
18056
|
+
if (categoryId) {
|
|
18057
|
+
counts.byCategory[categoryId] = (counts.byCategory[categoryId] || 0) + 1;
|
|
18058
|
+
}
|
|
18059
|
+
if (subcategoryId) {
|
|
18060
|
+
counts.bySubcategory[subcategoryId] = (counts.bySubcategory[subcategoryId] || 0) + 1;
|
|
18061
|
+
}
|
|
18062
|
+
if (technologyId) {
|
|
18063
|
+
counts.byTechnology[technologyId] = (counts.byTechnology[technologyId] || 0) + 1;
|
|
18064
|
+
}
|
|
18065
|
+
});
|
|
18066
|
+
return counts;
|
|
17079
18067
|
}
|
|
17080
18068
|
/**
|
|
17081
18069
|
* Gets all products for a brand by filtering through all technologies
|
|
@@ -17093,9 +18081,9 @@ var ProductService = class extends BaseService {
|
|
|
17093
18081
|
const snapshot = await getDocs35(q);
|
|
17094
18082
|
products.push(
|
|
17095
18083
|
...snapshot.docs.map(
|
|
17096
|
-
(
|
|
17097
|
-
id:
|
|
17098
|
-
...
|
|
18084
|
+
(doc38) => ({
|
|
18085
|
+
id: doc38.id,
|
|
18086
|
+
...doc38.data()
|
|
17099
18087
|
})
|
|
17100
18088
|
)
|
|
17101
18089
|
);
|
|
@@ -17136,6 +18124,262 @@ var ProductService = class extends BaseService {
|
|
|
17136
18124
|
}
|
|
17137
18125
|
};
|
|
17138
18126
|
|
|
18127
|
+
// src/backoffice/services/constants.service.ts
|
|
18128
|
+
import {
|
|
18129
|
+
arrayRemove as arrayRemove9,
|
|
18130
|
+
arrayUnion as arrayUnion10,
|
|
18131
|
+
doc as doc37,
|
|
18132
|
+
getDoc as getDoc39,
|
|
18133
|
+
setDoc as setDoc29,
|
|
18134
|
+
updateDoc as updateDoc35
|
|
18135
|
+
} from "firebase/firestore";
|
|
18136
|
+
var ADMIN_CONSTANTS_COLLECTION = "admin-constants";
|
|
18137
|
+
var TREATMENT_BENEFITS_DOC = "treatment-benefits";
|
|
18138
|
+
var CONTRAINDICATIONS_DOC = "contraindications";
|
|
18139
|
+
var ConstantsService = class extends BaseService {
|
|
18140
|
+
/**
|
|
18141
|
+
* @description Gets the reference to the document holding treatment benefits.
|
|
18142
|
+
* @private
|
|
18143
|
+
* @type {DocumentReference}
|
|
18144
|
+
*/
|
|
18145
|
+
get treatmentBenefitsDocRef() {
|
|
18146
|
+
return doc37(this.db, ADMIN_CONSTANTS_COLLECTION, TREATMENT_BENEFITS_DOC);
|
|
18147
|
+
}
|
|
18148
|
+
/**
|
|
18149
|
+
* @description Gets the reference to the document holding contraindications.
|
|
18150
|
+
* @private
|
|
18151
|
+
* @type {DocumentReference}
|
|
18152
|
+
*/
|
|
18153
|
+
get contraindicationsDocRef() {
|
|
18154
|
+
return doc37(this.db, ADMIN_CONSTANTS_COLLECTION, CONTRAINDICATIONS_DOC);
|
|
18155
|
+
}
|
|
18156
|
+
// =================================================================
|
|
18157
|
+
// Treatment Benefits
|
|
18158
|
+
// =================================================================
|
|
18159
|
+
/**
|
|
18160
|
+
* @description Retrieves all treatment benefits without pagination.
|
|
18161
|
+
* @returns {Promise<TreatmentBenefitDynamic[]>} An array of all treatment benefits.
|
|
18162
|
+
*/
|
|
18163
|
+
async getAllBenefitsForFilter() {
|
|
18164
|
+
const docSnap = await getDoc39(this.treatmentBenefitsDocRef);
|
|
18165
|
+
if (!docSnap.exists()) {
|
|
18166
|
+
return [];
|
|
18167
|
+
}
|
|
18168
|
+
return docSnap.data().benefits;
|
|
18169
|
+
}
|
|
18170
|
+
/**
|
|
18171
|
+
* @description Retrieves a paginated list of treatment benefits.
|
|
18172
|
+
* @param {{ page: number; limit: number }} options - Pagination options.
|
|
18173
|
+
* @returns {Promise<{ benefits: TreatmentBenefitDynamic[]; total: number }>} A paginated list of benefits and the total count.
|
|
18174
|
+
*/
|
|
18175
|
+
async getAllBenefits(options) {
|
|
18176
|
+
const allBenefits = await this.getAllBenefitsForFilter();
|
|
18177
|
+
const { page, limit: limit21 } = options;
|
|
18178
|
+
const startIndex = page * limit21;
|
|
18179
|
+
const endIndex = startIndex + limit21;
|
|
18180
|
+
const paginatedBenefits = allBenefits.slice(startIndex, endIndex);
|
|
18181
|
+
return { benefits: paginatedBenefits, total: allBenefits.length };
|
|
18182
|
+
}
|
|
18183
|
+
/**
|
|
18184
|
+
* @description Adds a new treatment benefit.
|
|
18185
|
+
* @param {Omit<TreatmentBenefitDynamic, "id">} benefit - The treatment benefit to add, without an ID.
|
|
18186
|
+
* @returns {Promise<TreatmentBenefitDynamic>} The newly created treatment benefit with its generated ID.
|
|
18187
|
+
*/
|
|
18188
|
+
async addTreatmentBenefit(benefit) {
|
|
18189
|
+
const newBenefit = {
|
|
18190
|
+
id: this.generateId(),
|
|
18191
|
+
...benefit
|
|
18192
|
+
};
|
|
18193
|
+
const docSnap = await getDoc39(this.treatmentBenefitsDocRef);
|
|
18194
|
+
if (!docSnap.exists()) {
|
|
18195
|
+
await setDoc29(this.treatmentBenefitsDocRef, { benefits: [newBenefit] });
|
|
18196
|
+
} else {
|
|
18197
|
+
await updateDoc35(this.treatmentBenefitsDocRef, {
|
|
18198
|
+
benefits: arrayUnion10(newBenefit)
|
|
18199
|
+
});
|
|
18200
|
+
}
|
|
18201
|
+
return newBenefit;
|
|
18202
|
+
}
|
|
18203
|
+
/**
|
|
18204
|
+
* @description Retrieves a single treatment benefit by its ID.
|
|
18205
|
+
* @param {string} benefitId - The ID of the treatment benefit to retrieve.
|
|
18206
|
+
* @returns {Promise<TreatmentBenefitDynamic | undefined>} The found treatment benefit or undefined.
|
|
18207
|
+
*/
|
|
18208
|
+
async getBenefitById(benefitId) {
|
|
18209
|
+
const benefits = await this.getAllBenefitsForFilter();
|
|
18210
|
+
return benefits.find((b) => b.id === benefitId);
|
|
18211
|
+
}
|
|
18212
|
+
/**
|
|
18213
|
+
* @description Searches for treatment benefits by name (case-insensitive).
|
|
18214
|
+
* @param {string} searchTerm - The term to search for in the benefit names.
|
|
18215
|
+
* @returns {Promise<TreatmentBenefitDynamic[]>} An array of matching treatment benefits.
|
|
18216
|
+
*/
|
|
18217
|
+
async searchBenefitsByName(searchTerm) {
|
|
18218
|
+
const benefits = await this.getAllBenefitsForFilter();
|
|
18219
|
+
const normalizedSearchTerm = searchTerm.toLowerCase();
|
|
18220
|
+
return benefits.filter(
|
|
18221
|
+
(b) => b.name.toLowerCase().includes(normalizedSearchTerm)
|
|
18222
|
+
);
|
|
18223
|
+
}
|
|
18224
|
+
/**
|
|
18225
|
+
* @description Updates an existing treatment benefit.
|
|
18226
|
+
* @param {TreatmentBenefitDynamic} benefit - The treatment benefit with updated data. Its ID must match an existing benefit.
|
|
18227
|
+
* @returns {Promise<TreatmentBenefitDynamic>} The updated treatment benefit.
|
|
18228
|
+
* @throws {Error} If the treatment benefit is not found.
|
|
18229
|
+
*/
|
|
18230
|
+
async updateTreatmentBenefit(benefit) {
|
|
18231
|
+
const benefits = await this.getAllBenefitsForFilter();
|
|
18232
|
+
const benefitIndex = benefits.findIndex((b) => b.id === benefit.id);
|
|
18233
|
+
if (benefitIndex === -1) {
|
|
18234
|
+
throw new Error("Treatment benefit not found.");
|
|
18235
|
+
}
|
|
18236
|
+
benefits[benefitIndex] = benefit;
|
|
18237
|
+
await updateDoc35(this.treatmentBenefitsDocRef, { benefits });
|
|
18238
|
+
return benefit;
|
|
18239
|
+
}
|
|
18240
|
+
/**
|
|
18241
|
+
* @description Deletes a treatment benefit by its ID.
|
|
18242
|
+
* @param {string} benefitId - The ID of the treatment benefit to delete.
|
|
18243
|
+
* @returns {Promise<void>}
|
|
18244
|
+
*/
|
|
18245
|
+
async deleteTreatmentBenefit(benefitId) {
|
|
18246
|
+
const benefits = await this.getAllBenefitsForFilter();
|
|
18247
|
+
const benefitToRemove = benefits.find((b) => b.id === benefitId);
|
|
18248
|
+
if (!benefitToRemove) {
|
|
18249
|
+
return;
|
|
18250
|
+
}
|
|
18251
|
+
await updateDoc35(this.treatmentBenefitsDocRef, {
|
|
18252
|
+
benefits: arrayRemove9(benefitToRemove)
|
|
18253
|
+
});
|
|
18254
|
+
}
|
|
18255
|
+
// =================================================================
|
|
18256
|
+
// Contraindications
|
|
18257
|
+
// =================================================================
|
|
18258
|
+
/**
|
|
18259
|
+
* @description Retrieves all contraindications without pagination.
|
|
18260
|
+
* @returns {Promise<ContraindicationDynamic[]>} An array of all contraindications.
|
|
18261
|
+
*/
|
|
18262
|
+
async getAllContraindicationsForFilter() {
|
|
18263
|
+
const docSnap = await getDoc39(this.contraindicationsDocRef);
|
|
18264
|
+
if (!docSnap.exists()) {
|
|
18265
|
+
return [];
|
|
18266
|
+
}
|
|
18267
|
+
return docSnap.data().contraindications;
|
|
18268
|
+
}
|
|
18269
|
+
/**
|
|
18270
|
+
* @description Retrieves a paginated list of contraindications.
|
|
18271
|
+
* @param {{ page: number; limit: number }} options - Pagination options.
|
|
18272
|
+
* @returns {Promise<{ contraindications: ContraindicationDynamic[]; total: number }>} A paginated list and the total count.
|
|
18273
|
+
*/
|
|
18274
|
+
async getAllContraindications(options) {
|
|
18275
|
+
const allContraindications = await this.getAllContraindicationsForFilter();
|
|
18276
|
+
const { page, limit: limit21 } = options;
|
|
18277
|
+
const startIndex = page * limit21;
|
|
18278
|
+
const endIndex = startIndex + limit21;
|
|
18279
|
+
const paginatedContraindications = allContraindications.slice(
|
|
18280
|
+
startIndex,
|
|
18281
|
+
endIndex
|
|
18282
|
+
);
|
|
18283
|
+
return {
|
|
18284
|
+
contraindications: paginatedContraindications,
|
|
18285
|
+
total: allContraindications.length
|
|
18286
|
+
};
|
|
18287
|
+
}
|
|
18288
|
+
/**
|
|
18289
|
+
* @description Adds a new contraindication.
|
|
18290
|
+
* @param {Omit<ContraindicationDynamic, "id">} contraindication - The contraindication to add, without an ID.
|
|
18291
|
+
* @returns {Promise<ContraindicationDynamic>} The newly created contraindication with its generated ID.
|
|
18292
|
+
*/
|
|
18293
|
+
async addContraindication(contraindication) {
|
|
18294
|
+
const newContraindication = {
|
|
18295
|
+
id: this.generateId(),
|
|
18296
|
+
...contraindication
|
|
18297
|
+
};
|
|
18298
|
+
const docSnap = await getDoc39(this.contraindicationsDocRef);
|
|
18299
|
+
if (!docSnap.exists()) {
|
|
18300
|
+
await setDoc29(this.contraindicationsDocRef, {
|
|
18301
|
+
contraindications: [newContraindication]
|
|
18302
|
+
});
|
|
18303
|
+
} else {
|
|
18304
|
+
await updateDoc35(this.contraindicationsDocRef, {
|
|
18305
|
+
contraindications: arrayUnion10(newContraindication)
|
|
18306
|
+
});
|
|
18307
|
+
}
|
|
18308
|
+
return newContraindication;
|
|
18309
|
+
}
|
|
18310
|
+
/**
|
|
18311
|
+
* @description Retrieves a single contraindication by its ID.
|
|
18312
|
+
* @param {string} contraindicationId - The ID of the contraindication to retrieve.
|
|
18313
|
+
* @returns {Promise<ContraindicationDynamic | undefined>} The found contraindication or undefined.
|
|
18314
|
+
*/
|
|
18315
|
+
async getContraindicationById(contraindicationId) {
|
|
18316
|
+
const contraindications = await this.getAllContraindicationsForFilter();
|
|
18317
|
+
return contraindications.find((c) => c.id === contraindicationId);
|
|
18318
|
+
}
|
|
18319
|
+
/**
|
|
18320
|
+
* @description Searches for contraindications by name (case-insensitive).
|
|
18321
|
+
* @param {string} searchTerm - The term to search for in the contraindication names.
|
|
18322
|
+
* @returns {Promise<ContraindicationDynamic[]>} An array of matching contraindications.
|
|
18323
|
+
*/
|
|
18324
|
+
async searchContraindicationsByName(searchTerm) {
|
|
18325
|
+
const contraindications = await this.getAllContraindicationsForFilter();
|
|
18326
|
+
const normalizedSearchTerm = searchTerm.toLowerCase();
|
|
18327
|
+
return contraindications.filter(
|
|
18328
|
+
(c) => c.name.toLowerCase().includes(normalizedSearchTerm)
|
|
18329
|
+
);
|
|
18330
|
+
}
|
|
18331
|
+
/**
|
|
18332
|
+
* @description Updates an existing contraindication.
|
|
18333
|
+
* @param {ContraindicationDynamic} contraindication - The contraindication with updated data. Its ID must match an existing one.
|
|
18334
|
+
* @returns {Promise<ContraindicationDynamic>} The updated contraindication.
|
|
18335
|
+
* @throws {Error} If the contraindication is not found.
|
|
18336
|
+
*/
|
|
18337
|
+
async updateContraindication(contraindication) {
|
|
18338
|
+
const contraindications = await this.getAllContraindicationsForFilter();
|
|
18339
|
+
const index = contraindications.findIndex(
|
|
18340
|
+
(c) => c.id === contraindication.id
|
|
18341
|
+
);
|
|
18342
|
+
if (index === -1) {
|
|
18343
|
+
throw new Error("Contraindication not found.");
|
|
18344
|
+
}
|
|
18345
|
+
contraindications[index] = contraindication;
|
|
18346
|
+
await updateDoc35(this.contraindicationsDocRef, { contraindications });
|
|
18347
|
+
return contraindication;
|
|
18348
|
+
}
|
|
18349
|
+
/**
|
|
18350
|
+
* @description Deletes a contraindication by its ID.
|
|
18351
|
+
* @param {string} contraindicationId - The ID of the contraindication to delete.
|
|
18352
|
+
* @returns {Promise<void>}
|
|
18353
|
+
*/
|
|
18354
|
+
async deleteContraindication(contraindicationId) {
|
|
18355
|
+
const contraindications = await this.getAllContraindicationsForFilter();
|
|
18356
|
+
const toRemove = contraindications.find((c) => c.id === contraindicationId);
|
|
18357
|
+
if (!toRemove) {
|
|
18358
|
+
return;
|
|
18359
|
+
}
|
|
18360
|
+
await updateDoc35(this.contraindicationsDocRef, {
|
|
18361
|
+
contraindications: arrayRemove9(toRemove)
|
|
18362
|
+
});
|
|
18363
|
+
}
|
|
18364
|
+
};
|
|
18365
|
+
|
|
18366
|
+
// src/backoffice/types/static/contraindication.types.ts
|
|
18367
|
+
var Contraindication = /* @__PURE__ */ ((Contraindication2) => {
|
|
18368
|
+
Contraindication2["SENSITIVE_SKIN"] = "sensitive_skin";
|
|
18369
|
+
Contraindication2["RECENT_TANNING"] = "recent_tanning";
|
|
18370
|
+
Contraindication2["RECENT_BOTOX"] = "recent_botox";
|
|
18371
|
+
Contraindication2["RECENT_FILLERS"] = "recent_fillers";
|
|
18372
|
+
Contraindication2["SKIN_ALLERGIES"] = "skin_allergies";
|
|
18373
|
+
Contraindication2["MEDICATIONS"] = "medications";
|
|
18374
|
+
Contraindication2["RECENT_CHEMICAL_PEEL"] = "recent_chemical_peel";
|
|
18375
|
+
Contraindication2["RECENT_LASER"] = "recent_laser";
|
|
18376
|
+
Contraindication2["SKIN_INFLAMMATION"] = "skin_inflammation";
|
|
18377
|
+
Contraindication2["OPEN_WOUNDS"] = "open_wounds";
|
|
18378
|
+
Contraindication2["HERPES_SIMPLEX"] = "herpes_simplex";
|
|
18379
|
+
Contraindication2["COLD_SORES"] = "cold_sores";
|
|
18380
|
+
return Contraindication2;
|
|
18381
|
+
})(Contraindication || {});
|
|
18382
|
+
|
|
17139
18383
|
// src/backoffice/types/static/treatment-benefit.types.ts
|
|
17140
18384
|
var TreatmentBenefit = /* @__PURE__ */ ((TreatmentBenefit2) => {
|
|
17141
18385
|
TreatmentBenefit2["WRINKLE_REDUCTION"] = "wrinkle_reduction";
|
|
@@ -17194,6 +18438,7 @@ export {
|
|
|
17194
18438
|
ClinicPhotoTag,
|
|
17195
18439
|
ClinicService,
|
|
17196
18440
|
ClinicTag,
|
|
18441
|
+
ConstantsService,
|
|
17197
18442
|
Contraindication,
|
|
17198
18443
|
CosmeticAllergySubtype,
|
|
17199
18444
|
Currency,
|