@blackcode_sa/metaestetics-api 1.10.0 → 1.11.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/admin/index.d.mts +337 -319
- package/dist/admin/index.d.ts +337 -319
- package/dist/admin/index.js +98 -79
- package/dist/admin/index.mjs +98 -79
- package/dist/backoffice/index.d.mts +284 -67
- package/dist/backoffice/index.d.ts +284 -67
- package/dist/backoffice/index.js +114 -6
- package/dist/backoffice/index.mjs +112 -6
- package/dist/index.d.mts +3145 -3065
- package/dist/index.d.ts +3145 -3065
- package/dist/index.js +460 -141
- package/dist/index.mjs +463 -143
- package/package.json +3 -1
- package/src/admin/booking/booking.admin.ts +2 -0
- package/src/admin/booking/booking.calculator.ts +121 -117
- package/src/admin/booking/booking.types.ts +3 -0
- package/src/backoffice/expo-safe/index.ts +2 -0
- package/src/backoffice/services/README.md +40 -0
- package/src/backoffice/services/constants.service.ts +268 -0
- package/src/backoffice/services/technology.service.ts +122 -10
- package/src/backoffice/types/admin-constants.types.ts +69 -0
- package/src/backoffice/types/index.ts +1 -0
- package/src/backoffice/types/product.types.ts +3 -1
- package/src/backoffice/types/technology.types.ts +4 -4
- package/src/backoffice/validations/schemas.ts +35 -9
- package/src/services/appointment/appointment.service.ts +0 -5
- package/src/services/appointment/utils/appointment.utils.ts +124 -113
- package/src/services/clinic/clinic.service.ts +163 -82
- package/src/services/procedure/procedure.service.ts +435 -234
- package/src/types/appointment/index.ts +9 -3
- package/src/types/clinic/index.ts +3 -6
- package/src/types/patient/medical-info.types.ts +3 -3
- package/src/types/procedure/index.ts +20 -17
- package/src/validations/appointment.schema.ts +2 -0
- package/src/validations/clinic.schema.ts +3 -6
- package/src/validations/patient/medical-info.schema.ts +7 -2
- package/src/validations/procedure.schema.ts +8 -10
- package/src/backoffice/services/__tests__/brand.service.test.ts +0 -196
- package/src/backoffice/services/__tests__/category.service.test.ts +0 -201
- package/src/backoffice/services/__tests__/product.service.test.ts +0 -358
- package/src/backoffice/services/__tests__/requirement.service.test.ts +0 -226
- package/src/backoffice/services/__tests__/subcategory.service.test.ts +0 -181
- package/src/backoffice/services/__tests__/technology.service.test.ts +0 -1097
|
@@ -10,10 +10,12 @@ import {
|
|
|
10
10
|
type PricingMeasure,
|
|
11
11
|
} from "../../backoffice/types/static/pricing.types";
|
|
12
12
|
import { BlockingCondition } from "../../backoffice/types/static/blocking-condition.types";
|
|
13
|
-
import { Contraindication } from "../../backoffice/types/static/contraindication.types";
|
|
14
13
|
import { Requirement } from "../../backoffice/types/requirement.types";
|
|
15
14
|
import { FilledDocumentStatus } from "../documentation-templates";
|
|
16
|
-
import type {
|
|
15
|
+
import type {
|
|
16
|
+
ContraindicationDynamic,
|
|
17
|
+
ProcedureFamily,
|
|
18
|
+
} from "../../backoffice";
|
|
17
19
|
import type { MediaResource } from "../../services/media/media.service";
|
|
18
20
|
|
|
19
21
|
/**
|
|
@@ -196,6 +198,8 @@ export interface Appointment {
|
|
|
196
198
|
clinicBranchId: string;
|
|
197
199
|
/** Aggregated clinic information (snapshot) */
|
|
198
200
|
clinicInfo: ClinicInfo;
|
|
201
|
+
/** IANA timezone of the clinic */
|
|
202
|
+
clinic_tz: string;
|
|
199
203
|
|
|
200
204
|
/** ID of the practitioner */
|
|
201
205
|
practitionerId: string;
|
|
@@ -243,7 +247,7 @@ export interface Appointment {
|
|
|
243
247
|
|
|
244
248
|
/** Procedure-related conditions and requirements */
|
|
245
249
|
blockingConditions: BlockingCondition[];
|
|
246
|
-
contraindications:
|
|
250
|
+
contraindications: ContraindicationDynamic[];
|
|
247
251
|
preProcedureRequirements: Requirement[];
|
|
248
252
|
postProcedureRequirements: Requirement[];
|
|
249
253
|
|
|
@@ -299,6 +303,7 @@ export interface CreateAppointmentData {
|
|
|
299
303
|
patientNotes?: string | null;
|
|
300
304
|
initialStatus: AppointmentStatus;
|
|
301
305
|
initialPaymentStatus?: PaymentStatus; // Defaults to UNPAID if not provided
|
|
306
|
+
clinic_tz: string;
|
|
302
307
|
}
|
|
303
308
|
|
|
304
309
|
/**
|
|
@@ -336,6 +341,7 @@ export interface UpdateAppointmentData {
|
|
|
336
341
|
cost?: number; // If cost is adjusted
|
|
337
342
|
clinicBranchId?: string; // If appointment is moved to another branch (complex scenario)
|
|
338
343
|
practitionerId?: string; // If practitioner is changed
|
|
344
|
+
clinic_tz?: string;
|
|
339
345
|
|
|
340
346
|
/** NEW: For updating linked forms - typically managed by dedicated methods */
|
|
341
347
|
linkedFormIds?: string[] | FieldValue;
|
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
import { Timestamp, FieldValue } from "firebase/firestore";
|
|
2
|
-
|
|
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";
|
|
@@ -44,6 +39,7 @@ export interface ClinicLocation {
|
|
|
44
39
|
latitude: number;
|
|
45
40
|
longitude: number;
|
|
46
41
|
geohash?: string | null;
|
|
42
|
+
tz?: string | null;
|
|
47
43
|
}
|
|
48
44
|
|
|
49
45
|
/**
|
|
@@ -228,6 +224,7 @@ export interface CreateClinicGroupData {
|
|
|
228
224
|
calendarSyncEnabled?: boolean;
|
|
229
225
|
autoConfirmAppointments?: boolean;
|
|
230
226
|
businessIdentificationNumber?: string | null;
|
|
227
|
+
tz?: string | null;
|
|
231
228
|
onboarding?: {
|
|
232
229
|
completed?: boolean;
|
|
233
230
|
step?: number;
|
|
@@ -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:
|
|
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:
|
|
116
|
+
condition: ContraindicationDynamic;
|
|
117
117
|
lastOccurrence: Timestamp;
|
|
118
118
|
frequency: "rare" | "occasional" | "frequent";
|
|
119
119
|
isActive: boolean;
|
|
@@ -1,25 +1,24 @@
|
|
|
1
|
-
import {
|
|
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";
|
|
24
23
|
|
|
25
24
|
/**
|
|
@@ -58,9 +57,13 @@ export interface Procedure {
|
|
|
58
57
|
/** Blocking conditions that prevent this procedure */
|
|
59
58
|
blockingConditions: BlockingCondition[];
|
|
60
59
|
/** Treatment benefits of this procedure */
|
|
61
|
-
treatmentBenefits:
|
|
60
|
+
treatmentBenefits: TreatmentBenefitDynamic[];
|
|
61
|
+
/** A list of just the string IDs of the treatment benefits, for efficient querying. */
|
|
62
|
+
treatmentBenefitIds: string[];
|
|
62
63
|
/** Contraindications of this procedure */
|
|
63
|
-
contraindications:
|
|
64
|
+
contraindications: ContraindicationDynamic[];
|
|
65
|
+
/** A list of just the string IDs of the contraindications, for efficient querying. */
|
|
66
|
+
contraindicationIds: string[];
|
|
64
67
|
/** Pre-procedure requirements */
|
|
65
68
|
preRequirements: Requirement[];
|
|
66
69
|
/** Post-procedure requirements */
|
|
@@ -248,6 +248,7 @@ export const createAppointmentSchema = z
|
|
|
248
248
|
initialPaymentStatus: paymentStatusSchema
|
|
249
249
|
.optional()
|
|
250
250
|
.default(PaymentStatus.UNPAID),
|
|
251
|
+
clinic_tz: z.string().min(1, "Timezone is required"),
|
|
251
252
|
})
|
|
252
253
|
.refine((data) => data.appointmentEndTime > data.appointmentStartTime, {
|
|
253
254
|
message: "Appointment end time must be after start time",
|
|
@@ -325,6 +326,7 @@ export const updateAppointmentSchema = z
|
|
|
325
326
|
cost: z.number().min(0).optional(),
|
|
326
327
|
clinicBranchId: z.string().min(MIN_STRING_LENGTH).optional(),
|
|
327
328
|
practitionerId: z.string().min(MIN_STRING_LENGTH).optional(),
|
|
329
|
+
clinic_tz: z.string().min(MIN_STRING_LENGTH).optional(),
|
|
328
330
|
linkedForms: z
|
|
329
331
|
.union([z.array(linkedFormInfoSchema).max(MAX_ARRAY_LENGTH), z.any()])
|
|
330
332
|
.optional(),
|
|
@@ -7,12 +7,7 @@ import {
|
|
|
7
7
|
PracticeType,
|
|
8
8
|
Language,
|
|
9
9
|
} from "../types/clinic";
|
|
10
|
-
|
|
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,
|
|
@@ -42,6 +37,7 @@ export const clinicLocationSchema = z.object({
|
|
|
42
37
|
latitude: z.number().min(-90).max(90),
|
|
43
38
|
longitude: z.number().min(-180).max(180),
|
|
44
39
|
geohash: z.string().nullable().optional(),
|
|
40
|
+
tz: z.string().nullable().optional(),
|
|
45
41
|
});
|
|
46
42
|
|
|
47
43
|
/**
|
|
@@ -261,6 +257,7 @@ export const createClinicGroupSchema = z.object({
|
|
|
261
257
|
calendarSyncEnabled: z.boolean().optional(),
|
|
262
258
|
autoConfirmAppointments: z.boolean().optional(),
|
|
263
259
|
businessIdentificationNumber: z.string().optional().nullable(),
|
|
260
|
+
tz: z.string().nullable().optional(),
|
|
264
261
|
onboarding: z
|
|
265
262
|
.object({
|
|
266
263
|
completed: z.boolean().optional().default(false),
|
|
@@ -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:
|
|
59
|
+
condition: contraindicationDynamicSchema,
|
|
55
60
|
lastOccurrence: timestampSchema,
|
|
56
61
|
frequency: z.enum(["rare", "occasional", "frequent"]),
|
|
57
62
|
notes: z.string().optional().nullable(),
|
|
@@ -1,18 +1,16 @@
|
|
|
1
|
-
import { z } from
|
|
2
|
-
import { ProcedureFamily } from
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
} from
|
|
7
|
-
import { clinicInfoSchema, doctorInfoSchema } from "./shared.schema";
|
|
8
|
-
import { procedureReviewInfoSchema } from "./reviews.schema";
|
|
9
|
-
import { mediaResourceSchema } from "./media.schema";
|
|
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';
|
|
10
7
|
/**
|
|
11
8
|
* Schema for creating a new procedure
|
|
12
9
|
*/
|
|
13
10
|
export const createProcedureSchema = z.object({
|
|
14
11
|
name: z.string().min(1).max(200),
|
|
15
|
-
|
|
12
|
+
// Optional: service will derive from name if not provided by client
|
|
13
|
+
nameLower: z.string().min(1).max(200).optional(),
|
|
16
14
|
description: z.string().min(1).max(2000),
|
|
17
15
|
family: z.nativeEnum(ProcedureFamily),
|
|
18
16
|
categoryId: z.string().min(1),
|
|
@@ -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
|
-
});
|
|
@@ -1,201 +0,0 @@
|
|
|
1
|
-
import { CategoryService } from "../category.service";
|
|
2
|
-
import { Category } from "../../types/category.types";
|
|
3
|
-
import { ProcedureFamily } from "../../types/static/procedure-family.types";
|
|
4
|
-
import {
|
|
5
|
-
collection,
|
|
6
|
-
doc,
|
|
7
|
-
getDoc,
|
|
8
|
-
getDocs,
|
|
9
|
-
addDoc,
|
|
10
|
-
updateDoc,
|
|
11
|
-
query,
|
|
12
|
-
where,
|
|
13
|
-
} from "firebase/firestore";
|
|
14
|
-
|
|
15
|
-
// Mock Firebase
|
|
16
|
-
jest.mock("firebase/firestore");
|
|
17
|
-
jest.mock("../../../config/firebase", () => ({
|
|
18
|
-
getFirebaseInstance: jest.fn().mockResolvedValue({
|
|
19
|
-
db: {},
|
|
20
|
-
auth: {},
|
|
21
|
-
}),
|
|
22
|
-
}));
|
|
23
|
-
|
|
24
|
-
const COLLECTION = "backoffice_categories";
|
|
25
|
-
|
|
26
|
-
describe("CategoryService", () => {
|
|
27
|
-
let service: CategoryService;
|
|
28
|
-
|
|
29
|
-
const mockCategory: Omit<Category, "id" | "createdAt" | "updatedAt"> = {
|
|
30
|
-
name: "Test Category",
|
|
31
|
-
description: "Test Description",
|
|
32
|
-
family: ProcedureFamily.AESTHETICS,
|
|
33
|
-
isActive: true,
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
beforeEach(() => {
|
|
37
|
-
jest.clearAllMocks();
|
|
38
|
-
service = new CategoryService();
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
describe("create", () => {
|
|
42
|
-
it("treba da kreira novu kategoriju", async () => {
|
|
43
|
-
const mockDocRef = { id: "test-id" };
|
|
44
|
-
(collection as jest.Mock).mockReturnValue("categories-collection");
|
|
45
|
-
(addDoc as jest.Mock).mockResolvedValue(mockDocRef);
|
|
46
|
-
|
|
47
|
-
const result = await service.create(mockCategory);
|
|
48
|
-
|
|
49
|
-
expect(collection).toHaveBeenCalledWith({}, COLLECTION);
|
|
50
|
-
expect(addDoc).toHaveBeenCalledWith(
|
|
51
|
-
"categories-collection",
|
|
52
|
-
expect.objectContaining({
|
|
53
|
-
...mockCategory,
|
|
54
|
-
createdAt: expect.any(Date),
|
|
55
|
-
updatedAt: expect.any(Date),
|
|
56
|
-
})
|
|
57
|
-
);
|
|
58
|
-
expect(result).toEqual({
|
|
59
|
-
id: "test-id",
|
|
60
|
-
...mockCategory,
|
|
61
|
-
createdAt: expect.any(Date),
|
|
62
|
-
updatedAt: expect.any(Date),
|
|
63
|
-
});
|
|
64
|
-
});
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
describe("getAll", () => {
|
|
68
|
-
it("treba da vrati sve aktivne kategorije", async () => {
|
|
69
|
-
const mockDocs = [
|
|
70
|
-
{
|
|
71
|
-
id: "cat-1",
|
|
72
|
-
data: () => ({
|
|
73
|
-
...mockCategory,
|
|
74
|
-
createdAt: new Date(),
|
|
75
|
-
updatedAt: new Date(),
|
|
76
|
-
}),
|
|
77
|
-
},
|
|
78
|
-
{
|
|
79
|
-
id: "cat-2",
|
|
80
|
-
data: () => ({
|
|
81
|
-
...mockCategory,
|
|
82
|
-
name: "Test Category 2",
|
|
83
|
-
createdAt: new Date(),
|
|
84
|
-
updatedAt: new Date(),
|
|
85
|
-
}),
|
|
86
|
-
},
|
|
87
|
-
];
|
|
88
|
-
|
|
89
|
-
(collection as jest.Mock).mockReturnValue("categories-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 results = await service.getAll();
|
|
95
|
-
|
|
96
|
-
expect(collection).toHaveBeenCalledWith({}, COLLECTION);
|
|
97
|
-
expect(query).toHaveBeenCalledWith(
|
|
98
|
-
"categories-collection",
|
|
99
|
-
"where-clause"
|
|
100
|
-
);
|
|
101
|
-
expect(where).toHaveBeenCalledWith("isActive", "==", true);
|
|
102
|
-
expect(results).toHaveLength(2);
|
|
103
|
-
expect(results[0]).toEqual({
|
|
104
|
-
id: "cat-1",
|
|
105
|
-
...mockCategory,
|
|
106
|
-
createdAt: expect.any(Date),
|
|
107
|
-
updatedAt: expect.any(Date),
|
|
108
|
-
});
|
|
109
|
-
});
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
describe("getAllByFamily", () => {
|
|
113
|
-
it("treba da vrati sve kategorije za određenu familiju", async () => {
|
|
114
|
-
const mockDocs = [
|
|
115
|
-
{
|
|
116
|
-
id: "cat-1",
|
|
117
|
-
data: () => ({
|
|
118
|
-
...mockCategory,
|
|
119
|
-
family: ProcedureFamily.AESTHETICS,
|
|
120
|
-
createdAt: new Date(),
|
|
121
|
-
updatedAt: new Date(),
|
|
122
|
-
}),
|
|
123
|
-
},
|
|
124
|
-
];
|
|
125
|
-
|
|
126
|
-
(collection as jest.Mock).mockReturnValue("categories-collection");
|
|
127
|
-
(query as jest.Mock).mockReturnValue("filtered-query");
|
|
128
|
-
(where as jest.Mock).mockReturnValue("where-clause");
|
|
129
|
-
(getDocs as jest.Mock).mockResolvedValue({ docs: mockDocs });
|
|
130
|
-
|
|
131
|
-
const results = await service.getAllByFamily(ProcedureFamily.AESTHETICS);
|
|
132
|
-
|
|
133
|
-
expect(query).toHaveBeenCalledWith(
|
|
134
|
-
"categories-collection",
|
|
135
|
-
"where-clause",
|
|
136
|
-
"where-clause"
|
|
137
|
-
);
|
|
138
|
-
expect(where).toHaveBeenCalledWith(
|
|
139
|
-
"family",
|
|
140
|
-
"==",
|
|
141
|
-
ProcedureFamily.AESTHETICS
|
|
142
|
-
);
|
|
143
|
-
expect(where).toHaveBeenCalledWith("isActive", "==", true);
|
|
144
|
-
expect(results).toHaveLength(1);
|
|
145
|
-
expect(results[0].family).toBe(ProcedureFamily.AESTHETICS);
|
|
146
|
-
});
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
describe("update", () => {
|
|
150
|
-
it("treba da ažurira postojeću kategoriju", async () => {
|
|
151
|
-
const updateData = { name: "Updated Category" };
|
|
152
|
-
|
|
153
|
-
(collection as jest.Mock).mockReturnValue("categories-collection");
|
|
154
|
-
(doc as jest.Mock).mockReturnValue("doc-ref");
|
|
155
|
-
(updateDoc as jest.Mock).mockResolvedValue(undefined);
|
|
156
|
-
(getDoc as jest.Mock).mockResolvedValue({
|
|
157
|
-
exists: () => true,
|
|
158
|
-
id: "test-id",
|
|
159
|
-
data: () => ({
|
|
160
|
-
...mockCategory,
|
|
161
|
-
...updateData,
|
|
162
|
-
createdAt: new Date(),
|
|
163
|
-
updatedAt: new Date(),
|
|
164
|
-
}),
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
const result = await service.update("test-id", updateData);
|
|
168
|
-
|
|
169
|
-
expect(collection).toHaveBeenCalledWith({}, COLLECTION);
|
|
170
|
-
expect(doc).toHaveBeenCalledWith("categories-collection", "test-id");
|
|
171
|
-
expect(updateDoc).toHaveBeenCalledWith("doc-ref", {
|
|
172
|
-
...updateData,
|
|
173
|
-
updatedAt: expect.any(Date),
|
|
174
|
-
});
|
|
175
|
-
expect(result).toEqual({
|
|
176
|
-
id: "test-id",
|
|
177
|
-
...mockCategory,
|
|
178
|
-
...updateData,
|
|
179
|
-
createdAt: expect.any(Date),
|
|
180
|
-
updatedAt: expect.any(Date),
|
|
181
|
-
});
|
|
182
|
-
});
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
describe("delete", () => {
|
|
186
|
-
it("treba da izvrši soft delete kategorije", async () => {
|
|
187
|
-
(collection as jest.Mock).mockReturnValue("categories-collection");
|
|
188
|
-
(doc as jest.Mock).mockReturnValue("doc-ref");
|
|
189
|
-
(updateDoc as jest.Mock).mockResolvedValue(undefined);
|
|
190
|
-
|
|
191
|
-
await service.delete("test-id");
|
|
192
|
-
|
|
193
|
-
expect(collection).toHaveBeenCalledWith({}, COLLECTION);
|
|
194
|
-
expect(doc).toHaveBeenCalledWith("categories-collection", "test-id");
|
|
195
|
-
expect(updateDoc).toHaveBeenCalledWith("doc-ref", {
|
|
196
|
-
isActive: false,
|
|
197
|
-
updatedAt: expect.any(Date),
|
|
198
|
-
});
|
|
199
|
-
});
|
|
200
|
-
});
|
|
201
|
-
});
|