@blackcode_sa/metaestetics-api 1.5.28 → 1.5.30
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 +1324 -1
- package/dist/admin/index.d.ts +1324 -1
- package/dist/admin/index.js +1674 -2
- package/dist/admin/index.mjs +1668 -2
- package/dist/backoffice/index.d.mts +99 -7
- package/dist/backoffice/index.d.ts +99 -7
- package/dist/index.d.mts +4036 -2372
- package/dist/index.d.ts +4036 -2372
- package/dist/index.js +2331 -2009
- package/dist/index.mjs +2279 -1954
- 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/clinic/clinic.aggregation.service.ts +642 -0
- package/src/admin/aggregation/patient/README.md +27 -0
- package/src/admin/aggregation/patient/patient.aggregation.service.ts +141 -0
- package/src/admin/aggregation/practitioner/README.md +42 -0
- package/src/admin/aggregation/practitioner/practitioner.aggregation.service.ts +433 -0
- package/src/admin/aggregation/procedure/README.md +43 -0
- package/src/admin/aggregation/procedure/procedure.aggregation.service.ts +508 -0
- package/src/admin/index.ts +60 -4
- 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/index.ts +28 -4
- package/src/services/README.md +106 -0
- package/src/services/clinic/README.md +87 -0
- package/src/services/clinic/clinic.service.ts +197 -107
- package/src/services/clinic/utils/clinic.utils.ts +68 -119
- package/src/services/clinic/utils/filter.utils.d.ts +23 -0
- package/src/services/clinic/utils/filter.utils.ts +264 -0
- package/src/services/practitioner/README.md +145 -0
- package/src/services/practitioner/practitioner.service.ts +439 -104
- package/src/services/procedure/README.md +88 -0
- package/src/services/procedure/procedure.service.ts +521 -311
- package/src/services/reviews/reviews.service.ts +842 -0
- package/src/types/clinic/index.ts +24 -56
- package/src/types/practitioner/index.ts +34 -33
- package/src/types/procedure/index.ts +32 -0
- package/src/types/profile/index.ts +1 -1
- package/src/types/reviews/index.ts +126 -0
- package/src/validations/clinic.schema.ts +37 -64
- package/src/validations/practitioner.schema.ts +42 -32
- package/src/validations/procedure.schema.ts +11 -3
- package/src/validations/reviews.schema.ts +189 -0
- package/src/services/clinic/utils/review.utils.ts +0 -93
|
@@ -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`).
|
|
@@ -13,19 +13,26 @@ import {
|
|
|
13
13
|
GeoPoint,
|
|
14
14
|
QueryConstraint,
|
|
15
15
|
FieldValue,
|
|
16
|
+
writeBatch,
|
|
17
|
+
arrayUnion,
|
|
18
|
+
arrayRemove,
|
|
16
19
|
} from "firebase/firestore";
|
|
17
20
|
import { BaseService } from "../base.service";
|
|
18
21
|
import {
|
|
19
22
|
Clinic,
|
|
20
23
|
CreateClinicData,
|
|
21
24
|
CLINICS_COLLECTION,
|
|
22
|
-
ClinicReview,
|
|
23
25
|
ClinicTag,
|
|
24
26
|
ClinicTags,
|
|
25
27
|
ClinicGroup,
|
|
28
|
+
CLINIC_GROUPS_COLLECTION,
|
|
26
29
|
ClinicBranchSetupData,
|
|
27
30
|
CLINIC_ADMINS_COLLECTION,
|
|
31
|
+
DoctorInfo,
|
|
28
32
|
} from "../../types/clinic";
|
|
33
|
+
// Correct imports
|
|
34
|
+
import { ProcedureSummaryInfo } from "../../types/procedure";
|
|
35
|
+
import { ClinicInfo } from "../../types/profile";
|
|
29
36
|
import { ClinicGroupService } from "./clinic-group.service";
|
|
30
37
|
import { ClinicAdminService } from "./clinic-admin.service";
|
|
31
38
|
import {
|
|
@@ -36,17 +43,18 @@ import {
|
|
|
36
43
|
import {
|
|
37
44
|
clinicSchema,
|
|
38
45
|
createClinicSchema,
|
|
39
|
-
clinicReviewSchema,
|
|
40
46
|
} from "../../validations/clinic.schema";
|
|
41
47
|
import { z } from "zod";
|
|
42
48
|
import { Auth } from "firebase/auth";
|
|
43
49
|
import { Firestore } from "firebase/firestore";
|
|
44
50
|
import { FirebaseApp } from "firebase/app";
|
|
45
51
|
import * as ClinicUtils from "./utils/clinic.utils";
|
|
46
|
-
import * as ReviewUtils from "./utils/review.utils";
|
|
47
52
|
import * as TagUtils from "./utils/tag.utils";
|
|
48
53
|
import * as SearchUtils from "./utils/search.utils";
|
|
49
54
|
import * as AdminUtils from "./utils/admin.utils";
|
|
55
|
+
import * as FilterUtils from "./utils/filter.utils";
|
|
56
|
+
import { ClinicReviewInfo } from "../../types/reviews";
|
|
57
|
+
import { PRACTITIONERS_COLLECTION } from "../../types/practitioner";
|
|
50
58
|
|
|
51
59
|
export class ClinicService extends BaseService {
|
|
52
60
|
private clinicGroupService: ClinicGroupService;
|
|
@@ -65,20 +73,172 @@ export class ClinicService extends BaseService {
|
|
|
65
73
|
}
|
|
66
74
|
|
|
67
75
|
/**
|
|
68
|
-
*
|
|
76
|
+
* Creates a new clinic.
|
|
69
77
|
*/
|
|
70
78
|
async createClinic(
|
|
71
79
|
data: CreateClinicData,
|
|
72
80
|
creatorAdminId: string
|
|
73
81
|
): Promise<Clinic> {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
+
try {
|
|
83
|
+
const validatedData = createClinicSchema.parse(data);
|
|
84
|
+
const group = await this.clinicGroupService.getClinicGroup(
|
|
85
|
+
validatedData.clinicGroupId
|
|
86
|
+
);
|
|
87
|
+
if (!group)
|
|
88
|
+
throw new Error(
|
|
89
|
+
`Clinic group ${validatedData.clinicGroupId} not found`
|
|
90
|
+
);
|
|
91
|
+
const location = validatedData.location;
|
|
92
|
+
const hash = geohashForLocation([location.latitude, location.longitude]);
|
|
93
|
+
const defaultReviewInfo: ClinicReviewInfo = {
|
|
94
|
+
totalReviews: 0,
|
|
95
|
+
averageRating: 0,
|
|
96
|
+
cleanliness: 0,
|
|
97
|
+
facilities: 0,
|
|
98
|
+
staffFriendliness: 0,
|
|
99
|
+
waitingTime: 0,
|
|
100
|
+
accessibility: 0,
|
|
101
|
+
recommendationPercentage: 0,
|
|
102
|
+
};
|
|
103
|
+
const clinicId = this.generateId();
|
|
104
|
+
|
|
105
|
+
const clinicData: Omit<Clinic, "createdAt" | "updatedAt"> & {
|
|
106
|
+
createdAt: FieldValue;
|
|
107
|
+
updatedAt: FieldValue;
|
|
108
|
+
} = {
|
|
109
|
+
id: clinicId,
|
|
110
|
+
clinicGroupId: validatedData.clinicGroupId,
|
|
111
|
+
name: validatedData.name,
|
|
112
|
+
description: validatedData.description,
|
|
113
|
+
location: { ...location, geohash: hash },
|
|
114
|
+
contactInfo: validatedData.contactInfo,
|
|
115
|
+
workingHours: validatedData.workingHours,
|
|
116
|
+
tags: validatedData.tags,
|
|
117
|
+
featuredPhotos: validatedData.featuredPhotos || [],
|
|
118
|
+
coverPhoto: validatedData.coverPhoto,
|
|
119
|
+
photosWithTags: validatedData.photosWithTags,
|
|
120
|
+
doctors: [],
|
|
121
|
+
procedures: [],
|
|
122
|
+
doctorsInfo: [],
|
|
123
|
+
proceduresInfo: [],
|
|
124
|
+
reviewInfo: defaultReviewInfo,
|
|
125
|
+
admins: [creatorAdminId],
|
|
126
|
+
isActive: validatedData.isActive,
|
|
127
|
+
isVerified: validatedData.isVerified,
|
|
128
|
+
logo: validatedData.logo,
|
|
129
|
+
createdAt: serverTimestamp(),
|
|
130
|
+
updatedAt: serverTimestamp(),
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
// Re-validate before saving (ensure schema matches the final object)
|
|
134
|
+
clinicSchema.parse({
|
|
135
|
+
...clinicData,
|
|
136
|
+
createdAt: Timestamp.now(),
|
|
137
|
+
updatedAt: Timestamp.now(),
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
const batch = writeBatch(this.db);
|
|
141
|
+
const clinicRef = doc(this.db, CLINICS_COLLECTION, clinicId);
|
|
142
|
+
batch.set(clinicRef, clinicData);
|
|
143
|
+
|
|
144
|
+
// Update admin relationship - this part is still needed
|
|
145
|
+
const adminRef = doc(this.db, CLINIC_ADMINS_COLLECTION, creatorAdminId);
|
|
146
|
+
batch.update(adminRef, {
|
|
147
|
+
clinicsManaged: arrayUnion(clinicId),
|
|
148
|
+
updatedAt: serverTimestamp(),
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
await batch.commit();
|
|
152
|
+
const savedClinic = await this.getClinic(clinicId);
|
|
153
|
+
if (!savedClinic) throw new Error("Failed to retrieve created clinic");
|
|
154
|
+
return savedClinic;
|
|
155
|
+
} catch (error) {
|
|
156
|
+
if (error instanceof z.ZodError) {
|
|
157
|
+
throw new Error("Invalid clinic data: " + error.message);
|
|
158
|
+
}
|
|
159
|
+
console.error("Error creating clinic:", error);
|
|
160
|
+
throw error;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Updates a clinic.
|
|
166
|
+
*/
|
|
167
|
+
async updateClinic(
|
|
168
|
+
clinicId: string,
|
|
169
|
+
data: Partial<Omit<Clinic, "id" | "createdAt" | "clinicGroupId">>,
|
|
170
|
+
adminId: string
|
|
171
|
+
): Promise<Clinic> {
|
|
172
|
+
const clinicRef = doc(this.db, CLINICS_COLLECTION, clinicId);
|
|
173
|
+
const clinicDoc = await getDoc(clinicRef);
|
|
174
|
+
if (!clinicDoc.exists()) {
|
|
175
|
+
throw new Error(`Clinic ${clinicId} not found`);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
try {
|
|
179
|
+
const currentClinic = clinicDoc.data() as Clinic;
|
|
180
|
+
// Explicitly Omit fields managed by other services or internally
|
|
181
|
+
const { doctorsInfo, proceduresInfo, ...updatePayload } =
|
|
182
|
+
data as Partial<Clinic>;
|
|
183
|
+
|
|
184
|
+
if (updatePayload.location) {
|
|
185
|
+
const loc = updatePayload.location;
|
|
186
|
+
updatePayload.location = {
|
|
187
|
+
...loc,
|
|
188
|
+
geohash: geohashForLocation([loc.latitude, loc.longitude]),
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Merge with current data for validation and aggregation, preserving arrays managed elsewhere
|
|
193
|
+
const finalStateForValidation = {
|
|
194
|
+
...currentClinic,
|
|
195
|
+
...updatePayload, // Apply safe updates
|
|
196
|
+
// Explicitly keep arrays managed by other services from current state
|
|
197
|
+
doctorsInfo: currentClinic.doctorsInfo,
|
|
198
|
+
proceduresInfo: currentClinic.proceduresInfo,
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
// Ensure required fields for validation are present
|
|
202
|
+
clinicSchema.parse({
|
|
203
|
+
...finalStateForValidation,
|
|
204
|
+
updatedAt: Timestamp.now(), // Use current time for validation
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
// Prepare final update data for Firestore, including timestamp
|
|
208
|
+
const updateDataForFirestore = {
|
|
209
|
+
...updatePayload,
|
|
210
|
+
updatedAt: serverTimestamp(),
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
await updateDoc(clinicRef, updateDataForFirestore);
|
|
214
|
+
|
|
215
|
+
const updatedClinic = await this.getClinic(clinicId);
|
|
216
|
+
if (!updatedClinic) throw new Error("Failed to retrieve updated clinic");
|
|
217
|
+
return updatedClinic;
|
|
218
|
+
} catch (error) {
|
|
219
|
+
if (error instanceof z.ZodError) {
|
|
220
|
+
throw new Error(
|
|
221
|
+
"Invalid clinic update data: " +
|
|
222
|
+
error.errors
|
|
223
|
+
.map((e) => `${e.path.join(".")} - ${e.message}`)
|
|
224
|
+
.join(", ")
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
console.error(`Error updating clinic ${clinicId}:`, error);
|
|
228
|
+
throw error;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Deactivates a clinic.
|
|
234
|
+
*/
|
|
235
|
+
async deactivateClinic(clinicId: string, adminId: string): Promise<void> {
|
|
236
|
+
// Permission check omitted
|
|
237
|
+
const clinicRef = doc(this.db, CLINICS_COLLECTION, clinicId);
|
|
238
|
+
await updateDoc(clinicRef, {
|
|
239
|
+
isActive: false,
|
|
240
|
+
updatedAt: serverTimestamp(),
|
|
241
|
+
});
|
|
82
242
|
}
|
|
83
243
|
|
|
84
244
|
/**
|
|
@@ -102,8 +262,9 @@ export class ClinicService extends BaseService {
|
|
|
102
262
|
center: { latitude: number; longitude: number },
|
|
103
263
|
radiusInKm: number,
|
|
104
264
|
filters?: {
|
|
105
|
-
|
|
265
|
+
procedures?: string[];
|
|
106
266
|
tags?: ClinicTag[];
|
|
267
|
+
// Add other relevant filters based on Clinic/ProcedureSummaryInfo fields
|
|
107
268
|
}
|
|
108
269
|
): Promise<Clinic[]> {
|
|
109
270
|
return SearchUtils.findClinicsInRadius(
|
|
@@ -114,52 +275,6 @@ export class ClinicService extends BaseService {
|
|
|
114
275
|
);
|
|
115
276
|
}
|
|
116
277
|
|
|
117
|
-
/**
|
|
118
|
-
* Ažurira kliniku
|
|
119
|
-
*/
|
|
120
|
-
async updateClinic(
|
|
121
|
-
clinicId: string,
|
|
122
|
-
data: Partial<Clinic>,
|
|
123
|
-
adminId: string
|
|
124
|
-
): Promise<Clinic> {
|
|
125
|
-
return ClinicUtils.updateClinic(
|
|
126
|
-
this.db,
|
|
127
|
-
clinicId,
|
|
128
|
-
data,
|
|
129
|
-
adminId,
|
|
130
|
-
this.clinicAdminService,
|
|
131
|
-
this.app
|
|
132
|
-
);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Dodaje recenziju klinici
|
|
137
|
-
*/
|
|
138
|
-
async addReview(
|
|
139
|
-
clinicId: string,
|
|
140
|
-
review: Omit<
|
|
141
|
-
ClinicReview,
|
|
142
|
-
"id" | "clinicId" | "createdAt" | "updatedAt" | "isVerified"
|
|
143
|
-
>
|
|
144
|
-
): Promise<ClinicReview> {
|
|
145
|
-
return ReviewUtils.addReview(this.db, clinicId, review, this.app);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* Deaktivira kliniku
|
|
150
|
-
*/
|
|
151
|
-
async deactivateClinic(clinicId: string, adminId: string): Promise<void> {
|
|
152
|
-
return ClinicUtils.deactivateClinic(
|
|
153
|
-
this.db,
|
|
154
|
-
clinicId,
|
|
155
|
-
adminId,
|
|
156
|
-
this.clinicAdminService
|
|
157
|
-
);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* Dodaje tagove klinici
|
|
162
|
-
*/
|
|
163
278
|
async addTags(
|
|
164
279
|
clinicId: string,
|
|
165
280
|
adminId: string,
|
|
@@ -177,9 +292,6 @@ export class ClinicService extends BaseService {
|
|
|
177
292
|
);
|
|
178
293
|
}
|
|
179
294
|
|
|
180
|
-
/**
|
|
181
|
-
* Uklanja tagove iz klinike
|
|
182
|
-
*/
|
|
183
295
|
async removeTags(
|
|
184
296
|
clinicId: string,
|
|
185
297
|
adminId: string,
|
|
@@ -197,9 +309,6 @@ export class ClinicService extends BaseService {
|
|
|
197
309
|
);
|
|
198
310
|
}
|
|
199
311
|
|
|
200
|
-
/**
|
|
201
|
-
* Dohvata sve klinike gde je korisnik admin
|
|
202
|
-
*/
|
|
203
312
|
async getClinicsByAdmin(
|
|
204
313
|
adminId: string,
|
|
205
314
|
options?: {
|
|
@@ -216,9 +325,6 @@ export class ClinicService extends BaseService {
|
|
|
216
325
|
);
|
|
217
326
|
}
|
|
218
327
|
|
|
219
|
-
/**
|
|
220
|
-
* Dohvata sve aktivne klinike gde je korisnik admin
|
|
221
|
-
*/
|
|
222
328
|
async getActiveClinicsByAdmin(adminId: string): Promise<Clinic[]> {
|
|
223
329
|
return ClinicUtils.getActiveClinicsByAdmin(
|
|
224
330
|
this.db,
|
|
@@ -228,14 +334,6 @@ export class ClinicService extends BaseService {
|
|
|
228
334
|
);
|
|
229
335
|
}
|
|
230
336
|
|
|
231
|
-
/**
|
|
232
|
-
* Creates a new clinic branch for a clinic group
|
|
233
|
-
*
|
|
234
|
-
* @param clinicGroupId - The ID of the clinic group
|
|
235
|
-
* @param setupData - The setup data for the clinic branch
|
|
236
|
-
* @param adminId - The ID of the admin creating the branch
|
|
237
|
-
* @returns The created clinic
|
|
238
|
-
*/
|
|
239
337
|
async createClinicBranch(
|
|
240
338
|
clinicGroupId: string,
|
|
241
339
|
setupData: ClinicBranchSetupData,
|
|
@@ -276,7 +374,7 @@ export class ClinicService extends BaseService {
|
|
|
276
374
|
coverPhoto: setupData.coverPhoto || null,
|
|
277
375
|
photosWithTags: setupData.photosWithTags || [],
|
|
278
376
|
doctors: [],
|
|
279
|
-
|
|
377
|
+
procedures: [],
|
|
280
378
|
admins: [adminId],
|
|
281
379
|
isActive: true,
|
|
282
380
|
isVerified: false,
|
|
@@ -302,23 +400,10 @@ export class ClinicService extends BaseService {
|
|
|
302
400
|
return clinic;
|
|
303
401
|
}
|
|
304
402
|
|
|
305
|
-
/**
|
|
306
|
-
* Retrieves a clinic by its ID
|
|
307
|
-
*
|
|
308
|
-
* @param clinicId - ID of the clinic to retrieve
|
|
309
|
-
* @returns The clinic if found, null otherwise
|
|
310
|
-
*/
|
|
311
403
|
async getClinicById(clinicId: string): Promise<Clinic | null> {
|
|
312
404
|
return ClinicUtils.getClinicById(this.db, clinicId);
|
|
313
405
|
}
|
|
314
406
|
|
|
315
|
-
/**
|
|
316
|
-
* Retrieves all clinics with optional pagination
|
|
317
|
-
*
|
|
318
|
-
* @param pagination - Optional number of clinics per page (0 or undefined returns all)
|
|
319
|
-
* @param lastDoc - Optional last document for pagination (if continuing from a previous page)
|
|
320
|
-
* @returns Array of clinics and the last document for pagination
|
|
321
|
-
*/
|
|
322
407
|
async getAllClinics(
|
|
323
408
|
pagination?: number,
|
|
324
409
|
lastDoc?: any
|
|
@@ -326,33 +411,38 @@ export class ClinicService extends BaseService {
|
|
|
326
411
|
return ClinicUtils.getAllClinics(this.db, pagination, lastDoc);
|
|
327
412
|
}
|
|
328
413
|
|
|
329
|
-
/**
|
|
330
|
-
* Retrieves all clinics within a specified range from a location with optional pagination
|
|
331
|
-
*
|
|
332
|
-
* @param center - The center location coordinates {latitude, longitude}
|
|
333
|
-
* @param rangeInKm - The range in kilometers to search within
|
|
334
|
-
* @param pagination - Optional number of clinics per page (0 or undefined returns all)
|
|
335
|
-
* @param lastDoc - Optional last document for pagination (if continuing from a previous page)
|
|
336
|
-
* @param filters - Optional filters to apply to the search (isActive, tags, etc.)
|
|
337
|
-
* @returns Array of clinics with distance information and the last document for pagination
|
|
338
|
-
*/
|
|
339
414
|
async getAllClinicsInRange(
|
|
340
415
|
center: { latitude: number; longitude: number },
|
|
341
416
|
rangeInKm: number,
|
|
342
417
|
pagination?: number,
|
|
343
|
-
lastDoc?: any
|
|
344
|
-
filters?: {
|
|
345
|
-
isActive?: boolean;
|
|
346
|
-
tags?: ClinicTag[];
|
|
347
|
-
}
|
|
418
|
+
lastDoc?: any
|
|
348
419
|
): Promise<{ clinics: (Clinic & { distance: number })[]; lastDoc: any }> {
|
|
349
420
|
return ClinicUtils.getAllClinicsInRange(
|
|
350
421
|
this.db,
|
|
351
422
|
center,
|
|
352
423
|
rangeInKm,
|
|
353
424
|
pagination,
|
|
354
|
-
lastDoc
|
|
355
|
-
filters
|
|
425
|
+
lastDoc
|
|
356
426
|
);
|
|
357
427
|
}
|
|
428
|
+
|
|
429
|
+
async getClinicsByFilters(filters: {
|
|
430
|
+
center?: { latitude: number; longitude: number };
|
|
431
|
+
radiusInKm?: number;
|
|
432
|
+
tags?: ClinicTag[];
|
|
433
|
+
procedureFamily?: string;
|
|
434
|
+
procedureCategory?: string;
|
|
435
|
+
procedureSubcategory?: string;
|
|
436
|
+
procedureTechnology?: string;
|
|
437
|
+
minRating?: number;
|
|
438
|
+
maxRating?: number;
|
|
439
|
+
pagination?: number;
|
|
440
|
+
lastDoc?: any;
|
|
441
|
+
isActive?: boolean;
|
|
442
|
+
}): Promise<{
|
|
443
|
+
clinics: (Clinic & { distance?: number })[];
|
|
444
|
+
lastDoc: any;
|
|
445
|
+
}> {
|
|
446
|
+
return FilterUtils.getClinicsByFilters(this.db, filters);
|
|
447
|
+
}
|
|
358
448
|
}
|