@blackcode_sa/metaestetics-api 1.7.13 → 1.7.14

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.
@@ -661,6 +661,11 @@ declare enum Currency {
661
661
  AUD = "AUD"
662
662
  }
663
663
 
664
+ /**
665
+ * Type that allows a field to be either a URL string or a File object
666
+ */
667
+ type MediaResource = string | File | Blob;
668
+
664
669
  /**
665
670
  * Procedure represents a specific medical procedure that can be performed by a practitioner in a clinic
666
671
  * It inherits properties from technology and adds clinic/practitioner specific details
@@ -671,7 +676,7 @@ interface Procedure {
671
676
  /** Name of the procedure */
672
677
  name: string;
673
678
  /** Photos of the procedure */
674
- photos?: string[];
679
+ photos?: MediaResource[];
675
680
  /** Detailed description of the procedure */
676
681
  description: string;
677
682
  /** Family of procedures this belongs to (aesthetics/surgery) */
@@ -1019,11 +1024,6 @@ declare enum ClinicTag {
1019
1024
  HOLIDAY_HOURS = "holiday_hours"
1020
1025
  }
1021
1026
 
1022
- /**
1023
- * Type that allows a field to be either a URL string or a File object
1024
- */
1025
- type MediaResource = string | File | Blob;
1026
-
1027
1027
  /**
1028
1028
  * Interface for clinic contact information
1029
1029
  */
@@ -661,6 +661,11 @@ declare enum Currency {
661
661
  AUD = "AUD"
662
662
  }
663
663
 
664
+ /**
665
+ * Type that allows a field to be either a URL string or a File object
666
+ */
667
+ type MediaResource = string | File | Blob;
668
+
664
669
  /**
665
670
  * Procedure represents a specific medical procedure that can be performed by a practitioner in a clinic
666
671
  * It inherits properties from technology and adds clinic/practitioner specific details
@@ -671,7 +676,7 @@ interface Procedure {
671
676
  /** Name of the procedure */
672
677
  name: string;
673
678
  /** Photos of the procedure */
674
- photos?: string[];
679
+ photos?: MediaResource[];
675
680
  /** Detailed description of the procedure */
676
681
  description: string;
677
682
  /** Family of procedures this belongs to (aesthetics/surgery) */
@@ -1019,11 +1024,6 @@ declare enum ClinicTag {
1019
1024
  HOLIDAY_HOURS = "holiday_hours"
1020
1025
  }
1021
1026
 
1022
- /**
1023
- * Type that allows a field to be either a URL string or a File object
1024
- */
1025
- type MediaResource = string | File | Blob;
1026
-
1027
1027
  /**
1028
1028
  * Interface for clinic contact information
1029
1029
  */
package/dist/index.d.mts CHANGED
@@ -507,6 +507,102 @@ declare enum Currency {
507
507
  AUD = "AUD"
508
508
  }
509
509
 
510
+ declare class BaseService {
511
+ protected db: Firestore;
512
+ protected auth: Auth;
513
+ protected app: FirebaseApp;
514
+ protected storage: FirebaseStorage;
515
+ constructor(db: Firestore, auth: Auth, app: FirebaseApp);
516
+ /**
517
+ * Generiše jedinstveni ID za dokumente
518
+ * Format: xxxxxxxxxxxx-timestamp
519
+ * Gde je x random karakter (broj ili slovo)
520
+ */
521
+ protected generateId(): string;
522
+ }
523
+
524
+ /**
525
+ * Enum for media access levels
526
+ */
527
+ declare enum MediaAccessLevel {
528
+ PUBLIC = "public",
529
+ PRIVATE = "private",
530
+ CONFIDENTIAL = "confidential"
531
+ }
532
+ /**
533
+ * Type that allows a field to be either a URL string or a File object
534
+ */
535
+ type MediaResource = string | File | Blob;
536
+ /**
537
+ * Media file metadata interface
538
+ */
539
+ interface MediaMetadata {
540
+ id: string;
541
+ name: string;
542
+ url: string;
543
+ contentType: string;
544
+ size: number;
545
+ createdAt: Timestamp;
546
+ accessLevel: MediaAccessLevel;
547
+ ownerId: string;
548
+ collectionName: string;
549
+ path: string;
550
+ updatedAt?: Timestamp;
551
+ }
552
+ declare const MEDIA_METADATA_COLLECTION = "media_metadata";
553
+ declare class MediaService extends BaseService {
554
+ constructor(db: Firestore, auth: Auth, app: FirebaseApp);
555
+ /**
556
+ * Upload a media file, store its metadata, and return the metadata including the URL.
557
+ * @param file - The file to upload.
558
+ * @param ownerId - ID of the owner (user, patient, clinic, etc.).
559
+ * @param accessLevel - Access level (public, private, confidential).
560
+ * @param collectionName - The logical collection name this media belongs to (e.g., 'patient_profile_pictures', 'clinic_logos').
561
+ * @param originalFileName - Optional: the original name of the file, if not using file.name.
562
+ * @returns Promise with the media metadata.
563
+ */
564
+ uploadMedia(file: File | Blob, ownerId: string, accessLevel: MediaAccessLevel, collectionName: string, originalFileName?: string): Promise<MediaMetadata>;
565
+ /**
566
+ * Get media metadata from Firestore by its ID.
567
+ * @param mediaId - ID of the media.
568
+ * @returns Promise with the media metadata or null if not found.
569
+ */
570
+ getMediaMetadata(mediaId: string): Promise<MediaMetadata | null>;
571
+ /**
572
+ * Get media metadata from Firestore by its public URL.
573
+ * @param url - The public URL of the media file.
574
+ * @returns Promise with the media metadata or null if not found.
575
+ */
576
+ getMediaMetadataByUrl(url: string): Promise<MediaMetadata | null>;
577
+ /**
578
+ * Delete media from storage and remove metadata from Firestore.
579
+ * @param mediaId - ID of the media to delete.
580
+ */
581
+ deleteMedia(mediaId: string): Promise<void>;
582
+ /**
583
+ * Update media access level. This involves moving the file in Firebase Storage
584
+ * to a new path reflecting the new access level, and updating its metadata.
585
+ * @param mediaId - ID of the media to update.
586
+ * @param newAccessLevel - New access level.
587
+ * @returns Promise with the updated media metadata, or null if metadata not found.
588
+ */
589
+ updateMediaAccessLevel(mediaId: string, newAccessLevel: MediaAccessLevel): Promise<MediaMetadata | null>;
590
+ /**
591
+ * List all media for an owner, optionally filtered by collection and access level.
592
+ * @param ownerId - ID of the owner.
593
+ * @param collectionName - Optional: Filter by collection name.
594
+ * @param accessLevel - Optional: Filter by access level.
595
+ * @param count - Optional: Number of items to fetch.
596
+ * @param startAfterId - Optional: ID of the document to start after (for pagination).
597
+ */
598
+ listMedia(ownerId: string, collectionName?: string, accessLevel?: MediaAccessLevel, count?: number, startAfterId?: string): Promise<MediaMetadata[]>;
599
+ /**
600
+ * Get download URL for media. (Convenience, as URL is in metadata)
601
+ * @param mediaId - ID of the media.
602
+ */
603
+ getMediaDownloadUrl(mediaId: string): Promise<string | null>;
604
+ }
605
+
510
606
  /**
511
607
  * Procedure represents a specific medical procedure that can be performed by a practitioner in a clinic
512
608
  * It inherits properties from technology and adds clinic/practitioner specific details
@@ -517,7 +613,7 @@ interface Procedure {
517
613
  /** Name of the procedure */
