@blackcode_sa/metaestetics-api 1.11.3 → 1.12.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.
Files changed (53) hide show
  1. package/dist/admin/index.d.mts +378 -334
  2. package/dist/admin/index.d.ts +378 -334
  3. package/dist/backoffice/index.d.mts +1198 -430
  4. package/dist/backoffice/index.d.ts +1198 -430
  5. package/dist/backoffice/index.js +1128 -245
  6. package/dist/backoffice/index.mjs +1119 -209
  7. package/dist/index.d.mts +4478 -4031
  8. package/dist/index.d.ts +4478 -4031
  9. package/dist/index.js +1974 -757
  10. package/dist/index.mjs +1735 -490
  11. package/package.json +1 -1
  12. package/src/backoffice/expo-safe/index.ts +4 -0
  13. package/src/backoffice/services/README.md +40 -0
  14. package/src/backoffice/services/brand.service.ts +85 -6
  15. package/src/backoffice/services/category.service.ts +92 -10
  16. package/src/backoffice/services/constants.service.ts +308 -0
  17. package/src/backoffice/services/documentation-template.service.ts +56 -2
  18. package/src/backoffice/services/index.ts +1 -0
  19. package/src/backoffice/services/product.service.ts +126 -5
  20. package/src/backoffice/services/requirement.service.ts +13 -0
  21. package/src/backoffice/services/subcategory.service.ts +184 -13
  22. package/src/backoffice/services/technology.service.ts +344 -129
  23. package/src/backoffice/types/admin-constants.types.ts +69 -0
  24. package/src/backoffice/types/brand.types.ts +1 -0
  25. package/src/backoffice/types/index.ts +2 -0
  26. package/src/backoffice/types/procedure-product.types.ts +38 -0
  27. package/src/backoffice/types/product.types.ts +31 -4
  28. package/src/backoffice/types/static/contraindication.types.ts +1 -0
  29. package/src/backoffice/types/static/treatment-benefit.types.ts +1 -0
  30. package/src/backoffice/types/technology.types.ts +113 -4
  31. package/src/backoffice/validations/schemas.ts +35 -9
  32. package/src/services/appointment/appointment.service.ts +0 -5
  33. package/src/services/appointment/utils/appointment.utils.ts +124 -113
  34. package/src/services/base.service.ts +10 -3
  35. package/src/services/documentation-templates/documentation-template.service.ts +116 -0
  36. package/src/services/media/media.service.ts +2 -2
  37. package/src/services/practitioner/practitioner.service.ts +201 -83
  38. package/src/services/procedure/README.md +76 -1
  39. package/src/services/procedure/procedure.service.ts +538 -235
  40. package/src/types/appointment/index.ts +2 -3
  41. package/src/types/clinic/index.ts +1 -6
  42. package/src/types/patient/medical-info.types.ts +3 -3
  43. package/src/types/procedure/index.ts +39 -20
  44. package/src/validations/clinic.schema.ts +1 -6
  45. package/src/validations/patient/medical-info.schema.ts +7 -2
  46. package/src/validations/procedure-product.schema.ts +41 -0
  47. package/src/validations/procedure.schema.ts +59 -8
  48. package/src/backoffice/services/__tests__/brand.service.test.ts +0 -196
  49. package/src/backoffice/services/__tests__/category.service.test.ts +0 -201
  50. package/src/backoffice/services/__tests__/product.service.test.ts +0 -358
  51. package/src/backoffice/services/__tests__/requirement.service.test.ts +0 -226
  52. package/src/backoffice/services/__tests__/subcategory.service.test.ts +0 -181
  53. package/src/backoffice/services/__tests__/technology.service.test.ts +0 -1097
@@ -3,10 +3,9 @@ import { ClinicInfo, PractitionerProfileInfo, PatientProfileInfo } from '../prof
3
3
  import { ProcedureSummaryInfo } from '../procedure';
