@blackcode_sa/metaestetics-api 1.6.1 → 1.6.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +34 -184
- package/dist/index.d.ts +34 -184
- package/dist/index.js +493 -412
- package/dist/index.mjs +476 -392
- package/package.json +1 -1
- package/src/services/patient/patient.service.ts +31 -1
- package/src/services/patient/utils/practitioner.utils.ts +79 -1
- package/src/services/practitioner/practitioner.service.ts +6 -1
- package/src/types/patient/index.ts +6 -0
- package/src/validations/clinic.schema.ts +5 -25
- package/src/validations/practitioner.schema.ts +3 -3
- package/src/validations/procedure.schema.ts +1 -1
- package/src/validations/shared.schema.ts +78 -0
package/package.json
CHANGED
|
@@ -31,6 +31,7 @@ import {
|
|
|
31
31
|
PatientClinic,
|
|
32
32
|
SearchPatientsParams,
|
|
33
33
|
RequesterInfo,
|
|
34
|
+
PatientProfileForDoctor,
|
|
34
35
|
} from "../../types/patient";
|
|
35
36
|
import { Auth } from "firebase/auth";
|
|
36
37
|
import { Firestore } from "firebase/firestore";
|
|
@@ -99,7 +100,10 @@ import {
|
|
|
99
100
|
removeClinicUtil,
|
|
100
101
|
} from "./utils/medical-stuff.utils";
|
|
101
102
|
|
|
102
|
-
import {
|
|
103
|
+
import {
|
|
104
|
+
getPatientsByPractitionerUtil,
|
|
105
|
+
getPatientsByPractitionerWithDetailsUtil,
|
|
106
|
+
} from "./utils/practitioner.utils";
|
|
103
107
|
import { getPatientsByClinicUtil } from "./utils/clinic.utils";
|
|
104
108
|
|
|
105
109
|
export class PatientService extends BaseService {
|
|
@@ -543,6 +547,32 @@ export class PatientService extends BaseService {
|
|
|
543
547
|
return getPatientsByPractitionerUtil(this.db, practitionerId, options);
|
|
544
548
|
}
|
|
545
549
|
|
|
550
|
+
/**
|
|
551
|
+
* Gets all patients associated with a specific practitioner with their sensitive information.
|
|
552
|
+
*
|
|
553
|
+
* @param {string} practitionerId - ID of the practitioner whose patients to retrieve
|
|
554
|
+
* @param {Object} options - Optional parameters for pagination
|
|
555
|
+
* @param {number} options.limit - Maximum number of profiles to return
|
|
556
|
+
* @param {string} options.startAfter - The ID of the document to start after (for pagination)
|
|
557
|
+
* @returns {Promise<PatientProfileForDoctor[]>} A promise resolving to an array of patient profiles with sensitive info
|
|
558
|
+
*/
|
|
559
|
+
async getPatientsByPractitionerWithDetails(
|
|
560
|
+
practitionerId: string,
|
|
561
|
+
options?: {
|
|
562
|
+
limit?: number;
|
|
563
|
+
startAfter?: string;
|
|
564
|
+
}
|
|
565
|
+
): Promise<PatientProfileForDoctor[]> {
|
|
566
|
+
console.log(
|
|
567
|
+
`[PatientService.getPatientsByPractitionerWithDetails] Fetching detailed patient profiles for practitioner: ${practitionerId}`
|
|
568
|
+
);
|
|
569
|
+
return getPatientsByPractitionerWithDetailsUtil(
|
|
570
|
+
this.db,
|
|
571
|
+
practitionerId,
|
|
572
|
+
options
|
|
573
|
+
);
|
|
574
|
+
}
|
|
575
|
+
|
|
546
576
|
/**
|
|
547
577
|
* Gets all patients associated with a specific clinic.
|
|
548
578
|
*
|
|
@@ -10,7 +10,13 @@ import {
|
|
|
10
10
|
getDoc,
|
|
11
11
|
QueryConstraint,
|
|
12
12
|
} from "firebase/firestore";
|
|
13
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
PatientProfile,
|
|
15
|
+
PatientSensitiveInfo,
|
|
16
|
+
PatientProfileForDoctor,
|
|
17
|
+
PATIENTS_COLLECTION,
|
|
18
|
+
} from "../../../types/patient";
|
|
19
|
+
import { getSensitiveInfoDocRef } from "./docs.utils";
|
|
14
20
|
|
|
15
21
|
/**
|
|
16
22
|
* Retrieves all patients associated with a specific practitioner with pagination support.
|
|
@@ -78,3 +84,75 @@ export const getPatientsByPractitionerUtil = async (
|
|
|
78
84
|
);
|
|
79
85
|
}
|
|
80
86
|
};
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Retrieves all patients associated with a specific practitioner with their sensitive information.
|
|
90
|
+
*
|
|
91
|
+
* @param {Firestore} db - Firestore instance
|
|
92
|
+
* @param {string} practitionerId - ID of the practitioner whose patients to retrieve
|
|
93
|
+
* @param {Object} options - Optional parameters for pagination
|
|
94
|
+
* @param {number} options.limit - Maximum number of profiles to return
|
|
95
|
+
* @param {string} options.startAfter - The ID of the document to start after (for pagination)
|
|
96
|
+
* @returns {Promise<PatientProfileForDoctor[]>} A promise resolving to an array of patient profiles with sensitive info
|
|
97
|
+
*/
|
|
98
|
+
export const getPatientsByPractitionerWithDetailsUtil = async (
|
|
99
|
+
db: Firestore,
|
|
100
|
+
practitionerId: string,
|
|
101
|
+
options?: { limit?: number; startAfter?: string }
|
|
102
|
+
): Promise<PatientProfileForDoctor[]> => {
|
|
103
|
+
try {
|
|
104
|
+
console.log(
|
|
105
|
+
`[getPatientsByPractitionerWithDetailsUtil] Fetching detailed patient profiles for practitioner ID: ${practitionerId} with options:`,
|
|
106
|
+
options
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
// First, get all patient profiles for this practitioner
|
|
110
|
+
const patientProfiles = await getPatientsByPractitionerUtil(
|
|
111
|
+
db,
|
|
112
|
+
practitionerId,
|
|
113
|
+
options
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
// Then, fetch sensitive info for each patient
|
|
117
|
+
const patientProfilesWithDetails: PatientProfileForDoctor[] =
|
|
118
|
+
await Promise.all(
|
|
119
|
+
patientProfiles.map(async (profile) => {
|
|
120
|
+
try {
|
|
121
|
+
const sensitiveInfoDoc = await getDoc(
|
|
122
|
+
getSensitiveInfoDocRef(db, profile.id)
|
|
123
|
+
);
|
|
124
|
+
const sensitiveInfo = sensitiveInfoDoc.exists()
|
|
125
|
+
? (sensitiveInfoDoc.data() as PatientSensitiveInfo)
|
|
126
|
+
: undefined;
|
|
127
|
+
|
|
128
|
+
return {
|
|
129
|
+
patientProfile: profile,
|
|
130
|
+
patientSensitiveInfo: sensitiveInfo,
|
|
131
|
+
};
|
|
132
|
+
} catch (error) {
|
|
133
|
+
console.error(
|
|
134
|
+
`[getPatientsByPractitionerWithDetailsUtil] Error fetching sensitive info for patient ${profile.id}:`,
|
|
135
|
+
error
|
|
136
|
+
);
|
|
137
|
+
// Return profile without sensitive info in case of error
|
|
138
|
+
return { patientProfile: profile };
|
|
139
|
+
}
|
|
140
|
+
})
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
console.log(
|
|
144
|
+
`[getPatientsByPractitionerWithDetailsUtil] Found ${patientProfilesWithDetails.length} detailed patient profiles for practitioner ID: ${practitionerId}`
|
|
145
|
+
);
|
|
146
|
+
return patientProfilesWithDetails;
|
|
147
|
+
} catch (error) {
|
|
148
|
+
console.error(
|
|
149
|
+
`[getPatientsByPractitionerWithDetailsUtil] Error fetching detailed patient profiles:`,
|
|
150
|
+
error
|
|
151
|
+
);
|
|
152
|
+
throw new Error(
|
|
153
|
+
`Failed to retrieve detailed patient profiles: ${
|
|
154
|
+
error instanceof Error ? error.message : String(error)
|
|
155
|
+
}`
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
};
|
|
@@ -220,12 +220,17 @@ export class PractitionerService extends BaseService {
|
|
|
220
220
|
for (const cId of clinics) {
|
|
221
221
|
const clinicData = await this.getClinicService().getClinic(cId);
|
|
222
222
|
if (clinicData) {
|
|
223
|
+
// Ensure we're creating a ClinicInfo object that matches the interface structure
|
|
223
224
|
clinicsInfo.push({
|
|
224
225
|
id: clinicData.id,
|
|
225
226
|
name: clinicData.name,
|
|
226
227
|
location: clinicData.location,
|
|
227
228
|
contactInfo: clinicData.contactInfo,
|
|
228
|
-
|
|
229
|
+
// Make sure we're using the right property for featuredPhoto
|
|
230
|
+
featuredPhoto:
|
|
231
|
+
clinicData.featuredPhotos && clinicData.featuredPhotos.length > 0
|
|
232
|
+
? clinicData.featuredPhotos[0]
|
|
233
|
+
: clinicData.coverPhoto || "",
|
|
229
234
|
description: clinicData.description || null,
|
|
230
235
|
});
|
|
231
236
|
}
|
|
@@ -237,3 +237,9 @@ export interface PatientProfileComplete {
|
|
|
237
237
|
patientMedicalInfo?: PatientMedicalInfo;
|
|
238
238
|
patientLocationInfo?: PatientLocationInfo;
|
|
239
239
|
}
|
|
240
|
+
|
|
241
|
+
// Create a type that combines patientProfile and patientSensitiveInfo, this will be used in the doctor's app only
|
|
242
|
+
export interface PatientProfileForDoctor {
|
|
243
|
+
patientProfile?: PatientProfile;
|
|
244
|
+
patientSensitiveInfo?: PatientSensitiveInfo;
|
|
245
|
+
}
|
|
@@ -13,8 +13,12 @@ import {
|
|
|
13
13
|
Currency,
|
|
14
14
|
PricingMeasure,
|
|
15
15
|
} from "../backoffice/types/static/pricing.types";
|
|
16
|
-
import { procedureSummaryInfoSchema } from "./practitioner.schema";
|
|
17
16
|
import { clinicReviewInfoSchema } from "./reviews.schema";
|
|
17
|
+
import {
|
|
18
|
+
procedureSummaryInfoSchema,
|
|
19
|
+
clinicInfoSchema,
|
|
20
|
+
doctorInfoSchema,
|
|
21
|
+
} from "./shared.schema";
|
|
18
22
|
|
|
19
23
|
/**
|
|
20
24
|
* Validaciona šema za kontakt informacije
|
|
@@ -92,30 +96,6 @@ export const adminInfoSchema = z.object({
|
|
|
92
96
|
email: z.string().email(),
|
|
93
97
|
});
|
|
94
98
|
|
|
95
|
-
/**
|
|
96
|
-
* Validaciona šema za osnovne informacije o klinici (for aggregation)
|
|
97
|
-
*/
|
|
98
|
-
export const clinicInfoSchema = z.object({
|
|
99
|
-
id: z.string(),
|
|
100
|
-
featuredPhoto: z.string(),
|
|
101
|
-
name: z.string(),
|
|
102
|
-
description: z.string().nullable().optional(),
|
|
103
|
-
location: clinicLocationSchema,
|
|
104
|
-
contactInfo: clinicContactInfoSchema,
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Validaciona šema za informacije o doktoru (for aggregation in Clinic)
|
|
109
|
-
*/
|
|
110
|
-
export const doctorInfoSchema = z.object({
|
|
111
|
-
id: z.string(),
|
|
112
|
-
name: z.string(),
|
|
113
|
-
description: z.string().nullable().optional(),
|
|
114
|
-
photo: z.string(),
|
|
115
|
-
rating: z.number().min(0).max(5),
|
|
116
|
-
services: z.array(z.string()), // List of procedure IDs practitioner offers
|
|
117
|
-
});
|
|
118
|
-
|
|
119
99
|
/**
|
|
120
100
|
* @deprecated Replaced by procedureSummaryInfoSchema.
|
|
121
101
|
* Validaciona šema za informacije o usluzi (Old aggregation schema)
|
|
@@ -9,12 +9,12 @@ import {
|
|
|
9
9
|
PractitionerTokenStatus,
|
|
10
10
|
} from "../types/practitioner";
|
|
11
11
|
import { practitionerReviewInfoSchema } from "./reviews.schema";
|
|
12
|
-
import { clinicInfoSchema } from "./clinic.schema";
|
|
13
12
|
import { ProcedureFamily } from "../backoffice/types/static/procedure-family.types";
|
|
14
13
|
import {
|
|
15
14
|
Currency,
|
|
16
15
|
PricingMeasure,
|
|
17
16
|
} from "../backoffice/types/static/pricing.types";
|
|
17
|
+
import { clinicInfoSchema, procedureSummaryInfoSchema } from "./shared.schema";
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
20
|
* Šema za validaciju osnovnih informacija o zdravstvenom radniku
|
|
@@ -93,7 +93,7 @@ export const practitionerClinicWorkingHoursSchema = z.object({
|
|
|
93
93
|
/**
|
|
94
94
|
* Schema matching ProcedureSummaryInfo interface
|
|
95
95
|
*/
|
|
96
|
-
export const procedureSummaryInfoSchema = z.object({
|
|
96
|
+
/* export const procedureSummaryInfoSchema = z.object({
|
|
97
97
|
id: z.string().min(1),
|
|
98
98
|
name: z.string().min(1),
|
|
99
99
|
description: z.string().optional(),
|
|
@@ -110,7 +110,7 @@ export const procedureSummaryInfoSchema = z.object({
|
|
|
110
110
|
clinicName: z.string().min(1),
|
|
111
111
|
practitionerId: z.string().min(1),
|
|
112
112
|
practitionerName: z.string().min(1),
|
|
113
|
-
});
|
|
113
|
+
}); */
|
|
114
114
|
|
|
115
115
|
/**
|
|
116
116
|
* Šema za validaciju kompletnog profila zdravstvenog radnika
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
Currency,
|
|
5
5
|
PricingMeasure,
|
|
6
6
|
} from "../backoffice/types/static/pricing.types";
|
|
7
|
-
import { clinicInfoSchema, doctorInfoSchema } from "./
|
|
7
|
+
import { clinicInfoSchema, doctorInfoSchema } from "./shared.schema";
|
|
8
8
|
import { procedureReviewInfoSchema } from "./reviews.schema";
|
|
9
9
|
|
|
10
10
|
/**
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { Timestamp } from "firebase/firestore";
|
|
3
|
+
import { ProcedureFamily } from "../backoffice/types/static/procedure-family.types";
|
|
4
|
+
import {
|
|
5
|
+
Currency,
|
|
6
|
+
PricingMeasure,
|
|
7
|
+
} from "../backoffice/types/static/pricing.types";
|
|
8
|
+
// Remove import from clinic schema to avoid circular dependency
|
|
9
|
+
// import { clinicLocationSchema, clinicContactInfoSchema } from "./clinic.schema";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Validation schema for clinic contact information
|
|
13
|
+
*/
|
|
14
|
+
export const sharedClinicContactInfoSchema = z.object({
|
|
15
|
+
email: z.string().email(),
|
|
16
|
+
phoneNumber: z.string(),
|
|
17
|
+
alternativePhoneNumber: z.string().nullable().optional(),
|
|
18
|
+
website: z.string().nullable().optional(),
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Validation schema for clinic location
|
|
23
|
+
*/
|
|
24
|
+
export const sharedClinicLocationSchema = z.object({
|
|
25
|
+
address: z.string(),
|
|
26
|
+
city: z.string(),
|
|
27
|
+
country: z.string(),
|
|
28
|
+
postalCode: z.string(),
|
|
29
|
+
latitude: z.number().min(-90).max(90),
|
|
30
|
+
longitude: z.number().min(-180).max(180),
|
|
31
|
+
geohash: z.string().nullable().optional(),
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Schema for procedure summary info - shared between practitioner and clinic schemas
|
|
36
|
+
*/
|
|
37
|
+
export const procedureSummaryInfoSchema = z.object({
|
|
38
|
+
id: z.string().min(1),
|
|
39
|
+
name: z.string().min(1),
|
|
40
|
+
description: z.string().optional(),
|
|
41
|
+
photo: z.string().optional(),
|
|
42
|
+
family: z.nativeEnum(ProcedureFamily),
|
|
43
|
+
categoryName: z.string(),
|
|
44
|
+
subcategoryName: z.string(),
|
|
45
|
+
technologyName: z.string(),
|
|
46
|
+
price: z.number().nonnegative(),
|
|
47
|
+
pricingMeasure: z.nativeEnum(PricingMeasure),
|
|
48
|
+
currency: z.nativeEnum(Currency),
|
|
49
|
+
duration: z.number().int().positive(),
|
|
50
|
+
clinicId: z.string().min(1),
|
|
51
|
+
clinicName: z.string().min(1),
|
|
52
|
+
practitionerId: z.string().min(1),
|
|
53
|
+
practitionerName: z.string().min(1),
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Schema for clinic info - shared between practitioner and clinic schemas
|
|
58
|
+
*/
|
|
59
|
+
export const clinicInfoSchema = z.object({
|
|
60
|
+
id: z.string(),
|
|
61
|
+
featuredPhoto: z.string(),
|
|
62
|
+
name: z.string(),
|
|
63
|
+
description: z.string().nullable().optional(),
|
|
64
|
+
location: sharedClinicLocationSchema,
|
|
65
|
+
contactInfo: sharedClinicContactInfoSchema,
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Schema for doctor info - shared between procedure and clinic schemas
|
|
70
|
+
*/
|
|
71
|
+
export const doctorInfoSchema = z.object({
|
|
72
|
+
id: z.string(),
|
|
73
|
+
name: z.string(),
|
|
74
|
+
description: z.string().nullable().optional(),
|
|
75
|
+
photo: z.string(),
|
|
76
|
+
rating: z.number().min(0).max(5),
|
|
77
|
+
services: z.array(z.string()), // List of procedure IDs practitioner offers
|
|
78
|
+
});
|