@blackcode_sa/metaestetics-api 1.7.45 → 1.8.0

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 (55) hide show
  1. package/dist/admin/index.d.mts +999 -959
  2. package/dist/admin/index.d.ts +999 -959
  3. package/dist/admin/index.js +69 -69
  4. package/dist/admin/index.mjs +67 -69
  5. package/dist/index.d.mts +14675 -13040
  6. package/dist/index.d.ts +14675 -13040
  7. package/dist/index.js +12224 -14615
  8. package/dist/index.mjs +12452 -14968
  9. package/package.json +5 -5
  10. package/src/admin/index.ts +8 -1
  11. package/src/index.backup.ts +407 -0
  12. package/src/index.ts +5 -406
  13. package/src/services/PATIENTAUTH.MD +197 -0
  14. package/src/services/__tests__/auth/auth.setup.ts +2 -2
  15. package/src/services/__tests__/auth.service.test.ts +1 -1
  16. package/src/services/__tests__/user.service.test.ts +1 -1
  17. package/src/services/appointment/index.ts +1 -2
  18. package/src/services/{auth.service.ts → auth/auth.service.ts} +36 -22
  19. package/src/services/{auth.v2.service.ts → auth/auth.v2.service.ts} +17 -17
  20. package/src/services/auth/index.ts +2 -16
  21. package/src/services/calendar/calendar-refactored.service.ts +1 -1
  22. package/src/services/calendar/externalCalendar.service.ts +178 -0
  23. package/src/services/calendar/index.ts +5 -0
  24. package/src/services/clinic/index.ts +4 -0
  25. package/src/services/index.ts +14 -0
  26. package/src/services/media/index.ts +1 -0
  27. package/src/services/notifications/index.ts +1 -0
  28. package/src/services/patient/README.md +48 -0
  29. package/src/services/patient/To-Do.md +43 -0
  30. package/src/services/patient/index.ts +2 -0
  31. package/src/services/patient/patient.service.ts +289 -34
  32. package/src/services/patient/utils/index.ts +9 -0
  33. package/src/services/patient/utils/medical.utils.ts +114 -157
  34. package/src/services/patient/utils/profile.utils.ts +9 -0
  35. package/src/services/patient/utils/sensitive.utils.ts +79 -14
  36. package/src/services/patient/utils/token.utils.ts +211 -0
  37. package/src/services/practitioner/index.ts +1 -0
  38. package/src/services/procedure/index.ts +1 -0
  39. package/src/services/reviews/index.ts +1 -0
  40. package/src/services/user/index.ts +1 -0
  41. package/src/services/{user.service.ts → user/user.service.ts} +61 -12
  42. package/src/services/{user.v2.service.ts → user/user.v2.service.ts} +12 -12
  43. package/src/types/index.ts +42 -42
  44. package/src/types/patient/index.ts +33 -6
  45. package/src/types/patient/token.types.ts +61 -0
  46. package/src/types/user/index.ts +38 -0
  47. package/src/utils/index.ts +1 -0
  48. package/src/validations/calendar.schema.ts +6 -45
  49. package/src/validations/documentation-templates/index.ts +1 -0
  50. package/src/validations/documentation-templates.schema.ts +1 -1
  51. package/src/validations/index.ts +20 -0
  52. package/src/validations/patient/token.schema.ts +29 -0
  53. package/src/validations/patient.schema.ts +23 -6
  54. package/src/validations/profile-info.schema.ts +1 -1
  55. package/src/validations/schemas.ts +24 -24
@@ -10,17 +10,77 @@ import {
10
10
  CreatePatientSensitiveInfoData,
11
11
  UpdatePatientSensitiveInfoData,
12
12
  } from "../../../types/patient";
13
+ import { UserRole } from "../../../types";
13
14
  import { createPatientSensitiveInfoSchema } from "../../../validations/patient.schema";
14
15
  import { z } from "zod";