4
4
  import { Currency, type PricingMeasure } from '../../backoffice/types/static/pricing.types';
5
5
  import { BlockingCondition } from '../../backoffice/types/static/blocking-condition.types';
6
- import { Contraindication } from '../../backoffice/types/static/contraindication.types';
7
6
  import { Requirement } from '../../backoffice/types/requirement.types';
8
7
  import { FilledDocumentStatus } from '../documentation-templates';
9
- import type { ProcedureFamily } from '../../backoffice';
8
+ import type { ContraindicationDynamic, ProcedureFamily } from '../../backoffice';
10
9
  import type { MediaResource } from '../../services/media/media.service';
11
10
 
12
11
  /**
@@ -243,7 +242,7 @@ export interface Appointment {
243
242
 
244
243
  /** Procedure-related conditions and requirements */
245
244
  blockingConditions: BlockingCondition[];
246
- contraindications: Contraindication[];
245
+ contraindications: ContraindicationDynamic[];
247
246
  preProcedureRequirements: Requirement[];
248
247
  postProcedureRequirements: Requirement[];
249
248
 
@@ -1,10 +1,5 @@
1
1
  import { Timestamp, FieldValue } from "firebase/firestore";
2
- import type { ProcedureFamily } from "../../backoffice/types/static/procedure-family.types";
3
- import type { TreatmentBenefit } from "../../backoffice/types/static/treatment-benefit.types";
4
- import type {
5
- Currency,
6
- PricingMeasure,
7
- } from "../../backoffice/types/static/pricing.types";
2
+
8
3
  import type { ClinicInfo } from "../profile";
9
4
  import { ClinicReviewInfo } from "../reviews";
10
5
  import { ProcedureSummaryInfo } from "../procedure";
@@ -1,7 +1,7 @@
1
1
  import { Timestamp } from "firebase/firestore";
2
2
  import { BlockingCondition } from "../../backoffice/types/static/blocking-condition.types";
3
- import { Contraindication } from "../../backoffice/types/static/contraindication.types";
4
3
  import { AllergyType, AllergySubtype } from "./allergies";
4
+ import type { ContraindicationDynamic } from "../../backoffice";
5
5
 
6
6
  export const PATIENT_MEDICAL_INFO_COLLECTION = "medical_info";
7
7
 
@@ -38,7 +38,7 @@ export interface PatientMedicalInfo {
38
38
  }[];
39
39
 
