@blackcode_sa/metaestetics-api 1.12.1 → 1.12.3
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/backoffice/index.d.mts +64 -14
- package/dist/backoffice/index.d.ts +64 -14
- package/dist/backoffice/index.js +72 -53
- package/dist/backoffice/index.mjs +72 -53
- package/dist/index.d.mts +131 -19
- package/dist/index.d.ts +131 -19
- package/dist/index.js +141 -261
- package/dist/index.mjs +141 -261
- package/package.json +1 -1
- package/src/backoffice/services/FIXES_README.md +102 -0
- package/src/backoffice/services/category.service.ts +46 -27
- package/src/backoffice/services/product.service.ts +52 -74
- package/src/backoffice/services/technology.service.ts +99 -116
- package/src/backoffice/types/category.types.ts +28 -2
- package/src/backoffice/types/product.types.ts +10 -9
- package/src/backoffice/types/technology.types.ts +31 -59
- package/src/services/procedure/procedure.service.ts +275 -472
package/package.json
CHANGED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# Service Method Fixes for Procedure Creation
|
|
2
|
+
|
|
3
|
+
## Problem
|
|
4
|
+
The procedure creation components (`CreateProcedureBulk.tsx`, `CreateProcedure.tsx`, `UpdateProcedure.tsx`) were not loading categories and subcategories because the service methods were returning paginated objects instead of arrays.
|
|
5
|
+
|
|
6
|
+
## Root Cause
|
|
7
|
+
The procedure service was calling paginated methods that return objects like `{ categories, lastVisible }` instead of filter methods that return arrays like `Category[]`.
|
|
8
|
+
|
|
9
|
+
## Changes Made
|
|
10
|
+
|
|
11
|
+
### 1. Added Missing Methods
|
|
12
|
+
|
|
13
|
+
#### CategoryService
|
|
14
|
+
- **Added**: `getAllForFilterByFamily(family: ProcedureFamily): Promise<Category[]>`
|
|
15
|
+
- **Added**: Interface `ICategoryService` in `category.types.ts`
|
|
16
|
+
- **Modified**: `CategoryService` now implements `ICategoryService`
|
|
17
|
+
|
|
18
|
+
#### TechnologyService
|
|
19
|
+
- **Added**: `getAllForFilterBySubcategory(subcategoryId: string): Promise<Technology[]>`
|
|
20
|
+
- **Modified**: Added method to `ITechnologyService` interface
|
|
21
|
+
- **Modified**: `TechnologyService` implements `ITechnologyService`
|
|
22
|
+
|
|
23
|
+
#### ProductService
|
|
24
|
+
- **Added**: `getAllByTechnology(technologyId: string): Promise<Product[]>`
|
|
25
|
+
- **Modified**: Added method to `IProductService` interface
|
|
26
|
+
|
|
27
|
+
### 2. Updated Procedure Service Calls
|
|
28
|
+
|
|
29
|
+
Changed from paginated methods to filter methods:
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
// Before (returned { categories, lastVisible })
|
|
33
|
+
await getCategoryService().getAllByFamily(family)
|
|
34
|
+
|
|
35
|
+
// After (returns Category[])
|
|
36
|
+
await getCategoryService().getAllForFilterByFamily(family)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
// Before (returned { subcategories, lastVisible })
|
|
41
|
+
await getSubcategoryService().getAllByCategoryId(id)
|
|
42
|
+
|
|
43
|
+
// After (returns Subcategory[])
|
|
44
|
+
await getSubcategoryService().getAllForFilterByCategoryId(id)
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
// Before (returned { technologies, lastVisible })
|
|
49
|
+
await getTechnologyService().getAllBySubcategoryId(id)
|
|
50
|
+
|
|
51
|
+
// After (returns Technology[])
|
|
52
|
+
await getTechnologyService().getAllForFilterBySubcategory(id)
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
// Before (method didn't exist)
|
|
57
|
+
await getProductService().getAllByTechnology(technologyId)
|
|
58
|
+
|
|
59
|
+
// After (returns Product[])
|
|
60
|
+
await getProductService().getAllByTechnology(technologyId)
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### 3. Service Method Patterns
|
|
64
|
+
|
|
65
|
+
#### Paginated Methods (for admin UI with pagination)
|
|
66
|
+
- Return: `{ items: T[], lastVisible: DocumentData }`
|
|
67
|
+
- Example: `getAllByFamily()`, `getAllByCategoryId()`, `getAllBySubcategoryId()`
|
|
68
|
+
|
|
69
|
+
#### Filter Methods (for dropdowns/filters)
|
|
70
|
+
- Return: `T[]`
|
|
71
|
+
- Example: `getAllForFilter()`, `getAllForFilterByFamily()`, `getAllForFilterByCategoryId()`
|
|
72
|
+
|
|
73
|
+
## Files Modified
|
|
74
|
+
|
|
75
|
+
### API (Backend)
|
|
76
|
+
- `Api/src/backoffice/services/category.service.ts`
|
|
77
|
+
- `Api/src/backoffice/services/technology.service.ts`
|
|
78
|
+
- `Api/src/backoffice/services/product.service.ts`
|
|
79
|
+
- `Api/src/backoffice/types/category.types.ts`
|
|
80
|
+
- `Api/src/backoffice/types/technology.types.ts`
|
|
81
|
+
- `Api/src/backoffice/types/product.types.ts`
|
|
82
|
+
|
|
83
|
+
### Client App
|
|
84
|
+
- `ClinicApp/src/store/procedure/procedure.service.ts`
|
|
85
|
+
|
|
86
|
+
## Expected Result
|
|
87
|
+
Categories, subcategories, technologies, and products should now load correctly in:
|
|
88
|
+
- Create Procedure Bulk form
|
|
89
|
+
- Create Procedure form
|
|
90
|
+
- Update Procedure form
|
|
91
|
+
|
|
92
|
+
The dropdowns should populate with the correct options when selecting families, categories, subcategories, and technologies.
|
|
93
|
+
|
|
94
|
+
## Testing
|
|
95
|
+
1. Navigate to Create Procedure Bulk
|
|
96
|
+
2. Select a family (Aesthetics/Surgery) - categories should load
|
|
97
|
+
3. Select a category - subcategories should load
|
|
98
|
+
4. Select a subcategory - technologies should load
|
|
99
|
+
5. Select a technology - products should load and qualified practitioners should appear
|
|
100
|
+
|
|
101
|
+
## Note
|
|
102
|
+
If TypeScript errors persist about missing methods, the API package may need to be rebuilt (`npm run build` in the Api directory) for the type definitions to be updated in the consuming client application.
|
|
@@ -12,10 +12,10 @@ import {
|
|
|
12
12
|
startAfter,
|
|
13
13
|
updateDoc,
|
|
14
14
|
where,
|
|
15
|
-
} from
|
|
16
|
-
import { Category, CATEGORIES_COLLECTION } from
|
|
17
|
-
import { BaseService } from
|
|
18
|
-
import { ProcedureFamily } from
|
|
15
|
+
} from 'firebase/firestore';
|
|
16
|
+
import { Category, CATEGORIES_COLLECTION, ICategoryService } from '../types/category.types';
|
|
17
|
+
import { BaseService } from '../../services/base.service';
|
|
18
|
+
import { ProcedureFamily } from '../types/static/procedure-family.types';
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
21
|
* Servis za upravljanje kategorijama procedura.
|
|
@@ -30,7 +30,7 @@ import { ProcedureFamily } from "../types/static/procedure-family.types";
|
|
|
30
30
|
* family: ProcedureFamily.AESTHETICS
|
|
31
31
|
* });
|
|
32
32
|
*/
|
|
33
|
-
export class CategoryService extends BaseService {
|
|
33
|
+
export class CategoryService extends BaseService implements ICategoryService {
|
|
34
34
|
/**
|
|
35
35
|
* Referenca na Firestore kolekciju kategorija
|
|
36
36
|
*/
|
|
@@ -43,9 +43,9 @@ export class CategoryService extends BaseService {
|
|
|
43
43
|
* @param category - Podaci za novu kategoriju
|
|
44
44
|
* @returns Kreirana kategorija sa generisanim ID-em
|
|
45
45
|
*/
|
|
46
|
-
async create(category: Omit<Category,
|
|
46
|
+
async create(category: Omit<Category, 'id' | 'createdAt' | 'updatedAt'>) {
|
|
47
47
|
const now = new Date();
|
|
48
|
-
const newCategory: Omit<Category,
|
|
48
|
+
const newCategory: Omit<Category, 'id'> = {
|
|
49
49
|
...category,
|
|
50
50
|
createdAt: now,
|
|
51
51
|
updatedAt: now,
|
|
@@ -68,8 +68,8 @@ export class CategoryService extends BaseService {
|
|
|
68
68
|
for (const family of families) {
|
|
69
69
|
const q = query(
|
|
70
70
|
this.categoriesRef,
|
|
71
|
-
where(
|
|
72
|
-
where(
|
|
71
|
+
where('family', '==', family),
|
|
72
|
+
where('isActive', '==', active),
|
|
73
73
|
);
|
|
74
74
|
const snapshot = await getCountFromServer(q);
|
|
75
75
|
counts[family] = snapshot.data().count;
|
|
@@ -82,14 +82,36 @@ export class CategoryService extends BaseService {
|
|
|
82
82
|
* @returns Lista svih aktivnih kategorija
|
|
83
83
|
*/
|
|
84
84
|
async getAllForFilter() {
|
|
85
|
-
const q = query(this.categoriesRef, where(
|
|
85
|
+
const q = query(this.categoriesRef, where('isActive', '==', true));
|
|
86
86
|
const snapshot = await getDocs(q);
|
|
87
87
|
return snapshot.docs.map(
|
|
88
|
-
|
|
88
|
+
doc =>
|
|
89
89
|
({
|
|
90
90
|
id: doc.id,
|
|
91
91
|
...doc.data(),
|
|
92
|
-
} as Category)
|
|
92
|
+
} as Category),
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Vraća sve kategorije za određenu familiju za potrebe filtera (bez paginacije)
|
|
98
|
+
* @param family - Familija procedura (aesthetics/surgery)
|
|
99
|
+
* @returns Lista aktivnih kategorija koje pripadaju traženoj familiji
|
|
100
|
+
*/
|
|
101
|
+
async getAllForFilterByFamily(family: ProcedureFamily) {
|
|
102
|
+
const q = query(
|
|
103
|
+
this.categoriesRef,
|
|
104
|
+
where('family', '==', family),
|
|
105
|
+
where('isActive', '==', true),
|
|
106
|
+
orderBy('name'),
|
|
107
|
+
);
|
|
108
|
+
const snapshot = await getDocs(q);
|
|
109
|
+
return snapshot.docs.map(
|
|
110
|
+
doc =>
|
|
111
|
+
({
|
|
112
|
+
id: doc.id,
|
|
113
|
+
...doc.data(),
|
|
114
|
+
} as Category),
|
|
93
115
|
);
|
|
94
116
|
}
|
|
95
117
|
|
|
@@ -103,12 +125,12 @@ export class CategoryService extends BaseService {
|
|
|
103
125
|
active?: boolean;
|
|
104
126
|
limit?: number;
|
|
105
127
|
lastVisible?: DocumentData;
|
|
106
|
-
} = {}
|
|
128
|
+
} = {},
|
|
107
129
|
) {
|
|
108
130
|
const { active = true, limit: queryLimit = 10, lastVisible } = options;
|
|
109
131
|
const constraints = [
|
|
110
|
-
where(
|
|
111
|
-
orderBy(
|
|
132
|
+
where('isActive', '==', active),
|
|
133
|
+
orderBy('name'),
|
|
112
134
|
queryLimit ? limit(queryLimit) : undefined,
|
|
113
135
|
lastVisible ? startAfter(lastVisible) : undefined,
|
|
114
136
|
].filter((c): c is NonNullable<typeof c> => !!c);
|
|
@@ -116,11 +138,11 @@ export class CategoryService extends BaseService {
|
|
|
116
138
|
const q = query(this.categoriesRef, ...constraints);
|
|
117
139
|
const snapshot = await getDocs(q);
|
|
118
140
|
const categories = snapshot.docs.map(
|
|
119
|
-
|
|
141
|
+
doc =>
|
|
120
142
|
({
|
|
121
143
|
id: doc.id,
|
|
122
144
|
...doc.data(),
|
|
123
|
-
} as Category)
|
|
145
|
+
} as Category),
|
|
124
146
|
);
|
|
125
147
|
const newLastVisible = snapshot.docs[snapshot.docs.length - 1];
|
|
126
148
|
return { categories, lastVisible: newLastVisible };
|
|
@@ -138,13 +160,13 @@ export class CategoryService extends BaseService {
|
|
|
138
160
|
active?: boolean;
|
|
139
161
|
limit?: number;
|
|
140
162
|
lastVisible?: DocumentData;
|
|
141
|
-
} = {}
|
|
163
|
+
} = {},
|
|
142
164
|
) {
|
|
143
165
|
const { active = true, limit: queryLimit = 10, lastVisible } = options;
|
|
144
166
|
const constraints = [
|
|
145
|
-
where(
|
|
146
|
-
where(
|
|
147
|
-
orderBy(
|
|
167
|
+
where('family', '==', family),
|
|
168
|
+
where('isActive', '==', active),
|
|
169
|
+
orderBy('name'),
|
|
148
170
|
queryLimit ? limit(queryLimit) : undefined,
|
|
149
171
|
lastVisible ? startAfter(lastVisible) : undefined,
|
|
150
172
|
].filter((c): c is NonNullable<typeof c> => !!c);
|
|
@@ -152,11 +174,11 @@ export class CategoryService extends BaseService {
|
|
|
152
174
|
const q = query(this.categoriesRef, ...constraints);
|
|
153
175
|
const snapshot = await getDocs(q);
|
|
154
176
|
const categories = snapshot.docs.map(
|
|
155
|
-
|
|
177
|
+
doc =>
|
|
156
178
|
({
|
|
157
179
|
id: doc.id,
|
|
158
180
|
...doc.data(),
|
|
159
|
-
} as Category)
|
|
181
|
+
} as Category),
|
|
160
182
|
);
|
|
161
183
|
const newLastVisible = snapshot.docs[snapshot.docs.length - 1];
|
|
162
184
|
return { categories, lastVisible: newLastVisible };
|
|
@@ -168,10 +190,7 @@ export class CategoryService extends BaseService {
|
|
|
168
190
|
* @param category - Novi podaci za kategoriju
|
|
169
191
|
* @returns Ažurirana kategorija
|
|
170
192
|
*/
|
|
171
|
-
async update(
|
|
172
|
-
id: string,
|
|
173
|
-
category: Partial<Omit<Category, "id" | "createdAt">>
|
|
174
|
-
) {
|
|
193
|
+
async update(id: string, category: Partial<Omit<Category, 'id' | 'createdAt'>>) {
|
|
175
194
|
const updateData = {
|
|
176
195
|
...category,
|
|
177
196
|
updatedAt: new Date(),
|
|
@@ -13,14 +13,10 @@ import {
|
|
|
13
13
|
startAfter,
|
|
14
14
|
getCountFromServer,
|
|
15
15
|
QueryConstraint,
|
|
16
|
-
} from
|
|
17
|
-
import {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
IProductService,
|
|
21
|
-
} from "../types/product.types";
|
|
22
|
-
import { BaseService } from "../../services/base.service";
|
|
23
|
-
import { TECHNOLOGIES_COLLECTION } from "../types/technology.types";
|
|
16
|
+
} from 'firebase/firestore';
|
|
17
|
+
import { Product, PRODUCTS_COLLECTION, IProductService } from '../types/product.types';
|
|
18
|
+
import { BaseService } from '../../services/base.service';
|
|
19
|
+
import { TECHNOLOGIES_COLLECTION } from '../types/technology.types';
|
|
24
20
|
|
|
25
21
|
export class ProductService extends BaseService implements IProductService {
|
|
26
22
|
/**
|
|
@@ -29,12 +25,7 @@ export class ProductService extends BaseService implements IProductService {
|
|
|
29
25
|
* @returns Firestore collection reference
|
|
30
26
|
*/
|
|
31
27
|
private getProductsRef(technologyId: string) {
|
|
32
|
-
return collection(
|
|
33
|
-
this.db,
|
|
34
|
-
TECHNOLOGIES_COLLECTION,
|
|
35
|
-
technologyId,
|
|
36
|
-
PRODUCTS_COLLECTION
|
|
37
|
-
);
|
|
28
|
+
return collection(this.db, TECHNOLOGIES_COLLECTION, technologyId, PRODUCTS_COLLECTION);
|
|
38
29
|
}
|
|
39
30
|
|
|
40
31
|
/**
|
|
@@ -43,14 +34,11 @@ export class ProductService extends BaseService implements IProductService {
|
|
|
43
34
|
async create(
|
|
44
35
|
technologyId: string,
|
|
45
36
|
brandId: string,
|
|
46
|
-
product: Omit<
|
|
47
|
-
Product,
|
|
48
|
-
"id" | "createdAt" | "updatedAt" | "brandId" | "technologyId"
|
|
49
|
-
>
|
|
37
|
+
product: Omit<Product, 'id' | 'createdAt' | 'updatedAt' | 'brandId' | 'technologyId'>,
|
|
50
38
|
): Promise<Product> {
|
|
51
39
|
const now = new Date();
|
|
52
40
|
// categoryId and subcategoryId are now expected to be part of the product object
|
|
53
|
-
const newProduct: Omit<Product,
|
|
41
|
+
const newProduct: Omit<Product, 'id'> = {
|
|
54
42
|
...product,
|
|
55
43
|
brandId,
|
|
56
44
|
technologyId,
|
|
@@ -59,10 +47,7 @@ export class ProductService extends BaseService implements IProductService {
|
|
|
59
47
|
isActive: true,
|
|
60
48
|
};
|
|
61
49
|
|
|
62
|
-
const productRef = await addDoc(
|
|
63
|
-
this.getProductsRef(technologyId),
|
|
64
|
-
newProduct
|
|
65
|
-
);
|
|
50
|
+
const productRef = await addDoc(this.getProductsRef(technologyId), newProduct);
|
|
66
51
|
|
|
67
52
|
return { id: productRef.id, ...newProduct };
|
|
68
53
|
}
|
|
@@ -78,27 +63,18 @@ export class ProductService extends BaseService implements IProductService {
|
|
|
78
63
|
subcategoryId?: string;
|
|
79
64
|
technologyId?: string;
|
|
80
65
|
}): Promise<{ products: Product[]; lastVisible: any }> {
|
|
81
|
-
const {
|
|
82
|
-
rowsPerPage,
|
|
83
|
-
lastVisible,
|
|
84
|
-
categoryId,
|
|
85
|
-
subcategoryId,
|
|
86
|
-
technologyId,
|
|
87
|
-
} = options;
|
|
66
|
+
const { rowsPerPage, lastVisible, categoryId, subcategoryId, technologyId } = options;
|
|
88
67
|
|
|
89
|
-
const constraints: QueryConstraint[] = [
|
|
90
|
-
where("isActive", "==", true),
|
|
91
|
-
orderBy("name"),
|
|
92
|
-
];
|
|
68
|
+
const constraints: QueryConstraint[] = [where('isActive', '==', true), orderBy('name')];
|
|
93
69
|
|
|
94
70
|
if (categoryId) {
|
|
95
|
-
constraints.push(where(
|
|
71
|
+
constraints.push(where('categoryId', '==', categoryId));
|
|
96
72
|
}
|
|
97
73
|
if (subcategoryId) {
|
|
98
|
-
constraints.push(where(
|
|
74
|
+
constraints.push(where('subcategoryId', '==', subcategoryId));
|
|
99
75
|
}
|
|
100
76
|
if (technologyId) {
|
|
101
|
-
constraints.push(where(
|
|
77
|
+
constraints.push(where('technologyId', '==', technologyId));
|
|
102
78
|
}
|
|
103
79
|
|
|
104
80
|
if (lastVisible) {
|
|
@@ -106,18 +82,15 @@ export class ProductService extends BaseService implements IProductService {
|
|
|
106
82
|
}
|
|
107
83
|
constraints.push(limit(rowsPerPage));
|
|
108
84
|
|
|
109
|
-
const q = query(
|
|
110
|
-
collectionGroup(this.db, PRODUCTS_COLLECTION),
|
|
111
|
-
...constraints
|
|
112
|
-
);
|
|
85
|
+
const q = query(collectionGroup(this.db, PRODUCTS_COLLECTION), ...constraints);
|
|
113
86
|
const snapshot = await getDocs(q);
|
|
114
87
|
|
|
115
88
|
const products = snapshot.docs.map(
|
|
116
|
-
|
|
89
|
+
doc =>
|
|
117
90
|
({
|
|
118
91
|
id: doc.id,
|
|
119
92
|
...doc.data(),
|
|
120
|
-
} as Product)
|
|
93
|
+
} as Product),
|
|
121
94
|
);
|
|
122
95
|
const newLastVisible = snapshot.docs[snapshot.docs.length - 1];
|
|
123
96
|
|
|
@@ -133,22 +106,19 @@ export class ProductService extends BaseService implements IProductService {
|
|
|
133
106
|
technologyId?: string;
|
|
134
107
|
}): Promise<number> {
|
|
135
108
|
const { categoryId, subcategoryId, technologyId } = options;
|
|
136
|
-
const constraints: QueryConstraint[] = [where(
|
|
109
|
+
const constraints: QueryConstraint[] = [where('isActive', '==', true)];
|
|
137
110
|
|
|
138
111
|
if (categoryId) {
|
|
139
|
-
constraints.push(where(
|
|
112
|
+
constraints.push(where('categoryId', '==', categoryId));
|
|
140
113
|
}
|
|
141
114
|
if (subcategoryId) {
|
|
142
|
-
constraints.push(where(
|
|
115
|
+
constraints.push(where('subcategoryId', '==', subcategoryId));
|
|
143
116
|
}
|
|
144
117
|
if (technologyId) {
|
|
145
|
-
constraints.push(where(
|
|
118
|
+
constraints.push(where('technologyId', '==', technologyId));
|
|
146
119
|
}
|
|
147
120
|
|
|
148
|
-
const q = query(
|
|
149
|
-
collectionGroup(this.db, PRODUCTS_COLLECTION),
|
|
150
|
-
...constraints
|
|
151
|
-
);
|
|
121
|
+
const q = query(collectionGroup(this.db, PRODUCTS_COLLECTION), ...constraints);
|
|
152
122
|
const snapshot = await getCountFromServer(q);
|
|
153
123
|
return snapshot.data().count;
|
|
154
124
|
}
|
|
@@ -162,10 +132,7 @@ export class ProductService extends BaseService implements IProductService {
|
|
|
162
132
|
bySubcategory: Record<string, number>;
|
|
163
133
|
byTechnology: Record<string, number>;
|
|
164
134
|
}> {
|
|
165
|
-
const q = query(
|
|
166
|
-
collectionGroup(this.db, PRODUCTS_COLLECTION),
|
|
167
|
-
where("isActive", "==", true)
|
|
168
|
-
);
|
|
135
|
+
const q = query(collectionGroup(this.db, PRODUCTS_COLLECTION), where('isActive', '==', true));
|
|
169
136
|
const snapshot = await getDocs(q);
|
|
170
137
|
|
|
171
138
|
const counts = {
|
|
@@ -178,27 +145,43 @@ export class ProductService extends BaseService implements IProductService {
|
|
|
178
145
|
return counts;
|
|
179
146
|
}
|
|
180
147
|
|
|
181
|
-
snapshot.docs.forEach(
|
|
148
|
+
snapshot.docs.forEach(doc => {
|
|
182
149
|
const product = doc.data() as Product;
|
|
183
150
|
const { categoryId, subcategoryId, technologyId } = product;
|
|
184
151
|
|
|
185
152
|
if (categoryId) {
|
|
186
|
-
counts.byCategory[categoryId] =
|
|
187
|
-
(counts.byCategory[categoryId] || 0) + 1;
|
|
153
|
+
counts.byCategory[categoryId] = (counts.byCategory[categoryId] || 0) + 1;
|
|
188
154
|
}
|
|
189
155
|
if (subcategoryId) {
|
|
190
|
-
counts.bySubcategory[subcategoryId] =
|
|
191
|
-
(counts.bySubcategory[subcategoryId] || 0) + 1;
|
|
156
|
+
counts.bySubcategory[subcategoryId] = (counts.bySubcategory[subcategoryId] || 0) + 1;
|
|
192
157
|
}
|
|
193
158
|
if (technologyId) {
|
|
194
|
-
counts.byTechnology[technologyId] =
|
|
195
|
-
(counts.byTechnology[technologyId] || 0) + 1;
|
|
159
|
+
counts.byTechnology[technologyId] = (counts.byTechnology[technologyId] || 0) + 1;
|
|
196
160
|
}
|
|
197
161
|
});
|
|
198
162
|
|
|
199
163
|
return counts;
|
|
200
164
|
}
|
|
201
165
|
|
|
166
|
+
/**
|
|
167
|
+
* Gets all products for a specific technology (non-paginated, for filters/dropdowns)
|
|
168
|
+
*/
|
|
169
|
+
async getAllByTechnology(technologyId: string): Promise<Product[]> {
|
|
170
|
+
const q = query(
|
|
171
|
+
this.getProductsRef(technologyId),
|
|
172
|
+
where('isActive', '==', true),
|
|
173
|
+
orderBy('name'),
|
|
174
|
+
);
|
|
175
|
+
const snapshot = await getDocs(q);
|
|
176
|
+
return snapshot.docs.map(
|
|
177
|
+
doc =>
|
|
178
|
+
({
|
|
179
|
+
id: doc.id,
|
|
180
|
+
...doc.data(),
|
|
181
|
+
} as Product),
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
|
|
202
185
|
/**
|
|
203
186
|
* Gets all products for a brand by filtering through all technologies
|
|
204
187
|
*/
|
|
@@ -211,18 +194,18 @@ export class ProductService extends BaseService implements IProductService {
|
|
|
211
194
|
for (const techDoc of technologiesSnapshot.docs) {
|
|
212
195
|
const q = query(
|
|
213
196
|
this.getProductsRef(techDoc.id),
|
|
214
|
-
where(
|
|
215
|
-
where(
|
|
197
|
+
where('brandId', '==', brandId),
|
|
198
|
+
where('isActive', '==', true),
|
|
216
199
|
);
|
|
217
200
|
const snapshot = await getDocs(q);
|
|
218
201
|
products.push(
|
|
219
202
|
...snapshot.docs.map(
|
|
220
|
-
|
|
203
|
+
doc =>
|
|
221
204
|
({
|
|
222
205
|
id: doc.id,
|
|
223
206
|
...doc.data(),
|
|
224
|
-
} as Product)
|
|
225
|
-
)
|
|
207
|
+
} as Product),
|
|
208
|
+
),
|
|
226
209
|
);
|
|
227
210
|
}
|
|
228
211
|
|
|
@@ -235,9 +218,7 @@ export class ProductService extends BaseService implements IProductService {
|
|
|
235
218
|
async update(
|
|
236
219
|
technologyId: string,
|
|
237
220
|
productId: string,
|
|
238
|
-
product: Partial<
|
|
239
|
-
Omit<Product, "id" | "createdAt" | "brandId" | "technologyId">
|
|
240
|
-
>
|
|
221
|
+
product: Partial<Omit<Product, 'id' | 'createdAt' | 'brandId' | 'technologyId'>>,
|
|
241
222
|
): Promise<Product | null> {
|
|
242
223
|
const updateData = {
|
|
243
224
|
...product,
|
|
@@ -262,10 +243,7 @@ export class ProductService extends BaseService implements IProductService {
|
|
|
262
243
|
/**
|
|
263
244
|
* Gets a product by ID
|
|
264
245
|
*/
|
|
265
|
-
async getById(
|
|
266
|
-
technologyId: string,
|
|
267
|
-
productId: string
|
|
268
|
-
): Promise<Product | null> {
|
|
246
|
+
async getById(technologyId: string, productId: string): Promise<Product | null> {
|
|
269
247
|
const docRef = doc(this.getProductsRef(technologyId), productId);
|
|
270
248
|
const docSnap = await getDoc(docRef);
|
|
271
249
|
if (!docSnap.exists()) return null;
|