@blackcode_sa/metaestetics-api 1.5.29 → 1.5.31
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 +126 -1
- package/dist/admin/index.d.ts +126 -1
- package/dist/admin/index.js +347 -10
- package/dist/admin/index.mjs +345 -10
- package/dist/index.d.mts +64 -71
- package/dist/index.d.ts +64 -71
- package/dist/index.js +327 -710
- package/dist/index.mjs +363 -750
- package/package.json +2 -1
- package/src/admin/aggregation/README.md +79 -0
- package/src/admin/aggregation/clinic/README.md +52 -0
- package/src/admin/aggregation/patient/README.md +27 -0
- package/src/admin/aggregation/practitioner/README.md +42 -0
- package/src/admin/aggregation/procedure/README.md +43 -0
- package/src/admin/index.ts +17 -2
- package/src/admin/mailing/README.md +95 -0
- package/src/admin/mailing/base.mailing.service.ts +131 -0
- package/src/admin/mailing/index.ts +2 -0
- package/src/admin/mailing/practitionerInvite/index.ts +1 -0
- package/src/admin/mailing/practitionerInvite/practitionerInvite.mailing.ts +256 -0
- package/src/admin/mailing/practitionerInvite/templates/invitation.template.ts +101 -0
- package/src/services/README.md +106 -0
- package/src/services/calendar/utils/appointment.utils.ts +42 -91
- package/src/services/clinic/README.md +87 -0
- package/src/services/clinic/clinic.service.ts +3 -126
- package/src/services/clinic/utils/clinic.utils.ts +2 -2
- package/src/services/practitioner/README.md +145 -0
- package/src/services/practitioner/practitioner.service.ts +119 -395
- package/src/services/procedure/README.md +88 -0
- package/src/services/procedure/procedure.service.ts +332 -369
|
@@ -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
|
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# Clinic Service
|
|
2
|
+
|
|
3
|
+
This service manages clinic data within the Firestore database. It provides methods for creating, reading, updating, and managing clinics, their branches, and associated data like tags and administrators.
|
|
4
|
+
|
|
5
|
+
**Note:** This service relies on helper functions defined in the `./utils` directory for specific operations like photo uploads, geo-queries, and data fetching. Cloud Functions handle data aggregation into related entities (Practitioners, Procedures, ClinicGroups).
|
|
6
|
+
|
|
7
|
+
## `ClinicService` Class
|
|
8
|
+
|
|
9
|
+
Extends `BaseService`.
|
|
10
|
+
|
|
11
|
+
### Constructor
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
constructor(
|
|
15
|
+
db: Firestore,
|
|
16
|
+
auth: Auth,
|
|
17
|
+
app: FirebaseApp,
|
|
18
|
+
clinicGroupService: ClinicGroupService,
|
|
19
|
+
clinicAdminService: ClinicAdminService
|
|
20
|
+
)
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Initializes the service with Firestore, Auth, App instances, and required dependency services (`ClinicGroupService`, `ClinicAdminService`).
|
|
24
|
+
|
|
25
|
+
### Core Methods
|
|
26
|
+
|
|
27
|
+
- **`createClinic(data: CreateClinicData, creatorAdminId: string): Promise<Clinic>`**
|
|
28
|
+
- Creates a new clinic document in Firestore.
|
|
29
|
+
- Validates input data using `createClinicSchema`.
|
|
30
|
+
- Verifies the `creatorAdminId` and their association with the specified `clinicGroupId`.
|
|
31
|
+
- Calculates the geohash for the clinic's location.
|
|
32
|
+
- Initializes default review information.
|
|
33
|
+
- Handles photo uploads for logo, cover photo, featured photos, and photos with tags using utility functions.
|
|
34
|
+
- Updates the creator admin's `clinicsManaged` list.
|
|
35
|
+
- **Aggregation Note:** Adding the clinic to the `ClinicGroup` is handled by Cloud Functions.
|
|
36
|
+
- **`updateClinic(clinicId: string, data: Partial<Omit<Clinic, \"id\" | \"createdAt\" | \"clinicGroupId\">>, adminId: string): Promise<Clinic>`**
|
|
37
|
+
- Updates an existing clinic document.
|
|
38
|
+
- Fetches the current clinic data.
|
|
39
|
+
- Validates the partial update data against the `clinicSchema` after merging with existing data.
|
|
40
|
+
- Updates the geohash if the location is changed.
|
|
41
|
+
- Handles photo updates/uploads if necessary.
|
|
42
|
+
- **Aggregation Note:** Aggregation updates in related entities (Practitioners, Procedures, ClinicGroup) are handled by Cloud Functions.
|
|
43
|
+
- **`deactivateClinic(clinicId: string, adminId: string): Promise<void>`**
|
|
44
|
+
- Sets the `isActive` flag of a clinic to `false`.
|
|
45
|
+
- Requires admin permission (verification logic might be in the calling function or assumed).
|
|
46
|
+
- **Aggregation Note:** Aggregation updates (e.g., removing from active lists, updating related entities) are handled by Cloud Functions.
|
|
47
|
+
- **`getClinic(clinicId: string): Promise<Clinic | null>`**
|
|
48
|
+
- Retrieves a single clinic document by its ID using `ClinicUtils.getClinic`.
|
|
49
|
+
- **`getClinicsByGroup(groupId: string): Promise<Clinic[]>`**
|
|
50
|
+
- Retrieves all clinic documents belonging to a specific `clinicGroupId` using `ClinicUtils.getClinicsByGroup`.
|
|
51
|
+
- **`findClinicsInRadius(center: { latitude: number; longitude: number }, radiusInKm: number, filters?: { procedures?: string[]; tags?: ClinicTag[] }): Promise<Clinic[]>`**
|
|
52
|
+
- Finds active clinics within a specified radius using geohash queries.
|
|
53
|
+
- Leverages `SearchUtils.findClinicsInRadius`.
|
|
54
|
+
- Optionally filters results by procedure IDs (services) and tags.
|
|
55
|
+
- **`addTags(clinicId: string, adminId: string, newTags: { tags?: ClinicTag[] }): Promise<Clinic>`**
|
|
56
|
+
- Adds specified tags to a clinic's `tags` array using `TagUtils.addTags`.
|
|
57
|
+
- Ensures the admin has permission. Prevents duplicates.
|
|
58
|
+
- **`removeTags(clinicId: string, adminId: string, tagsToRemove: { tags?: ClinicTag[] }): Promise<Clinic>`**
|
|
59
|
+
- Removes specified tags from a clinic's `tags` array using `TagUtils.removeTags`.
|
|
60
|
+
- Ensures the admin has permission.
|
|
61
|
+
- **`getClinicsByAdmin(adminId: string, options?: { isActive?: boolean; includeGroupClinics?: boolean }): Promise<Clinic[]>`**
|
|
62
|
+
- Retrieves clinics associated with a specific admin using `ClinicUtils.getClinicsByAdmin`.
|
|
63
|
+
- Handles options for filtering by `isActive` and including all group clinics for owners.
|
|
64
|
+
- **`getActiveClinicsByAdmin(adminId: string): Promise<Clinic[]>`**
|
|
65
|
+
- Retrieves only the active clinics associated with a specific admin using `ClinicUtils.getActiveClinicsByAdmin`.
|
|
66
|
+
- **`createClinicBranch(clinicGroupId: string, setupData: ClinicBranchSetupData, adminId: string): Promise<Clinic>`**
|
|
67
|
+
- Creates a new clinic (branch) within an existing `clinicGroupId`.
|
|
68
|
+
- Validates group existence. Uses `createClinic` internally.
|
|
69
|
+
- **`getClinicById(clinicId: string): Promise<Clinic | null>`**
|
|
70
|
+
- Retrieves a single clinic document by its ID using `ClinicUtils.getClinicById`.
|
|
71
|
+
- **`getAllClinics(pagination?: number, lastDoc?: any): Promise<{ clinics: Clinic[]; lastDoc: any }>`**
|
|
72
|
+
- Retrieves all clinics, ordered by ID, optionally with pagination, using `ClinicUtils.getAllClinics`.
|
|
73
|
+
- **`getAllClinicsInRange(center: { latitude: number; longitude: number }, rangeInKm: number, pagination?: number, lastDoc?: any): Promise<{ clinics: (Clinic & { distance: number })[]; lastDoc: any }>`**
|
|
74
|
+
- Retrieves all clinics within a range, sorted by distance, optionally with pagination, using `ClinicUtils.getAllClinicsInRange`.
|
|
75
|
+
- **`getClinicsByFilters(filters: { ... }): Promise<{ clinics: (Clinic & { distance?: number })[]; lastDoc: any }>`**
|
|
76
|
+
- Retrieves clinics based on complex filters (location, tags, procedures, rating, etc.).
|
|
77
|
+
- Uses `FilterUtils.getClinicsByFilters` for combined query and in-memory filtering.
|
|
78
|
+
|
|
79
|
+
### Utility Functions (`./utils`)
|
|
80
|
+
|
|
81
|
+
- **`clinic.utils.ts`**: Core fetching (`getClinic`, `getClinicsByGroup`, etc.), create/update/deactivate logic wrappers, admin checks.
|
|
82
|
+
- **`filter.utils.ts`**: Complex filtering logic (`getClinicsByFilters`), including geo-queries.
|
|
83
|
+
- **`clinic-group.utils.ts`**: Helpers for clinic group interactions (used by `ClinicGroupService`).
|
|
84
|
+
- **`tag.utils.ts`**: Logic for adding/removing tags (`addTags`, `removeTags`).
|
|
85
|
+
- **`photos.utils.ts`**: Firebase Storage interactions (`uploadPhoto`, `uploadMultiplePhotos`, `deletePhoto`).
|
|
86
|
+
- **`admin.utils.ts`**: Helpers for clinic admin interactions (used by `ClinicAdminService`).
|
|
87
|
+
- **`search.utils.ts`**: Geo-radius search logic (`findClinicsInRadius`).
|
|
@@ -29,9 +29,6 @@ import {
|
|
|
29
29
|
ClinicBranchSetupData,
|
|
30
30
|
CLINIC_ADMINS_COLLECTION,
|
|
31
31
|
DoctorInfo,
|
|
32
|
-
// Remove incorrect imports below
|
|
33
|
-
// ProcedureSummaryInfo,
|
|
34
|
-
// ClinicInfo
|
|
35
32
|
} from "../../types/clinic";
|
|
36
33
|
// Correct imports
|
|
37
34
|
import { ProcedureSummaryInfo } from "../../types/procedure";
|
|
@@ -75,73 +72,8 @@ export class ClinicService extends BaseService {
|
|
|
75
72
|
this.clinicGroupService = clinicGroupService;
|
|
76
73
|
}
|
|
77
74
|
|
|
78
|
-
// --- Helper Functions ---
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Creates an aggregated ClinicInfo object from Clinic data.
|
|
82
|
-
* @param clinic The clinic object
|
|
83
|
-
* @returns ClinicInfo object
|
|
84
|
-
*/
|
|
85
|
-
private _createClinicInfoForAggregation(clinic: Clinic): ClinicInfo {
|
|
86
|
-
return {
|
|
87
|
-
id: clinic.id,
|
|
88
|
-
featuredPhoto:
|
|
89
|
-
clinic.featuredPhotos && clinic.featuredPhotos.length > 0
|
|
90
|
-
? clinic.featuredPhotos[0]
|
|
91
|
-
: clinic.coverPhoto || "",
|
|
92
|
-
name: clinic.name,
|
|
93
|
-
description: clinic.description || "",
|
|
94
|
-
location: clinic.location,
|
|
95
|
-
contactInfo: clinic.contactInfo,
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Updates the ClinicInfo within the clinicsInfo array for multiple practitioners.
|
|
101
|
-
* @param practitionerIds IDs of practitioners to update
|
|
102
|
-
* @param clinicInfo The updated ClinicInfo object
|
|
103
|
-
*/
|
|
104
|
-
private async _updateClinicInfoInPractitioners(
|
|
105
|
-
practitionerIds: string[],
|
|
106
|
-
clinicInfo: ClinicInfo
|
|
107
|
-
): Promise<void> {
|
|
108
|
-
const batch = writeBatch(this.db);
|
|
109
|
-
const clinicId = clinicInfo.id;
|
|
110
|
-
|
|
111
|
-
for (const practitionerId of practitionerIds) {
|
|
112
|
-
const practitionerRef = doc(
|
|
113
|
-
this.db,
|
|
114
|
-
PRACTITIONERS_COLLECTION,
|
|
115
|
-
practitionerId
|
|
116
|
-
);
|
|
117
|
-
// Remove old clinic info based on ID
|
|
118
|
-
batch.update(practitionerRef, {
|
|
119
|
-
clinicsInfo: arrayRemove(...[{ id: clinicId }]),
|
|
120
|
-
updatedAt: serverTimestamp(),
|
|
121
|
-
});
|
|
122
|
-
// Add updated clinic info
|
|
123
|
-
batch.update(practitionerRef, {
|
|
124
|
-
clinicsInfo: arrayUnion(clinicInfo),
|
|
125
|
-
updatedAt: serverTimestamp(),
|
|
126
|
-
});
|
|
127
|
-
}
|
|
128
|
-
try {
|
|
129
|
-
await batch.commit();
|
|
130
|
-
} catch (error) {
|
|
131
|
-
console.error(
|
|
132
|
-
`Error updating clinic info in practitioners for clinic ${clinicId}:`,
|
|
133
|
-
error
|
|
134
|
-
);
|
|
135
|
-
// Decide on error handling
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// --- Core Service Methods (Updated) ---
|
|
140
|
-
|
|
141
75
|
/**
|
|
142
76
|
* Creates a new clinic.
|
|
143
|
-
* Initializes empty doctorsInfo and proceduresInfo.
|
|
144
|
-
* Aggregation into Clinic happens via PractitionerService and ProcedureService.
|
|
145
77
|
*/
|
|
146
78
|
async createClinic(
|
|
147
79
|
data: CreateClinicData,
|
|
@@ -209,21 +141,7 @@ export class ClinicService extends BaseService {
|
|
|
209
141
|
const clinicRef = doc(this.db, CLINICS_COLLECTION, clinicId);
|
|
210
142
|
batch.set(clinicRef, clinicData);
|
|
211
143
|
|
|
212
|
-
|
|
213
|
-
this.db,
|
|
214
|
-
CLINIC_GROUPS_COLLECTION,
|
|
215
|
-
validatedData.clinicGroupId
|
|
216
|
-
);
|
|
217
|
-
const newClinicInfoForGroup = this._createClinicInfoForAggregation({
|
|
218
|
-
...clinicData,
|
|
219
|
-
id: clinicId,
|
|
220
|
-
} as Clinic);
|
|
221
|
-
batch.update(groupRef, {
|
|
222
|
-
clinics: arrayUnion(clinicId),
|
|
223
|
-
clinicsInfo: arrayUnion(newClinicInfoForGroup),
|
|
224
|
-
updatedAt: serverTimestamp(),
|
|
225
|
-
});
|
|
226
|
-
|
|
144
|
+
// Update admin relationship - this part is still needed
|
|
227
145
|
const adminRef = doc(this.db, CLINIC_ADMINS_COLLECTION, creatorAdminId);
|
|
228
146
|
batch.update(adminRef, {
|
|
229
147
|
clinicsManaged: arrayUnion(clinicId),
|
|
@@ -244,7 +162,7 @@ export class ClinicService extends BaseService {
|
|
|
244
162
|
}
|
|
245
163
|
|
|
246
164
|
/**
|
|
247
|
-
* Updates a clinic
|
|
165
|
+
* Updates a clinic.
|
|
248
166
|
*/
|
|
249
167
|
async updateClinic(
|
|
250
168
|
clinicId: string,
|
|
@@ -292,37 +210,8 @@ export class ClinicService extends BaseService {
|
|
|
292
210
|
updatedAt: serverTimestamp(),
|
|
293
211
|
};
|
|
294
212
|
|
|
295
|
-
|
|
296
|
-
batch.update(clinicRef, updateDataForFirestore);
|
|
213
|
+
await updateDoc(clinicRef, updateDataForFirestore);
|
|
297
214
|
|
|
298
|
-
const groupRef = doc(
|
|
299
|
-
this.db,
|
|
300
|
-
CLINIC_GROUPS_COLLECTION,
|
|
301
|
-
currentClinic.clinicGroupId
|
|
302
|
-
);
|
|
303
|
-
// Use the validated final state for aggregation
|
|
304
|
-
const updatedClinicInfoForGroup = this._createClinicInfoForAggregation(
|
|
305
|
-
finalStateForValidation as Clinic
|
|
306
|
-
);
|
|
307
|
-
batch.update(groupRef, {
|
|
308
|
-
clinicsInfo: arrayRemove(...[{ id: clinicId }]),
|
|
309
|
-
updatedAt: serverTimestamp(),
|
|
310
|
-
});
|
|
311
|
-
batch.update(groupRef, {
|
|
312
|
-
clinicsInfo: arrayUnion(updatedClinicInfoForGroup),
|
|
313
|
-
updatedAt: serverTimestamp(),
|
|
314
|
-
});
|
|
315
|
-
|
|
316
|
-
const practitionerIds = currentClinic.doctors || [];
|
|
317
|
-
if (practitionerIds.length > 0) {
|
|
318
|
-
// Pass the aggregated info based on the final validated state
|
|
319
|
-
await this._updateClinicInfoInPractitioners(
|
|
320
|
-
practitionerIds,
|
|
321
|
-
updatedClinicInfoForGroup
|
|
322
|
-
);
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
await batch.commit();
|
|
326
215
|
const updatedClinic = await this.getClinic(clinicId);
|
|
327
216
|
if (!updatedClinic) throw new Error("Failed to retrieve updated clinic");
|
|
328
217
|
return updatedClinic;
|
|
@@ -342,7 +231,6 @@ export class ClinicService extends BaseService {
|
|
|
342
231
|
|
|
343
232
|
/**
|
|
344
233
|
* Deactivates a clinic.
|
|
345
|
-
* Note: Does not currently remove ClinicInfo from practitioners (might be desired).
|
|
346
234
|
*/
|
|
347
235
|
async deactivateClinic(clinicId: string, adminId: string): Promise<void> {
|
|
348
236
|
// Permission check omitted
|
|
@@ -351,14 +239,8 @@ export class ClinicService extends BaseService {
|
|
|
351
239
|
isActive: false,
|
|
352
240
|
updatedAt: serverTimestamp(),
|
|
353
241
|
});
|
|
354
|
-
// Consider whether to also update ClinicInfo in practitioners to reflect inactive status
|
|
355
242
|
}
|
|
356
243
|
|
|
357
|
-
// --- Other Methods ---
|
|
358
|
-
// (getClinic, getClinicsByGroup, findClinicsInRadius, addTags, removeTags, getClinicsByAdmin, etc.)
|
|
359
|
-
// Review these methods to ensure they don't rely on outdated aggregation logic (e.g., filtering ServiceInfo)
|
|
360
|
-
// and update them to use proceduresInfo if necessary for filtering.
|
|
361
|
-
|
|
362
244
|
/**
|
|
363
245
|
* Dohvata kliniku po ID-u
|
|
364
246
|
*/
|
|
@@ -375,7 +257,6 @@ export class ClinicService extends BaseService {
|
|
|
375
257
|
|
|
376
258
|
/**
|
|
377
259
|
* Pretražuje klinike u određenom radijusu
|
|
378
|
-
* REVIEW: SearchUtils.findClinicsInRadius might need updating for filters.
|
|
379
260
|
*/
|
|
380
261
|
async findClinicsInRadius(
|
|
381
262
|
center: { latitude: number; longitude: number },
|
|
@@ -386,10 +267,6 @@ export class ClinicService extends BaseService {
|
|
|
386
267
|
// Add other relevant filters based on Clinic/ProcedureSummaryInfo fields
|
|
387
268
|
}
|
|
388
269
|
): Promise<Clinic[]> {
|
|
389
|
-
console.warn(
|
|
390
|
-
"SearchUtils.findClinicsInRadius filter logic might need updating for proceduresInfo."
|
|
391
|
-
);
|
|
392
|
-
// Pass filters directly, assuming SearchUtils handles it.
|
|
393
270
|
return SearchUtils.findClinicsInRadius(
|
|
394
271
|
this.db,
|
|
395
272
|
center,
|
|
@@ -13,6 +13,8 @@ import {
|
|
|
13
13
|
QueryConstraint,
|
|
14
14
|
addDoc,
|
|
15
15
|
writeBatch,
|
|
16
|
+
limit,
|
|
17
|
+
startAfter,
|
|
16
18
|
} from "firebase/firestore";
|
|
17
19
|
import {
|
|
18
20
|
Clinic,
|
|
@@ -827,8 +829,6 @@ export async function getAllClinics(
|
|
|
827
829
|
|
|
828
830
|
// If pagination is specified and greater than 0, limit the query
|
|
829
831
|
if (pagination && pagination > 0) {
|
|
830
|
-
const { limit, startAfter } = require("firebase/firestore");
|
|
831
|
-
|
|
832
832
|
if (lastDoc) {
|
|
833
833
|
clinicsQuery = query(
|
|
834
834
|
clinicsCollection,
|