15
16
  import {
16
17
  getSensitiveInfoDocRef,
17
18
  initSensitiveInfoDocIfNotExists,
19
+ getPatientDocRef,
18
20
  } from "./docs.utils";
19
21
  import {
20
22
  MediaService,
21
23
  MediaAccessLevel,
22
24
  MediaResource,
23
25
  } from "../../media/media.service";
26
+ import { AuthError } from "../../../errors/auth.errors";
27
+ import { getPractitionerProfileByUserRef } from "./practitioner.utils";
28
+ import { getClinicAdminByUserRef } from "../../clinic/utils/admin.utils";
29
+
30
+ /**
31
+ * Checks if the requester has permission to access/modify sensitive info.
32
+ * Access is granted to the patient owner, or an associated practitioner/clinic admin.
33
+ */
34
+ const checkSensitiveAccessUtil = async (
35
+ db: Firestore,
36
+ patientId: string,
37
+ requesterId: string,
38
+ requesterRoles: UserRole[]
39
+ ): Promise<void> => {
40
+ const patientDoc = await getDoc(getPatientDocRef(db, patientId));
41
+ if (!patientDoc.exists()) {
42
+ throw new Error("Patient profile not found");
43
+ }
44
+ const patientData = patientDoc.data() as any; // Cast to any to access properties
45
+
46
+ // 1. Patient is the owner
47
+ if (patientData.userRef && patientData.userRef === requesterId) {
48
+ return;
49
+ }
50
+
51
+ // 2. Requester is an associated practitioner
52
+ if (requesterRoles.includes(UserRole.PRACTITIONER)) {
53
+ const practitionerProfile = await getPractitionerProfileByUserRef(
54
+ db,
55
+ requesterId
56
+ );
57
+ if (
58
+ practitionerProfile &&
59
+ patientData.doctorIds?.includes(practitionerProfile.id)
60
+ ) {
61
+ return;
62
+ }
63
+ }
64
+
65
+ // 3. Requester is an associated clinic admin
66
+ if (requesterRoles.includes(UserRole.CLINIC_ADMIN)) {
67
+ const adminProfile = await getClinicAdminByUserRef(db, requesterId);
68
+ if (adminProfile && adminProfile.clinicsManaged) {
69
+ const hasAccess = adminProfile.clinicsManaged.some((managedClinicId) =>
70
+ patientData.clinicIds?.includes(managedClinicId)
71
+ );
72
+ if (hasAccess) {
73
+ return;
74
+ }
75
+ }
76
+ }
77
+
78
+ throw new AuthError(
79
+ "Unauthorized access to sensitive information.",
80
+ "AUTH/UNAUTHORIZED_ACCESS",
81
+ 403
82
+ );
83
+ };
24
84
 
