@blackcode_sa/metaestetics-api 1.5.0 → 1.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +571 -7
- package/dist/index.d.ts +571 -7
- package/dist/index.js +293 -2
- package/dist/index.mjs +287 -2
- package/package.json +1 -1
- package/src/index.ts +9 -1
- package/src/services/practitioner/practitioner.service.ts +345 -1
- package/src/types/practitioner/index.ts +60 -0
- package/src/validations/practitioner.schema.ts +45 -0
|
@@ -17,11 +17,20 @@ import {
|
|
|
17
17
|
CreatePractitionerData,
|
|
18
18
|
UpdatePractitionerData,
|
|
19
19
|
PRACTITIONERS_COLLECTION,
|
|
20
|
+
REGISTER_TOKENS_COLLECTION,
|
|
21
|
+
PractitionerStatus,
|
|
22
|
+
CreateDraftPractitionerData,
|
|
23
|
+
PractitionerToken,
|
|
24
|
+
CreatePractitionerTokenData,
|
|
25
|
+
PractitionerTokenStatus,
|
|
20
26
|
} from "../../types/practitioner";
|
|
21
27
|
import { ClinicService } from "../clinic/clinic.service";
|
|
22
28
|
import {
|
|
23
29
|
practitionerSchema,
|
|
24
30
|
createPractitionerSchema,
|
|
31
|
+
createDraftPractitionerSchema,
|
|
32
|
+
practitionerTokenSchema,
|
|
33
|
+
createPractitionerTokenSchema,
|
|
25
34
|
} from "../../validations/practitioner.schema";
|
|
26
35
|
import { z } from "zod";
|
|
27
36
|
import { Auth } from "firebase/auth";
|
|
@@ -95,6 +104,7 @@ export class PractitionerService extends BaseService {
|
|
|
95
104
|
clinicWorkingHours: validatedData.clinicWorkingHours || [],
|
|
96
105
|
isActive: validatedData.isActive,
|
|
97
106
|
isVerified: validatedData.isVerified,
|
|
107
|
+
status: validatedData.status || PractitionerStatus.ACTIVE,
|
|
98
108
|
createdAt: serverTimestamp(),
|
|
99
109
|
updatedAt: serverTimestamp(),
|
|
100
110
|
};
|
|
@@ -126,6 +136,278 @@ export class PractitionerService extends BaseService {
|
|
|
126
136
|
}
|
|
127
137
|
}
|
|
128
138
|
|
|
139
|
+
/**
|
|
140
|
+
* Kreira novi draft profil zdravstvenog radnika bez povezanog korisnika
|
|
141
|
+
* Koristi se od strane administratora klinike za kreiranje profila i kasnije pozivanje
|
|
142
|
+
* @param data Podaci za kreiranje draft profila
|
|
143
|
+
* @param createdBy ID administratora koji kreira profil
|
|
144
|
+
* @param clinicId ID klinike za koju se kreira profil
|
|
145
|
+
* @returns Objekt koji sadrži kreirani draft profil i token za registraciju
|
|
146
|
+
*/
|
|
147
|
+
async createDraftPractitioner(
|
|
148
|
+
data: CreateDraftPractitionerData,
|
|
149
|
+
createdBy: string,
|
|
150
|
+
clinicId: string
|
|
151
|
+
): Promise<{ practitioner: Practitioner; token: PractitionerToken }> {
|
|
152
|
+
try {
|
|
153
|
+
// Validacija ulaznih podataka
|
|
154
|
+
const validatedData = createDraftPractitionerSchema.parse(data);
|
|
155
|
+
|
|
156
|
+
// Provera da li klinika postoji
|
|
157
|
+
const clinic = await this.getClinicService().getClinic(clinicId);
|
|
158
|
+
if (!clinic) {
|
|
159
|
+
throw new Error(`Clinic ${clinicId} not found`);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Priprema podataka za kreiranje profila
|
|
163
|
+
const clinics = data.clinics || [clinicId];
|
|
164
|
+
|
|
165
|
+
// Provera da li sve dodatno navedene klinike postoje
|
|
166
|
+
if (data.clinics) {
|
|
167
|
+
for (const cId of data.clinics) {
|
|
168
|
+
if (cId !== clinicId) {
|
|
169
|
+
const otherClinic = await this.getClinicService().getClinic(cId);
|
|
170
|
+
if (!otherClinic) {
|
|
171
|
+
throw new Error(`Clinic ${cId} not found`);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const practitionerId = this.generateId();
|
|
178
|
+
const practitionerData: Omit<Practitioner, "createdAt" | "updatedAt"> & {
|
|
179
|
+
createdAt: ReturnType<typeof serverTimestamp>;
|
|
180
|
+
updatedAt: ReturnType<typeof serverTimestamp>;
|
|
181
|
+
} = {
|
|
182
|
+
id: practitionerId,
|
|
183
|
+
userRef: "", // Prazno - biće popunjeno kada korisnik kreira nalog
|
|
184
|
+
basicInfo: validatedData.basicInfo,
|
|
185
|
+
certification: validatedData.certification,
|
|
186
|
+
clinics: clinics,
|
|
187
|
+
clinicWorkingHours: validatedData.clinicWorkingHours || [],
|
|
188
|
+
isActive:
|
|
189
|
+
validatedData.isActive !== undefined ? validatedData.isActive : false,
|
|
190
|
+
isVerified:
|
|
191
|
+
validatedData.isVerified !== undefined
|
|
192
|
+
? validatedData.isVerified
|
|
193
|
+
: false,
|
|
194
|
+
status: PractitionerStatus.DRAFT,
|
|
195
|
+
createdAt: serverTimestamp(),
|
|
196
|
+
updatedAt: serverTimestamp(),
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
// Validacija kompletnog objekta
|
|
200
|
+
// Koristimo privremeni userRef za validaciju, biće prazan u bazi
|
|
201
|
+
practitionerSchema.parse({
|
|
202
|
+
...practitionerData,
|
|
203
|
+
userRef: "temp-for-validation",
|
|
204
|
+
createdAt: Timestamp.now(),
|
|
205
|
+
updatedAt: Timestamp.now(),
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
// Čuvamo u Firestore
|
|
209
|
+
await setDoc(
|
|
210
|
+
doc(this.db, PRACTITIONERS_COLLECTION, practitionerData.id),
|
|
211
|
+
practitionerData
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
const savedPractitioner = await this.getPractitioner(practitionerData.id);
|
|
215
|
+
if (!savedPractitioner) {
|
|
216
|
+
throw new Error("Failed to create draft practitioner profile");
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Automatski kreiramo token za registraciju
|
|
220
|
+
const tokenString = this.generateId().slice(0, 6).toUpperCase();
|
|
221
|
+
|
|
222
|
+
// Default expiration is 7 days from now
|
|
223
|
+
const expiration = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000);
|
|
224
|
+
|
|
225
|
+
const token: PractitionerToken = {
|
|
226
|
+
id: this.generateId(),
|
|
227
|
+
token: tokenString,
|
|
228
|
+
practitionerId: practitionerId,
|
|
229
|
+
email: practitionerData.basicInfo.email,
|
|
230
|
+
clinicId: clinicId,
|
|
231
|
+
status: PractitionerTokenStatus.ACTIVE,
|
|
232
|
+
createdBy: createdBy,
|
|
233
|
+
createdAt: Timestamp.now(),
|
|
234
|
+
expiresAt: Timestamp.fromDate(expiration),
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
// Validate token object
|
|
238
|
+
practitionerTokenSchema.parse(token);
|
|
239
|
+
|
|
240
|
+
// Store the token in the practitioner document's register_tokens subcollection
|
|
241
|
+
const tokenPath = `${PRACTITIONERS_COLLECTION}/${practitionerId}/${REGISTER_TOKENS_COLLECTION}/${token.id}`;
|
|
242
|
+
await setDoc(doc(this.db, tokenPath), token);
|
|
243
|
+
|
|
244
|
+
// Ovde bi bilo slanje emaila sa tokenom, ali to ćemo implementirati kasnije
|
|
245
|
+
// TODO: Implement email sending with Cloud Functions
|
|
246
|
+
|
|
247
|
+
return { practitioner: savedPractitioner, token };
|
|
248
|
+
} catch (error) {
|
|
249
|
+
if (error instanceof z.ZodError) {
|
|
250
|
+
throw new Error("Invalid practitioner data: " + error.message);
|
|
251
|
+
}
|
|
252
|
+
throw error;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Creates a token for inviting practitioner to claim their profile
|
|
258
|
+
* @param data Data for creating token
|
|
259
|
+
* @param createdBy ID of the user creating the token
|
|
260
|
+
* @returns Created token
|
|
261
|
+
*/
|
|
262
|
+
async createPractitionerToken(
|
|
263
|
+
data: CreatePractitionerTokenData,
|
|
264
|
+
createdBy: string
|
|
265
|
+
): Promise<PractitionerToken> {
|
|
266
|
+
try {
|
|
267
|
+
// Validate data
|
|
268
|
+
const validatedData = createPractitionerTokenSchema.parse(data);
|
|
269
|
+
|
|
270
|
+
// Check if practitioner exists and is in DRAFT status
|
|
271
|
+
const practitioner = await this.getPractitioner(
|
|
272
|
+
validatedData.practitionerId
|
|
273
|
+
);
|
|
274
|
+
if (!practitioner) {
|
|
275
|
+
throw new Error("Practitioner not found");
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
if (practitioner.status !== PractitionerStatus.DRAFT) {
|
|
279
|
+
throw new Error(
|
|
280
|
+
"Can only create tokens for practitioners in DRAFT status"
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Check if clinic exists and practitioner belongs to it
|
|
285
|
+
const clinic = await this.getClinicService().getClinic(
|
|
286
|
+
validatedData.clinicId
|
|
287
|
+
);
|
|
288
|
+
if (!clinic) {
|
|
289
|
+
throw new Error(`Clinic ${validatedData.clinicId} not found`);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
if (!practitioner.clinics.includes(validatedData.clinicId)) {
|
|
293
|
+
throw new Error("Practitioner is not associated with this clinic");
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Default expiration is 7 days from now if not specified
|
|
297
|
+
const expiration =
|
|
298
|
+
validatedData.expiresAt ||
|
|
299
|
+
new Date(Date.now() + 7 * 24 * 60 * 60 * 1000);
|
|
300
|
+
|
|
301
|
+
// Generate a token (6 characters) using generateId from BaseService
|
|
302
|
+
const tokenString = this.generateId().slice(0, 6).toUpperCase();
|
|
303
|
+
|
|
304
|
+
const token: PractitionerToken = {
|
|
305
|
+
id: this.generateId(),
|
|
306
|
+
token: tokenString,
|
|
307
|
+
practitionerId: validatedData.practitionerId,
|
|
308
|
+
email: validatedData.email,
|
|
309
|
+
clinicId: validatedData.clinicId,
|
|
310
|
+
status: PractitionerTokenStatus.ACTIVE,
|
|
311
|
+
createdBy: createdBy,
|
|
312
|
+
createdAt: Timestamp.now(),
|
|
313
|
+
expiresAt: Timestamp.fromDate(expiration),
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
// Validate token object
|
|
317
|
+
practitionerTokenSchema.parse(token);
|
|
318
|
+
|
|
319
|
+
// Store the token in the practitioner document's register_tokens subcollection
|
|
320
|
+
const tokenPath = `${PRACTITIONERS_COLLECTION}/${validatedData.practitionerId}/${REGISTER_TOKENS_COLLECTION}/${token.id}`;
|
|
321
|
+
await setDoc(doc(this.db, tokenPath), token);
|
|
322
|
+
|
|
323
|
+
return token;
|
|
324
|
+
} catch (error) {
|
|
325
|
+
if (error instanceof z.ZodError) {
|
|
326
|
+
throw new Error("Invalid token data: " + error.message);
|
|
327
|
+
}
|
|
328
|
+
throw error;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Gets active tokens for a practitioner
|
|
334
|
+
* @param practitionerId ID of the practitioner
|
|
335
|
+
* @returns Array of active tokens
|
|
336
|
+
*/
|
|
337
|
+
async getPractitionerActiveTokens(
|
|
338
|
+
practitionerId: string
|
|
339
|
+
): Promise<PractitionerToken[]> {
|
|
340
|
+
const tokensRef = collection(
|
|
341
|
+
this.db,
|
|
342
|
+
`${PRACTITIONERS_COLLECTION}/${practitionerId}/${REGISTER_TOKENS_COLLECTION}`
|
|
343
|
+
);
|
|
344
|
+
|
|
345
|
+
const q = query(
|
|
346
|
+
tokensRef,
|
|
347
|
+
where("status", "==", PractitionerTokenStatus.ACTIVE),
|
|
348
|
+
where("expiresAt", ">", Timestamp.now())
|
|
349
|
+
);
|
|
350
|
+
|
|
351
|
+
const querySnapshot = await getDocs(q);
|
|
352
|
+
return querySnapshot.docs.map((doc) => doc.data() as PractitionerToken);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Gets a token by its string value and validates it
|
|
357
|
+
* @param tokenString The token string to find
|
|
358
|
+
* @returns The token if found and valid, null otherwise
|
|
359
|
+
*/
|
|
360
|
+
async validateToken(tokenString: string): Promise<PractitionerToken | null> {
|
|
361
|
+
// We need to search through all practitioners' register_tokens subcollections
|
|
362
|
+
const practitionersRef = collection(this.db, PRACTITIONERS_COLLECTION);
|
|
363
|
+
const practitionersSnapshot = await getDocs(practitionersRef);
|
|
364
|
+
|
|
365
|
+
for (const practitionerDoc of practitionersSnapshot.docs) {
|
|
366
|
+
const practitionerId = practitionerDoc.id;
|
|
367
|
+
const tokensRef = collection(
|
|
368
|
+
this.db,
|
|
369
|
+
`${PRACTITIONERS_COLLECTION}/${practitionerId}/${REGISTER_TOKENS_COLLECTION}`
|
|
370
|
+
);
|
|
371
|
+
|
|
372
|
+
const q = query(
|
|
373
|
+
tokensRef,
|
|
374
|
+
where("token", "==", tokenString),
|
|
375
|
+
where("status", "==", PractitionerTokenStatus.ACTIVE),
|
|
376
|
+
where("expiresAt", ">", Timestamp.now())
|
|
377
|
+
);
|
|
378
|
+
|
|
379
|
+
const tokenSnapshot = await getDocs(q);
|
|
380
|
+
if (!tokenSnapshot.empty) {
|
|
381
|
+
return tokenSnapshot.docs[0].data() as PractitionerToken;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
return null;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Marks a token as used
|
|
390
|
+
* @param tokenId ID of the token
|
|
391
|
+
* @param practitionerId ID of the practitioner
|
|
392
|
+
* @param userId ID of the user using the token
|
|
393
|
+
*/
|
|
394
|
+
async markTokenAsUsed(
|
|
395
|
+
tokenId: string,
|
|
396
|
+
practitionerId: string,
|
|
397
|
+
userId: string
|
|
398
|
+
): Promise<void> {
|
|
399
|
+
const tokenRef = doc(
|
|
400
|
+
this.db,
|
|
401
|
+
`${PRACTITIONERS_COLLECTION}/${practitionerId}/${REGISTER_TOKENS_COLLECTION}/${tokenId}`
|
|
402
|
+
);
|
|
403
|
+
|
|
404
|
+
await updateDoc(tokenRef, {
|
|
405
|
+
status: PractitionerTokenStatus.USED,
|
|
406
|
+
usedBy: userId,
|
|
407
|
+
usedAt: Timestamp.now(),
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
|
|
129
411
|
/**
|
|
130
412
|
* Dohvata zdravstvenog radnika po ID-u
|
|
131
413
|
*/
|
|
@@ -167,7 +449,24 @@ export class PractitionerService extends BaseService {
|
|
|
167
449
|
const q = query(
|
|
168
450
|
collection(this.db, PRACTITIONERS_COLLECTION),
|
|
169
451
|
where("clinics", "array-contains", clinicId),
|
|
170
|
-
where("isActive", "==", true)
|
|
452
|
+
where("isActive", "==", true),
|
|
453
|
+
where("status", "==", PractitionerStatus.ACTIVE)
|
|
454
|
+
);
|
|
455
|
+
|
|
456
|
+
const querySnapshot = await getDocs(q);
|
|
457
|
+
return querySnapshot.docs.map((doc) => doc.data() as Practitioner);
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* Dohvata sve draft zdravstvene radnike za određenu kliniku
|
|
462
|
+
*/
|
|
463
|
+
async getDraftPractitionersByClinic(
|
|
464
|
+
clinicId: string
|
|
465
|
+
): Promise<Practitioner[]> {
|
|
466
|
+
const q = query(
|
|
467
|
+
collection(this.db, PRACTITIONERS_COLLECTION),
|
|
468
|
+
where("clinics", "array-contains", clinicId),
|
|
469
|
+
where("status", "==", PractitionerStatus.DRAFT)
|
|
171
470
|
);
|
|
172
471
|
|
|
173
472
|
const querySnapshot = await getDocs(q);
|
|
@@ -303,4 +602,49 @@ export class PractitionerService extends BaseService {
|
|
|
303
602
|
|
|
304
603
|
await deleteDoc(doc(this.db, PRACTITIONERS_COLLECTION, practitionerId));
|
|
305
604
|
}
|
|
605
|
+
|
|
606
|
+
/**
|
|
607
|
+
* Validates a registration token and claims the associated draft practitioner profile
|
|
608
|
+
* @param tokenString The token provided by the practitioner
|
|
609
|
+
* @param userId The ID of the user claiming the profile
|
|
610
|
+
* @returns The claimed practitioner profile or null if token is invalid
|
|
611
|
+
*/
|
|
612
|
+
async validateTokenAndClaimProfile(
|
|
613
|
+
tokenString: string,
|
|
614
|
+
userId: string
|
|
615
|
+
): Promise<Practitioner | null> {
|
|
616
|
+
// Find the token
|
|
617
|
+
const token = await this.validateToken(tokenString);
|
|
618
|
+
if (!token) {
|
|
619
|
+
return null; // Token not found or not valid
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
// Get the practitioner profile
|
|
623
|
+
const practitioner = await this.getPractitioner(token.practitionerId);
|
|
624
|
+
if (!practitioner) {
|
|
625
|
+
return null; // Practitioner not found
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
// Ensure practitioner is in DRAFT status
|
|
629
|
+
if (practitioner.status !== PractitionerStatus.DRAFT) {
|
|
630
|
+
throw new Error("This practitioner profile has already been claimed");
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
// Check if user already has a practitioner profile
|
|
634
|
+
const existingPractitioner = await this.getPractitionerByUserRef(userId);
|
|
635
|
+
if (existingPractitioner) {
|
|
636
|
+
throw new Error("User already has a practitioner profile");
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
// Claim the profile by linking it to the user
|
|
640
|
+
const updatedPractitioner = await this.updatePractitioner(practitioner.id, {
|
|
641
|
+
userRef: userId,
|
|
642
|
+
status: PractitionerStatus.ACTIVE,
|
|
643
|
+
});
|
|
644
|
+
|
|
645
|
+
// Mark the token as used
|
|
646
|
+
await this.markTokenAsUsed(token.id, token.practitionerId, userId);
|
|
647
|
+
|
|
648
|
+
return updatedPractitioner;
|
|
649
|
+
}
|
|
306
650
|
}
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
} from "../../backoffice/types/static/certification.types";
|
|
6
6
|
|
|
7
7
|
export const PRACTITIONERS_COLLECTION = "practitioners";
|
|
8
|
+
export const REGISTER_TOKENS_COLLECTION = "register_tokens";
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* Osnovne informacije o zdravstvenom radniku
|
|
@@ -54,6 +55,24 @@ export interface PractitionerClinicWorkingHours {
|
|
|
54
55
|
updatedAt: Timestamp;
|
|
55
56
|
}
|
|
56
57
|
|
|
58
|
+
/**
|
|
59
|
+
* Status of practitioner profile
|
|
60
|
+
*/
|
|
61
|
+
export enum PractitionerStatus {
|
|
62
|
+
DRAFT = "draft",
|
|
63
|
+
ACTIVE = "active",
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Token status for practitioner invitations
|
|
68
|
+
*/
|
|
69
|
+
export enum PractitionerTokenStatus {
|
|
70
|
+
ACTIVE = "active",
|
|
71
|
+
USED = "used",
|
|
72
|
+
EXPIRED = "expired",
|
|
73
|
+
REVOKED = "revoked",
|
|
74
|
+
}
|
|
75
|
+
|
|
57
76
|
/**
|
|
58
77
|
* Interfejs za zdravstvenog radnika
|
|
59
78
|
*/
|
|
@@ -66,6 +85,7 @@ export interface Practitioner {
|
|
|
66
85
|
clinicWorkingHours: PractitionerClinicWorkingHours[]; // Radno vreme za svaku kliniku
|
|
67
86
|
isActive: boolean;
|
|
68
87
|
isVerified: boolean;
|
|
88
|
+
status: PractitionerStatus;
|
|
69
89
|
createdAt: Timestamp;
|
|
70
90
|
updatedAt: Timestamp;
|
|
71
91
|
}
|
|
@@ -81,6 +101,19 @@ export interface CreatePractitionerData {
|
|
|
81
101
|
clinicWorkingHours?: PractitionerClinicWorkingHours[];
|
|
82
102
|
isActive: boolean;
|
|
83
103
|
isVerified: boolean;
|
|
104
|
+
status?: PractitionerStatus;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Tip za kreiranje draft profila zdravstvenog radnika
|
|
109
|
+
*/
|
|
110
|
+
export interface CreateDraftPractitionerData {
|
|
111
|
+
basicInfo: PractitionerBasicInfo;
|
|
112
|
+
certification: PractitionerCertification;
|
|
113
|
+
clinics?: string[];
|
|
114
|
+
clinicWorkingHours?: PractitionerClinicWorkingHours[];
|
|
115
|
+
isActive?: boolean;
|
|
116
|
+
isVerified?: boolean;
|
|
84
117
|
}
|
|
85
118
|
|
|
86
119
|
/**
|
|
@@ -134,3 +167,30 @@ export interface PractitionerWorkingHours {
|
|
|
134
167
|
createdAt: Timestamp;
|
|
135
168
|
updatedAt: Timestamp;
|
|
136
169
|
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Token za pozivanje zdravstvenog radnika
|
|
173
|
+
*/
|
|
174
|
+
export interface PractitionerToken {
|
|
175
|
+
id: string;
|
|
176
|
+
token: string;
|
|
177
|
+
practitionerId: string;
|
|
178
|
+
email: string;
|
|
179
|
+
clinicId: string;
|
|
180
|
+
status: PractitionerTokenStatus;
|
|
181
|
+
createdBy: string;
|
|
182
|
+
createdAt: Timestamp;
|
|
183
|
+
expiresAt: Timestamp;
|
|
184
|
+
usedBy?: string;
|
|
185
|
+
usedAt?: Timestamp;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Tip za kreiranje tokena za zdravstvenog radnika
|
|
190
|
+
*/
|
|
191
|
+
export interface CreatePractitionerTokenData {
|
|
192
|
+
practitionerId: string;
|
|
193
|
+
email: string;
|
|
194
|
+
clinicId: string;
|
|
195
|
+
expiresAt?: Date;
|
|
196
|
+
}
|
|
@@ -4,6 +4,10 @@ import {
|
|
|
4
4
|
CertificationLevel,
|
|
5
5
|
CertificationSpecialty,
|
|
6
6
|
} from "../backoffice/types/static/certification.types";
|
|
7
|
+
import {
|
|
8
|
+
PractitionerStatus,
|
|
9
|
+
PractitionerTokenStatus,
|
|
10
|
+
} from "../types/practitioner";
|
|
7
11
|
|
|
8
12
|
/**
|
|
9
13
|
* Šema za validaciju osnovnih informacija o zdravstvenom radniku
|
|
@@ -118,6 +122,7 @@ export const practitionerSchema = z.object({
|
|
|
118
122
|
clinicWorkingHours: z.array(practitionerClinicWorkingHoursSchema),
|
|
119
123
|
isActive: z.boolean(),
|
|
120
124
|
isVerified: z.boolean(),
|
|
125
|
+
status: z.nativeEnum(PractitionerStatus),
|
|
121
126
|
createdAt: z.instanceof(Timestamp),
|
|
122
127
|
updatedAt: z.instanceof(Timestamp),
|
|
123
128
|
});
|
|
@@ -133,4 +138,44 @@ export const createPractitionerSchema = z.object({
|
|
|
133
138
|
clinicWorkingHours: z.array(practitionerClinicWorkingHoursSchema).optional(),
|
|
134
139
|
isActive: z.boolean(),
|
|
135
140
|
isVerified: z.boolean(),
|
|
141
|
+
status: z.nativeEnum(PractitionerStatus).optional(),
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Šema za validaciju podataka pri kreiranju draft profila zdravstvenog radnika
|
|
146
|
+
*/
|
|
147
|
+
export const createDraftPractitionerSchema = z.object({
|
|
148
|
+
basicInfo: practitionerBasicInfoSchema,
|
|
149
|
+
certification: practitionerCertificationSchema,
|
|
150
|
+
clinics: z.array(z.string()).optional(),
|
|
151
|
+
clinicWorkingHours: z.array(practitionerClinicWorkingHoursSchema).optional(),
|
|
152
|
+
isActive: z.boolean().optional().default(false),
|
|
153
|
+
isVerified: z.boolean().optional().default(false),
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Šema za validaciju tokena za zdravstvenog radnika
|
|
158
|
+
*/
|
|
159
|
+
export const practitionerTokenSchema = z.object({
|
|
160
|
+
id: z.string().min(1),
|
|
161
|
+
token: z.string().min(6),
|
|
162
|
+
practitionerId: z.string().min(1),
|
|
163
|
+
email: z.string().email(),
|
|
164
|
+
clinicId: z.string().min(1),
|
|
165
|
+
status: z.nativeEnum(PractitionerTokenStatus),
|
|
166
|
+
createdBy: z.string().min(1),
|
|
167
|
+
createdAt: z.instanceof(Timestamp),
|
|
168
|
+
expiresAt: z.instanceof(Timestamp),
|
|
169
|
+
usedBy: z.string().optional(),
|
|
170
|
+
usedAt: z.instanceof(Timestamp).optional(),
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Šema za validaciju podataka pri kreiranju tokena za zdravstvenog radnika
|
|
175
|
+
*/
|
|
176
|
+
export const createPractitionerTokenSchema = z.object({
|
|
177
|
+
practitionerId: z.string().min(1),
|
|
178
|
+
email: z.string().email(),
|
|
179
|
+
clinicId: z.string().min(1),
|
|
180
|
+
expiresAt: z.date().optional(),
|
|
136
181
|
});
|