@blackcode_sa/metaestetics-api 1.7.16 → 1.7.17
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 +12 -1
- package/dist/admin/index.d.ts +12 -1
- package/dist/backoffice/index.js +54 -49
- package/dist/backoffice/index.mjs +76 -53
- package/dist/index.d.mts +55 -2
- package/dist/index.d.ts +55 -2
- package/dist/index.js +155 -0
- package/dist/index.mjs +155 -0
- package/package.json +1 -1
- package/src/index.ts +1 -0
- package/src/services/documentation-templates/filled-document.service.ts +220 -0
- package/src/types/documentation-templates/index.ts +13 -1
|
@@ -19,11 +19,17 @@ import {
|
|
|
19
19
|
// FILLED_DOCUMENTS_COLLECTION, // This will be replaced by subcollection paths
|
|
20
20
|
FilledDocument,
|
|
21
21
|
FilledDocumentStatus,
|
|
22
|
+
FilledDocumentFileValue,
|
|
22
23
|
USER_FORMS_SUBCOLLECTION,
|
|
23
24
|
DOCTOR_FORMS_SUBCOLLECTION,
|
|
24
25
|
} from "../../types"; // General types
|
|
25
26
|
import { APPOINTMENTS_COLLECTION } from "../../types/appointment"; // Specific import for the constant
|
|
26
27
|
import { DocumentationTemplateService } from "./documentation-template.service";
|
|
28
|
+
import {
|
|
29
|
+
MediaService,
|
|
30
|
+
MediaAccessLevel,
|
|
31
|
+
MediaMetadata,
|
|
32
|
+
} from "../media/media.service";
|
|
27
33
|
// Import the new validation schemas if you plan to use them for input validation here
|
|
28
34
|
// import { createFilledDocumentDataSchema, updateFilledDocumentDataSchema } from '../../validations/documentation-templates.schema';
|
|
29
35
|
|
|
@@ -33,10 +39,12 @@ import { DocumentationTemplateService } from "./documentation-template.service";
|
|
|
33
39
|
export class FilledDocumentService extends BaseService {
|
|
34
40
|
// No single collectionRef anymore, paths will be dynamic
|
|
35
41
|
private readonly templateService: DocumentationTemplateService;
|
|
42
|
+
private readonly mediaService: MediaService;
|
|
36
43
|
|
|
37
44
|
constructor(...args: ConstructorParameters<typeof BaseService>) {
|
|
38
45
|
super(...args);
|
|
39
46
|
this.templateService = new DocumentationTemplateService(...args); // Pass db and other args
|
|
47
|
+
this.mediaService = new MediaService(...args); // Initialize media service with the same args
|
|
40
48
|
}
|
|
41
49
|
|
|
42
50
|
private getFormSubcollectionPath(
|
|
@@ -468,4 +476,216 @@ export class FilledDocumentService extends BaseService {
|
|
|
468
476
|
*/
|
|
469
477
|
return { documents: [], lastDoc: null }; // Placeholder return
|
|
470
478
|
}
|
|
479
|
+
|
|
480
|
+
/**
|
|
481
|
+
* Upload a file and associate it with a filled document field.
|
|
482
|
+
* @param appointmentId - ID of the appointment.
|
|
483
|
+
* @param formId - ID of the filled document.
|
|
484
|
+
* @param isUserForm - Boolean indicating if it's a user form or doctor form.
|
|
485
|
+
* @param file - The file to upload.
|
|
486
|
+
* @param fieldId - The ID of the field in the document to associate with this file.
|
|
487
|
+
* @param accessLevel - Access level for the file, defaults to PRIVATE.
|
|
488
|
+
* @returns The updated filled document with file information.
|
|
489
|
+
*/
|
|
490
|
+
async uploadFileForFilledDocument(
|
|
491
|
+
appointmentId: string,
|
|
492
|
+
formId: string,
|
|
493
|
+
isUserForm: boolean,
|
|
494
|
+
file: File | Blob,
|
|
495
|
+
fieldId: string,
|
|
496
|
+
accessLevel: MediaAccessLevel = MediaAccessLevel.PRIVATE
|
|
497
|
+
): Promise<FilledDocument> {
|
|
498
|
+
console.log(
|
|
499
|
+
`[FilledDocumentService] Uploading file for field ${fieldId} in form ${formId}`
|
|
500
|
+
);
|
|
501
|
+
|
|
502
|
+
// 1. Get the existing document to verify it exists and to get the patientId
|
|
503
|
+
const existingDoc = await this.getFilledDocumentFromAppointmentById(
|
|
504
|
+
appointmentId,
|
|
505
|
+
formId,
|
|
506
|
+
isUserForm
|
|
507
|
+
);
|
|
508
|
+
|
|
509
|
+
if (!existingDoc) {
|
|
510
|
+
throw new Error(
|
|
511
|
+
`Filled document with ID ${formId} not found in appointment ${appointmentId}`
|
|
512
|
+
);
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
// 2. Upload the file using MediaService
|
|
516
|
+
const ownerId = existingDoc.patientId; // Using patientId as ownerId for permissions
|
|
517
|
+
const collectionName = isUserForm
|
|
518
|
+
? "patient_forms_files"
|
|
519
|
+
: "doctor_forms_files";
|
|
520
|
+
|
|
521
|
+
const mediaMetadata = await this.mediaService.uploadMedia(
|
|
522
|
+
file,
|
|
523
|
+
ownerId,
|
|
524
|
+
accessLevel,
|
|
525
|
+
collectionName,
|
|
526
|
+
file instanceof File ? file.name : `${fieldId}_file`
|
|
527
|
+
);
|
|
528
|
+
|
|
529
|
+
// 3. Create a file value object to store in the document
|
|
530
|
+
const fileValue: FilledDocumentFileValue = {
|
|
531
|
+
mediaId: mediaMetadata.id,
|
|
532
|
+
url: mediaMetadata.url,
|
|
533
|
+
name: mediaMetadata.name,
|
|
534
|
+
contentType: mediaMetadata.contentType,
|
|
535
|
+
size: mediaMetadata.size,
|
|
536
|
+
uploadedAt: Date.now(),
|
|
537
|
+
};
|
|
538
|
+
|
|
539
|
+
// 4. Update the document with the file value
|
|
540
|
+
const values = {
|
|
541
|
+
[fieldId]: fileValue,
|
|
542
|
+
};
|
|
543
|
+
|
|
544
|
+
// 5. Use the existing update method to save the file reference
|
|
545
|
+
return this.updateFilledDocumentInAppointment(
|
|
546
|
+
appointmentId,
|
|
547
|
+
formId,
|
|
548
|
+
isUserForm,
|
|
549
|
+
values
|
|
550
|
+
);
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
/**
|
|
554
|
+
* Upload a signature image for a filled document field.
|
|
555
|
+
* This is a specialized version of uploadFileForFilledDocument specifically for signatures.
|
|
556
|
+
* @param appointmentId - ID of the appointment.
|
|
557
|
+
* @param formId - ID of the filled document.
|
|
558
|
+
* @param isUserForm - Boolean indicating if it's a user form or doctor form.
|
|
559
|
+
* @param signatureBlob - The signature image as a Blob.
|
|
560
|
+
* @param fieldId - The ID of the signature field in the document.
|
|
561
|
+
* @returns The updated filled document with signature information.
|
|
562
|
+
*/
|
|
563
|
+
async uploadSignatureForFilledDocument(
|
|
564
|
+
appointmentId: string,
|
|
565
|
+
formId: string,
|
|
566
|
+
isUserForm: boolean,
|
|
567
|
+
signatureBlob: Blob,
|
|
568
|
+
fieldId: string
|
|
569
|
+
): Promise<FilledDocument> {
|
|
570
|
+
console.log(
|
|
571
|
+
`[FilledDocumentService] Uploading signature for field ${fieldId} in form ${formId}`
|
|
572
|
+
);
|
|
573
|
+
|
|
574
|
+
// Use the general file upload method, but specify a fixed name and access level for signatures
|
|
575
|
+
const signatureFile = new File(
|
|
576
|
+
[signatureBlob],
|
|
577
|
+
`signature_${fieldId}.png`,
|
|
578
|
+
{
|
|
579
|
+
type: "image/png",
|
|
580
|
+
}
|
|
581
|
+
);
|
|
582
|
+
|
|
583
|
+
return this.uploadFileForFilledDocument(
|
|
584
|
+
appointmentId,
|
|
585
|
+
formId,
|
|
586
|
+
isUserForm,
|
|
587
|
+
signatureFile,
|
|
588
|
+
fieldId,
|
|
589
|
+
MediaAccessLevel.CONFIDENTIAL // Signatures should be confidential
|
|
590
|
+
);
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
/**
|
|
594
|
+
* Remove a file from a filled document field.
|
|
595
|
+
* This will both update the document and delete the media file.
|
|
596
|
+
* @param appointmentId - ID of the appointment.
|
|
597
|
+
* @param formId - ID of the filled document.
|
|
598
|
+
* @param isUserForm - Boolean indicating if it's a user form or doctor form.
|
|
599
|
+
* @param fieldId - The ID of the field containing the file.
|
|
600
|
+
* @returns The updated filled document with the file removed.
|
|
601
|
+
*/
|
|
602
|
+
async removeFileFromFilledDocument(
|
|
603
|
+
appointmentId: string,
|
|
604
|
+
formId: string,
|
|
605
|
+
isUserForm: boolean,
|
|
606
|
+
fieldId: string
|
|
607
|
+
): Promise<FilledDocument> {
|
|
608
|
+
console.log(
|
|
609
|
+
`[FilledDocumentService] Removing file from field ${fieldId} in form ${formId}`
|
|
610
|
+
);
|
|
611
|
+
|
|
612
|
+
// 1. Get the existing document to verify it exists and to get the file info
|
|
613
|
+
const existingDoc = await this.getFilledDocumentFromAppointmentById(
|
|
614
|
+
appointmentId,
|
|
615
|
+
formId,
|
|
616
|
+
isUserForm
|
|
617
|
+
);
|
|
618
|
+
|
|
619
|
+
if (!existingDoc) {
|
|
620
|
+
throw new Error(
|
|
621
|
+
`Filled document with ID ${formId} not found in appointment ${appointmentId}`
|
|
622
|
+
);
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
// 2. Check if the field has a file value
|
|
626
|
+
const fileValue = existingDoc.values?.[fieldId] as FilledDocumentFileValue;
|
|
627
|
+
if (fileValue && fileValue.mediaId) {
|
|
628
|
+
// 3. Delete the file using MediaService
|
|
629
|
+
try {
|
|
630
|
+
await this.mediaService.deleteMedia(fileValue.mediaId);
|
|
631
|
+
} catch (error) {
|
|
632
|
+
console.error(
|
|
633
|
+
`[FilledDocumentService] Error deleting media ${fileValue.mediaId}:`,
|
|
634
|
+
error
|
|
635
|
+
);
|
|
636
|
+
// Continue with document update even if media deletion fails
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
// 4. Update the document to remove the file reference
|
|
641
|
+
const values = {
|
|
642
|
+
[fieldId]: null,
|
|
643
|
+
};
|
|
644
|
+
|
|
645
|
+
// 5. Use the existing update method to save the changes
|
|
646
|
+
return this.updateFilledDocumentInAppointment(
|
|
647
|
+
appointmentId,
|
|
648
|
+
formId,
|
|
649
|
+
isUserForm,
|
|
650
|
+
values
|
|
651
|
+
);
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
/**
|
|
655
|
+
* Get the download URL for a file in a filled document.
|
|
656
|
+
* @param appointmentId - ID of the appointment.
|
|
657
|
+
* @param formId - ID of the filled document.
|
|
658
|
+
* @param isUserForm - Boolean indicating if it's a user form or doctor form.
|
|
659
|
+
* @param fieldId - The ID of the field containing the file.
|
|
660
|
+
* @returns The download URL for the file, or null if not found.
|
|
661
|
+
*/
|
|
662
|
+
async getFileUrlFromFilledDocument(
|
|
663
|
+
appointmentId: string,
|
|
664
|
+
formId: string,
|
|
665
|
+
isUserForm: boolean,
|
|
666
|
+
fieldId: string
|
|
667
|
+
): Promise<string | null> {
|
|
668
|
+
console.log(
|
|
669
|
+
`[FilledDocumentService] Getting file URL for field ${fieldId} in form ${formId}`
|
|
670
|
+
);
|
|
671
|
+
|
|
672
|
+
// 1. Get the document
|
|
673
|
+
const doc = await this.getFilledDocumentFromAppointmentById(
|
|
674
|
+
appointmentId,
|
|
675
|
+
formId,
|
|
676
|
+
isUserForm
|
|
677
|
+
);
|
|
678
|
+
|
|
679
|
+
if (!doc) {
|
|
680
|
+
return null;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
// 2. Check if the field has a file value
|
|
684
|
+
const fileValue = doc.values?.[fieldId] as FilledDocumentFileValue;
|
|
685
|
+
if (fileValue && fileValue.url) {
|
|
686
|
+
return fileValue.url;
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
return null;
|
|
690
|
+
}
|
|
471
691
|
}
|
|
@@ -279,7 +279,7 @@ export interface FilledDocument {
|
|
|
279
279
|
clinicId: string;
|
|
280
280
|
createdAt: number;
|
|
281
281
|
updatedAt: number;
|
|
282
|
-
values: { [elementId: string]: any }; // Values for each element
|
|
282
|
+
values: { [elementId: string]: any | FilledDocumentFileValue }; // Values for each element
|
|
283
283
|
status: FilledDocumentStatus;
|
|
284
284
|
}
|
|
285
285
|
|
|
@@ -294,3 +294,15 @@ export enum FilledDocumentStatus {
|
|
|
294
294
|
SIGNED = "signed", // Only used for user forms
|
|
295
295
|
REJECTED = "rejected", // Only used for user forms
|
|
296
296
|
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Interface for file upload results to be stored in filled document values
|
|
300
|
+
*/
|
|
301
|
+
export interface FilledDocumentFileValue {
|
|
302
|
+
mediaId: string;
|
|
303
|
+
url: string;
|
|
304
|
+
name: string;
|
|
305
|
+
contentType: string;
|
|
306
|
+
size: number;
|
|
307
|
+
uploadedAt: number;
|
|
308
|
+
}
|