@blackcode_sa/metaestetics-api 1.5.27 → 1.5.29

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.
Files changed (35) hide show
  1. package/dist/admin/index.d.mts +1199 -1
  2. package/dist/admin/index.d.ts +1199 -1
  3. package/dist/admin/index.js +1337 -2
  4. package/dist/admin/index.mjs +1333 -2
  5. package/dist/backoffice/index.d.mts +99 -7
  6. package/dist/backoffice/index.d.ts +99 -7
  7. package/dist/index.d.mts +4184 -2426
  8. package/dist/index.d.ts +4184 -2426
  9. package/dist/index.js +2692 -1546
  10. package/dist/index.mjs +2663 -1502
  11. package/package.json +1 -1
  12. package/src/admin/aggregation/clinic/clinic.aggregation.service.ts +642 -0
  13. package/src/admin/aggregation/patient/patient.aggregation.service.ts +141 -0
  14. package/src/admin/aggregation/practitioner/practitioner.aggregation.service.ts +433 -0
  15. package/src/admin/aggregation/procedure/procedure.aggregation.service.ts +508 -0
  16. package/src/admin/index.ts +53 -4
  17. package/src/index.ts +28 -4
  18. package/src/services/calendar/calendar-refactored.service.ts +1 -1
  19. package/src/services/clinic/clinic.service.ts +344 -77
  20. package/src/services/clinic/utils/clinic.utils.ts +187 -8
  21. package/src/services/clinic/utils/filter.utils.d.ts +23 -0
  22. package/src/services/clinic/utils/filter.utils.ts +264 -0
  23. package/src/services/practitioner/practitioner.service.ts +616 -5
  24. package/src/services/procedure/procedure.service.ts +678 -52
  25. package/src/services/reviews/reviews.service.ts +842 -0
  26. package/src/types/clinic/index.ts +24 -56
  27. package/src/types/practitioner/index.ts +34 -33
  28. package/src/types/procedure/index.ts +39 -0
  29. package/src/types/profile/index.ts +1 -1
  30. package/src/types/reviews/index.ts +126 -0
  31. package/src/validations/clinic.schema.ts +37 -64
  32. package/src/validations/practitioner.schema.ts +42 -32
  33. package/src/validations/procedure.schema.ts +14 -3
  34. package/src/validations/reviews.schema.ts +189 -0
  35. package/src/services/clinic/utils/review.utils.ts +0 -93
@@ -8,6 +8,13 @@ import {
8
8
  PractitionerStatus,
9
9
  PractitionerTokenStatus,
10
10
  } from "../types/practitioner";
11
+ import { practitionerReviewInfoSchema } from "./reviews.schema";
12
+ import { clinicInfoSchema } from "./clinic.schema";
13
+ import { ProcedureFamily } from "../backoffice/types/static/procedure-family.types";
14
+ import {
15
+ Currency,
16
+ PricingMeasure,
17
+ } from "../backoffice/types/static/pricing.types";
11
18
 
