vasuzex 2.1.23 → 2.1.25
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.
|
@@ -224,8 +224,9 @@ export class DatabaseConfigService {
|
|
|
224
224
|
}
|
|
225
225
|
|
|
226
226
|
/**
|
|
227
|
-
* Load API configs (scope='api') from database
|
|
227
|
+
* Load API configs (scope='api' and scope='all') from database
|
|
228
228
|
* These are the backend configs that get loaded into Config facade
|
|
229
|
+
* scope='all' means shared between frontend and backend (no duplication)
|
|
229
230
|
* @private
|
|
230
231
|
*/
|
|
231
232
|
async #loadApiConfigs() {
|
|
@@ -237,9 +238,9 @@ export class DatabaseConfigService {
|
|
|
237
238
|
return;
|
|
238
239
|
}
|
|
239
240
|
|
|
240
|
-
// Load
|
|
241
|
+
// Load API-scoped AND shared (scope='all') configs for backend
|
|
241
242
|
const configs = await AppConfig.query()
|
|
242
|
-
.
|
|
243
|
+
.whereIn('scope', ['api', 'app', 'all'])
|
|
243
244
|
.where('is_active', true)
|
|
244
245
|
.where((query) => {
|
|
245
246
|
query.where('environment', this.#environment)
|
|
@@ -12,8 +12,23 @@ import { createRequire } from 'module';
|
|
|
12
12
|
|
|
13
13
|
const require = createRequire(import.meta.url);
|
|
14
14
|
|
|
15
|
-
// Load environment variables
|
|
16
|
-
|
|
15
|
+
// Load environment variables based on NODE_ENV
|
|
16
|
+
const loadEnvFiles = () => {
|
|
17
|
+
const cwd = process.cwd();
|
|
18
|
+
const nodeEnv = process.env.NODE_ENV || 'development';
|
|
19
|
+
|
|
20
|
+
// Load base .env first
|
|
21
|
+
dotenv.config({ path: resolve(cwd, '.env') });
|
|
22
|
+
|
|
23
|
+
// Then load environment-specific .env file (overrides base)
|
|
24
|
+
const envFile = `.env.${nodeEnv}`;
|
|
25
|
+
const envPath = resolve(cwd, envFile);
|
|
26
|
+
if (existsSync(envPath)) {
|
|
27
|
+
dotenv.config({ path: envPath, override: true });
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
loadEnvFiles();
|
|
17
32
|
|
|
18
33
|
/**
|
|
19
34
|
* Generate GuruORM config from framework config
|
package/package.json
CHANGED
|
@@ -1,688 +0,0 @@
|
|
|
1
|
-
# Amazon-Style SRP Architecture & Data Audit (vasuzex-v2)
|
|
2
|
-
|
|
3
|
-
## 1. Conversation Overview
|
|
4
|
-
- **Primary Objectives:**
|
|
5
|
-
- Confirm slug helper in vasuzex-v2
|
|
6
|
-
- Refactor customer `searchProducts` to match admin (single query, primary photo, no patching)
|
|
7
|
-
- Design Amazon-style SRP: dynamic filters, multi-store logic, no assumptions/patching
|
|
8
|
-
- **Session Context:**
|
|
9
|
-
- Started with a helper query, escalated to strict, enterprise-grade requirements
|
|
10
|
-
- **Intent Evolution:**
|
|
11
|
-
- From helper to full DB/data audit and robust SRP architecture
|
|
12
|
-
|
|
13
|
-
## 2. Technical Foundation
|
|
14
|
-
- **vasuzex-v2:**
|
|
15
|
-
- Has `generateSlug` helper
|
|
16
|
-
- Uses GuruORM models, BaseService patterns
|
|
17
|
-
- **GuruORM:**
|
|
18
|
-
- Eager loading, scopes, relationships
|
|
19
|
-
- **Redux:**
|
|
20
|
-
- For search state management
|
|
21
|
-
- **PostgreSQL:**
|
|
22
|
-
- Backing DB, schema audited via migrations/models
|
|
23
|
-
- **Constraints:**
|
|
24
|
-
- No patching, no assumptions, no fallback logic
|
|
25
|
-
|
|
26
|
-
## 3. Codebase Status
|
|
27
|
-
- **ProductService.js (customer):**
|
|
28
|
-
- Refactored `searchProducts` to use `BaseService.getList`, return only primary photo, fixed async bug
|
|
29
|
-
- **ProductService.js (admin):**
|
|
30
|
-
- Used as reference for single-query, robust search
|
|
31
|
-
- **Models/Migrations Audited:**
|
|
32
|
-
- Product, ProductVariant, StoreInventory, Brand, Category, Rating, Unit, Packaging, Store
|
|
33
|
-
- **No dead code left behind**
|
|
34
|
-
|
|
35
|
-
## 4. Problem Resolution
|
|
36
|
-
- **Issues:**
|
|
37
|
-
- N+1 in `searchProducts`, async mapping bug, unclear filter dimensions
|
|
38
|
-
- **Solutions:**
|
|
39
|
-
- Refactored to single-query, fixed async bug, full DB/data audit
|
|
40
|
-
- **Debugging:**
|
|
41
|
-
- No patching, fallback, partials, or assumptions
|
|
42
|
-
- **Lessons:**
|
|
43
|
-
- Filters must be DB-driven, multi-store logic is critical, code must be robust/reusable
|
|
44
|
-
|
|
45
|
-
## 5. Progress Tracking
|
|
46
|
-
- **Completed:**
|
|
47
|
-
- Slug helper confirmed
|
|
48
|
-
- `searchProducts` refactored and bugfixed
|
|
49
|
-
- DB/data audit and filter matrix produced
|
|
50
|
-
- **Partially Complete:**
|
|
51
|
-
- Awaiting user answers to critical questions before SRP/filter implementation
|
|
52
|
-
- **Validated:**
|
|
53
|
-
- All code changes tested for single-query, primary photo, async correctness
|
|
54
|
-
|
|
55
|
-
## 6. Active Work State
|
|
56
|
-
- **Current Focus:**
|
|
57
|
-
- DB/data audit, filter matrix, SRP architecture, multi-store rules
|
|
58
|
-
- **Recent Context:**
|
|
59
|
-
- Reading models, migrations, services to map filterable fields and multi-store logic
|
|
60
|
-
- **Working Code:**
|
|
61
|
-
- `ProductService.js` `searchProducts` (single-query, primary photo, Promise.all fix)
|
|
62
|
-
- **Immediate Context:**
|
|
63
|
-
- Producing architecture and question list for user confirmation
|
|
64
|
-
|
|
65
|
-
## 7. Recent Operations
|
|
66
|
-
- **Last Agent Commands:**
|
|
67
|
-
- file_search, read_file, grep_search, apply_patch, multi_tool_use.parallel
|
|
68
|
-
- **Tool Results:**
|
|
69
|
-
- Models/migrations read for filterable fields
|
|
70
|
-
- `ProductService.js` refactored and bugfixed
|
|
71
|
-
- Filter matrix and architecture produced
|
|
72
|
-
- **Pre-Summary State:**
|
|
73
|
-
- Finalizing DB/data audit and architecture document for Amazon-style SRP
|
|
74
|
-
- **Operation Context:**
|
|
75
|
-
- All recent commands directly supported robust, DB-driven, multi-store search/filter system
|
|
76
|
-
|
|
77
|
-
## 8. Business Rules & Planning (Updated)
|
|
78
|
-
|
|
79
|
-
### Confirmed Business Rules
|
|
80
|
-
- **Price Management:** Store manages inventory, inventory has pricing. MRP is always present for every product/variant.
|
|
81
|
-
- **Variant Display:** Config-driven via `store.show_all_products` (boolean, public). System must respect this setting globally.
|
|
82
|
-
- **Related Searches:** Strictly DB-driven. Static suggestions are not production-ready.
|
|
83
|
-
- **Sponsored Products:** Full admin-driven workflow required (like Amazon/Flipkart). See detailed architecture below.
|
|
84
|
-
- **Store Ratings:** Recommend global aggregation for multi-store, with per-store support for analytics and A/B testing.
|
|
85
|
-
|
|
86
|
-
### Actionable Implementation Plan
|
|
87
|
-
|
|
88
|
-
#### Phase 1: Database Schema & Migrations
|
|
89
|
-
1. **Sponsored Products Schema:**
|
|
90
|
-
- Add to `products` table:
|
|
91
|
-
- `is_sponsored` (boolean, default false)
|
|
92
|
-
- `sponsored_rank` (integer, nullable, for ordering)
|
|
93
|
-
- `sponsored_start_date` (timestamp, nullable)
|
|
94
|
-
- `sponsored_end_date` (timestamp, nullable)
|
|
95
|
-
- `sponsored_store_id` (uuid, nullable, FK to stores - null = global)
|
|
96
|
-
- `sponsored_budget` (decimal, nullable, for future bidding)
|
|
97
|
-
- `sponsored_clicks` (integer, default 0, for analytics)
|
|
98
|
-
- `sponsored_impressions` (integer, default 0, for analytics)
|
|
99
|
-
- Indexes: `is_sponsored`, `sponsored_rank`, `sponsored_start_date`, `sponsored_end_date`, `sponsored_store_id`
|
|
100
|
-
|
|
101
|
-
2. **Related Searches Schema:**
|
|
102
|
-
- Create `search_queries` table:
|
|
103
|
-
- `id` (uuid, PK)
|
|
104
|
-
- `query` (string, indexed)
|
|
105
|
-
- `result_count` (integer)
|
|
106
|
-
- `click_count` (integer)
|
|
107
|
-
- `created_at`, `updated_at`
|
|
108
|
-
- Create `related_searches` table:
|
|
109
|
-
- `id` (uuid, PK)
|
|
110
|
-
- `search_query_id` (uuid, FK)
|
|
111
|
-
- `related_query_id` (uuid, FK)
|
|
112
|
-
- `relevance_score` (decimal)
|
|
113
|
-
|
|
114
|
-
3. **Store Ratings Enhancement:**
|
|
115
|
-
- Add to `ratings` table (if not exists):
|
|
116
|
-
- `store_id` (uuid, nullable, FK to stores)
|
|
117
|
-
- Indexes: `product_id`, `store_id`, `rating`
|
|
118
|
-
|
|
119
|
-
#### Phase 2: Backend Search API
|
|
120
|
-
1. **Endpoint:** `GET /api/search/products`
|
|
121
|
-
2. **Query Parameters:**
|
|
122
|
-
- `q` (string, search query)
|
|
123
|
-
- `category_id` (uuid[])
|
|
124
|
-
- `brand_id` (uuid[])
|
|
125
|
-
- `price_min`, `price_max` (decimal)
|
|
126
|
-
- `rating_min` (decimal)
|
|
127
|
-
- `store_id` (uuid[])
|
|
128
|
-
- `in_stock` (boolean)
|
|
129
|
-
- `attributes` (json, e.g., `{"color": ["red", "blue"]}`)
|
|
130
|
-
- `page`, `limit` (integer)
|
|
131
|
-
- `sort` (string: price_asc, price_desc, rating_desc, relevance)
|
|
132
|
-
3. **Response:**
|
|
133
|
-
```json
|
|
134
|
-
{
|
|
135
|
-
"products": [],
|
|
136
|
-
"filters": {
|
|
137
|
-
"categories": [{"id": "", "name": "", "count": 0}],
|
|
138
|
-
"brands": [{"id": "", "name": "", "count": 0}],
|
|
139
|
-
"price_range": {"min": 0, "max": 0},
|
|
140
|
-
"attributes": {"color": [{"value": "red", "count": 5}]}
|
|
141
|
-
},
|
|
142
|
-
"sponsored": [],
|
|
143
|
-
"related_searches": [],
|
|
144
|
-
"pagination": {"page": 1, "total": 100}
|
|
145
|
-
}
|
|
146
|
-
```
|
|
147
|
-
4. **Business Logic:**
|
|
148
|
-
- Single-query with joins: products, variants, inventory, brands, categories, ratings
|
|
149
|
-
- Filter sponsored products separately (display at top or interspersed)
|
|
150
|
-
- Respect `store.show_all_products` config for variant filtering
|
|
151
|
-
- Aggregate ratings globally by default, support per-store filter
|
|
152
|
-
- Generate dynamic filters based on search results (no hardcoding)
|
|
153
|
-
- Track search query for related searches analytics
|
|
154
|
-
|
|
155
|
-
#### Phase 3: Admin Panel - Sponsored Products
|
|
156
|
-
1. **UI Components:**
|
|
157
|
-
- Product list with "Sponsor" action button
|
|
158
|
-
- Sponsored product management page:
|
|
159
|
-
- Schedule form (start/end dates)
|
|
160
|
-
- Rank input (ordering)
|
|
161
|
-
- Store targeting (global or specific stores)
|
|
162
|
-
- Budget allocation (future)
|
|
163
|
-
- Analytics dashboard (impressions, clicks, CTR)
|
|
164
|
-
2. **API Endpoints:**
|
|
165
|
-
- `POST /api/admin/products/:id/sponsor`
|
|
166
|
-
- `PUT /api/admin/products/:id/sponsor`
|
|
167
|
-
- `DELETE /api/admin/products/:id/sponsor`
|
|
168
|
-
- `GET /api/admin/sponsored-products` (list with filters)
|
|
169
|
-
- `GET /api/admin/sponsored-products/analytics`
|
|
170
|
-
3. **Validation:**
|
|
171
|
-
- Start date < end date
|
|
172
|
-
- Rank must be unique per store (or global)
|
|
173
|
-
- Budget > 0 (if provided)
|
|
174
|
-
4. **Audit Trail:**
|
|
175
|
-
- Log all sponsored product changes (who, when, what)
|
|
176
|
-
- Track performance metrics for reporting
|
|
177
|
-
|
|
178
|
-
#### Phase 4: Frontend - SRP UI
|
|
179
|
-
1. **Components:**
|
|
180
|
-
- `SearchResultsPage` (container)
|
|
181
|
-
- `FilterSidebar` (dynamic filters)
|
|
182
|
-
- `ProductGrid` (results display)
|
|
183
|
-
- `SponsoredProductCard` (highlighted)
|
|
184
|
-
- `RelatedSearches` (horizontal list)
|
|
185
|
-
- `Pagination`
|
|
186
|
-
2. **Redux State:**
|
|
187
|
-
```javascript
|
|
188
|
-
search: {
|
|
189
|
-
query: '',
|
|
190
|
-
filters: {},
|
|
191
|
-
results: [],
|
|
192
|
-
sponsored: [],
|
|
193
|
-
relatedSearches: [],
|
|
194
|
-
filterMetadata: {},
|
|
195
|
-
loading: false,
|
|
196
|
-
error: null,
|
|
197
|
-
pagination: {}
|
|
198
|
-
}
|
|
199
|
-
```
|
|
200
|
-
3. **Routing:**
|
|
201
|
-
- `/search?q=laptop&category=electronics&price_min=500&price_max=1000`
|
|
202
|
-
4. **Features:**
|
|
203
|
-
- Real-time filter updates (debounced)
|
|
204
|
-
- Sponsored product highlighting (badge, border)
|
|
205
|
-
- Variant display per store config
|
|
206
|
-
- Store aggregation (show all stores with inventory)
|
|
207
|
-
- Global ratings with per-store toggle (A/B test)
|
|
208
|
-
|
|
209
|
-
#### Phase 5: Analytics & Market Study
|
|
210
|
-
1. **Store Ratings Research:**
|
|
211
|
-
- Study Amazon, Flipkart, Walmart, Alibaba
|
|
212
|
-
- Key questions:
|
|
213
|
-
- How do they display ratings in multi-vendor scenarios?
|
|
214
|
-
- Do they show per-seller ratings or global?
|
|
215
|
-
- How do they handle rating aggregation?
|
|
216
|
-
- Implement both global and per-store, track user engagement
|
|
217
|
-
- A/B test: 50% users see global, 50% see per-store
|
|
218
|
-
- Measure: CTR, conversion rate, user feedback
|
|
219
|
-
2. **Sponsored Products Analytics:**
|
|
220
|
-
- Track impressions, clicks, conversions per sponsored product
|
|
221
|
-
- ROI calculation for future bidding system
|
|
222
|
-
- Admin dashboard with charts (daily/weekly/monthly)
|
|
223
|
-
3. **Related Searches Analytics:**
|
|
224
|
-
- Track query popularity, click-through from related searches
|
|
225
|
-
- Use for refining search algorithm and suggestions
|
|
226
|
-
|
|
227
|
-
#### Phase 6: Testing & Validation
|
|
228
|
-
1. **Unit Tests:**
|
|
229
|
-
- Search service logic
|
|
230
|
-
- Filter generation
|
|
231
|
-
- Sponsored product ranking
|
|
232
|
-
2. **Integration Tests:**
|
|
233
|
-
- Search API with all filters
|
|
234
|
-
- Admin sponsored product workflow
|
|
235
|
-
- Multi-store inventory aggregation
|
|
236
|
-
3. **E2E Tests:**
|
|
237
|
-
- Complete search flow
|
|
238
|
-
- Filter interactions
|
|
239
|
-
- Sponsored product display
|
|
240
|
-
4. **Performance Tests:**
|
|
241
|
-
- Search query performance (target < 200ms)
|
|
242
|
-
- Large result set handling (10k+ products)
|
|
243
|
-
- Concurrent user load testing
|
|
244
|
-
|
|
245
|
-
---
|
|
246
|
-
|
|
247
|
-
## Filter Matrix & Data Audit (Summary)
|
|
248
|
-
|
|
249
|
-
| Filter/Dimension | Source Table/Model | Notes/Constraints |
|
|
250
|
-
|--------------------|------------------------|------------------------------------|
|
|
251
|
-
| Category | categories | Hierarchical, DB-driven |
|
|
252
|
-
| Brand | brands | DB-driven |
|
|
253
|
-
| Price (MRP, Sale) | product_variants, store_inventory | Multi-store, per-variant, no fallback |
|
|
254
|
-
| Rating | ratings | Aggregate, store-specific? |
|
|
255
|
-
| Unit/Packaging | units, packaging | Variant-level |
|
|
256
|
-
| Store | stores, store_inventory| Multi-store, must aggregate |
|
|
257
|
-
| Stock/Availability | store_inventory | Per-store, per-variant |
|
|
258
|
-
| Attributes | product_variants | Color, size, etc. |
|
|
259
|
-
| ... | ... | ... |
|
|
260
|
-
|
|
261
|
-
- **All filters must be DB-driven, no hardcoding.**
|
|
262
|
-
- **Multi-store logic:**
|
|
263
|
-
- Aggregate inventory, price, and availability per store
|
|
264
|
-
- No patching or fallback if data missing
|
|
265
|
-
|
|
266
|
-
---
|
|
267
|
-
|
|
268
|
-
## SRP Architecture (Detailed)
|
|
269
|
-
|
|
270
|
-
### Backend Architecture
|
|
271
|
-
- **Single Search API:**
|
|
272
|
-
- Endpoint: `GET /api/search/products`
|
|
273
|
-
- Single-query execution with optimized joins
|
|
274
|
-
- DB-driven filters, no hardcoding
|
|
275
|
-
- Multi-store aggregation at query level
|
|
276
|
-
- Sponsored product injection (separate query or CTE)
|
|
277
|
-
- **Service Layer:**
|
|
278
|
-
- `SearchService.search(query, filters, pagination)`
|
|
279
|
-
- `FilterService.generateFilters(searchResults)`
|
|
280
|
-
- `SponsoredService.getSponsoredProducts(query, storeId)`
|
|
281
|
-
- `RelatedSearchService.getRelatedSearches(query)`
|
|
282
|
-
- **Data Flow:**
|
|
283
|
-
1. Receive search query and filters
|
|
284
|
-
2. Build dynamic SQL with filters (GuruORM query builder)
|
|
285
|
-
3. Execute single query with joins: products → variants → inventory → brands → categories → ratings
|
|
286
|
-
4. Fetch sponsored products (time-based filtering, ranking)
|
|
287
|
-
5. Generate dynamic filters from result set
|
|
288
|
-
6. Fetch related searches from analytics
|
|
289
|
-
7. Return unified response
|
|
290
|
-
- **Performance:**
|
|
291
|
-
- Database indexes on all filterable fields
|
|
292
|
-
- Query caching (Redis) for popular searches
|
|
293
|
-
- Pagination with cursor-based approach for large result sets
|
|
294
|
-
- Target response time: < 200ms
|
|
295
|
-
|
|
296
|
-
### Frontend Architecture
|
|
297
|
-
- **Component Structure:**
|
|
298
|
-
```
|
|
299
|
-
SearchResultsPage
|
|
300
|
-
├── SearchBar (header)
|
|
301
|
-
├── FilterSidebar
|
|
302
|
-
│ ├── CategoryFilter (hierarchical)
|
|
303
|
-
│ ├── BrandFilter (checkbox list)
|
|
304
|
-
│ ├── PriceRangeFilter (slider)
|
|
305
|
-
│ ├── RatingFilter (stars)
|
|
306
|
-
│ ├── StoreFilter (multi-select)
|
|
307
|
-
│ ├── AttributeFilter (dynamic, per-category)
|
|
308
|
-
│ └── AppliedFilters (removable chips)
|
|
309
|
-
├── SearchResults
|
|
310
|
-
│ ├── SponsoredProducts (highlighted grid)
|
|
311
|
-
│ ├── ProductGrid
|
|
312
|
-
│ │ └── ProductCard (with store aggregation)
|
|
313
|
-
│ └── Pagination
|
|
314
|
-
└── RelatedSearches (horizontal list)
|
|
315
|
-
```
|
|
316
|
-
- **Redux State Management:**
|
|
317
|
-
```javascript
|
|
318
|
-
search: {
|
|
319
|
-
query: string,
|
|
320
|
-
filters: {
|
|
321
|
-
categories: string[],
|
|
322
|
-
brands: string[],
|
|
323
|
-
priceRange: { min: number, max: number },
|
|
324
|
-
rating: number,
|
|
325
|
-
stores: string[],
|
|
326
|
-
attributes: Record<string, string[]>,
|
|
327
|
-
inStock: boolean
|
|
328
|
-
},
|
|
329
|
-
results: Product[],
|
|
330
|
-
sponsored: Product[],
|
|
331
|
-
relatedSearches: string[],
|
|
332
|
-
filterMetadata: {
|
|
333
|
-
categories: { id: string, name: string, count: number }[],
|
|
334
|
-
brands: { id: string, name: string, count: number }[],
|
|
335
|
-
priceRange: { min: number, max: number },
|
|
336
|
-
attributes: Record<string, { value: string, count: number }[]>
|
|
337
|
-
},
|
|
338
|
-
loading: boolean,
|
|
339
|
-
error: string | null,
|
|
340
|
-
pagination: { page: number, total: number, limit: number }
|
|
341
|
-
}
|
|
342
|
-
```
|
|
343
|
-
- **Actions:**
|
|
344
|
-
- `searchProducts(query, filters, page)`
|
|
345
|
-
- `applyFilter(filterType, value)`
|
|
346
|
-
- `removeFilter(filterType, value)`
|
|
347
|
-
- `clearFilters()`
|
|
348
|
-
- `sortResults(sortType)`
|
|
349
|
-
- **Routing:**
|
|
350
|
-
- `/search?q=laptop&category=electronics&brand=dell&price_min=500&price_max=1000&page=1`
|
|
351
|
-
- URL sync with Redux state
|
|
352
|
-
- Browser back/forward support
|
|
353
|
-
|
|
354
|
-
### Multi-Store Logic
|
|
355
|
-
- **Display Strategy:**
|
|
356
|
-
- Show all stores with inventory for each product/variant
|
|
357
|
-
- Group by product, display available stores with prices
|
|
358
|
-
- Highlight best price across stores
|
|
359
|
-
- **Price Aggregation:**
|
|
360
|
-
- Show price range if varies across stores
|
|
361
|
-
- Display individual store prices on hover/expand
|
|
362
|
-
- **Stock Aggregation:**
|
|
363
|
-
- Respect `store.show_all_products` config
|
|
364
|
-
- If true: show all variants regardless of stock
|
|
365
|
-
- If false: show only in-stock variants
|
|
366
|
-
- **No Fallback:**
|
|
367
|
-
- If store data missing, don't display the product for that store
|
|
368
|
-
- No patching, no assumptions
|
|
369
|
-
|
|
370
|
-
### Sponsored Products Logic
|
|
371
|
-
- **Display Strategy:**
|
|
372
|
-
- Top 3-5 sponsored products at page top (clearly marked)
|
|
373
|
-
- Intersperse 1 sponsored product every 10 organic results
|
|
374
|
-
- Clear "Sponsored" badge on each
|
|
375
|
-
- **Ranking Algorithm:**
|
|
376
|
-
- Sort by `sponsored_rank` (admin-defined)
|
|
377
|
-
- Filter by date range (active sponsorships only)
|
|
378
|
-
- Respect store targeting (if `sponsored_store_id` is set)
|
|
379
|
-
- **Analytics Tracking:**
|
|
380
|
-
- Track impressions on render
|
|
381
|
-
- Track clicks on product card click
|
|
382
|
-
- Send to analytics service (async)
|
|
383
|
-
|
|
384
|
-
### Variant Display Logic
|
|
385
|
-
- **Config-Driven:**
|
|
386
|
-
- Fetch `store.show_all_products` from settings/config
|
|
387
|
-
- Apply globally across search, category pages, product lists
|
|
388
|
-
- **Implementation:**
|
|
389
|
-
- If true: `SELECT * FROM products JOIN variants ...`
|
|
390
|
-
- If false: `SELECT * FROM products JOIN variants ... WHERE stock > 0`
|
|
391
|
-
|
|
392
|
-
### Store Ratings Logic
|
|
393
|
-
- **Default: Global Aggregation**
|
|
394
|
-
- `AVG(ratings.rating) FROM ratings WHERE product_id = ?`
|
|
395
|
-
- Display as product's overall rating
|
|
396
|
-
- **Optional: Per-Store Filter**
|
|
397
|
-
- `AVG(ratings.rating) FROM ratings WHERE product_id = ? AND store_id = ?`
|
|
398
|
-
- Show when user filters by specific store
|
|
399
|
-
- **A/B Testing:**
|
|
400
|
-
- 50% users see global ratings
|
|
401
|
-
- 50% users see per-store ratings (when filtered)
|
|
402
|
-
- Track CTR, conversion, user engagement
|
|
403
|
-
- Decide final implementation based on data
|
|
404
|
-
|
|
405
|
-
---
|
|
406
|
-
|
|
407
|
-
## Sponsored Products: Complete Workflow
|
|
408
|
-
|
|
409
|
-
### Amazon/Flipkart-Style Sponsorship Model
|
|
410
|
-
|
|
411
|
-
#### Admin Workflow
|
|
412
|
-
1. **Product Selection:**
|
|
413
|
-
- Admin navigates to Products list
|
|
414
|
-
- Selects product(s) to sponsor
|
|
415
|
-
- Clicks "Sponsor Product" button
|
|
416
|
-
|
|
417
|
-
2. **Sponsorship Configuration:**
|
|
418
|
-
- **Duration:** Start date & end date (with timezone)
|
|
419
|
-
- **Targeting:** Global or specific store(s)
|
|
420
|
-
- **Ranking:** Priority/position (1-100, lower = higher priority)
|
|
421
|
-
- **Budget:** (Optional, for future bidding system)
|
|
422
|
-
- **Display Settings:**
|
|
423
|
-
- Show in search results (yes/no)
|
|
424
|
-
- Show in category pages (yes/no)
|
|
425
|
-
- Show on homepage (yes/no)
|
|
426
|
-
|
|
427
|
-
3. **Approval & Activation:**
|
|
428
|
-
- Preview sponsored product display
|
|
429
|
-
- Confirm and activate
|
|
430
|
-
- Sponsorship goes live at start_date
|
|
431
|
-
|
|
432
|
-
4. **Monitoring & Analytics:**
|
|
433
|
-
- Real-time dashboard with:
|
|
434
|
-
- Impressions (how many times displayed)
|
|
435
|
-
- Clicks (how many users clicked)
|
|
436
|
-
- CTR (click-through rate)
|
|
437
|
-
- Conversions (if user purchased)
|
|
438
|
-
- Spend (if budget-based)
|
|
439
|
-
- Export reports (CSV, PDF)
|
|
440
|
-
|
|
441
|
-
5. **Management:**
|
|
442
|
-
- Pause/resume sponsorship
|
|
443
|
-
- Edit dates, ranking, targeting
|
|
444
|
-
- End sponsorship early
|
|
445
|
-
- Clone sponsorship for new campaign
|
|
446
|
-
|
|
447
|
-
#### Database Schema
|
|
448
|
-
|
|
449
|
-
**Migration: `add_sponsored_products_fields.js`**
|
|
450
|
-
```javascript
|
|
451
|
-
// Add to products table
|
|
452
|
-
table.boolean('is_sponsored').defaultTo(false).index();
|
|
453
|
-
table.integer('sponsored_rank').nullable().index();
|
|
454
|
-
table.timestamp('sponsored_start_date').nullable().index();
|
|
455
|
-
table.timestamp('sponsored_end_date').nullable().index();
|
|
456
|
-
table.uuid('sponsored_store_id').nullable().references('id').inTable('stores').index();
|
|
457
|
-
table.decimal('sponsored_budget', 10, 2).nullable();
|
|
458
|
-
table.decimal('sponsored_spent', 10, 2).defaultTo(0);
|
|
459
|
-
table.integer('sponsored_impressions').defaultTo(0);
|
|
460
|
-
table.integer('sponsored_clicks').defaultTo(0);
|
|
461
|
-
table.boolean('sponsored_show_in_search').defaultTo(true);
|
|
462
|
-
table.boolean('sponsored_show_in_category').defaultTo(true);
|
|
463
|
-
table.boolean('sponsored_show_in_homepage').defaultTo(false);
|
|
464
|
-
```
|
|
465
|
-
|
|
466
|
-
**New Table: `sponsored_product_logs`**
|
|
467
|
-
```javascript
|
|
468
|
-
table.uuid('id').primary();
|
|
469
|
-
table.uuid('product_id').references('id').inTable('products');
|
|
470
|
-
table.enum('event_type', ['impression', 'click', 'conversion']);
|
|
471
|
-
table.uuid('user_id').nullable().references('id').inTable('users');
|
|
472
|
-
table.uuid('store_id').nullable().references('id').inTable('stores');
|
|
473
|
-
table.string('search_query').nullable();
|
|
474
|
-
table.string('page_url').nullable();
|
|
475
|
-
table.jsonb('metadata').nullable(); // IP, user agent, etc.
|
|
476
|
-
table.timestamps();
|
|
477
|
-
|
|
478
|
-
// Indexes
|
|
479
|
-
table.index(['product_id', 'event_type', 'created_at']);
|
|
480
|
-
table.index(['product_id', 'store_id']);
|
|
481
|
-
```
|
|
482
|
-
|
|
483
|
-
**New Table: `search_queries` (for related searches)**
|
|
484
|
-
```javascript
|
|
485
|
-
table.uuid('id').primary();
|
|
486
|
-
table.string('query').index();
|
|
487
|
-
table.integer('search_count').defaultTo(0);
|
|
488
|
-
table.integer('result_count').defaultTo(0);
|
|
489
|
-
table.integer('click_count').defaultTo(0);
|
|
490
|
-
table.decimal('avg_position_clicked', 5, 2).nullable();
|
|
491
|
-
table.timestamps();
|
|
492
|
-
```
|
|
493
|
-
|
|
494
|
-
**New Table: `related_searches`**
|
|
495
|
-
```javascript
|
|
496
|
-
table.uuid('id').primary();
|
|
497
|
-
table.uuid('search_query_id').references('id').inTable('search_queries');
|
|
498
|
-
table.uuid('related_query_id').references('id').inTable('search_queries');
|
|
499
|
-
table.decimal('relevance_score', 5, 4); // 0.0 to 1.0
|
|
500
|
-
table.integer('click_through_count').defaultTo(0);
|
|
501
|
-
table.timestamps();
|
|
502
|
-
|
|
503
|
-
// Indexes
|
|
504
|
-
table.index(['search_query_id', 'relevance_score']);
|
|
505
|
-
```
|
|
506
|
-
|
|
507
|
-
#### API Endpoints
|
|
508
|
-
|
|
509
|
-
**Admin API:**
|
|
510
|
-
- `POST /api/admin/products/:id/sponsor` - Create sponsorship
|
|
511
|
-
- `PUT /api/admin/products/:id/sponsor` - Update sponsorship
|
|
512
|
-
- `DELETE /api/admin/products/:id/sponsor` - End sponsorship
|
|
513
|
-
- `GET /api/admin/sponsored-products` - List all sponsored products
|
|
514
|
-
- `GET /api/admin/sponsored-products/:id/analytics` - Get analytics
|
|
515
|
-
- `POST /api/admin/sponsored-products/:id/pause` - Pause
|
|
516
|
-
- `POST /api/admin/sponsored-products/:id/resume` - Resume
|
|
517
|
-
|
|
518
|
-
**Customer API:**
|
|
519
|
-
- `POST /api/analytics/sponsored/impression` - Track impression
|
|
520
|
-
- `POST /api/analytics/sponsored/click` - Track click
|
|
521
|
-
|
|
522
|
-
#### Frontend Components
|
|
523
|
-
|
|
524
|
-
**Admin Panel:**
|
|
525
|
-
- `SponsoredProductsPage` - List and manage
|
|
526
|
-
- `SponsorProductModal` - Create/edit form
|
|
527
|
-
- `SponsoredAnalyticsDashboard` - Charts and metrics
|
|
528
|
-
- `SponsoredProductCard` - Preview card
|
|
529
|
-
|
|
530
|
-
**Customer App:**
|
|
531
|
-
- `SponsoredProductBadge` - "Sponsored" label
|
|
532
|
-
- `SponsoredProductCard` - Highlighted card with tracking
|
|
533
|
-
- Update `ProductCard` to handle sponsored flag
|
|
534
|
-
|
|
535
|
-
#### Business Logic
|
|
536
|
-
|
|
537
|
-
**Fetching Sponsored Products:**
|
|
538
|
-
```javascript
|
|
539
|
-
const now = new Date();
|
|
540
|
-
const sponsoredProducts = await Product.query()
|
|
541
|
-
.where('is_sponsored', true)
|
|
542
|
-
.where('sponsored_start_date', '<=', now)
|
|
543
|
-
.where('sponsored_end_date', '>=', now)
|
|
544
|
-
.where(builder => {
|
|
545
|
-
builder
|
|
546
|
-
.whereNull('sponsored_store_id') // Global
|
|
547
|
-
.orWhere('sponsored_store_id', currentStoreId); // Or specific store
|
|
548
|
-
})
|
|
549
|
-
.where('sponsored_show_in_search', true)
|
|
550
|
-
.orderBy('sponsored_rank', 'asc')
|
|
551
|
-
.limit(20);
|
|
552
|
-
```
|
|
553
|
-
|
|
554
|
-
**Ranking & Display:**
|
|
555
|
-
- Top 3 sponsored: positions 1-3 in results (above organic)
|
|
556
|
-
- Interspersed: 1 sponsored every 10 organic results
|
|
557
|
-
- Clear visual distinction (border, badge, background color)
|
|
558
|
-
|
|
559
|
-
**Analytics Tracking:**
|
|
560
|
-
```javascript
|
|
561
|
-
// On product card render
|
|
562
|
-
trackImpression(productId, userId, searchQuery, storeId);
|
|
563
|
-
|
|
564
|
-
// On product card click
|
|
565
|
-
trackClick(productId, userId, searchQuery, storeId, pageUrl);
|
|
566
|
-
|
|
567
|
-
// On purchase
|
|
568
|
-
trackConversion(productId, userId, orderId, revenue);
|
|
569
|
-
```
|
|
570
|
-
|
|
571
|
-
#### Future Enhancements
|
|
572
|
-
1. **Bidding System:**
|
|
573
|
-
- Stores/sellers bid for sponsorship slots
|
|
574
|
-
- Automated ranking based on bid amount + relevance
|
|
575
|
-
- Real-time budget tracking and pause when exhausted
|
|
576
|
-
|
|
577
|
-
2. **A/B Testing:**
|
|
578
|
-
- Test different sponsored product positions
|
|
579
|
-
- Test sponsored vs organic CTR
|
|
580
|
-
- Optimize for user experience and revenue
|
|
581
|
-
|
|
582
|
-
3. **Advanced Analytics:**
|
|
583
|
-
- Cohort analysis (users who clicked sponsored vs didn't)
|
|
584
|
-
- Heatmaps (where users click most)
|
|
585
|
-
- Conversion funnel (impression → click → add to cart → purchase)
|
|
586
|
-
|
|
587
|
-
4. **Machine Learning:**
|
|
588
|
-
- Predict which products to sponsor based on trends
|
|
589
|
-
- Personalized sponsored products per user
|
|
590
|
-
- Optimize ranking algorithm based on historical data
|
|
591
|
-
---
|
|
592
|
-
|
|
593
|
-
## Store Ratings: Market Study & Recommendations
|
|
594
|
-
|
|
595
|
-
### Research Questions
|
|
596
|
-
1. **How do major marketplaces handle ratings in multi-vendor scenarios?**
|
|
597
|
-
- Amazon: Shows seller rating separately from product rating
|
|
598
|
-
- Flipkart: Shows overall product rating (aggregated across sellers)
|
|
599
|
-
- eBay: Shows both product rating and seller rating side-by-side
|
|
600
|
-
- Alibaba: Shows supplier rating prominently
|
|
601
|
-
|
|
602
|
-
2. **What's the user expectation?**
|
|
603
|
-
- Users generally expect to see product quality ratings (global)
|
|
604
|
-
- Users also want to know store/seller reliability (per-store)
|
|
605
|
-
- Best practice: Show both, but emphasize product rating
|
|
606
|
-
|
|
607
|
-
3. **What drives conversion?**
|
|
608
|
-
- Product rating is primary decision factor
|
|
609
|
-
- Store rating is secondary (trust factor)
|
|
610
|
-
- Combination of both maximizes trust and conversion
|
|
611
|
-
|
|
612
|
-
### Recommended Implementation
|
|
613
|
-
|
|
614
|
-
**Primary Display: Global Product Rating**
|
|
615
|
-
- Aggregate all ratings for a product across all stores
|
|
616
|
-
- Display prominently on product card and detail page
|
|
617
|
-
- Formula: `AVG(rating) FROM ratings WHERE product_id = ?`
|
|
618
|
-
|
|
619
|
-
**Secondary Display: Per-Store Rating (on hover/expand)**
|
|
620
|
-
- Show when user hovers over store name or price
|
|
621
|
-
- Display in store selection dropdown
|
|
622
|
-
- Formula: `AVG(rating) FROM ratings WHERE product_id = ? AND store_id = ?`
|
|
623
|
-
|
|
624
|
-
**Tertiary Display: Store/Seller Rating (separate)**
|
|
625
|
-
- Show store's overall rating (across all products)
|
|
626
|
-
- Display on store profile page and in search results (small badge)
|
|
627
|
-
- Formula: `AVG(rating) FROM ratings WHERE store_id = ?`
|
|
628
|
-
|
|
629
|
-
### A/B Testing Plan
|
|
630
|
-
|
|
631
|
-
**Test Groups:**
|
|
632
|
-
- **Group A (50%):** Global product rating only
|
|
633
|
-
- **Group B (50%):** Global product rating + per-store rating (on hover)
|
|
634
|
-
|
|
635
|
-
**Metrics to Track:**
|
|
636
|
-
- CTR (click-through rate)
|
|
637
|
-
- Conversion rate
|
|
638
|
-
- Time to purchase decision
|
|
639
|
-
- Cart abandonment rate
|
|
640
|
-
- User feedback/surveys
|
|
641
|
-
|
|
642
|
-
**Duration:** 4 weeks
|
|
643
|
-
|
|
644
|
-
**Success Criteria:**
|
|
645
|
-
- If Group B shows 5%+ higher conversion: implement per-store ratings
|
|
646
|
-
- If no significant difference: stick with global ratings (simpler UX)
|
|
647
|
-
|
|
648
|
-
### Implementation Timeline
|
|
649
|
-
|
|
650
|
-
**Week 1-2: Database & Backend**
|
|
651
|
-
- Implement search API with filters and sponsored products
|
|
652
|
-
- Add global rating aggregation
|
|
653
|
-
- Add per-store rating query (optional)
|
|
654
|
-
|
|
655
|
-
**Week 3-4: Frontend - SRP**
|
|
656
|
-
- Build search results page with dynamic filters
|
|
657
|
-
- Implement product cards with global ratings
|
|
658
|
-
- Add A/B test logic for per-store ratings
|
|
659
|
-
|
|
660
|
-
**Week 5-6: Admin Panel**
|
|
661
|
-
- Build sponsored products management UI
|
|
662
|
-
- Build analytics dashboard
|
|
663
|
-
- Implement sponsorship workflow
|
|
664
|
-
|
|
665
|
-
**Week 7-8: Testing & Analytics**
|
|
666
|
-
- Run A/B test for store ratings
|
|
667
|
-
- Monitor sponsored products performance
|
|
668
|
-
- Collect user feedback
|
|
669
|
-
|
|
670
|
-
**Week 9-10: Optimization**
|
|
671
|
-
- Analyze A/B test results
|
|
672
|
-
- Optimize search query performance
|
|
673
|
-
- Refine UI based on user feedback
|
|
674
|
-
|
|
675
|
-
---
|
|
676
|
-
|
|
677
|
-
## Critical Success Factors
|
|
678
|
-
|
|
679
|
-
1. **Performance:** Search must be fast (< 200ms)
|
|
680
|
-
2. **Accuracy:** Filters must reflect actual data, no patching
|
|
681
|
-
3. **Scalability:** Handle 10k+ products, 100+ concurrent users
|
|
682
|
-
4. **User Experience:** Intuitive filters, clear sponsored product marking
|
|
683
|
-
5. **Analytics:** Robust tracking for data-driven decisions
|
|
684
|
-
6. **Maintainability:** Clean code, well-documented, reusable components
|
|
685
|
-
|
|
686
|
-
---
|
|
687
|
-
|
|
688
|
-
*Prepared by GitHub Copilot, 21 December 2025.*
|