518
614
  name: string;
519
615
  /** Photos of the procedure */
520
- photos?: string[];
616
+ photos?: MediaResource[];
521
617
  /** Detailed description of the procedure */
522
618
  description: string;
523
619
  /** Family of procedures this belongs to (aesthetics/surgery) */
@@ -586,7 +682,7 @@ interface CreateProcedureData {
586
682
  duration: number;
587
683
  practitionerId: string;
588
684
  clinicBranchId: string;
589
- photos?: string[];
685
+ photos?: MediaResource[];
590
686
  }
591
687
  /**
592
688
  * Data that can be updated for an existing procedure
@@ -605,7 +701,7 @@ interface UpdateProcedureData {
605
701
  technologyId?: string;
606
702
  productId?: string;
607
703
  clinicBranchId?: string;
608
- photos?: string[];
704
+ photos?: MediaResource[];
609
705
  }
610
706
  /**
611
707
  * Collection name for procedures in Firestore
@@ -749,102 +845,6 @@ declare enum ClinicPhotoTag {
749
845
  OTHER = "other"
750
846
  }
751
847
 
752
- declare class BaseService {
753
- protected db: Firestore;
754
- protected auth: Auth;
755
- protected app: FirebaseApp;
756
- protected storage: FirebaseStorage;
757
- constructor(db: Firestore, auth: Auth, app: FirebaseApp);
758
- /**
759
- * Generiše jedinstveni ID za dokumente
760
- * Format: xxxxxxxxxxxx-timestamp
761
- * Gde je x random karakter (broj ili slovo)
762
- */
763
- protected generateId(): string;
764
- }
765
-
766
- /**
767
- * Enum for media access levels
768
- */
769
- declare enum MediaAccessLevel {
770
- PUBLIC = "public",
771
- PRIVATE = "private",
772
- CONFIDENTIAL = "confidential"
773
- }
774
- /**
775
- * Type that allows a field to be either a URL string or a File object
776
- */
777
- type MediaResource = string | File | Blob;
778
- /**
779
- * Media file metadata interface
780
- */
781
- interface MediaMetadata {
782
- id: string;
783
- name: string;
784
- url: string;
785
- contentType: string;
786
- size: number;
787
- createdAt: Timestamp;
788
- accessLevel: MediaAccessLevel;
789
- ownerId: string;
790
- collectionName: string;
791
- path: string;
792
- updatedAt?: Timestamp;
793
- }
794
- declare const MEDIA_METADATA_COLLECTION = "media_metadata";
795
- declare class MediaService extends BaseService {
796
- constructor(db: Firestore, auth: Auth, app: FirebaseApp);
797
- /**
798
- * Upload a media file, store its metadata, and return the metadata including the URL.
799
- * @param file - The file to upload.
800
- * @param ownerId - ID of the owner (user, patient, clinic, etc.).
801
- * @param accessLevel - Access level (public, private, confidential).
802
- * @param collectionName - The logical collection name this media belongs to (e.g., 'patient_profile_pictures', 'clinic_logos').
803
- * @param originalFileName - Optional: the original name of the file, if not using file.name.
804
- * @returns Promise with the media metadata.
805
- */
806
- uploadMedia(file: File | Blob, ownerId: string, accessLevel: MediaAccessLevel, collectionName: string, originalFileName?: string): Promise<MediaMetadata>;
807
- /**
808
- * Get media metadata from Firestore by its ID.
809
- * @param mediaId - ID of the media.
810
- * @returns Promise with the media metadata or null if not found.
811
- */
812
- getMediaMetadata(mediaId: string): Promise<MediaMetadata | null>;
813
- /**
814
- * Get media metadata from Firestore by its public URL.
815
- * @param url - The public URL of the media file.
816
- * @returns Promise with the media metadata or null if not found.
817
- */
818
- getMediaMetadataByUrl(url: string): Promise<MediaMetadata | null>;
819
- /**
820
- * Delete media from storage and remove metadata from Firestore.
821
- * @param mediaId - ID of the media to delete.
822
- */
823
- deleteMedia(mediaId: string): Promise<void>;
824
- /**
825
- * Update media access level. This involves moving the file in Firebase Storage
826
- * to a new path reflecting the new access level, and updating its metadata.
827
- * @param mediaId - ID of the media to update.
828
- * @param newAccessLevel - New access level.
829
- * @returns Promise with the updated media metadata, or null if metadata not found.
830
- */
831
- updateMediaAccessLevel(mediaId: string, newAccessLevel: MediaAccessLevel): Promise<MediaMetadata | null>;
832
- /**
833
- * List all media for an owner, optionally filtered by collection and access level.
834
- * @param ownerId - ID of the owner.
835
- * @param collectionName - Optional: Filter by collection name.
836
- * @param accessLevel - Optional: Filter by access level.
837
- * @param count - Optional: Number of items to fetch.
838
- * @param startAfterId - Optional: ID of the document to start after (for pagination).
839
- */
840
- listMedia(ownerId: string, collectionName?: string, accessLevel?: MediaAccessLevel, count?: number, startAfterId?: string): Promise<MediaMetadata[]>;
841
- /**
842
- * Get download URL for media. (Convenience, as URL is in metadata)
843
- * @param mediaId - ID of the media.
844
- */
845
- getMediaDownloadUrl(mediaId: string): Promise<string | null>;
846
- }
847
-
848
848
  declare const CLINIC_GROUPS_COLLECTION = "clinic_groups";
849
849
  declare const CLINIC_ADMINS_COLLECTION = "clinic_admins";
850
850
  declare const CLINICS_COLLECTION = "clinics";