12
19
  /**
13
20
  * Šema za validaciju osnovnih informacija o zdravstvenom radniku
@@ -18,7 +25,7 @@ export const practitionerBasicInfoSchema = z.object({
18
25
  title: z.string().min(2).max(100),
19
26
  email: z.string().email(),
20
27
  phoneNumber: z.string().regex(/^\+?[1-9]\d{1,14}$/, "Invalid phone number"),
21
- dateOfBirth: z.instanceof(Timestamp),
28
+ dateOfBirth: z.instanceof(Timestamp).or(z.date()),
22
29
  gender: z.enum(["male", "female", "other"]),
23
30
  profileImageUrl: z.string().url().optional(),
24
31
  bio: z.string().max(1000).optional(),
@@ -33,8 +40,8 @@ export const practitionerCertificationSchema = z.object({
33
40
  specialties: z.array(z.nativeEnum(CertificationSpecialty)),
34
41
  licenseNumber: z.string().min(3).max(50),
35
42
  issuingAuthority: z.string().min(2).max(100),
36
- issueDate: z.instanceof(Timestamp),
37
- expiryDate: z.instanceof(Timestamp).optional(),
43
+ issueDate: z.instanceof(Timestamp).or(z.date()),
44
+ expiryDate: z.instanceof(Timestamp).or(z.date()).optional(),
38
45
  verificationStatus: z.enum(["pending", "verified", "rejected"]),
39
46
  });
40
47
 
@@ -60,8 +67,8 @@ export const practitionerWorkingHoursSchema = z.object({
60
67
  friday: timeSlotSchema,
61
68
  saturday: timeSlotSchema,
62
69
  sunday: timeSlotSchema,
63
- createdAt: z.instanceof(Timestamp),
64
- updatedAt: z.instanceof(Timestamp),
70
+ createdAt: z.instanceof(Timestamp).or(z.date()),
71
+ updatedAt: z.instanceof(Timestamp).or(z.date()),
65
72
  });
66
73
 
67
74
  /**
@@ -79,35 +86,30 @@ export const practitionerClinicWorkingHoursSchema = z.object({
79
86
  sunday: timeSlotSchema,
80
87
  }),
81
88
  isActive: z.boolean(),
82
- createdAt: z.instanceof(Timestamp),
83
- updatedAt: z.instanceof(Timestamp),
89
+ createdAt: z.instanceof(Timestamp).or(z.date()),
90
+ updatedAt: z.instanceof(Timestamp).or(z.date()),
84
91
  });
85
92
 
86
93
  /**
87
- * Šema za validaciju recenzije zdravstvenog radnika
94
+ * Schema matching ProcedureSummaryInfo interface
88
95
  */