25
85
  /**
26
86
  * Handles photoUrl upload for sensitive info (supports MediaResource)
@@ -62,13 +122,18 @@ const handlePhotoUrlUpload = async (
62
122
  export const createSensitiveInfoUtil = async (
63
123
  db: Firestore,
64
124
  data: CreatePatientSensitiveInfoData,
65
- requesterUserId: string,
125
+ requesterId: string,
126
+ requesterRoles: UserRole[],
66
127
  mediaService?: MediaService
67
128
  ): Promise<PatientSensitiveInfo> => {
68
129
  try {
69
- if (data.userRef !== requesterUserId) {
70
- throw new Error("Only patient can create their sensitive information");
71
- }
130
+ // Security check
131
+ await checkSensitiveAccessUtil(
132
+ db,
133
+ data.patientId,
134
+ requesterId,
135
+ requesterRoles
136
+ );
72
137
 
73
138
  const validatedData = createPatientSensitiveInfoSchema.parse(data);
74
139
 
@@ -118,14 +183,14 @@ export const createSensitiveInfoUtil = async (
118
183
  export const getSensitiveInfoUtil = async (
119
184
  db: Firestore,
120
185
  patientId: string,
121
- requesterUserId: string
186
+ requesterId: string,
187
+ requesterRoles: UserRole[]
122
188
  ): Promise<PatientSensitiveInfo | null> => {
123
- // if (patientId !== requesterUserId) {
124
- // throw new Error("Unauthorized access to sensitive information");
125
- // }
189
+ // Security check
190
+ await checkSensitiveAccessUtil(db, patientId, requesterId, requesterRoles);
126
191
 
127
192
  // Inicijalizacija dokumenta ako ne postoji
128
- await initSensitiveInfoDocIfNotExists(db, patientId, requesterUserId);
193
+ await initSensitiveInfoDocIfNotExists(db, patientId, requesterId);
129
194
 
130
195
  const sensitiveDoc = await getDoc(getSensitiveInfoDocRef(db, patientId));
131
196
  return sensitiveDoc.exists()
@@ -137,15 +202,15 @@ export const updateSensitiveInfoUtil = async (
137
202
  db: Firestore,
138
203
  patientId: string,
139
204
  data: UpdatePatientSensitiveInfoData,
140
- requesterUserId: string,
205
+ requesterId: string,
206
+ requesterRoles: UserRole[],
141
207
  mediaService?: MediaService
142
208
  ): Promise<PatientSensitiveInfo> => {
143
- // if (data.userRef !== requesterUserId) {
144
- // throw new Error("Only patient can update their sensitive information");
145
- // }
209
+ // Security check
210
+ await checkSensitiveAccessUtil(db, patientId, requesterId, requesterRoles);
146
211
 
147
212
  // Inicijalizacija dokumenta ako ne postoji
148
- await initSensitiveInfoDocIfNotExists(db, patientId, requesterUserId);
213
+ await initSensitiveInfoDocIfNotExists(db, patientId, requesterId);
149
214
 
150
215
  // Process photoUrl if it's a MediaResource and mediaService is provided
151
216
  let processedPhotoUrl: string | null | undefined = undefined;
@@ -0,0 +1,211 @@
1
+ import {
2
+ collection,
3
+ doc,
4
+ getDoc,
5
+ getDocs,
6
+ query,
7
+ where,
8
+ setDoc,
9
+ updateDoc,
10
+ Timestamp,
11
+ collectionGroup,
12
+ } from "firebase/firestore";
13
+ import { Firestore } from "firebase/firestore";
14
+ import { z } from "zod";
15
+ import {
16
+ PatientToken,
17
+ CreatePatientTokenData,
18
+ PatientTokenStatus,
19
+ INVITE_TOKENS_COLLECTION,
20
+ } from "../../../types/patient/token.types";
21
+ import {
22
+ patientTokenSchema,
23
+ createPatientTokenSchema,
24
+ } from "../../../validations/patient/token.schema";
25
+ import { getPatientDocRef } from "./docs.utils";
26
+ import { PATIENTS_COLLECTION } from "../../../types/patient";
27
+
28
+ /**
29
+ * Creates a token for inviting a patient to claim their profile.
30
+ *
31
+ * @param {Firestore} db - The Firestore database instance.
32
+ * @param {CreatePatientTokenData} data - Data for creating the token.
33
+ * @param {string} createdBy - ID of the user creating the token.
34
+ * @param {() => string} generateId - Function to generate a unique ID.
35
+ * @returns {Promise<PatientToken>} The created token.
36
+ * @throws {Error} If the patient profile is not found, is not a manual profile, or if data is invalid.
37
+ */
38
+ export const createPatientTokenUtil = async (
39
+ db: Firestore,
40
+ data: CreatePatientTokenData,
41
+ createdBy: string,
42
+ generateId: () => string
43
+ ): Promise<PatientToken> => {
44
+ const validatedData = createPatientTokenSchema.parse(data);
45
+
46
+ // Check if patient exists and is a manual profile
47
+ const patientRef = getPatientDocRef(db, validatedData.patientId);
48
+ const patientDoc = await getDoc(patientRef);
49
+ if (!patientDoc.exists() || !patientDoc.data()?.isManual) {
50
+ throw new Error(
51
+ "Patient profile not found or is not a manually created profile."
52
+ );
53
+ }
54
+
55
+ // Default expiration is 7 days from now if not specified
56
+ const expiration =
57
+ validatedData.expiresAt || new Date(Date.now() + 7 * 24 * 60 * 60 * 1000);
58
+
59
+ const tokenString = generateId().slice(0, 6).toUpperCase();
60
+
61
+ const token: PatientToken = {
62
+ id: generateId(),
63
+ token: tokenString,
64
+ patientId: validatedData.patientId,
65
+ email: validatedData.email,
66
+ clinicId: validatedData.clinicId,
67
+ status: PatientTokenStatus.ACTIVE,
68
+ createdBy: createdBy,
69
+ createdAt: Timestamp.now(),
70
+ expiresAt: Timestamp.fromDate(expiration),
71
+ };
72
+
73
+ patientTokenSchema.parse(token);
74
+
75
+ const tokenRef = doc(
76
+ db,
77
+ PATIENTS_COLLECTION,
78
+ validatedData.patientId,
79
+ INVITE_TOKENS_COLLECTION,
80
+ token.id
81
+ );
82
+ await setDoc(tokenRef, token);
83
+
84
+ return token;
85
+ };
86
+
87
+ /**
88
+ * Validates a patient invitation token.
89
+ *
90
+ * @param {Firestore} db - The Firestore database instance.
91
+ * @param {string} tokenString - The token string to validate.
92
+ * @returns {Promise<PatientToken | null>} The token if found and valid, otherwise null.
93
+ */
94
+ export const validatePatientTokenUtil = async (
95
+ db: Firestore,
96
+ tokenString: string
97
+ ): Promise<PatientToken | null> => {
98
+ const patientsRef = collection(db, PATIENTS_COLLECTION);
99
+ const patientsSnapshot = await getDocs(patientsRef);
100
+
101
+ for (const patientDoc of patientsSnapshot.docs) {
102
+ const tokensRef = collection(
103
+ db,
104
+ patientDoc.ref.path,
105
+ INVITE_TOKENS_COLLECTION
106
+ );
107
+ const q = query(
108
+ tokensRef,
109
+ where("token", "==", tokenString),
110
+ where("status", "==", PatientTokenStatus.ACTIVE),
111
+ where("expiresAt", ">", Timestamp.now())
112
+ );
113
+
114
+ const tokenSnapshot = await getDocs(q);
115
+ if (!tokenSnapshot.empty) {
116
+ return tokenSnapshot.docs[0].data() as PatientToken;
117
+ }
118
+ }
119
+
120
+ return null;
121
+ };
122
+
123
+ /**
124
+ * Marks a patient invitation token as used.
125
+ *
126
+ * @param {Firestore} db - The Firestore database instance.
127
+ * @param {string} tokenId - The ID of the token to mark as used.
128
+ * @param {string} patientId - The ID of the patient associated with the token.
129
+ * @param {string} userId - The ID of the user who is using the token.
130
+ * @returns {Promise<void>}
131
+ */
132
+ export const markPatientTokenAsUsedUtil = async (
133
+ db: Firestore,
134
+ tokenId: string,
135
+ patientId: string,
136
+ userId: string
137
+ ): Promise<void> => {
138
+ const tokenRef = doc(
139
+ db,
140
+ PATIENTS_COLLECTION,
141
+ patientId,
142
+ INVITE_TOKENS_COLLECTION,
143
+ tokenId
144
+ );
145
+ await updateDoc(tokenRef, {
146
+ status: PatientTokenStatus.USED,
147
+ usedBy: userId,
148
+ usedAt: Timestamp.now(),
149
+ });
150
+ };
151
+
152
+ /**
153
+ * Retrieves all active invitation tokens for a specific clinic.
154
+ * This uses a collection group query to find tokens across all patients.
155
+ *
156
+ * @param {Firestore} db - The Firestore database instance.
157
+ * @param {string} clinicId - The ID of the clinic.
158
+ * @returns {Promise<PatientToken[]>} An array of active tokens for the clinic.
159
+ */
160
+ export const getActiveInviteTokensByClinicUtil = async (
161
+ db: Firestore,
162
+ clinicId: string
163
+ ): Promise<PatientToken[]> => {
164
+ const tokensQuery = query(
165
+ collectionGroup(db, INVITE_TOKENS_COLLECTION),
166
+ where("clinicId", "==", clinicId),
167
+ where("status", "==", PatientTokenStatus.ACTIVE),
168
+ where("expiresAt", ">", Timestamp.now())
169
+ );
170
+
171
+ const querySnapshot = await getDocs(tokensQuery);
172
+
173
+ if (querySnapshot.empty) {
174
+ return [];
175
+ }
176
+
177
+ return querySnapshot.docs.map((doc) => doc.data() as PatientToken);
178
+ };
179
+
180
+ /**
181
+ * Retrieves all active invitation tokens for a specific patient.
182
+ *
183
+ * @param {Firestore} db - The Firestore database instance.
184
+ * @param {string} patientId - The ID of the patient.
185
+ * @returns {Promise<PatientToken[]>} An array of active tokens for the patient.
186
+ */
187
+ export const getActiveInviteTokensByPatientUtil = async (
188
+ db: Firestore,
189
+ patientId: string
190
+ ): Promise<PatientToken[]> => {
191
+ const tokensRef = collection(
192
+ db,
193
+ PATIENTS_COLLECTION,
194
+ patientId,
195
+ INVITE_TOKENS_COLLECTION
196
+ );
197
+
198
+ const q = query(
199
+ tokensRef,
200
+ where("status", "==", PatientTokenStatus.ACTIVE),
201
+ where("expiresAt", ">", Timestamp.now())
202
+ );
203
+
204
+ const querySnapshot = await getDocs(q);
205
+
206
+ if (querySnapshot.empty) {
207
+ return [];
208
+ }
209
+
210
+ return querySnapshot.docs.map((doc) => doc.data() as PatientToken);
211
+ };
@@ -0,0 +1 @@
1
+ export * from "./practitioner.service";
@@ -0,0 +1 @@
1
+ export * from "./procedure.service";
@@ -0,0 +1 @@
1
+ export * from "./reviews.service";
@@ -0,0 +1 @@
1
+ export * from "./user.service";
@@ -13,21 +13,21 @@ import {
13
13
  serverTimestamp,
14
14
  FieldValue,
15
15
  } from "firebase/firestore";