40
40
  contraindications: {
41
- condition: Contraindication;
41
+ condition: ContraindicationDynamic;
42
42
  lastOccurrence: Timestamp;
43
43
  frequency: "rare" | "occasional" | "frequent";
44
44
  isActive: boolean;
@@ -113,7 +113,7 @@ export interface UpdateBlockingConditionData
113
113
 
114
114
  // Interfejsi za kontraindikacije
115
115
  export interface AddContraindicationData {
116
- condition: Contraindication;
116
+ condition: ContraindicationDynamic;
117
117
  lastOccurrence: Timestamp;
118
118
  frequency: "rare" | "occasional" | "frequent";
119
119
  isActive: boolean;
@@ -1,26 +1,26 @@
1
- import { ProcedureFamily } from "../../backoffice/types/static/procedure-family.types";
1
+ import {
2
+ BlockingCondition,
3
+ CertificationRequirement,
4
+ ProcedureFamily,
5
+ } from "../../backoffice/types";
6
+ import {
7
+ ContraindicationDynamic,
8
+ TreatmentBenefitDynamic,
9
+ } from "../../backoffice/types/admin-constants.types";
10
+ import { Requirement } from "../../backoffice/types/requirement.types";
2
11
  import { Category } from "../../backoffice/types/category.types";
3
12
  import { Subcategory } from "../../backoffice/types/subcategory.types";
4
- import {
5
- Technology,
6
- type TechnologyDocumentationTemplate,
7
- } from "../../backoffice/types/technology.types";
13
+ import { Technology } from "../../backoffice/types/technology.types";
8
14
  import { Product } from "../../backoffice/types/product.types";
15
+ import { ClinicInfo, DoctorInfo } from "../../types/";
16
+ import { ProcedureReviewInfo } from "../reviews";
17
+ import { TechnologyDocumentationTemplate } from "../../backoffice/types/technology.types";
9
18
  import {
10
19
  PricingMeasure,
11
20
  Currency,
12
21
  } from "../../backoffice/types/static/pricing.types";
13
- import { Requirement } from "../../backoffice/types/requirement.types";
14
- import { BlockingCondition } from "../../backoffice/types/static/blocking-condition.types";
15
- import { TreatmentBenefit } from "../../backoffice/types/static/treatment-benefit.types";
16
- import { CertificationRequirement } from "../../backoffice/types/static/certification.types";
17
- import { DocumentTemplate } from "../documentation-templates";
18
- import { ClinicInfo } from "../profile";
19
- import { DoctorInfo } from "../clinic";
20
- import { PRACTITIONERS_COLLECTION } from "../practitioner";
21
- import { ProcedureReviewInfo } from "../reviews";
22
- import type { Contraindication } from "../../backoffice/types/static/contraindication.types";
23
22
  import { MediaResource } from "../../services/media/media.service";
23
+ import type { ProcedureProduct } from "../../backoffice/types/procedure-product.types";
24
24
 
25
25
  /**
26
26
  * Procedure represents a specific medical procedure that can be performed by a practitioner in a clinic
@@ -45,22 +45,27 @@ export interface Procedure {
45
45
  subcategory: Subcategory;
46
46
  /** Technology used in this procedure */
47
47
  technology: Technology;
48
- /** Product used in this procedure */
48
+ /** Default product used in this procedure */
49
49
  product: Product;
50
- /** Price of the procedure */
50
+ /** Default price of the procedure */
51
51
  price: number;
52
52
  /** Currency for the price */
53
53
  currency: Currency;
54
- /** How the price is measured (per ml, per zone, etc.) */
54
+ /** How the price is measured (per ml, per zone, etc.) - for default product*/
55
55
  pricingMeasure: PricingMeasure;
56
56
  /** Duration of the procedure in minutes */
57
+ productsMetadata: ProcedureProduct[];
57
58
  duration: number;
58
59
  /** Blocking conditions that prevent this procedure */
59
60
  blockingConditions: BlockingCondition[];
60
61
  /** Treatment benefits of this procedure */
61
- treatmentBenefits: TreatmentBenefit[];
62
+ treatmentBenefits: TreatmentBenefitDynamic[];
63
+ /** A list of just the string IDs of the treatment benefits, for efficient querying. */
64
+ treatmentBenefitIds: string[];
62
65
  /** Contraindications of this procedure */
63
- contraindications: Contraindication[];
66
+ contraindications: ContraindicationDynamic[];
67
+ /** A list of just the string IDs of the contraindications, for efficient querying. */
68
+ contraindicationIds: string[];
64
69
  /** Pre-procedure requirements */
65
70
  preRequirements: Requirement[];
66
71
  /** Post-procedure requirements */
@@ -101,6 +106,13 @@ export interface CreateProcedureData {
101
106
  technologyId: string;
102
107
  productId: string;
103
108
  price: number;
109
+ productsMetadata: {
110
+ productId: string;
111
+ price: number;
112
+ currency: Currency;
113
+ pricingMeasure: PricingMeasure;
114
+ isDefault?: boolean;
115
+ }[];
104
116
  currency: Currency;
105
117
  pricingMeasure: PricingMeasure;
106
118
  duration: number;
@@ -120,6 +132,13 @@ export interface UpdateProcedureData {
120
132
  price?: number;
121
133
  currency?: Currency;
122
134
  pricingMeasure?: PricingMeasure;
135
+ productsMetadata?: {
136
+ productId: string;
137
+ price: number;
138
+ currency: Currency;
139
+ pricingMeasure: PricingMeasure;
140
+ isDefault?: boolean;
141
+ }[];
123
142
  duration?: number;
124
143
  isActive?: boolean;
125
144
  practitionerId?: string;
@@ -7,12 +7,7 @@ import {
7
7
  PracticeType,
8
8
  Language,
9
9
  } from "../types/clinic";
10
- import { ProcedureFamily } from "../backoffice/types/static/procedure-family.types";
11
- import { TreatmentBenefit } from "../backoffice/types/static/treatment-benefit.types";
12
- import {
13
- Currency,
14
- PricingMeasure,
15
- } from "../backoffice/types/static/pricing.types";
10
+
16
11
  import { clinicReviewInfoSchema } from "./reviews.schema";
17
12
  import {
18
13
  procedureSummaryInfoSchema,
@@ -7,7 +7,6 @@ import {
7
7
  CosmeticAllergySubtype,
8
8
  } from "../../types/patient/allergies";
9
9
  import { BlockingCondition } from "../../backoffice/types/static/blocking-condition.types";
10
- import { Contraindication } from "../../backoffice/types/static/contraindication.types";
11
10
  import { timestampSchema } from "../common.schema";
12
11
 
13
12
  export const allergySubtypeSchema = z.union([
@@ -50,8 +49,14 @@ export const blockingConditionSchema = z.object({
50
49
  isActive: z.boolean(),
51
50
  });
52
51
 
52
+ export const contraindicationDynamicSchema = z.object({
53
+ id: z.string(),
54
+ name: z.string(),
55
+ description: z.string().optional(),
56
+ });
57
+
53
58
  export const contraindicationSchema = z.object({
54
- condition: z.nativeEnum(Contraindication),
59
+ condition: contraindicationDynamicSchema,
55
60
  lastOccurrence: timestampSchema,
56
61
  frequency: z.enum(["rare", "occasional", "frequent"]),
57
62
  notes: z.string().optional().nullable(),
@@ -0,0 +1,41 @@
1
+ import { z } from "zod";
2
+ import {
3
+ Currency,
4
+ PricingMeasure,
5
+ } from "../backoffice/types/static/pricing.types";
6
+
7
+ /**
8
+ * Schema for validating procedure product data.
9
+ * This is used when creating or updating a procedure to validate the products associated with it.
10
+ */
11
+ export const procedureProductDataSchema = z.object({
12
+ /**
13
+ * The ID of the product. Must be a non-empty string.
14
+ * @validation
15
+ */
16
+ productId: z.string().min(1, "Product ID is required"),
17
+
18
+ /**
19
+ * The price of the product. Must be a non-negative number.
20
+ * @validation
21
+ */
22
+ price: z.number().min(0, "Price must be a non-negative number"),
23
+
24
+ /**
25
+ * The currency for the price. Must be one of the values from the Currency enum.
26
+ * @validation
27
+ */
28
+ currency: z.nativeEnum(Currency),
29
+
30
+ /**
31
+ * The pricing measure for the product. Must be one of the values from the PricingMeasure enum.
32
+ * @validation
33
+ */
34
+ pricingMeasure: z.nativeEnum(PricingMeasure),
35
+
36
+ /**
37
+ * Whether this is the default product for the procedure.
38
+ * @validation
39
+ */
40
+ isDefault: z.boolean().optional(),
41
+ });
@@ -1,9 +1,45 @@
1
- import { z } from 'zod';
2
- import { ProcedureFamily } from '../backoffice/types/static/procedure-family.types';
3
- import { Currency, PricingMeasure } from '../backoffice/types/static/pricing.types';
4
- import { clinicInfoSchema, doctorInfoSchema } from './shared.schema';
5
- import { procedureReviewInfoSchema } from './reviews.schema';
6
- import { mediaResourceSchema } from './media.schema';
1
+ import { z } from "zod";
2
+ import { ProcedureFamily } from "../backoffice/types/static/procedure-family.types";
3
+ import {
4
+ Currency,
5
+ PricingMeasure,
6
+ } from "../backoffice/types/static/pricing.types";
7
+ import { clinicInfoSchema, doctorInfoSchema } from "./shared.schema";
8
+ import { procedureReviewInfoSchema } from "./reviews.schema";
9
+ import { mediaResourceSchema } from "./media.schema";
10
+ import { procedureProductDataSchema } from "./procedure-product.schema";
11
+
12
+ /**
13
+ * Schema for validating stored procedure product data (with full product objects).
14
+ * This is used when validating complete procedure documents from Firestore.
15
+ */
16
+ export const storedProcedureProductSchema = z.object({
17
+ /**
18
+ * The full product object used in the procedure.
19
+ */
20
+ product: z.any(), // We'll validate the full product object separately
21
+
22
+ /**
23
+ * The price of the procedure when using this specific product.
24
+ */
25
+ price: z.number().min(0, "Price must be a non-negative number"),
26
+
27
+ /**
28
+ * The currency for the price of this product.
29
+ */
30
+ currency: z.nativeEnum(Currency),
31
+
32
+ /**
33
+ * How the price is measured (e.g., per ml, per zone).
34
+ */
35
+ pricingMeasure: z.nativeEnum(PricingMeasure),
36
+
37
+ /**
38
+ * Whether this is the default product for the procedure.
39
+ */
40
+ isDefault: z.boolean().optional(),
41
+ });
42
+
7
43
  /**
8
44
  * Schema for creating a new procedure
9
45
  */
@@ -20,6 +56,7 @@ export const createProcedureSchema = z.object({
20
56
  price: z.number().min(0),
21
57
  currency: z.nativeEnum(Currency),
22
58
  pricingMeasure: z.nativeEnum(PricingMeasure),
59
+ productsMetadata: z.array(procedureProductDataSchema).min(1),
23
60
  duration: z.number().min(1).max(480), // Max 8 hours
24
61
  practitionerId: z.string().min(1),
25
62
  clinicBranchId: z.string().min(1),
@@ -36,6 +73,7 @@ export const updateProcedureSchema = z.object({
36
73
  price: z.number().min(0).optional(),
37
74
  currency: z.nativeEnum(Currency).optional(),
38
75
  pricingMeasure: z.nativeEnum(PricingMeasure).optional(),
76
+ productsMetadata: z.array(procedureProductDataSchema).min(1).optional(),
39
77
  duration: z.number().min(0).optional(),
40
78
  isActive: z.boolean().optional(),
41
79
  practitionerId: z.string().optional(),
@@ -48,18 +86,31 @@ export const updateProcedureSchema = z.object({
48
86
  });
49
87
 
50
88
  /**
51
- * Schema for validating a complete procedure object
89
+ * Schema for validating a complete procedure object (as stored in Firestore)
52
90
  */
53
- export const procedureSchema = createProcedureSchema.extend({
91
+ export const procedureSchema = z.object({
54
92
  id: z.string().min(1),
93
+ name: z.string().min(1).max(200),
55
94
  nameLower: z.string().min(1).max(200),
95
+ description: z.string().min(1).max(2000),
96
+ family: z.nativeEnum(ProcedureFamily),
56
97
  category: z.any(), // We'll validate the full category object separately
57
98
  subcategory: z.any(), // We'll validate the full subcategory object separately
58
99
  technology: z.any(), // We'll validate the full technology object separately
59
100
  product: z.any(), // We'll validate the full product object separately
101
+ productsMetadata: z.array(storedProcedureProductSchema).min(1), // Use stored format schema
102
+ price: z.number().min(0),
103
+ currency: z.nativeEnum(Currency),
104
+ pricingMeasure: z.nativeEnum(PricingMeasure),
105
+ duration: z.number().min(1).max(480),
106
+ practitionerId: z.string().min(1),
107
+ clinicBranchId: z.string().min(1),
108
+ photos: z.array(z.string()).optional(), // Stored as URL strings
60
109
  blockingConditions: z.array(z.any()), // We'll validate blocking conditions separately
61
110
  contraindications: z.array(z.any()), // We'll validate contraindications separately
111
+ contraindicationIds: z.array(z.string()), // Array of IDs for efficient querying
62
112
  treatmentBenefits: z.array(z.any()), // We'll validate treatment benefits separately
113
+ treatmentBenefitIds: z.array(z.string()), // Array of IDs for efficient querying
63
114
  preRequirements: z.array(z.any()), // We'll validate requirements separately
64
115
  postRequirements: z.array(z.any()), // We'll validate requirements separately
65
116
  certificationRequirement: z.any(), // We'll validate certification requirement separately
@@ -1,196 +0,0 @@
1
- import { BrandService } from "../brand.service";
2
- import { Brand } from "../../types/brand.types";
3
- import {
4
- collection,
5
- doc,
6
- getDoc,
7
- getDocs,
8
- addDoc,
9
- updateDoc,
10
- query,
11
- where,
12
- } from "firebase/firestore";
13
-
14
- // Mock Firebase
15
- jest.mock("firebase/firestore");
16
- jest.mock("../../../config/firebase", () => ({
17
- getFirebaseInstance: jest.fn().mockResolvedValue({
18
- db: {},
19
- auth: {},
20
- }),
21
- }));
22
-
23
- const COLLECTION = "backoffice_brands";
24
-
25
- describe("BrandService", () => {
26
- let service: BrandService;
27
-
28
- const mockBrand: Omit<Brand, "id" | "createdAt" | "updatedAt"> = {
29
- name: "Test Brand",
30
- manufacturer: "Test Manufacturer",
31
- website: "https://test.com",
32
- description: "Test Description",
33
- isActive: true,
34
- };
35
-
36
- beforeEach(() => {
37
- jest.clearAllMocks();
38
- service = new BrandService();
39
- });
40
-
41
- describe("create", () => {
42
- it("treba da kreira novi brend sa ispravnim podacima", async () => {
43
- const mockDocRef = { id: "test-id" };
44
- (collection as jest.Mock).mockReturnValue("brands-collection");
45
- (addDoc as jest.Mock).mockResolvedValue(mockDocRef);
46
-
47
- const result = await service.create(mockBrand);
48
-
49
- expect(collection).toHaveBeenCalledWith({}, COLLECTION);
50
- expect(addDoc).toHaveBeenCalledWith(
51
- "brands-collection",
52
- expect.objectContaining({
53
- ...mockBrand,
54
- createdAt: expect.any(Date),
55
- updatedAt: expect.any(Date),
56
- })
57
- );
58
- expect(result).toEqual({
59
- id: "test-id",
60
- ...mockBrand,
61
- createdAt: expect.any(Date),
62
- updatedAt: expect.any(Date),
63
- });
64
- });
65
- });
66
-
67
- describe("getAll", () => {
68
- it("treba da vrati sve aktivne brendove", async () => {
69
- const mockDocs = [
70
- {
71
- id: "test-id-1",
72
- data: () => ({
73
- ...mockBrand,
74
- createdAt: new Date(),
75
- updatedAt: new Date(),
76
- }),
77
- },
78
- {
79
- id: "test-id-2",
80
- data: () => ({
81
- ...mockBrand,
82
- name: "Test Brand 2",
83
- createdAt: new Date(),
84
- updatedAt: new Date(),
85
- }),
86
- },
87
- ];
88
-
89
- (collection as jest.Mock).mockReturnValue("brands-collection");
90
- (query as jest.Mock).mockReturnValue("filtered-query");
91
- (where as jest.Mock).mockReturnValue("where-clause");
92
- (getDocs as jest.Mock).mockResolvedValue({ docs: mockDocs });
93
-
94
- const result = await service.getAll();
95
-
96
- expect(collection).toHaveBeenCalledWith({}, COLLECTION);
97
- expect(query).toHaveBeenCalledWith("brands-collection", "where-clause");
98
- expect(where).toHaveBeenCalledWith("isActive", "==", true);
99
- expect(result).toHaveLength(2);
100
- expect(result[0]).toEqual({
101
- id: "test-id-1",
102
- ...mockBrand,
103
- createdAt: expect.any(Date),
104
- updatedAt: expect.any(Date),
105
- });
106
- });
107
- });
108
-
109
- describe("update", () => {
110
- it("treba da ažurira postojeći brend", async () => {
111
- const updateData = { name: "Updated Brand" };
112
-
113
- (collection as jest.Mock).mockReturnValue("brands-collection");
114
- (doc as jest.Mock).mockReturnValue("doc-ref");
115
- (getDoc as jest.Mock).mockResolvedValue({
116
- exists: () => true,
117
- id: "test-id",
118
- data: () => ({
119
- ...mockBrand,
120
- ...updateData,
121
- createdAt: new Date(),
122
- updatedAt: new Date(),
123
- }),
124
- });
125
- (updateDoc as jest.Mock).mockResolvedValue(undefined);
126
-
127
- const result = await service.update("test-id", updateData);
128
-
129
- expect(collection).toHaveBeenCalledWith({}, COLLECTION);
130
- expect(doc).toHaveBeenCalledWith("brands-collection", "test-id");
131
- expect(updateDoc).toHaveBeenCalledWith(
132
- "doc-ref",
133
- expect.objectContaining({
134
- ...updateData,
135
- updatedAt: expect.any(Date),
136
- })
137
- );
138
- });
139
- });
140
-
141
- describe("delete", () => {
142
- it("treba da izvrši soft delete brenda", async () => {
143
- (collection as jest.Mock).mockReturnValue("brands-collection");
144
- (doc as jest.Mock).mockReturnValue("doc-ref");
145
- (updateDoc as jest.Mock).mockResolvedValue(undefined);
146
-
147
- await service.delete("test-id");
148
-
149
- expect(collection).toHaveBeenCalledWith({}, COLLECTION);
150
- expect(doc).toHaveBeenCalledWith("brands-collection", "test-id");
151
- expect(updateDoc).toHaveBeenCalledWith("doc-ref", {
152
- isActive: false,
153
- updatedAt: expect.any(Date),
154
- });
155
- });
156
- });
157
-
158
- describe("getById", () => {
159
- it("treba da vrati brend po ID-u", async () => {
160
- (collection as jest.Mock).mockReturnValue("brands-collection");
161
- (doc as jest.Mock).mockReturnValue("doc-ref");
162
- (getDoc as jest.Mock).mockResolvedValue({
163
- exists: () => true,
164
- id: "test-id",
165
- data: () => ({
166
- ...mockBrand,
167
- createdAt: new Date(),
168
- updatedAt: new Date(),
169
- }),
170
- });
171
-
172
- const result = await service.getById("test-id");
173
-
174
- expect(collection).toHaveBeenCalledWith({}, COLLECTION);
175
- expect(doc).toHaveBeenCalledWith("brands-collection", "test-id");
176
- expect(result).toEqual({
177
- id: "test-id",
178
- ...mockBrand,
179
- createdAt: expect.any(Date),
180
- updatedAt: expect.any(Date),
181
- });
182
- });
183
-
184
- it("treba da vrati null ako brend ne postoji", async () => {
185
- (collection as jest.Mock).mockReturnValue("brands-collection");
186
- (doc as jest.Mock).mockReturnValue("doc-ref");
187
- (getDoc as jest.Mock).mockResolvedValue({
188
- exists: () => false,
189
- });
190
-
191
- const result = await service.getById("non-existent-id");
192
-
193
- expect(result).toBeNull();
194
- });
195
- });
196
- });