@blackcode_sa/metaestetics-api 1.5.30 → 1.5.32
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 +1 -1
- package/dist/admin/index.d.ts +1 -1
- package/dist/index.d.mts +100 -1
- package/dist/index.d.ts +100 -1
- package/dist/index.js +949 -289
- package/dist/index.mjs +972 -305
- package/package.json +1 -1
- package/src/admin/index.ts +10 -2
- package/src/index.ts +5 -0
- package/src/services/calendar/utils/appointment.utils.ts +42 -91
- package/src/services/clinic/clinic.service.ts +6 -0
- package/src/services/clinic/utils/filter.utils.ts +27 -1
- package/src/services/procedure/procedure.service.ts +43 -5
package/package.json
CHANGED
package/src/admin/index.ts
CHANGED
|
@@ -8,7 +8,11 @@ import { UserRole } from "../types";
|
|
|
8
8
|
// Import types needed by admin consumers (like Cloud Functions)
|
|
9
9
|
import { Clinic, ClinicLocation } from "../types/clinic";
|
|
10
10
|
import { ClinicInfo } from "../types/profile";
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
Practitioner,
|
|
13
|
+
PractitionerToken,
|
|
14
|
+
PractitionerTokenStatus,
|
|
15
|
+
} from "../types/practitioner";
|
|
12
16
|
import { DoctorInfo } from "../types/clinic";
|
|
13
17
|
import { Procedure, ProcedureSummaryInfo } from "../types/procedure";
|
|
14
18
|
import { PatientProfile } from "../types/patient";
|
|
@@ -36,7 +40,11 @@ export type {
|
|
|
36
40
|
// Re-export types needed by cloud functions
|
|
37
41
|
export type { Clinic, ClinicLocation } from "../types/clinic";
|
|
38
42
|
export type { ClinicInfo } from "../types/profile";
|
|
39
|
-
export type {
|
|
43
|
+
export type {
|
|
44
|
+
Practitioner,
|
|
45
|
+
PractitionerToken,
|
|
46
|
+
PractitionerTokenStatus,
|
|
47
|
+
} from "../types/practitioner";
|
|
40
48
|
export type { DoctorInfo } from "../types/clinic";
|
|
41
49
|
export type { Procedure, ProcedureSummaryInfo } from "../types/procedure";
|
|
42
50
|
export type { PatientProfile as Patient } from "../types/patient";
|
package/src/index.ts
CHANGED
|
@@ -26,6 +26,7 @@ export {
|
|
|
26
26
|
} from "./services/documentation-templates";
|
|
27
27
|
export { CalendarServiceV2 } from "./services/calendar/calendar-refactored.service";
|
|
28
28
|
export { SyncedCalendarsService } from "./services/calendar/synced-calendars.service";
|
|
29
|
+
export { ReviewService } from "./services/reviews/reviews.service";
|
|
29
30
|
|
|
30
31
|
// Backoffice services
|
|
31
32
|
export { BrandService } from "./backoffice/services/brand.service";
|
|
@@ -259,6 +260,10 @@ export {
|
|
|
259
260
|
export { Contraindication } from "./backoffice/types/static/contraindication.types";
|
|
260
261
|
export { ProcedureFamily } from "./backoffice/types/static/procedure-family.types";
|
|
261
262
|
export { TreatmentBenefit } from "./backoffice/types/static/treatment-benefit.types";
|
|
263
|
+
export {
|
|
264
|
+
RequirementType,
|
|
265
|
+
TimeUnit,
|
|
266
|
+
} from "./backoffice/types/requirement.types";
|
|
262
267
|
|
|
263
268
|
// Documentation Templates types
|
|
264
269
|
export type {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Firestore, Timestamp } from
|
|
1
|
+
import { Firestore, Timestamp } from 'firebase/firestore';
|
|
2
2
|
import {
|
|
3
3
|
CalendarEvent,
|
|
4
4
|
CalendarEventStatus,
|
|
@@ -7,23 +7,23 @@ import {
|
|
|
7
7
|
CalendarSyncStatus,
|
|
8
8
|
CreateCalendarEventData,
|
|
9
9
|
UpdateCalendarEventData,
|
|
10
|
-
} from
|
|
10
|
+
} from '../../../types/calendar';
|
|
11
11
|
import {
|
|
12
12
|
createClinicCalendarEventUtil,
|
|
13
13
|
updateClinicCalendarEventUtil,
|
|
14
14
|
deleteClinicCalendarEventUtil,
|
|
15
15
|
checkAutoConfirmAppointmentsUtil,
|
|
16
|
-
} from
|
|
16
|
+
} from './clinic.utils';
|
|
17
17
|
import {
|
|
18
18
|
createPatientCalendarEventUtil,
|
|
19
19
|
updatePatientCalendarEventUtil,
|
|
20
20
|
deletePatientCalendarEventUtil,
|
|
21
|
-
} from
|
|
21
|
+
} from './patient.utils';
|
|
22
22
|
import {
|
|
23
23
|
createPractitionerCalendarEventUtil,
|
|
24
24
|
updatePractitionerCalendarEventUtil,
|
|
25
25
|
deletePractitionerCalendarEventUtil,
|
|
26
|
-
} from
|
|
26
|
+
} from './practitioner.utils';
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
29
|
* Creates an appointment across all relevant calendars (practitioner, patient, clinic)
|
|
@@ -42,14 +42,14 @@ export async function createAppointmentUtil(
|
|
|
42
42
|
patientId: string,
|
|
43
43
|
eventData: Omit<
|
|
44
44
|
CreateCalendarEventData,
|
|
45
|
-
|
|
|
46
|
-
|
|
|
47
|
-
|
|
|
48
|
-
|
|
|
49
|
-
|
|
|
50
|
-
|
|
|
45
|
+
| 'id'
|
|
46
|
+
| 'createdAt'
|
|
47
|
+
| 'updatedAt'
|
|
48
|
+
| 'clinicBranchId'
|
|
49
|
+
| 'practitionerProfileId'
|
|
50
|
+
| 'patientProfileId'
|
|
51
51
|
>,
|
|
52
|
-
generateId: () => string
|
|
52
|
+
generateId: () => string,
|
|
53
53
|
): Promise<CalendarEvent> {
|
|
54
54
|
// TODO: Add validation for appointment data
|
|
55
55
|
// - Check if all entities exist
|
|
@@ -64,15 +64,10 @@ export async function createAppointmentUtil(
|
|
|
64
64
|
const autoConfirm = await checkAutoConfirmAppointmentsUtil(db, clinicId);
|
|
65
65
|
|
|
66
66
|
// Set the initial status based on auto-confirm setting
|
|
67
|
-
const initialStatus = autoConfirm
|
|
68
|
-
? CalendarEventStatus.CONFIRMED
|
|
69
|
-
: CalendarEventStatus.PENDING;
|
|
67
|
+
const initialStatus = autoConfirm ? CalendarEventStatus.CONFIRMED : CalendarEventStatus.PENDING;
|
|
70
68
|
|
|
71
69
|
// Prepare the event data with all required IDs
|
|
72
|
-
const appointmentData: Omit<
|
|
73
|
-
CreateCalendarEventData,
|
|
74
|
-
"id" | "createdAt" | "updatedAt"
|
|
75
|
-
> = {
|
|
70
|
+
const appointmentData: Omit<CreateCalendarEventData, 'id' | 'createdAt' | 'updatedAt'> = {
|
|
76
71
|
...eventData,
|
|
77
72
|
clinicBranchId: clinicId,
|
|
78
73
|
practitionerProfileId: practitionerId,
|
|
@@ -86,29 +81,25 @@ export async function createAppointmentUtil(
|
|
|
86
81
|
db,
|
|
87
82
|
clinicId,
|
|
88
83
|
appointmentData,
|
|
89
|
-
() => eventId // Use the same ID for all calendars
|
|
84
|
+
() => eventId, // Use the same ID for all calendars
|
|
90
85
|
);
|
|
91
86
|
|
|
92
87
|
const practitionerPromise = createPractitionerCalendarEventUtil(
|
|
93
88
|
db,
|
|
94
89
|
practitionerId,
|
|
95
90
|
appointmentData,
|
|
96
|
-
() => eventId // Use the same ID for all calendars
|
|
91
|
+
() => eventId, // Use the same ID for all calendars
|
|
97
92
|
);
|
|
98
93
|
|
|
99
94
|
const patientPromise = createPatientCalendarEventUtil(
|
|
100
95
|
db,
|
|
101
96
|
patientId,
|
|
102
97
|
appointmentData,
|
|
103
|
-
() => eventId // Use the same ID for all calendars
|
|
98
|
+
() => eventId, // Use the same ID for all calendars
|
|
104
99
|
);
|
|
105
100
|
|
|
106
101
|
// Wait for all operations to complete
|
|
107
|
-
const [clinicEvent] = await Promise.all([
|
|
108
|
-
clinicPromise,
|
|
109
|
-
practitionerPromise,
|
|
110
|
-
patientPromise,
|
|
111
|
-
]);
|
|
102
|
+
const [clinicEvent] = await Promise.all([clinicPromise, practitionerPromise, patientPromise]);
|
|
112
103
|
|
|
113
104
|
// Return the event from the clinic calendar
|
|
114
105
|
return clinicEvent;
|
|
@@ -130,7 +121,7 @@ export async function updateAppointmentUtil(
|
|
|
130
121
|
practitionerId: string,
|
|
131
122
|
patientId: string,
|
|
132
123
|
eventId: string,
|
|
133
|
-
updateData: Omit<UpdateCalendarEventData,
|
|
124
|
+
updateData: Omit<UpdateCalendarEventData, 'updatedAt'>,
|
|
134
125
|
): Promise<CalendarEvent> {
|
|
135
126
|
// TODO: Add validation for update data
|
|
136
127
|
// - Check if event exists in all calendars
|
|
@@ -138,33 +129,19 @@ export async function updateAppointmentUtil(
|
|
|
138
129
|
// - Check for overlapping events
|
|
139
130
|
|
|
140
131
|
// Update the event in all three calendars
|
|
141
|
-
const clinicPromise = updateClinicCalendarEventUtil(
|
|
142
|
-
db,
|
|
143
|
-
clinicId,
|
|
144
|
-
eventId,
|
|
145
|
-
updateData
|
|
146
|
-
);
|
|
132
|
+
const clinicPromise = updateClinicCalendarEventUtil(db, clinicId, eventId, updateData);
|
|
147
133
|
|
|
148
134
|
const practitionerPromise = updatePractitionerCalendarEventUtil(
|
|
149
135
|
db,
|
|
150
136
|
practitionerId,
|
|
151
137
|
eventId,
|
|
152
|
-
updateData
|
|
138
|
+
updateData,
|
|
153
139
|
);
|
|
154
140
|
|
|
155
|
-
const patientPromise = updatePatientCalendarEventUtil(
|
|
156
|
-
db,
|
|
157
|
-
patientId,
|
|
158
|
-
eventId,
|
|
159
|
-
updateData
|
|
160
|
-
);
|
|
141
|
+
const patientPromise = updatePatientCalendarEventUtil(db, patientId, eventId, updateData);
|
|
161
142
|
|
|
162
143
|
// Wait for all operations to complete
|
|
163
|
-
const [clinicEvent] = await Promise.all([
|
|
164
|
-
clinicPromise,
|
|
165
|
-
practitionerPromise,
|
|
166
|
-
patientPromise,
|
|
167
|
-
]);
|
|
144
|
+
const [clinicEvent] = await Promise.all([clinicPromise, practitionerPromise, patientPromise]);
|
|
168
145
|
|
|
169
146
|
// Return the event from the clinic calendar
|
|
170
147
|
return clinicEvent;
|
|
@@ -183,16 +160,12 @@ export async function deleteAppointmentUtil(
|
|
|
183
160
|
clinicId: string,
|
|
184
161
|
practitionerId: string,
|
|
185
162
|
patientId: string,
|
|
186
|
-
eventId: string
|
|
163
|
+
eventId: string,
|
|
187
164
|
): Promise<void> {
|
|
188
165
|
// Delete the event from all three calendars
|
|
189
166
|
const clinicPromise = deleteClinicCalendarEventUtil(db, clinicId, eventId);
|
|
190
167
|
|
|
191
|
-
const practitionerPromise = deletePractitionerCalendarEventUtil(
|
|
192
|
-
db,
|
|
193
|
-
practitionerId,
|
|
194
|
-
eventId
|
|
195
|
-
);
|
|
168
|
+
const practitionerPromise = deletePractitionerCalendarEventUtil(db, practitionerId, eventId);
|
|
196
169
|
|
|
197
170
|
const patientPromise = deletePatientCalendarEventUtil(db, patientId, eventId);
|
|
198
171
|
|
|
@@ -214,16 +187,11 @@ export async function confirmAppointmentUtil(
|
|
|
214
187
|
clinicId: string,
|
|
215
188
|
practitionerId: string,
|
|
216
189
|
patientId: string,
|
|
217
|
-
eventId: string
|
|
190
|
+
eventId: string,
|
|
218
191
|
): Promise<CalendarEvent> {
|
|
219
|
-
return updateAppointmentUtil(
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
practitionerId,
|
|
223
|
-
patientId,
|
|
224
|
-
eventId,
|
|
225
|
-
{ status: CalendarEventStatus.CONFIRMED }
|
|
226
|
-
);
|
|
192
|
+
return updateAppointmentUtil(db, clinicId, practitionerId, patientId, eventId, {
|
|
193
|
+
status: CalendarEventStatus.CONFIRMED,
|
|
194
|
+
});
|
|
227
195
|
}
|
|
228
196
|
|
|
229
197
|
/**
|
|
@@ -240,16 +208,11 @@ export async function rejectAppointmentUtil(
|
|
|
240
208
|
clinicId: string,
|
|
241
209
|
practitionerId: string,
|
|
242
210
|
patientId: string,
|
|
243
|
-
eventId: string
|
|
211
|
+
eventId: string,
|
|
244
212
|
): Promise<CalendarEvent> {
|
|
245
|
-
return updateAppointmentUtil(
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
practitionerId,
|
|
249
|
-
patientId,
|
|
250
|
-
eventId,
|
|
251
|
-
{ status: CalendarEventStatus.REJECTED }
|
|
252
|
-
);
|
|
213
|
+
return updateAppointmentUtil(db, clinicId, practitionerId, patientId, eventId, {
|
|
214
|
+
status: CalendarEventStatus.REJECTED,
|
|
215
|
+
});
|
|
253
216
|
}
|
|
254
217
|
|
|
255
218
|
/**
|
|
@@ -266,16 +229,11 @@ export async function cancelAppointmentUtil(
|
|
|
266
229
|
clinicId: string,
|
|
267
230
|
practitionerId: string,
|
|
268
231
|
patientId: string,
|
|
269
|
-
eventId: string
|
|
232
|
+
eventId: string,
|
|
270
233
|
): Promise<CalendarEvent> {
|
|
271
|
-
return updateAppointmentUtil(
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
practitionerId,
|
|
275
|
-
patientId,
|
|
276
|
-
eventId,
|
|
277
|
-
{ status: CalendarEventStatus.CANCELED }
|
|
278
|
-
);
|
|
234
|
+
return updateAppointmentUtil(db, clinicId, practitionerId, patientId, eventId, {
|
|
235
|
+
status: CalendarEventStatus.CANCELED,
|
|
236
|
+
});
|
|
279
237
|
}
|
|
280
238
|
|
|
281
239
|
/**
|
|
@@ -294,21 +252,14 @@ export async function rescheduleAppointmentUtil(
|
|
|
294
252
|
practitionerId: string,
|
|
295
253
|
patientId: string,
|
|
296
254
|
eventId: string,
|
|
297
|
-
newEventTime: CalendarEventTime
|
|
255
|
+
newEventTime: CalendarEventTime,
|
|
298
256
|
): Promise<CalendarEvent> {
|
|
299
257
|
// TODO: Add validation for new event time
|
|
300
258
|
// - Validate event time (start < end)
|
|
301
259
|
// - Check for overlapping events
|
|
302
260
|
|
|
303
|
-
return updateAppointmentUtil(
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
patientId,
|
|
308
|
-
eventId,
|
|
309
|
-
{
|
|
310
|
-
status: CalendarEventStatus.RESCHEDULED,
|
|
311
|
-
eventTime: newEventTime,
|
|
312
|
-
}
|
|
313
|
-
);
|
|
261
|
+
return updateAppointmentUtil(db, clinicId, practitionerId, patientId, eventId, {
|
|
262
|
+
status: CalendarEventStatus.RESCHEDULED,
|
|
263
|
+
eventTime: newEventTime,
|
|
264
|
+
});
|
|
314
265
|
}
|
|
@@ -426,6 +426,12 @@ export class ClinicService extends BaseService {
|
|
|
426
426
|
);
|
|
427
427
|
}
|
|
428
428
|
|
|
429
|
+
/**
|
|
430
|
+
* Get clinics based on multiple filtering criteria
|
|
431
|
+
*
|
|
432
|
+
* @param filters - Various filters to apply
|
|
433
|
+
* @returns Filtered clinics and the last document for pagination
|
|
434
|
+
*/
|
|
429
435
|
async getClinicsByFilters(filters: {
|
|
430
436
|
center?: { latitude: number; longitude: number };
|
|
431
437
|
radiusInKm?: number;
|
|
@@ -95,7 +95,7 @@ export async function getClinicsByFilters(
|
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
// Add ordering to make pagination consistent
|
|
98
|
-
constraints.push(orderBy(
|
|
98
|
+
constraints.push(orderBy("location.geohash"));
|
|
99
99
|
|
|
100
100
|
let clinicsResult: (Clinic & { distance?: number })[] = [];
|
|
101
101
|
let lastVisibleDoc = null;
|
|
@@ -227,6 +227,32 @@ export async function getClinicsByFilters(
|
|
|
227
227
|
// Apply filters that couldn't be applied in the query
|
|
228
228
|
let filteredClinics = clinics;
|
|
229
229
|
|
|
230
|
+
// Calculate distance for each clinic if center coordinates are provided
|
|
231
|
+
if (filters.center) {
|
|
232
|
+
const center = filters.center;
|
|
233
|
+
const clinicsWithDistance: (Clinic & { distance: number })[] = [];
|
|
234
|
+
|
|
235
|
+
filteredClinics.forEach((clinic) => {
|
|
236
|
+
const distance = distanceBetween(
|
|
237
|
+
[center.latitude, center.longitude],
|
|
238
|
+
[clinic.location.latitude, clinic.location.longitude]
|
|
239
|
+
);
|
|
240
|
+
|
|
241
|
+
clinicsWithDistance.push({
|
|
242
|
+
...clinic,
|
|
243
|
+
distance: distance / 1000, // Convert to kilometers
|
|
244
|
+
});
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
// Replace filtered clinics with the version that includes distances
|
|
248
|
+
filteredClinics = clinicsWithDistance;
|
|
249
|
+
|
|
250
|
+
// Sort by distance - use type assertion to fix type error
|
|
251
|
+
(filteredClinics as (Clinic & { distance: number })[]).sort(
|
|
252
|
+
(a, b) => a.distance - b.distance
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
|
|
230
256
|
// Filter by multiple tags if more than one tag was specified
|
|
231
257
|
if (filters.tags && filters.tags.length > 1) {
|
|
232
258
|
filteredClinics = filteredClinics.filter((clinic) => {
|
|
@@ -612,7 +612,7 @@ export class ProcedureService extends BaseService {
|
|
|
612
612
|
}
|
|
613
613
|
|
|
614
614
|
// Add ordering to make pagination consistent
|
|
615
|
-
constraints.push(orderBy(
|
|
615
|
+
constraints.push(orderBy("clinicInfo.location.geohash"));
|
|
616
616
|
|
|
617
617
|
// Add pagination if specified
|
|
618
618
|
if (filters.pagination && filters.pagination > 0 && filters.lastDoc) {
|
|
@@ -743,16 +743,54 @@ export class ProcedureService extends BaseService {
|
|
|
743
743
|
return { ...doc.data(), id: doc.id } as Procedure;
|
|
744
744
|
});
|
|
745
745
|
|
|
746
|
-
//
|
|
747
|
-
|
|
746
|
+
// Calculate distance for each procedure if location is provided
|
|
747
|
+
if (filters.location) {
|
|
748
|
+
const center = filters.location;
|
|
749
|
+
const proceduresWithDistance: (Procedure & { distance: number })[] =
|
|
750
|
+
[];
|
|
751
|
+
|
|
752
|
+
procedures.forEach((procedure) => {
|
|
753
|
+
const distance = distanceBetween(
|
|
754
|
+
[center.latitude, center.longitude],
|
|
755
|
+
[
|
|
756
|
+
procedure.clinicInfo.location.latitude,
|
|
757
|
+
procedure.clinicInfo.location.longitude,
|
|
758
|
+
]
|
|
759
|
+
);
|
|
760
|
+
|
|
761
|
+
proceduresWithDistance.push({
|
|
762
|
+
...procedure,
|
|
763
|
+
distance: distance / 1000, // Convert to kilometers
|
|
764
|
+
});
|
|
765
|
+
});
|
|
766
|
+
|
|
767
|
+
// Replace procedures with version that includes distances
|
|
768
|
+
let filteredProcedures = proceduresWithDistance;
|
|
769
|
+
|
|
770
|
+
// Apply in-memory filters
|
|
771
|
+
filteredProcedures = this.applyInMemoryFilters(
|
|
772
|
+
filteredProcedures,
|
|
773
|
+
filters
|
|
774
|
+
);
|
|
775
|
+
|
|
776
|
+
// Sort by distance
|
|
777
|
+
filteredProcedures.sort((a, b) => a.distance - b.distance);
|
|
778
|
+
|
|
779
|
+
proceduresResult = filteredProcedures;
|
|
780
|
+
} else {
|
|
781
|
+
// Apply filters that couldn't be applied in the query
|
|
782
|
+
let filteredProcedures = this.applyInMemoryFilters(
|
|
783
|
+
procedures,
|
|
784
|
+
filters
|
|
785
|
+
);
|
|
786
|
+
proceduresResult = filteredProcedures;
|
|
787
|
+
}
|
|
748
788
|
|
|
749
789
|
// Set last document for pagination
|
|
750
790
|
lastVisibleDoc =
|
|
751
791
|
querySnapshot.docs.length > 0
|
|
752
792
|
? querySnapshot.docs[querySnapshot.docs.length - 1]
|
|
753
793
|
: null;
|
|
754
|
-
|
|
755
|
-
proceduresResult = filteredProcedures;
|
|
756
794
|
}
|
|
757
795
|
|
|
758
796
|
return {
|