16
- import { initializeFirebase } from "../config/firebase";
17
- import { User, UserRole, USERS_COLLECTION, CreateUserData } from "../types";
18
- import { userSchema } from "../validations/schemas";
19
- import { AuthError } from "../errors/auth.errors";
20
- import { USER_ERRORS } from "../errors/user.errors";
21
- import { AUTH_ERRORS } from "../errors/auth.errors";
16
+ import { initializeFirebase } from "../../config/firebase";
17
+ import { User, UserRole, USERS_COLLECTION, CreateUserData } from "../../types";
18
+ import { userSchema } from "../../validations/schemas";
19
+ import { AuthError } from "../../errors/auth.errors";
20
+ import { USER_ERRORS } from "../../errors/user.errors";
21
+ import { AUTH_ERRORS } from "../../errors/auth.errors";
22
22
  import { z } from "zod";
23
- import { BaseService } from "./base.service";
24
- import { PatientService } from "./patient/patient.service";
25
- import { ClinicAdminService } from "./clinic/clinic-admin.service";
26
- import { PatientProfile, PATIENTS_COLLECTION } from "../types/patient";
23
+ import { BaseService } from "../base.service";
24
+ import { PatientService } from "../patient/patient.service";
25
+ import { ClinicAdminService } from "../clinic/clinic-admin.service";
26
+ import { PatientProfile, PATIENTS_COLLECTION } from "../../types/patient";
27
27
  import { User as FirebaseUser } from "firebase/auth";
