From 989f77e1069b750451f6ccf0fd2bbaaefa8b8a56 Mon Sep 17 00:00:00 2001
From: parsa aghaei
Date: Sun, 26 Jul 2026 20:03:45 +0330
Subject: [PATCH] fix(shop): map requiresRx in productService, enable backend
query param filtering, and classify 11 prescription vs 24 OTC products
---
backend/prisma/seed-products.ts | 5 +++++
backend/src/products/dto/get-products.dto.ts | 5 +++++
backend/src/products/products.service.ts | 6 +++++-
frontend/application/components/ArchivePage.tsx | 7 ++++---
frontend/application/lib/services/productService.ts | 1 +
5 files changed, 20 insertions(+), 4 deletions(-)
diff --git a/backend/prisma/seed-products.ts b/backend/prisma/seed-products.ts
index 3b04d31..de0ee1a 100644
--- a/backend/prisma/seed-products.ts
+++ b/backend/prisma/seed-products.ts
@@ -201,6 +201,9 @@ async function main() {
const nameEn = `${baseNameEn} - ${sizeLabel}`;
const priceDisplay = `${sellPrice.toLocaleString()} تومان`;
+ const RX_PRODUCTS = ['canina-canhydrox-gag', 'canina-petvital-gag', 'canina-herz-vital', 'canina-petvital-arthro-tabletten', 'canina-immun-booster', 'canina-petvital-bio-aktivator', 'canina-rinderblut-pulver', 'canina-velox-gelenkenergie'];
+ const requiresRx = RX_PRODUCTS.includes(baseSlug);
+
const product = await prisma.product.upsert({
where: { artNo },
update: {
@@ -217,6 +220,7 @@ async function main() {
categorySlug: category.slug,
priceValue: sellPrice,
wholesalePrice: Math.round(sellPrice * 0.7),
+ requiresRx,
buyPrice,
priceDisplay,
unit,
@@ -240,6 +244,7 @@ async function main() {
categorySlug: category.slug,
priceValue: sellPrice,
wholesalePrice: Math.round(sellPrice * 0.7),
+ requiresRx,
buyPrice,
priceDisplay,
unit,
diff --git a/backend/src/products/dto/get-products.dto.ts b/backend/src/products/dto/get-products.dto.ts
index 77316fc..8601d21 100644
--- a/backend/src/products/dto/get-products.dto.ts
+++ b/backend/src/products/dto/get-products.dto.ts
@@ -17,4 +17,9 @@ export class GetProductsDto extends PaginationDto {
@IsOptional()
@IsString()
symptom?: string;
+
+ @ApiPropertyOptional({ description: 'فیلتر داروی نیازمند نسخه (true/false)' })
+ @IsOptional()
+ @IsString()
+ requiresRx?: string;
}
diff --git a/backend/src/products/products.service.ts b/backend/src/products/products.service.ts
index 8449843..ab9617c 100644
--- a/backend/src/products/products.service.ts
+++ b/backend/src/products/products.service.ts
@@ -7,10 +7,14 @@ export class ProductsService {
constructor(private prisma: PrismaService) {}
async findAll(filters: GetProductsDto, userRole?: string) {
- const { category, petType, search, symptom, page = 1, limit = 10, sortBy = 'createdAt', sortOrder = 'desc' } = filters;
+ const { category, petType, search, symptom, requiresRx, page = 1, limit = 10, sortBy = 'createdAt', sortOrder = 'desc' } = filters;
const whereClause: any = {};
+ if (requiresRx !== undefined && requiresRx !== null && requiresRx !== '') {
+ whereClause.requiresRx = requiresRx === 'true' || requiresRx === '1';
+ }
+
if (category) {
whereClause.categorySlug = category;
}
diff --git a/frontend/application/components/ArchivePage.tsx b/frontend/application/components/ArchivePage.tsx
index 99a9d64..b8f408b 100644
--- a/frontend/application/components/ArchivePage.tsx
+++ b/frontend/application/components/ArchivePage.tsx
@@ -322,11 +322,12 @@ export default function ArchivePage({
if (selectedPet && selectedPet !== "all") params.set('petType', selectedPet);
if (searchQuery) params.set('search', searchQuery);
if (activeSymptoms.length > 0) params.set('symptoms', activeSymptoms.join(','));
+ if (prescriptionFilter !== "all") params.set('rx', prescriptionFilter);
const queryString = params.toString();
const newUrl = `/shop${queryString ? `?${queryString}` : ''}`;
router.push(newUrl, { scroll: false });
- }, [selectedCategory, selectedPet, searchQuery, activeSymptoms]);
+ }, [selectedCategory, selectedPet, searchQuery, activeSymptoms, prescriptionFilter]);
const fetchProducts = async () => {
setIsUpdating(true);
@@ -346,9 +347,9 @@ export default function ArchivePage({
// Filter by prescription requirement (OTC vs Prescription)
if (prescriptionFilter === "prescription") {
- result = result.filter(p => p.requiresRx || p.category === "special-care");
+ result = result.filter(p => Boolean(p.requiresRx) || p.categorySlug === "special-care");
} else if (prescriptionFilter === "otc") {
- result = result.filter(p => !p.requiresRx && p.category !== "special-care");
+ result = result.filter(p => !p.requiresRx && p.categorySlug !== "special-care");
}
setFilteredProducts(result);
diff --git a/frontend/application/lib/services/productService.ts b/frontend/application/lib/services/productService.ts
index 8b73697..e429d18 100644
--- a/frontend/application/lib/services/productService.ts
+++ b/frontend/application/lib/services/productService.ts
@@ -57,6 +57,7 @@ export class ProductService {
dosage_logic: data.dosageLogic,
benefits: data.benefits,
suitableFor: data.suitableFor as PetType,
+ requiresRx: Boolean(data.requiresRx),
storage: data.storage,
specialBadge: data.specialBadge,
onSetOfAction: data.onSetOfAction,