@@ -4836,8 +4836,8 @@ declare const updateAppointmentSchema: z.ZodEffects<z.ZodEffects<z.ZodObject<{
4836
4836
  submittedAt: z.ZodOptional<z.ZodEffects<z.ZodAny, any, any>>;
4837
4837
  completedAt: z.ZodOptional<z.ZodEffects<z.ZodAny, any, any>>;
4838
4838
  }, "strip", z.ZodTypeAny, {
4839
- status: FilledDocumentStatus;
4840
4839
  path: string;
4840
+ status: FilledDocumentStatus;
4841
4841
  title: string;
4842
4842
  isUserForm: boolean;
4843
4843
  templateId: string;
@@ -4847,8 +4847,8 @@ declare const updateAppointmentSchema: z.ZodEffects<z.ZodEffects<z.ZodObject<{
4847
4847
  submittedAt?: any;
4848
4848
  completedAt?: any;
4849
4849
  }, {
4850
- status: FilledDocumentStatus;
4851
4850
  path: string;
4851
+ status: FilledDocumentStatus;
4852
4852
  title: string;
4853
4853
  isUserForm: boolean;
4854
4854
  templateId: string;
@@ -5103,56 +5103,56 @@ declare const searchAppointmentsSchema: z.ZodEffects<z.ZodEffects<z.ZodObject<{
5103
5103
  startAfter: z.ZodOptional<z.ZodAny>;
5104
5104
  }, "strip", z.ZodTypeAny, {
5105
5105
  limit: number;
5106
+ startAfter?: any;
5106
5107
  status?: AppointmentStatus | [AppointmentStatus, ...AppointmentStatus[]] | undefined;
5107
5108
  patientId?: string | undefined;
5108
5109
  startDate?: any;
5109
5110
  endDate?: any;
5110
- startAfter?: any;
5111
5111
  practitionerId?: string | undefined;
5112
5112
  clinicBranchId?: string | undefined;
5113
5113
  }, {
5114
+ limit?: number | undefined;
5115
+ startAfter?: any;
5114
5116
  status?: AppointmentStatus | [AppointmentStatus, ...AppointmentStatus[]] | undefined;
5115
5117
  patientId?: string | undefined;
5116
5118
  startDate?: any;
5117
5119
  endDate?: any;
5118
- limit?: number | undefined;
5119
- startAfter?: any;
5120
5120
  practitionerId?: string | undefined;
5121
5121
  clinicBranchId?: string | undefined;
5122
5122
  }>, {
5123
5123
  limit: number;
5124
+ startAfter?: any;
5124
5125
  status?: AppointmentStatus | [AppointmentStatus, ...AppointmentStatus[]] | undefined;
5125
5126
  patientId?: string | undefined;
5126
5127
  startDate?: any;
5127
5128
  endDate?: any;
5128
- startAfter?: any;
5129
5129
  practitionerId?: string | undefined;
5130
5130
  clinicBranchId?: string | undefined;
5131
5131
  }, {
5132
+ limit?: number | undefined;
5133
+ startAfter?: any;
5132
5134
  status?: AppointmentStatus | [AppointmentStatus, ...AppointmentStatus[]] | undefined;
5133
5135
  patientId?: string | undefined;
5134
5136
  startDate?: any;
5135
5137
  endDate?: any;
5136
- limit?: number | undefined;
5137
- startAfter?: any;
5138
5138
  practitionerId?: string | undefined;
5139
5139
  clinicBranchId?: string | undefined;
5140
5140
  }>, {
5141
5141
  limit: number;
5142
+ startAfter?: any;
5142
5143
  status?: AppointmentStatus | [AppointmentStatus, ...AppointmentStatus[]] | undefined;
5143
5144
  patientId?: string | undefined;
5144
5145
  startDate?: any;
5145
5146
  endDate?: any;
5146
- startAfter?: any;
5147
5147
  practitionerId?: string | undefined;
5148
5148
  clinicBranchId?: string | undefined;
5149
5149
  }, {
5150
+ limit?: number | undefined;
5151
+ startAfter?: any;
5150
5152
  status?: AppointmentStatus | [AppointmentStatus, ...AppointmentStatus[]] | undefined;
5151
5153
  patientId?: string | undefined;
5152
5154
  startDate?: any;
5153
5155
  endDate?: any;
5154
- limit?: number | undefined;
5155
- startAfter?: any;
5156
5156
  practitionerId?: string | undefined;
5157
5157
  clinicBranchId?: string | undefined;
5158
5158
  }>;
@@ -6444,7 +6444,24 @@ declare class ProcedureService extends BaseService {
6444
6444
  private subcategoryService;
6445
6445
  private technologyService;
6446
6446
  private productService;
6447
- constructor(db: Firestore, auth: Auth, app: FirebaseApp, categoryService: CategoryService, subcategoryService: SubcategoryService, technologyService: TechnologyService, productService: ProductService);
6447
+ private mediaService;
6448
+ constructor(db: Firestore, auth: Auth, app: FirebaseApp, categoryService: CategoryService, subcategoryService: SubcategoryService, technologyService: TechnologyService, productService: ProductService, mediaService: MediaService);
6449
+ /**
6450
+ * Process media resource (string URL or File object)
6451
+ * @param media String URL or File object
6452
+ * @param ownerId Owner ID for the media (usually procedureId)
6453
+ * @param collectionName Collection name for organizing files
6454
+ * @returns URL string after processing
6455
+ */
6456
+ private processMedia;
6457
+ /**
6458
+ * Process array of media resources (strings or Files)
6459
+ * @param mediaArray Array of string URLs or File objects
6460
+ * @param ownerId Owner ID for the media
6461
+ * @param collectionName Collection name for organizing files
6462
+ * @returns Array of URL strings after processing
6463
+ */
6464
+ private processMediaArray;
6448
6465
  /**
6449
6466
  * Creates a new procedure
6450
6467
  * @param data - The data for creating a new procedure
@@ -14079,6 +14096,7 @@ declare const clinicGroupSchema: z.ZodObject<{
14079
14096
  updatedAt: Date | Timestamp;
14080
14097
  name: string;
14081
14098
  isActive: boolean;
14099
+ ownerId: string | null;
14082
14100
  clinics: string[];
14083
14101
  clinicsInfo: {
14084
14102
  id: string;
@@ -14101,7 +14119,6 @@ declare const clinicGroupSchema: z.ZodObject<{
14101
14119
  featuredPhoto: string;
14102
14120
  description?: string | null | undefined;
14103
14121
  }[];
14104
- ownerId: string | null;
14105
14122
  contactInfo: {
14106
14123
  email: string;
14107
14124
  phoneNumber: string;
@@ -14157,6 +14174,7 @@ declare const clinicGroupSchema: z.ZodObject<{
14157
14174
  updatedAt: Date | Timestamp;
14158
14175
  name: string;
14159
14176
  isActive: boolean;
14177
+ ownerId: string | null;
14160
14178
  clinics: string[];
14161
14179
  clinicsInfo: {
14162
14180
  id: string;
@@ -14179,7 +14197,6 @@ declare const clinicGroupSchema: z.ZodObject<{
14179
14197
  featuredPhoto: string;
14180
14198
  description?: string | null | undefined;
14181
14199
  }[];
14182
- ownerId: string | null;
14183
14200
  contactInfo: {
14184
14201
  email: string;
14185
14202
  phoneNumber: string;
package/dist/index.d.ts CHANGED
@@ -507,6 +507,102 @@ declare enum Currency {
507
507
  AUD = "AUD"
508
508
  }
509
509
 
510
+ declare class BaseService {
511
+ protected db: Firestore;
512
+ protected auth: Auth;
513
+ protected app: FirebaseApp;
514
+ protected storage: FirebaseStorage;
515
+ constructor(db: Firestore, auth: Auth, app: FirebaseApp);
516
+ /**
517
+ * Generiše jedinstveni ID za dokumente
518
+ * Format: xxxxxxxxxxxx-timestamp
519
+ * Gde je x random karakter (broj ili slovo)
520
+ */
521
+ protected generateId(): string;
522
+ }
523
+
524
+ /**
525
+ * Enum for media access levels
526
+ */
527
+ declare enum MediaAccessLevel {
528
+ PUBLIC = "public",
529
+ PRIVATE = "private",
530
+ CONFIDENTIAL = "confidential"
531
+ }
532
+ /**
533
+ * Type that allows a field to be either a URL string or a File object
534
+ */
535
+ type MediaResource = string | File | Blob;
536
+ /**
537
+ * Media file metadata interface
538
+ */
539
+ interface MediaMetadata {
540
+ id: string;
541
+ name: string;
542
+ url: string;
543
+ contentType: string;
544
+ size: number;
545
+ createdAt: Timestamp;
546
+ accessLevel: MediaAccessLevel;
547
+ ownerId: string;
548
+ collectionName: string;
549
+ path: string;
550
+ updatedAt?: Timestamp;
551
+ }
552
+ declare const MEDIA_METADATA_COLLECTION = "media_metadata";
553
+ declare class MediaService extends BaseService {
554
+ constructor(db: Firestore, auth: Auth, app: FirebaseApp);
555
+ /**
556
+ * Upload a media file, store its metadata, and return the metadata including the URL.
557
+ * @param file - The file to upload.
558
+ * @param ownerId - ID of the owner (user, patient, clinic, etc.).
559
+ * @param accessLevel - Access level (public, private, confidential).
560
+ * @param collectionName - The logical collection name this media belongs to (e.g., 'patient_profile_pictures', 'clinic_logos').
561
+ * @param originalFileName - Optional: the original name of the file, if not using file.name.
562
+ * @returns Promise with the media metadata.
563
+ */
564
+ uploadMedia(file: File | Blob, ownerId: string, accessLevel: MediaAccessLevel, collectionName: string, originalFileName?: string): Promise<MediaMetadata>;
565
+ /**
566
+ * Get media metadata from Firestore by its ID.
567
+ * @param mediaId - ID of the media.
568
+ * @returns Promise with the media metadata or null if not found.
569
+ */
570
+ getMediaMetadata(mediaId: string): Promise<MediaMetadata | null>;
571
+ /**
572
+ * Get media metadata from Firestore by its public URL.
573
+ * @param url - The public URL of the media file.
574
+ * @returns Promise with the media metadata or null if not found.
575
+ */
576
+ getMediaMetadataByUrl(url: string): Promise<MediaMetadata | null>;
577
+ /**
578
+ * Delete media from storage and remove metadata from Firestore.
579
+ * @param mediaId - ID of the media to delete.
580
+ */
581
+ deleteMedia(mediaId: string): Promise<void>;
582
+ /**
583
+ * Update media access level. This involves moving the file in Firebase Storage
584
+ * to a new path reflecting the new access level, and updating its metadata.
585
+ * @param mediaId - ID of the media to update.
586
+ * @param newAccessLevel - New access level.
587
+ * @returns Promise with the updated media metadata, or null if metadata not found.
588
+ */
589
+ updateMediaAccessLevel(mediaId: string, newAccessLevel: MediaAccessLevel): Promise<MediaMetadata | null>;
590
+ /**
591
+ * List all media for an owner, optionally filtered by collection and access level.
592
+ * @param ownerId - ID of the owner.
593
+ * @param collectionName - Optional: Filter by collection name.
594
+ * @param accessLevel - Optional: Filter by access level.
595
+ * @param count - Optional: Number of items to fetch.
596
+ * @param startAfterId - Optional: ID of the document to start after (for pagination).
597
+ */
598
+ listMedia(ownerId: string, collectionName?: string, accessLevel?: MediaAccessLevel, count?: number, startAfterId?: string): Promise<MediaMetadata[]>;
599
+ /**
600
+ * Get download URL for media. (Convenience, as URL is in metadata)
601
+ * @param mediaId - ID of the media.
602
+ */
603
+ getMediaDownloadUrl(mediaId: string): Promise<string | null>;
604
+ }
605
+
510
606
  /**
511
607
  * Procedure represents a specific medical procedure that can be performed by a practitioner in a clinic
512
608
  * It inherits properties from technology and adds clinic/practitioner specific details
@@ -517,7 +613,7 @@ interface Procedure {
517
613
  /** Name of the procedure */
518
614
  name: string;
519
615
  /** Photos of the procedure */
520
- photos?: string[];
616
+ photos?: MediaResource[];
521
617
  /** Detailed description of the procedure */
522
618
  description: string;
523
619
  /** Family of procedures this belongs to (aesthetics/surgery) */
@@ -586,7 +682,7 @@ interface CreateProcedureData {
586
682
  duration: number;
587
683
  practitionerId: string;
588
684
  clinicBranchId: string;
589
- photos?: string[];
685
+ photos?: MediaResource[];
590
686
  }
591
687
  /**
592
688
  * Data that can be updated for an existing procedure
@@ -605,7 +701,7 @@ interface UpdateProcedureData {
605
701
  technologyId?: string;
606
702
  productId?: string;
607
703
  clinicBranchId?: string;
608
- photos?: string[];
704
+ photos?: MediaResource[];
609
705
  }
610
706
  /**
611
707
  * Collection name for procedures in Firestore
@@ -749,102 +845,6 @@ declare enum ClinicPhotoTag {
749
845
  OTHER = "other"
750
846
  }
751
847
 
752
- declare class BaseService {
753
- protected db: Firestore;
754
- protected auth: Auth;
755
- protected app: FirebaseApp;
756
- protected storage: FirebaseStorage;
757
- constructor(db: Firestore, auth: Auth, app: FirebaseApp);
758
- /**
759
- * Generiše jedinstveni ID za dokumente
760
- * Format: xxxxxxxxxxxx-timestamp
761
- * Gde je x random karakter (broj ili slovo)
762
- */
763
- protected generateId(): string;
764
- }
765
-
766
- /**
767
- * Enum for media access levels
768
- */
769
- declare enum MediaAccessLevel {
770
- PUBLIC = "public",
771
- PRIVATE = "private",
772
- CONFIDENTIAL = "confidential"
773
- }
774
- /**
775
- * Type that allows a field to be either a URL string or a File object
776
- */
777
- type MediaResource = string | File | Blob;
778
- /**
779
- * Media file metadata interface
780
- */
781
- interface MediaMetadata {
782
- id: string;
783
- name: string;
784
- url: string;
785
- contentType: string;
786
- size: number;
787
- createdAt: Timestamp;
788
- accessLevel: MediaAccessLevel;
789
- ownerId: string;
790
- collectionName: string;
791
- path: string;
792
- updatedAt?: Timestamp;
793
- }
794
- declare const MEDIA_METADATA_COLLECTION = "media_metadata";
795
- declare class MediaService extends BaseService {
796
- constructor(db: Firestore, auth: Auth, app: FirebaseApp);
797
- /**
798
- * Upload a media file, store its metadata, and return the metadata including the URL.
799
- * @param file - The file to upload.
800
- * @param ownerId - ID of the owner (user, patient, clinic, etc.).
801
- * @param accessLevel - Access level (public, private, confidential).
802
- * @param collectionName - The logical collection name this media belongs to (e.g., 'patient_profile_pictures', 'clinic_logos').
803
- * @param originalFileName - Optional: the original name of the file, if not using file.name.
804
- * @returns Promise with the media metadata.
805
- */
806
- uploadMedia(file: File | Blob, ownerId: string, accessLevel: MediaAccessLevel, collectionName: string, originalFileName?: string): Promise<MediaMetadata>;
807
- /**
808
- * Get media metadata from Firestore by its ID.
809
- * @param mediaId - ID of the media.
810
- * @returns Promise with the media metadata or null if not found.
811
- */
812
- getMediaMetadata(mediaId: string): Promise<MediaMetadata | null>;
813
- /**
814
- * Get media metadata from Firestore by its public URL.
815
- * @param url - The public URL of the media file.
816
- * @returns Promise with the media metadata or null if not found.
817
- */
818
- getMediaMetadataByUrl(url: string): Promise<MediaMetadata | null>;
819
- /**
820
- * Delete media from storage and remove metadata from Firestore.
821
- * @param mediaId - ID of the media to delete.
822
- */
823
- deleteMedia(mediaId: string): Promise<void>;
824
- /**
825
- * Update media access level. This involves moving the file in Firebase Storage
826
- * to a new path reflecting the new access level, and updating its metadata.
827
- * @param mediaId - ID of the media to update.
828
- * @param newAccessLevel - New access level.
829
- * @returns Promise with the updated media metadata, or null if metadata not found.
830
- */
831
- updateMediaAccessLevel(mediaId: string, newAccessLevel: MediaAccessLevel): Promise<MediaMetadata | null>;
832
- /**
833
- * List all media for an owner, optionally filtered by collection and access level.
834
- * @param ownerId - ID of the owner.
835
- * @param collectionName - Optional: Filter by collection name.
836
- * @param accessLevel - Optional: Filter by access level.
837
- * @param count - Optional: Number of items to fetch.
838
- * @param startAfterId - Optional: ID of the document to start after (for pagination).
839
- */
840
- listMedia(ownerId: string, collectionName?: string, accessLevel?: MediaAccessLevel, count?: number, startAfterId?: string): Promise<MediaMetadata[]>;
841
- /**
842
- * Get download URL for media. (Convenience, as URL is in metadata)
843
- * @param mediaId - ID of the media.
844
- */
845
- getMediaDownloadUrl(mediaId: string): Promise<string | null>;
846
- }
847
-
848
848
  declare const CLINIC_GROUPS_COLLECTION = "clinic_groups";
849
849
  declare const CLINIC_ADMINS_COLLECTION = "clinic_admins";
850
850
  declare const CLINICS_COLLECTION = "clinics";
@@ -4836,8 +4836,8 @@ declare const updateAppointmentSchema: z.ZodEffects<z.ZodEffects<z.ZodObject<{
4836
4836
  submittedAt: z.ZodOptional<z.ZodEffects<z.ZodAny, any, any>>;
4837
4837
  completedAt: z.ZodOptional<z.ZodEffects<z.ZodAny, any, any>>;
4838
4838
  }, "strip", z.ZodTypeAny, {
4839
- status: FilledDocumentStatus;
4840
4839
  path: string;
4840
+ status: FilledDocumentStatus;
4841
4841
  title: string;
4842
4842
  isUserForm: boolean;
4843
4843
  templateId: string;
@@ -4847,8 +4847,8 @@ declare const updateAppointmentSchema: z.ZodEffects<z.ZodEffects<z.ZodObject<{
4847
4847
  submittedAt?: any;
4848
4848
  completedAt?: any;
4849
4849
  }, {
4850
- status: FilledDocumentStatus;
4851
4850
  path: string;
4851
+ status: FilledDocumentStatus;
4852
4852
  title: string;
4853
4853
  isUserForm: boolean;
4854
4854
  templateId: string;
@@ -5103,56 +5103,56 @@ declare const searchAppointmentsSchema: z.ZodEffects<z.ZodEffects<z.ZodObject<{
5103
5103
  startAfter: z.ZodOptional<z.ZodAny>;
5104
5104
  }, "strip", z.ZodTypeAny, {
5105
5105
  limit: number;
5106
+ startAfter?: any;
5106
5107
  status?: AppointmentStatus | [AppointmentStatus, ...AppointmentStatus[]] | undefined;
5107
5108
  patientId?: string | undefined;
5108
5109
  startDate?: any;
5109
5110
  endDate?: any;
5110
- startAfter?: any;
5111
5111
  practitionerId?: string | undefined;
5112
5112
  clinicBranchId?: string | undefined;
5113
5113
  }, {
5114
+ limit?: number | undefined;
5115
+ startAfter?: any;
5114
5116
  status?: AppointmentStatus | [AppointmentStatus, ...AppointmentStatus[]] | undefined;
5115
5117
  patientId?: string | undefined;
5116
5118
  startDate?: any;
5117
5119
  endDate?: any;
5118
- limit?: number | undefined;
5119
- startAfter?: any;
5120
5120
  practitionerId?: string | undefined;
5121
5121
  clinicBranchId?: string | undefined;
5122
5122
  }>, {
5123
5123
  limit: number;
5124
+ startAfter?: any;
5124
5125
  status?: AppointmentStatus | [AppointmentStatus, ...AppointmentStatus[]] | undefined;
5125
5126
  patientId?: string | undefined;
5126
5127
  startDate?: any;
5127
5128
  endDate?: any;
5128
- startAfter?: any;
5129
5129
  practitionerId?: string | undefined;
5130
5130
  clinicBranchId?: string | undefined;
5131
5131
  }, {
5132
+ limit?: number | undefined;
5133
+ startAfter?: any;
5132
5134
  status?: AppointmentStatus | [AppointmentStatus, ...AppointmentStatus[]] | undefined;
5133
5135
  patientId?: string | undefined;
5134
5136
  startDate?: any;
5135
5137
  endDate?: any;
5136
- limit?: number | undefined;
5137
- startAfter?: any;
5138
5138
  practitionerId?: string | undefined;
5139
5139
  clinicBranchId?: string | undefined;
5140
5140
  }>, {
5141
5141
  limit: number;
5142
+ startAfter?: any;
5142
5143
  status?: AppointmentStatus | [AppointmentStatus, ...AppointmentStatus[]] | undefined;
5143
5144
  patientId?: string | undefined;
5144
5145
  startDate?: any;
5145
5146
  endDate?: any;
5146
- startAfter?: any;
5147
5147
  practitionerId?: string | undefined;
5148
5148
  clinicBranchId?: string | undefined;
5149
5149
  }, {
5150
+ limit?: number | undefined;
5151
+ startAfter?: any;
5150
5152
  status?: AppointmentStatus | [AppointmentStatus, ...AppointmentStatus[]] | undefined;
5151
5153
  patientId?: string | undefined;
5152
5154
  startDate?: any;
5153
5155
  endDate?: any;
5154
- limit?: number | undefined;
5155
- startAfter?: any;
5156
5156
  practitionerId?: string | undefined;
5157
5157
  clinicBranchId?: string | undefined;
5158
5158
  }>;
@@ -6444,7 +6444,24 @@ declare class ProcedureService extends BaseService {
6444
6444
  private subcategoryService;
6445
6445
  private technologyService;
6446
6446
  private productService;
6447
- constructor(db: Firestore, auth: Auth, app: FirebaseApp, categoryService: CategoryService, subcategoryService: SubcategoryService, technologyService: TechnologyService, productService: ProductService);
6447
+ private mediaService;
6448
+ constructor(db: Firestore, auth: Auth, app: FirebaseApp, categoryService: CategoryService, subcategoryService: SubcategoryService, technologyService: TechnologyService, productService: ProductService, mediaService: MediaService);
6449
+ /**
6450
+ * Process media resource (string URL or File object)
6451
+ * @param media String URL or File object
6452
+ * @param ownerId Owner ID for the media (usually procedureId)
6453
+ * @param collectionName Collection name for organizing files
6454
+ * @returns URL string after processing
6455
+ */
6456
+ private processMedia;
6457
+ /**
6458
+ * Process array of media resources (strings or Files)
6459
+ * @param mediaArray Array of string URLs or File objects
6460
+ * @param ownerId Owner ID for the media
6461
+ * @param collectionName Collection name for organizing files
6462
+ * @returns Array of URL strings after processing
6463
+ */
6464
+ private processMediaArray;
6448
6465
  /**
6449
6466
  * Creates a new procedure
6450
6467
  * @param data - The data for creating a new procedure
@@ -14079,6 +14096,7 @@ declare const clinicGroupSchema: z.ZodObject<{
14079
14096
  updatedAt: Date | Timestamp;
14080
14097
  name: string;
14081
14098
  isActive: boolean;
14099
+ ownerId: string | null;
14082
14100
  clinics: string[];
14083
14101
  clinicsInfo: {
14084
14102
  id: string;
@@ -14101,7 +14119,6 @@ declare const clinicGroupSchema: z.ZodObject<{
14101
14119
  featuredPhoto: string;
14102
14120
  description?: string | null | undefined;
14103
14121
  }[];
14104
- ownerId: string | null;
14105
14122
  contactInfo: {
14106
14123
  email: string;
14107
14124
  phoneNumber: string;
@@ -14157,6 +14174,7 @@ declare const clinicGroupSchema: z.ZodObject<{
14157
14174
  updatedAt: Date | Timestamp;
14158
14175
  name: string;
14159
14176
  isActive: boolean;
14177
+ ownerId: string | null;
14160
14178
  clinics: string[];
14161
14179
  clinicsInfo: {
14162
14180
  id: string;
@@ -14179,7 +14197,6 @@ declare const clinicGroupSchema: z.ZodObject<{
14179
14197
  featuredPhoto: string;
14180
14198
  description?: string | null | undefined;
14181
14199
  }[];
14182
- ownerId: string | null;
14183
14200
  contactInfo: {
14184
14201
  email: string;
14185
14202
  phoneNumber: string;
package/dist/index.js CHANGED
@@ -8353,7 +8353,7 @@ var createProcedureSchema = import_zod21.z.object({
8353
8353
  // Max 8 hours
8354
8354
  practitionerId: import_zod21.z.string().min(1),
8355
8355
  clinicBranchId: import_zod21.z.string().min(1),
8356
- photos: import_zod21.z.array(import_zod21.z.string()).optional()
8356
+ photos: import_zod21.z.array(mediaResourceSchema).optional()
8357
8357
  });
8358
8358
  var updateProcedureSchema = import_zod21.z.object({
8359
8359
  name: import_zod21.z.string().min(3).max(100).optional(),
@@ -8369,7 +8369,7 @@ var updateProcedureSchema = import_zod21.z.object({
8369
8369
  technologyId: import_zod21.z.string().optional(),
8370
8370
  productId: import_zod21.z.string().optional(),
8371
8371
  clinicBranchId: import_zod21.z.string().optional(),
8372
- photos: import_zod21.z.array(import_zod21.z.string()).optional()
8372
+ photos: import_zod21.z.array(mediaResourceSchema).optional()
8373
8373
  });
8374
8374
  var procedureSchema = createProcedureSchema.extend({
8375
8375
  id: import_zod21.z.string().min(1),
@@ -8409,12 +8409,61 @@ var procedureSchema = createProcedureSchema.extend({
8409
8409
  // src/services/procedure/procedure.service.ts
8410
8410
  var import_geofire_common8 = require("geofire-common");
8411
8411
  var ProcedureService = class extends BaseService {
8412
- constructor(db, auth, app, categoryService, subcategoryService, technologyService, productService) {
8412
+ constructor(db, auth, app, categoryService, subcategoryService, technologyService, productService, mediaService) {
8413
8413
  super(db, auth, app);
8414
8414
  this.categoryService = categoryService;
8415
8415
  this.subcategoryService = subcategoryService;
8416
8416
  this.technologyService = technologyService;
8417
8417
  this.productService = productService;
8418
+ this.mediaService = mediaService;
8419
+ }
8420
+ /**
8421
+ * Process media resource (string URL or File object)
8422
+ * @param media String URL or File object
8423
+ * @param ownerId Owner ID for the media (usually procedureId)
8424
+ * @param collectionName Collection name for organizing files
8425
+ * @returns URL string after processing
8426
+ */
8427
+ async processMedia(media, ownerId, collectionName) {
8428
+ if (!media) return null;
8429
+ if (typeof media === "string") {
8430
+ return media;
8431
+ }
8432
+ if (media instanceof File || media instanceof Blob) {
8433
+ console.log(
8434
+ `[ProcedureService] Uploading ${collectionName} media for ${ownerId}`
8435
+ );
8436
+ const metadata = await this.mediaService.uploadMedia(
8437
+ media,
8438
+ ownerId,
8439
+ "public" /* PUBLIC */,
8440
+ collectionName
8441
+ );
8442
+ return metadata.url;
8443
+ }
8444
+ return null;
8445
+ }
8446
+ /**
8447
+ * Process array of media resources (strings or Files)
8448
+ * @param mediaArray Array of string URLs or File objects
8449
+ * @param ownerId Owner ID for the media
8450
+ * @param collectionName Collection name for organizing files
8451
+ * @returns Array of URL strings after processing
8452
+ */
8453
+ async processMediaArray(mediaArray, ownerId, collectionName) {
8454
+ if (!mediaArray || mediaArray.length === 0) return [];
8455
+ const result = [];
8456
+ for (const media of mediaArray) {
8457
+ const processedUrl = await this.processMedia(
8458
+ media,
8459
+ ownerId,
8460
+ collectionName
8461
+ );
8462
+ if (processedUrl) {
8463
+ result.push(processedUrl);
8464
+ }
8465
+ }
8466
+ return result;
8418
8467
  }
8419
8468
  /**
8420
8469
  * Creates a new procedure
@@ -8424,6 +8473,7 @@ var ProcedureService = class extends BaseService {
8424
8473
  async createProcedure(data) {
8425
8474
  var _a;
8426
8475
  const validatedData = createProcedureSchema.parse(data);
8476
+ const procedureId = this.generateId();
8427
8477
  const [category, subcategory, technology, product] = await Promise.all([
8428
8478
  this.categoryService.getById(validatedData.categoryId),
8429
8479
  this.subcategoryService.getById(
@@ -8463,6 +8513,14 @@ var ProcedureService = class extends BaseService {
8463
8513
  );
8464
8514
  }
8465
8515
  const practitioner = practitionerSnapshot.data();
8516
+ let processedPhotos = [];
8517
+ if (validatedData.photos && validatedData.photos.length > 0) {
8518
+ processedPhotos = await this.processMediaArray(
8519
+ validatedData.photos,
8520
+ procedureId,
8521
+ "procedure-photos"
8522
+ );
8523
+ }
8466
8524
  const clinicInfo = {
8467
8525
  id: clinicSnapshot.id,
8468
8526
  name: clinic.name,
@@ -8479,10 +8537,10 @@ var ProcedureService = class extends BaseService {
8479
8537
  rating: ((_a = practitioner.reviewInfo) == null ? void 0 : _a.averageRating) || 0,
8480
8538
  services: practitioner.procedures || []
8481
8539
  };
8482
- const procedureId = this.generateId();
8483
8540
  const newProcedure = {
8484
8541
  id: procedureId,
8485
8542
  ...validatedData,
8543
+ photos: processedPhotos,
8486
8544
  category,
8487
8545
  // Embed full objects
8488
8546
  subcategory,
@@ -8585,6 +8643,13 @@ var ProcedureService = class extends BaseService {
8585
8643
  const oldClinicId = existingProcedure.clinicBranchId;
8586
8644
  let newPractitioner = null;
8587
8645
  let newClinic = null;
8646
+ if (validatedData.photos !== void 0) {
8647
+ updatedProcedureData.photos = await this.processMediaArray(
8648
+ validatedData.photos,
8649
+ id,
8650
+ "procedure-photos"
8651
+ );
8652
+ }
8588
8653
  if (validatedData.practitionerId && validatedData.practitionerId !== oldPractitionerId) {
8589
8654
  practitionerChanged = true;
8590
8655
  const newPractitionerRef = (0, import_firestore27.doc)(
package/dist/index.mjs CHANGED
@@ -8342,7 +8342,7 @@ var createProcedureSchema = z21.object({
8342
8342
  // Max 8 hours
8343
8343
  practitionerId: z21.string().min(1),
8344
8344
  clinicBranchId: z21.string().min(1),
8345
- photos: z21.array(z21.string()).optional()
8345
+ photos: z21.array(mediaResourceSchema).optional()
8346
8346
  });
8347
8347
  var updateProcedureSchema = z21.object({
8348
8348
  name: z21.string().min(3).max(100).optional(),
@@ -8358,7 +8358,7 @@ var updateProcedureSchema = z21.object({
8358
8358
  technologyId: z21.string().optional(),
8359
8359
  productId: z21.string().optional(),
8360
8360
  clinicBranchId: z21.string().optional(),
8361
- photos: z21.array(z21.string()).optional()
8361
+ photos: z21.array(mediaResourceSchema).optional()
8362
8362
  });
8363
8363
  var procedureSchema = createProcedureSchema.extend({
8364
8364
  id: z21.string().min(1),
@@ -8398,12 +8398,61 @@ var procedureSchema = createProcedureSchema.extend({
8398
8398
  // src/services/procedure/procedure.service.ts
8399
8399
  import { distanceBetween as distanceBetween6, geohashQueryBounds as geohashQueryBounds5 } from "geofire-common";
8400
8400
  var ProcedureService = class extends BaseService {
8401
- constructor(db, auth, app, categoryService, subcategoryService, technologyService, productService) {
8401
+ constructor(db, auth, app, categoryService, subcategoryService, technologyService, productService, mediaService) {
8402
8402
  super(db, auth, app);
8403
8403
  this.categoryService = categoryService;
8404
8404
  this.subcategoryService = subcategoryService;
8405
8405
  this.technologyService = technologyService;
8406
8406
  this.productService = productService;
8407
+ this.mediaService = mediaService;
8408
+ }
8409
+ /**
8410
+ * Process media resource (string URL or File object)
8411
+ * @param media String URL or File object
8412
+ * @param ownerId Owner ID for the media (usually procedureId)
8413
+ * @param collectionName Collection name for organizing files
8414
+ * @returns URL string after processing
8415
+ */
8416
+ async processMedia(media, ownerId, collectionName) {
8417
+ if (!media) return null;
8418
+ if (typeof media === "string") {
8419
+ return media;
8420
+ }
8421
+ if (media instanceof File || media instanceof Blob) {
8422
+ console.log(
8423
+ `[ProcedureService] Uploading ${collectionName} media for ${ownerId}`
8424
+ );
8425
+ const metadata = await this.mediaService.uploadMedia(
8426
+ media,
8427
+ ownerId,
8428
+ "public" /* PUBLIC */,
8429
+ collectionName
8430
+ );
8431
+ return metadata.url;
8432
+ }
8433
+ return null;
8434
+ }
8435
+ /**
8436
+ * Process array of media resources (strings or Files)
8437
+ * @param mediaArray Array of string URLs or File objects
8438
+ * @param ownerId Owner ID for the media
8439
+ * @param collectionName Collection name for organizing files
8440
+ * @returns Array of URL strings after processing
8441
+ */
8442
+ async processMediaArray(mediaArray, ownerId, collectionName) {
8443
+ if (!mediaArray || mediaArray.length === 0) return [];
8444
+ const result = [];
8445
+ for (const media of mediaArray) {
8446
+ const processedUrl = await this.processMedia(
8447
+ media,
8448
+ ownerId,
8449
+ collectionName
8450
+ );
8451
+ if (processedUrl) {
8452
+ result.push(processedUrl);
8453
+ }
8454
+ }
8455
+ return result;
8407
8456
  }
8408
8457
  /**
8409
8458
  * Creates a new procedure
@@ -8413,6 +8462,7 @@ var ProcedureService = class extends BaseService {
8413
8462
  async createProcedure(data) {
8414
8463
  var _a;
8415
8464
  const validatedData = createProcedureSchema.parse(data);
8465
+ const procedureId = this.generateId();
8416
8466
  const [category, subcategory, technology, product] = await Promise.all([
8417
8467
  this.categoryService.getById(validatedData.categoryId),
8418
8468
  this.subcategoryService.getById(
@@ -8452,6 +8502,14 @@ var ProcedureService = class extends BaseService {
8452
8502
  );
8453
8503
  }
8454
8504
  const practitioner = practitionerSnapshot.data();
8505
+ let processedPhotos = [];
8506
+ if (validatedData.photos && validatedData.photos.length > 0) {
8507
+ processedPhotos = await this.processMediaArray(
8508
+ validatedData.photos,
8509
+ procedureId,
8510
+ "procedure-photos"
8511
+ );
8512
+ }
8455
8513
  const clinicInfo = {
8456
8514
  id: clinicSnapshot.id,
8457
8515
  name: clinic.name,
@@ -8468,10 +8526,10 @@ var ProcedureService = class extends BaseService {
8468
8526
  rating: ((_a = practitioner.reviewInfo) == null ? void 0 : _a.averageRating) || 0,
8469
8527
  services: practitioner.procedures || []
8470
8528
  };
8471
- const procedureId = this.generateId();
8472
8529
  const newProcedure = {
8473
8530
  id: procedureId,
8474
8531
  ...validatedData,
8532
+ photos: processedPhotos,
8475
8533
  category,
8476
8534
  // Embed full objects
8477
8535
  subcategory,
@@ -8574,6 +8632,13 @@ var ProcedureService = class extends BaseService {
8574
8632
  const oldClinicId = existingProcedure.clinicBranchId;
8575
8633
  let newPractitioner = null;
8576
8634
  let newClinic = null;
8635
+ if (validatedData.photos !== void 0) {
8636
+ updatedProcedureData.photos = await this.processMediaArray(
8637
+ validatedData.photos,
8638
+ id,
8639
+ "procedure-photos"
8640
+ );
8641
+ }
8577
8642
  if (validatedData.practitionerId && validatedData.practitionerId !== oldPractitionerId) {
8578
8643
  practitionerChanged = true;
8579
8644
  const newPractitionerRef = doc16(
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@blackcode_sa/metaestetics-api",
3
3
  "private": false,
4
- "version": "1.7.13",
4
+ "version": "1.7.14",
5
5
  "description": "Firebase authentication service with anonymous upgrade support",
6
6
  "main": "./dist/index.js",
7
7
  "module": "./dist/index.mjs",
@@ -70,12 +70,14 @@ import { Clinic, CLINICS_COLLECTION } from "../../types/clinic";
70
70
  import { ProcedureReviewInfo } from "../../types/reviews";
71
71
  import { distanceBetween, geohashQueryBounds } from "geofire-common";
72
72
  import { TreatmentBenefit } from "../../backoffice/types/static/treatment-benefit.types";
73
+ import { MediaService, MediaAccessLevel } from "../media/media.service";
73
74
 
74
75
  export class ProcedureService extends BaseService {
75
76
  private categoryService: CategoryService;
76
77
  private subcategoryService: SubcategoryService;
77
78
  private technologyService: TechnologyService;
78
79
  private productService: ProductService;
80
+ private mediaService: MediaService;
79
81
 
80
82
  constructor(
81
83
  db: Firestore,
@@ -84,13 +86,81 @@ export class ProcedureService extends BaseService {
84
86
  categoryService: CategoryService,
85
87
  subcategoryService: SubcategoryService,
86
88
  technologyService: TechnologyService,
87
- productService: ProductService
89
+ productService: ProductService,
90
+ mediaService: MediaService
88
91
  ) {
89
92
  super(db, auth, app);
90
93
  this.categoryService = categoryService;
91
94
  this.subcategoryService = subcategoryService;
92
95
  this.technologyService = technologyService;
93
96
  this.productService = productService;
97
+ this.mediaService = mediaService;
98
+ }
99
+
100
+ /**
101
+ * Process media resource (string URL or File object)
102
+ * @param media String URL or File object
103
+ * @param ownerId Owner ID for the media (usually procedureId)
104
+ * @param collectionName Collection name for organizing files
105
+ * @returns URL string after processing
106
+ */
107
+ private async processMedia(
108
+ media: string | File | Blob | null | undefined,
109
+ ownerId: string,
110
+ collectionName: string
111
+ ): Promise<string | null> {
112
+ if (!media) return null;
113
+
114
+ // If already a string URL, return it directly
115
+ if (typeof media === "string") {
116
+ return media;
117
+ }
118
+
119
+ // If it's a File, upload it using MediaService
120
+ if (media instanceof File || media instanceof Blob) {
121
+ console.log(
122
+ `[ProcedureService] Uploading ${collectionName} media for ${ownerId}`
123
+ );
124
+ const metadata = await this.mediaService.uploadMedia(
125
+ media,
126
+ ownerId,
127
+ MediaAccessLevel.PUBLIC,
128
+ collectionName
129
+ );
130
+ return metadata.url;
131
+ }
132
+
133
+ return null;
134
+ }
135
+
136
+ /**
137
+ * Process array of media resources (strings or Files)
138
+ * @param mediaArray Array of string URLs or File objects
139
+ * @param ownerId Owner ID for the media
140
+ * @param collectionName Collection name for organizing files
141
+ * @returns Array of URL strings after processing
142
+ */
143
+ private async processMediaArray(
144
+ mediaArray: (string | File | Blob)[] | undefined,
145
+ ownerId: string,
146
+ collectionName: string
147
+ ): Promise<string[]> {
148
+ if (!mediaArray || mediaArray.length === 0) return [];
149
+
150
+ const result: string[] = [];
151
+
152
+ for (const media of mediaArray) {
153
+ const processedUrl = await this.processMedia(
154
+ media,
155
+ ownerId,
156
+ collectionName
157
+ );
158
+ if (processedUrl) {
159
+ result.push(processedUrl);
160
+ }
161
+ }
162
+
163
+ return result;
94
164
  }
95
165
 
96
166
  /**
@@ -101,6 +171,9 @@ export class ProcedureService extends BaseService {
101
171
  async createProcedure(data: CreateProcedureData): Promise<Procedure> {
102
172
  const validatedData = createProcedureSchema.parse(data);
103
173
 
174
+ // Generate procedure ID first so we can use it for media uploads
175
+ const procedureId = this.generateId();
176
+
104
177
  // Get references to related entities (Category, Subcategory, Technology, Product)
105
178
  const [category, subcategory, technology, product] = await Promise.all([
106
179
  this.categoryService.getById(validatedData.categoryId),
@@ -146,6 +219,16 @@ export class ProcedureService extends BaseService {
146
219
  }
147
220
  const practitioner = practitionerSnapshot.data() as Practitioner; // Assert type
148
221
 
222
+ // Process photos if provided
223
+ let processedPhotos: string[] = [];
224
+ if (validatedData.photos && validatedData.photos.length > 0) {
225
+ processedPhotos = await this.processMediaArray(
226
+ validatedData.photos,
227
+ procedureId,
228
+ "procedure-photos"
229
+ );
230
+ }
231
+
149
232
  // Create aggregated clinic info for the procedure document
150
233
  const clinicInfo = {
151
234
  id: clinicSnapshot.id,
@@ -174,10 +257,10 @@ export class ProcedureService extends BaseService {
174
257
  };
175
258
 
176
259
  // Create the procedure object
177
- const procedureId = this.generateId();
178
260
  const newProcedure: Omit<Procedure, "createdAt" | "updatedAt"> = {
179
261
  id: procedureId,
180
262
  ...validatedData,
263
+ photos: processedPhotos,
181
264
  category, // Embed full objects
182
265
  subcategory,
183
266
  technology,
@@ -296,6 +379,15 @@ export class ProcedureService extends BaseService {
296
379
  let newPractitioner: Practitioner | null = null;
297
380
  let newClinic: Clinic | null = null;
298
381
 
382
+ // Process photos if provided
383
+ if (validatedData.photos !== undefined) {
384
+ updatedProcedureData.photos = await this.processMediaArray(
385
+ validatedData.photos,
386
+ id,
387
+ "procedure-photos"
388
+ );
389
+ }
390
+
299
391
  // --- Prepare updates and fetch new related data if IDs change ---
300
392
 
301
393
  // Handle Practitioner Change
@@ -20,6 +20,7 @@ import { DoctorInfo } from "../clinic";
20
20
  import { PRACTITIONERS_COLLECTION } from "../practitioner";
21
21
  import { ProcedureReviewInfo } from "../reviews";
22
22
  import type { Contraindication } from "../../backoffice/types/static/contraindication.types";
23
+ import { MediaResource } from "../../services/media/media.service";
23
24
 
24
25
  /**
25
26
  * Procedure represents a specific medical procedure that can be performed by a practitioner in a clinic
@@ -31,7 +32,7 @@ export interface Procedure {
31
32
  /** Name of the procedure */
32
33
  name: string;
33
34
  /** Photos of the procedure */
34
- photos?: string[];
35
+ photos?: MediaResource[];
35
36
  /** Detailed description of the procedure */
36
37
  description: string;
37
38
  /** Family of procedures this belongs to (aesthetics/surgery) */
@@ -101,7 +102,7 @@ export interface CreateProcedureData {
101
102
  duration: number;
102
103
  practitionerId: string;
103
104
  clinicBranchId: string;
104
- photos?: string[];
105
+ photos?: MediaResource[];
105
106
  }
106
107
 
107
108
  /**
@@ -121,7 +122,7 @@ export interface UpdateProcedureData {
121
122
  technologyId?: string;
122
123
  productId?: string;
123
124
  clinicBranchId?: string;
124
- photos?: string[];
125
+ photos?: MediaResource[];
125
126
  }
126
127
 
127
128
  /**
@@ -6,7 +6,7 @@ import {
6
6
  } from "../backoffice/types/static/pricing.types";
7
7
  import { clinicInfoSchema, doctorInfoSchema } from "./shared.schema";
8
8
  import { procedureReviewInfoSchema } from "./reviews.schema";
9
-
9
+ import { mediaResourceSchema } from "./media.schema";
10
10
  /**
11
11
  * Schema for creating a new procedure
12
12
  */
@@ -24,7 +24,7 @@ export const createProcedureSchema = z.object({
24
24
  duration: z.number().min(1).max(480), // Max 8 hours
25
25
  practitionerId: z.string().min(1),
26
26
  clinicBranchId: z.string().min(1),
27
- photos: z.array(z.string()).optional(),
27
+ photos: z.array(mediaResourceSchema).optional(),
28
28
  });
29
29
 
30
30
  /**
@@ -44,7 +44,7 @@ export const updateProcedureSchema = z.object({
44
44
  technologyId: z.string().optional(),
45
45
  productId: z.string().optional(),
46
46
  clinicBranchId: z.string().optional(),
47
- photos: z.array(z.string()).optional(),
47
+ photos: z.array(mediaResourceSchema).optional(),
48
48
  });
49
49
 
50
50
  /**