28
28
  import { Auth } from "firebase/auth";
29
- import { PractitionerService } from "./practitioner/practitioner.service";
30
- import { CertificationLevel } from "../backoffice/types/static/certification.types";
29
+ import { PractitionerService } from "../practitioner/practitioner.service";
30
+ import { CertificationLevel } from "../../backoffice/types/static/certification.types";
31
31
  import { Firestore } from "firebase/firestore";
32
32
  import { FirebaseApp } from "firebase/app";
33
33
 
@@ -86,6 +86,7 @@ export class UserService extends BaseService {
86
86
  groupToken?: string;
87
87
  groupId?: string;
88
88
  };
89
+ patientInviteToken?: string;
89
90
  skipProfileCreation?: boolean;
90
91
  }
91
92
  ): Promise<User> {
@@ -147,6 +148,7 @@ export class UserService extends BaseService {
147
148
  groupToken?: string;
148
149
  groupId?: string;
149
150
  };
151
+ patientInviteToken?: string;
150
152
  skipProfileCreation?: boolean;
151
153
  }
152
154
  ): Promise<{
@@ -163,6 +165,52 @@ export class UserService extends BaseService {
163
165
  for (const role of roles) {
164
166
  switch (role) {
165
167
  case UserRole.PATIENT:
168
+ // If a token is provided, claim the existing manual profile
169
+ if (options?.patientInviteToken) {
170
+ const patientService = this.getPatientService();
171
+ const token = await patientService.validatePatientToken(
172
+ options.patientInviteToken
173
+ );
174
+
175
+ if (!token) {
176
+ throw new Error("Invalid or expired patient invitation token.");
177
+ }
178
+
179
+ // Get the patient profile
180
+ const patientProfile = await patientService.getPatientProfile(
181
+ token.patientId
182
+ );
183
+ if (!patientProfile || !patientProfile.isManual) {
184
+ throw new Error(
185
+ "Patient profile not found or has already been claimed."
186
+ );
187
+ }
188
+
189
+ // Check if user already has a patient profile
190
+ if (
191
+ (await this.getUserById(userId)).patientProfile ||
192
+ patientProfile.userRef
193
+ ) {
194
+ throw new Error("User already has a patient profile.");
195
+ }
196
+
197
+ // Claim the profile: link userRef and set isManual to false
198
+ await patientService.updatePatientProfile(patientProfile.id, {
199
+ userRef: userId,
200
+ isManual: false,
201
+ });
202
+
203
+ // Mark the token as used
204
+ await patientService.markPatientTokenAsUsed(
205
+ token.id,
206
+ token.patientId,
207
+ userId
208
+ );
209
+
210
+ profiles.patientProfile = patientProfile.id;
211
+ break;
212
+ }
213
+
166
214
  const patientProfile =
167
215
  await this.getPatientService().createPatientProfile({
168
216
  userRef: userId,
@@ -174,6 +222,7 @@ export class UserService extends BaseService {
174
222
  },
175
223
  isActive: true,
176
224
  isVerified: false,
225
+ isManual: false, // Explicitly set to false for standard signups
177
226
  });
178
227
  profiles.patientProfile = patientProfile.id;
179
228
  break;
@@ -13,21 +13,21 @@ import {
13
13
  serverTimestamp,
14
14
  FieldValue,
15
15
  } from "firebase/firestore";
16
- import { initializeFirebase } from "../config/firebase";
17
- import { User, UserRole, USERS_COLLECTION, CreateUserData } from "../types";
18
- import { userSchema } from "../validations/schemas";
19
- import { AuthError } from "../errors/auth.errors";
20
- import { USER_ERRORS } from "../errors/user.errors";
21
- import { AUTH_ERRORS } from "../errors/auth.errors";
16
+ import { initializeFirebase } from "../../config/firebase";
17
+ import { User, UserRole, USERS_COLLECTION, CreateUserData } from "../../types";
18
+ import { userSchema } from "../../validations/schemas";
19
+ import { AuthError } from "../../errors/auth.errors";
20
+ import { USER_ERRORS } from "../../errors/user.errors";
21
+ import { AUTH_ERRORS } from "../../errors/auth.errors";
22
22
  import { z } from "zod";
23
- import { BaseService } from "./base.service";
24
- import { PatientService } from "./patient/patient.service";
25
- import { ClinicAdminService } from "./clinic/clinic-admin.service";
26
- import { PatientProfile, PATIENTS_COLLECTION } from "../types/patient";
23
+ import { BaseService } from "../base.service";
24
+ import { PatientService } from "../patient/patient.service";
25
+ import { ClinicAdminService } from "../clinic/clinic-admin.service";
26
+ import { PatientProfile, PATIENTS_COLLECTION } from "../../types/patient";
27
27
  import { User as FirebaseUser } from "firebase/auth";
28
28
  import { Auth } from "firebase/auth";
29
- import { PractitionerService } from "./practitioner/practitioner.service";
30
- import { CertificationLevel } from "../backoffice/types/static/certification.types";
29
+ import { PractitionerService } from "../practitioner/practitioner.service";
30
+ import { CertificationLevel } from "../../backoffice/types/static/certification.types";
31
31
  import { Firestore } from "firebase/firestore";
32
32
  import { FirebaseApp } from "firebase/app";
33
33
 
@@ -1,44 +1,44 @@
1
- import { User as FirebaseAuthUser } from "firebase/auth";
2
- import { Timestamp, FieldValue } from "firebase/firestore";
3
-
4
- // User tipovi
5
- export enum UserRole {
6
- PATIENT = "patient",
7
- PRACTITIONER = "practitioner",
8
- APP_ADMIN = "app_admin",
9
- CLINIC_ADMIN = "clinic_admin",
10
- }
11
-
12
- export interface User {
13
- uid: string;
14
- email: string | null;
15
- roles: UserRole[];
16
- isAnonymous: boolean;
17
- createdAt: any;
18
- updatedAt: any;
19
- lastLoginAt: any;
20
- patientProfile?: string;
21
- practitionerProfile?: string;
22
- adminProfile?: string;
23
- }
24
-
25
- export interface CreateUserData {
26
- uid: string;
27
- email: string | null;
28
- roles: UserRole[];
29
- isAnonymous: boolean;
30
- createdAt: FieldValue;
31
- updatedAt: FieldValue;
32
- lastLoginAt: FieldValue;
33
- }
34
-
35
- export const USERS_COLLECTION = "users";
36
-
37
- // Firebase tipovi
38
- export type FirebaseUser = FirebaseAuthUser;
39
-
40
- // Documentation Templates
41
- export * from "./documentation-templates";
1
+ // Export all type definitions for easy access
2
+
3
+ // Top-level user types
4
+ export * from "./user";
42
5
 
43
- // Calendar
6
+ // Appointment types
7
+ export * from "./appointment";
8
+
9
+ // Calendar types
44
10
  export * from "./calendar";
11
+ export * from "./calendar/synced-calendar.types";
12
+
13
+ // Clinic types
14
+ export * from "./clinic";
15
+ export * from "./clinic/practitioner-invite.types";
16
+ export * from "./clinic/preferences.types";
17
+
18
+ // Documentation Templates types
19
+ export * from "./documentation-templates";
20
+
21
+ // Notifications types
22
+ export * from "./notifications";
23
+
24
+ // Patient types
25
+ export * from "./patient";
26
+ export * from "./patient/allergies";
27
+ export * from "./patient/medical-info.types";
28
+ export * from "./patient/patient-requirements";
29
+ export * from "./patient/token.types";
30
+
31
+ // Practitioner types
32
+ export * from "./practitioner";
33
+
34
+ // Procedure types
35
+ export * from "./procedure";
36
+
37
+ // Profile types
38
+ export * from "./profile";
39
+
40
+ // Reviews types
41
+ export * from "./reviews";
42
+
43
+ // User types
44
+ export * from "./user";
@@ -66,7 +66,7 @@ export interface GamificationInfo {
66
66
  */
67
67
  export interface PatientLocationInfo {
68
68
  patientId: string;
69
- userRef: string;
69
+ userRef?: string;
70
70
  locationData: LocationData; // Samo za geopretragu
71
71
  createdAt: Timestamp;
72
72
  updatedAt: Timestamp;
@@ -77,7 +77,7 @@ export interface PatientLocationInfo {
77
77
  */
78
78
  export interface CreatePatientLocationInfoData {
79
79
  patientId: string;
80
- userRef: string;
80
+ userRef?: string;
81
81
  locationData: LocationData;
82
82
  }
83
83
 
@@ -94,7 +94,7 @@ export interface UpdatePatientLocationInfoData
94
94
  */
95
95
  export interface PatientSensitiveInfo {
96
96
  patientId: string;
97
- userRef: string;
97
+ userRef?: string;
98
98
  photoUrl?: string | null;
99
99
  firstName: string;
100
100
  lastName: string;
@@ -114,7 +114,7 @@ export interface PatientSensitiveInfo {
114
114
  */
115
115
  export interface CreatePatientSensitiveInfoData {
116
116
  patientId: string;
117
- userRef: string;
117
+ userRef?: string;
118
118
  photoUrl?: MediaResource | null;
119
119
  firstName: string;
120
120
  lastName: string;
@@ -162,12 +162,13 @@ export interface PatientClinic {
162
162
  */
163
163
  export interface PatientProfile {
164
164
  id: string;
165
- userRef: string;
165
+ userRef?: string;
166
166
  displayName: string; // Inicijali ili pseudonim
167
167
  gamification: GamificationInfo;
168
168
  expoTokens: string[];
169
169
  isActive: boolean;
170
170
  isVerified: boolean;
171
+ isManual: boolean; // Indicates if the profile was created manually by a clinic
171
172
  phoneNumber?: string | null;
172
173
  dateOfBirth?: Timestamp | null;
173
174
  doctors: PatientDoctor[]; // Lista doktora pacijenta
@@ -182,12 +183,13 @@ export interface PatientProfile {
182
183
  * Tip za kreiranje novog Patient profila
183
184
  */
184
185
  export interface CreatePatientProfileData {
185
- userRef: string;
186
+ userRef?: string;
186
187
  displayName: string;
187
188
  expoTokens: string[];
188
189
  gamification?: GamificationInfo;
189
190
  isActive: boolean;
190
191
  isVerified: boolean;
192
+ isManual: boolean;
191
193
  doctors?: PatientDoctor[];
192
194
  clinics?: PatientClinic[];
193
195
  doctorIds?: string[]; // Initialize as empty or with initial doctors
@@ -204,6 +206,31 @@ export interface UpdatePatientProfileData
204
206
  // Note: doctors, clinics, doctorIds, clinicIds should ideally be updated via specific methods (add/removeDoctor/Clinic)
205
207
  }
206
208
 
209
+ /**
210
+ * Data required for a clinic admin to manually create a new patient profile.
211
+ * This patient does not have a user account initially.
212
+ */
213
+ export interface CreateManualPatientData {
214
+ /** The clinic ID where the patient is being created. */
215
+ clinicId: string;
216
+ /** Patient's first name. */
217
+ firstName: string;
218
+ /** Patient's last name. */
219
+ lastName: string;
220
+ /** Patient's date of birth. */
221
+ dateOfBirth: Timestamp | null;
222
+ /** Patient's gender. */
223
+ gender: Gender;
224
+ /** Optional phone number. */
225
+ phoneNumber?: string;
226
+ /** Optional email address. */
227
+ email?: string;
228
+ /** Optional address information. */
229
+ addressData?: AddressData;
230
+ /** Optional notes about the patient. */
231
+ notes?: string;
232
+ }
233
+
207
234
  /**
208
235
  * Parameters for searching patient profiles.
209
236
  */