89
- export const practitionerReviewSchema = z.object({
96
+ export const procedureSummaryInfoSchema = z.object({
90
97
  id: z.string().min(1),
91
- practitionerId: z.string().min(1),
92
- patientId: z.string().min(1),
98
+ name: z.string().min(1),
99
+ description: z.string().optional(),
100
+ photo: z.string().optional(),
101
+ family: z.nativeEnum(ProcedureFamily),
102
+ categoryName: z.string(),
103
+ subcategoryName: z.string(),
104
+ technologyName: z.string(),
105
+ price: z.number().nonnegative(),
106
+ pricingMeasure: z.nativeEnum(PricingMeasure),
107
+ currency: z.nativeEnum(Currency),
108
+ duration: z.number().int().positive(),
93
109
  clinicId: z.string().min(1),
94
- rating: z.number().min(1).max(5),
95
- comment: z.string().max(1000),
96
- createdAt: z.instanceof(Timestamp),
97
- updatedAt: z.instanceof(Timestamp),
98
- isVerified: z.boolean(),
99
- });
100
-
101
- /**
102
- * Šema za validaciju procedura koje zdravstveni radnik izvodi u klinici
103
- */
104
- export const practitionerClinicProceduresSchema = z.object({
110
+ clinicName: z.string().min(1),
105
111
  practitionerId: z.string().min(1),
106
- clinicId: z.string().min(1),
107
- procedures: z.array(z.string()).min(1),
108
- isActive: z.boolean(),
109
- createdAt: z.instanceof(Timestamp),
110
- updatedAt: z.instanceof(Timestamp),
112
+ practitionerName: z.string().min(1),
111
113
  });
112
114
 
113
115
  /**
@@ -120,11 +122,15 @@ export const practitionerSchema = z.object({
120
122
  certification: practitionerCertificationSchema,
121
123
  clinics: z.array(z.string()),
122
124
  clinicWorkingHours: z.array(practitionerClinicWorkingHoursSchema),
125
+ clinicsInfo: z.array(clinicInfoSchema),
126
+ procedures: z.array(z.string()),
127
+ proceduresInfo: z.array(procedureSummaryInfoSchema),
128
+ reviewInfo: practitionerReviewInfoSchema,
123
129
  isActive: z.boolean(),
124
130
  isVerified: z.boolean(),
125
131
  status: z.nativeEnum(PractitionerStatus),
126
- createdAt: z.instanceof(Timestamp),
127
- updatedAt: z.instanceof(Timestamp),
132
+ createdAt: z.instanceof(Timestamp).or(z.date()),
133
+ updatedAt: z.instanceof(Timestamp).or(z.date()),
128
134
  });
129
135
 
130
136
  /**
@@ -136,6 +142,8 @@ export const createPractitionerSchema = z.object({
136
142
  certification: practitionerCertificationSchema,
137
143
  clinics: z.array(z.string()).optional(),
138
144
  clinicWorkingHours: z.array(practitionerClinicWorkingHoursSchema).optional(),
145
+ clinicsInfo: z.array(clinicInfoSchema).optional(),
146
+ proceduresInfo: z.array(procedureSummaryInfoSchema).optional(),
139
147
  isActive: z.boolean(),
140
148
  isVerified: z.boolean(),
141
149
  status: z.nativeEnum(PractitionerStatus).optional(),
@@ -149,6 +157,8 @@ export const createDraftPractitionerSchema = z.object({
149
157
  certification: practitionerCertificationSchema,
150
158
  clinics: z.array(z.string()).optional(),
151
159
  clinicWorkingHours: z.array(practitionerClinicWorkingHoursSchema).optional(),
160
+ clinicsInfo: z.array(clinicInfoSchema).optional(),
161
+ proceduresInfo: z.array(procedureSummaryInfoSchema).optional(),
152
162
  isActive: z.boolean().optional().default(false),
153
163
  isVerified: z.boolean().optional().default(false),
154
164
  });
@@ -164,10 +174,10 @@ export const practitionerTokenSchema = z.object({
164
174
  clinicId: z.string().min(1),
165
175
  status: z.nativeEnum(PractitionerTokenStatus),
166
176
  createdBy: z.string().min(1),
167
- createdAt: z.instanceof(Timestamp),
168
- expiresAt: z.instanceof(Timestamp),
177
+ createdAt: z.instanceof(Timestamp).or(z.date()),
178
+ expiresAt: z.instanceof(Timestamp).or(z.date()),
169
179
  usedBy: z.string().optional(),
170
- usedAt: z.instanceof(Timestamp).optional(),
180
+ usedAt: z.instanceof(Timestamp).or(z.date()).optional(),
171
181
  });
172
182
 
173
183
  /**
@@ -4,6 +4,8 @@ import {
4
4
  Currency,
5
5
  PricingMeasure,
6
6
  } from "../backoffice/types/static/pricing.types";
7
+ import { clinicInfoSchema, doctorInfoSchema } from "./clinic.schema";
8
+ import { procedureReviewInfoSchema } from "./reviews.schema";
7
9
 
8
10
  /**
9
11
  * Schema for creating a new procedure
@@ -28,13 +30,19 @@ export const createProcedureSchema = z.object({
28
30
  * Schema for updating an existing procedure
29
31
  */
30
32
  export const updateProcedureSchema = z.object({
31
- name: z.string().min(1).max(200).optional(),
32
- description: z.string().min(1).max(2000).optional(),
33
+ name: z.string().min(3).max(100).optional(),
34
+ description: z.string().min(3).max(1000).optional(),
33
35
  price: z.number().min(0).optional(),
34
36
  currency: z.nativeEnum(Currency).optional(),
35
37
  pricingMeasure: z.nativeEnum(PricingMeasure).optional(),
36
- duration: z.number().min(1).max(480).optional(), // Max 8 hours
38
+ duration: z.number().min(0).optional(),
37
39
  isActive: z.boolean().optional(),
40
+ practitionerId: z.string().optional(),
41
+ categoryId: z.string().optional(),
42
+ subcategoryId: z.string().optional(),
43
+ technologyId: z.string().optional(),
44
+ productId: z.string().optional(),
45
+ clinicBranchId: z.string().optional(),
38
46
  });
39
47
 
40
48
  /**
@@ -52,6 +60,9 @@ export const procedureSchema = createProcedureSchema.extend({
52
60
  postRequirements: z.array(z.any()), // We'll validate requirements separately
53
61
  certificationRequirement: z.any(), // We'll validate certification requirement separately
54
62
  documentationTemplates: z.array(z.any()), // We'll validate documentation templates separately
63
+ clinicInfo: clinicInfoSchema, // Clinic info validation
64
+ doctorInfo: doctorInfoSchema, // Doctor info validation
65
+ reviewInfo: procedureReviewInfoSchema, // Procedure review info validation
55
66
  isActive: z.boolean(),
56
67
  createdAt: z.date(),
57
68
  updatedAt: z.date(),
@@ -0,0 +1,189 @@
1
+ import { z } from "zod";
2
+
3
+ /**
4
+ * Base review schema with common properties
5
+ */
6
+ const baseReviewSchema = z.object({
7
+ id: z.string().min(1),
8
+ patientId: z.string().min(1),
9
+ fullReviewId: z.string().min(1),
10
+ createdAt: z.date(),
11
+ updatedAt: z.date(),
12
+ comment: z.string().min(1).max(2000),
13
+ isVerified: z.boolean(),
14
+ isPublished: z.boolean(),
15
+ });
16
+
17
+ /**
18
+ * Base review creation schema
19
+ */
20
+ const baseReviewCreateSchema = z.object({
21
+ patientId: z.string().min(1),
22
+ comment: z.string().min(1).max(2000),
23
+ isVerified: z.boolean().default(false),
24
+ isPublished: z.boolean().default(true),
25
+ });
26
+
27
+ /**
28
+ * Clinic review schema
29
+ */
30
+ export const clinicReviewSchema = baseReviewSchema.extend({
31
+ clinicId: z.string().min(1),
32
+ cleanliness: z.number().min(1).max(5),
33
+ facilities: z.number().min(1).max(5),
34
+ staffFriendliness: z.number().min(1).max(5),
35
+ waitingTime: z.number().min(1).max(5),
36
+ accessibility: z.number().min(1).max(5),
37
+ overallRating: z.number().min(1).max(5),
38
+ wouldRecommend: z.boolean(),
39
+ });
40
+
41
+ /**
42
+ * Clinic review creation schema
43
+ */
44
+ export const createClinicReviewSchema = baseReviewCreateSchema.extend({
45
+ clinicId: z.string().min(1),
46
+ cleanliness: z.number().min(1).max(5),
47
+ facilities: z.number().min(1).max(5),
48
+ staffFriendliness: z.number().min(1).max(5),
49
+ waitingTime: z.number().min(1).max(5),
50
+ accessibility: z.number().min(1).max(5),
51
+ wouldRecommend: z.boolean(),
52
+ });
53
+
54
+ /**
55
+ * Practitioner review schema
56
+ */
57
+ export const practitionerReviewSchema = baseReviewSchema.extend({
58
+ practitionerId: z.string().min(1),
59
+ knowledgeAndExpertise: z.number().min(1).max(5),
60
+ communicationSkills: z.number().min(1).max(5),
61
+ bedSideManner: z.number().min(1).max(5),
62
+ thoroughness: z.number().min(1).max(5),
63
+ trustworthiness: z.number().min(1).max(5),
64
+ overallRating: z.number().min(1).max(5),
65
+ wouldRecommend: z.boolean(),
66
+ });
67
+
68
+ /**
69
+ * Practitioner review creation schema
70
+ */
71
+ export const createPractitionerReviewSchema = baseReviewCreateSchema.extend({
72
+ practitionerId: z.string().min(1),
73
+ knowledgeAndExpertise: z.number().min(1).max(5),
74
+ communicationSkills: z.number().min(1).max(5),
75
+ bedSideManner: z.number().min(1).max(5),
76
+ thoroughness: z.number().min(1).max(5),
77
+ trustworthiness: z.number().min(1).max(5),
78
+ wouldRecommend: z.boolean(),
79
+ });
80
+
81
+ /**
82
+ * Procedure review schema
83
+ */
84
+ export const procedureReviewSchema = baseReviewSchema.extend({
85
+ procedureId: z.string().min(1),
86
+ effectivenessOfTreatment: z.number().min(1).max(5),
87
+ outcomeExplanation: z.number().min(1).max(5),
88
+ painManagement: z.number().min(1).max(5),
89
+ followUpCare: z.number().min(1).max(5),
90
+ valueForMoney: z.number().min(1).max(5),
91
+ overallRating: z.number().min(1).max(5),
92
+ wouldRecommend: z.boolean(),
93
+ });
94
+
95
+ /**
96
+ * Procedure review creation schema
97
+ */
98
+ export const createProcedureReviewSchema = baseReviewCreateSchema.extend({
99
+ procedureId: z.string().min(1),
100
+ effectivenessOfTreatment: z.number().min(1).max(5),
101
+ outcomeExplanation: z.number().min(1).max(5),
102
+ painManagement: z.number().min(1).max(5),
103
+ followUpCare: z.number().min(1).max(5),
104
+ valueForMoney: z.number().min(1).max(5),
105
+ wouldRecommend: z.boolean(),
106
+ });
107
+
108
+ /**
109
+ * Review info (condensed) schema for clinic
110
+ */
111
+ export const clinicReviewInfoSchema = z.object({
112
+ totalReviews: z.number().min(0),
113
+ averageRating: z.number().min(0).max(5),
114
+ cleanliness: z.number().min(0).max(5),
115
+ facilities: z.number().min(0).max(5),
116
+ staffFriendliness: z.number().min(0).max(5),
117
+ waitingTime: z.number().min(0).max(5),
118
+ accessibility: z.number().min(0).max(5),
119
+ recommendationPercentage: z.number().min(0).max(100),
120
+ });
121
+
122
+ /**
123
+ * Review info (condensed) schema for practitioner
124
+ */
125
+ export const practitionerReviewInfoSchema = z.object({
126
+ totalReviews: z.number().min(0),
127
+ averageRating: z.number().min(0).max(5),
128
+ knowledgeAndExpertise: z.number().min(0).max(5),
129
+ communicationSkills: z.number().min(0).max(5),
130
+ bedSideManner: z.number().min(0).max(5),
131
+ thoroughness: z.number().min(0).max(5),
132
+ trustworthiness: z.number().min(0).max(5),
133
+ recommendationPercentage: z.number().min(0).max(100),
134
+ });
135
+
136
+ /**
137
+ * Review info (condensed) schema for procedure
138
+ */
139
+ export const procedureReviewInfoSchema = z.object({
140
+ totalReviews: z.number().min(0),
141
+ averageRating: z.number().min(0).max(5),
142
+ effectivenessOfTreatment: z.number().min(0).max(5),
143
+ outcomeExplanation: z.number().min(0).max(5),
144
+ painManagement: z.number().min(0).max(5),
145
+ followUpCare: z.number().min(0).max(5),
146
+ valueForMoney: z.number().min(0).max(5),
147
+ recommendationPercentage: z.number().min(0).max(100),
148
+ });
149
+
150
+ /**
151
+ * Combined review schema
152
+ */
153
+ export const reviewSchema = z.object({
154
+ id: z.string().min(1),
155
+ appointmentId: z.string().min(1),
156
+ patientId: z.string().min(1),
157
+ createdAt: z.date(),
158
+ updatedAt: z.date(),
159
+ clinicReview: clinicReviewSchema.optional(),
160
+ practitionerReview: practitionerReviewSchema.optional(),
161
+ procedureReview: procedureReviewSchema.optional(),
162
+ overallComment: z.string().min(1).max(2000),
163
+ overallRating: z.number().min(1).max(5),
164
+ });
165
+
166
+ /**
167
+ * Combined review creation schema
168
+ */
169
+ export const createReviewSchema = z
170
+ .object({
171
+ patientId: z.string().min(1),
172
+ clinicReview: createClinicReviewSchema.optional(),
173
+ practitionerReview: createPractitionerReviewSchema.optional(),
174
+ procedureReview: createProcedureReviewSchema.optional(),
175
+ overallComment: z.string().min(1).max(2000),
176
+ })
177
+ .refine(
178
+ (data) => {
179
+ // Ensure at least one review type is provided
180
+ return (
181
+ data.clinicReview || data.practitionerReview || data.procedureReview
182
+ );
183
+ },
184
+ {
185
+ message:
186
+ "At least one review type (clinic, practitioner, or procedure) must be provided",
187
+ path: ["reviewType"],
188
+ }
189
+ );
@@ -1,93 +0,0 @@
1
- import {
2
- collection,
3
- doc,
4
- setDoc,
5
- Timestamp,
6
- Firestore,
7
- getDoc,
8
- addDoc,
9
- } from "firebase/firestore";
10
- import { Clinic, ClinicReview } from "../../../types/clinic";
11
- import { clinicReviewSchema } from "../../../validations/clinic.schema";
12
- import { getClinic, updateClinic } from "./clinic.utils";
13
- import { FirebaseApp } from "firebase/app";
14
-
15
- /**
16
- * Adds a review to a clinic
17
- * @param db - Firestore database instance
18
- * @param clinicId - ID of the clinic to add the review to
19
- * @param review - Review data
20
- * @param app - Firebase app instance
21
- * @returns The created review
22
- */
23
- export async function addReview(
24
- db: Firestore,
25
- clinicId: string,
26
- review: Omit<
27
- ClinicReview,
28
- "id" | "clinicId" | "createdAt" | "updatedAt" | "isVerified"
29
- >,
30
- app: FirebaseApp
31
- ): Promise<ClinicReview> {
32
- // Proveravamo da li klinika postoji
33
- const clinicRef = doc(db, "clinics", clinicId);
34
- const clinicSnap = await getDoc(clinicRef);
35
-
36
- if (!clinicSnap.exists()) {
37
- throw new Error("Clinic not found");
38
- }
39
-
40
- const clinic = clinicSnap.data();
41
-
42
- // Kreiramo recenziju
43
- const now = Timestamp.now();
44
- const reviewData: ClinicReview = {
45
- ...review,
46
- id: doc(collection(db, "clinic_reviews")).id,
47
- clinicId,
48
- createdAt: now,
49
- updatedAt: now,
50
- isVerified: false,
51
- };
52
-
53
- // Validacija
54
- clinicReviewSchema.parse(reviewData);
55
-
56
- // Čuvamo recenziju
57
- await addDoc(collection(db, "clinic_reviews"), reviewData);
58
-
59
- // Ažuriramo prosečnu ocenu klinike
60
- const newRating = clinic.rating
61
- ? {
62
- average:
63
- (clinic.rating.average * clinic.rating.count + review.rating) /
64
- (clinic.rating.count + 1),
65
- count: clinic.rating.count + 1,
66
- }
67
- : { average: review.rating, count: 1 };
68
-
69
- await updateClinic(
70
- db,
71
- clinicId,
72
- {
73
- rating: newRating,
74
- reviews: [...clinic.reviews, reviewData.id],
75
- // Add the review info to reviewsInfo array
76
- reviewsInfo: [
77
- ...clinic.reviewsInfo,
78
- {
79
- id: reviewData.id,
80
- patientId: reviewData.patientId,
81
- rating: reviewData.rating,
82
- comment: reviewData.comment,
83
- createdAt: reviewData.createdAt,
84
- },
85
- ],
86
- },
87
- "system", // System update, no admin ID needed
88
- null, // No clinic admin service needed for system updates
89
- app
90
- );
91
-
92
- return reviewData;
93
- }