From b661877d77c72b6447779e55220a1300cf93b2fd Mon Sep 17 00:00:00 2001 From: Alireza Hassani Date: Sun, 27 Sep 2026 20:46:17 +0330 Subject: [PATCH] Add category SEO description for storefront category pages. Co-authored-by: Cursor --- .../migration.sql | 2 ++ prisma/schema.prisma | 2 ++ src/categories/categories.service.ts | 4 ++++ src/categories/dto/category.dto.ts | 8 ++++++++ src/public/public.controller.ts | 18 ++++++++++++++---- src/public/public.service.ts | 3 +++ 6 files changed, 33 insertions(+), 4 deletions(-) create mode 100644 prisma/migrations/20260927193000_category_seo_description/migration.sql diff --git a/prisma/migrations/20260927193000_category_seo_description/migration.sql b/prisma/migrations/20260927193000_category_seo_description/migration.sql new file mode 100644 index 0000000..fd798e0 --- /dev/null +++ b/prisma/migrations/20260927193000_category_seo_description/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "Category" ADD COLUMN IF NOT EXISTS "seoDescription" TEXT NOT NULL DEFAULT ''; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index c1af31c..1a3dcdf 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -151,6 +151,8 @@ model Category { packages String[] @default([]) /// Ordered form-builder defs: [{ id, labelFa, labelEn?, type: "text", required? }] technicalFields Json @default("[]") + /// Long-form SEO / category page copy shown on the storefront + seoDescription String @default("") products Product[] @@index([storeId]) diff --git a/src/categories/categories.service.ts b/src/categories/categories.service.ts index 518cca9..9e21975 100644 --- a/src/categories/categories.service.ts +++ b/src/categories/categories.service.ts @@ -44,6 +44,7 @@ export class CategoriesService { packages: dto.packages ?? [], technicalFields: (dto.technicalFields ?? []) as unknown as Prisma.InputJsonValue, + seoDescription: dto.seoDescription ?? '', }, }); } @@ -71,6 +72,9 @@ export class CategoriesService { dto.technicalFields as unknown as Prisma.InputJsonValue, } : {}), + ...(dto.seoDescription !== undefined + ? { seoDescription: dto.seoDescription } + : {}), }, }); } diff --git a/src/categories/dto/category.dto.ts b/src/categories/dto/category.dto.ts index 6cda5eb..47038b6 100644 --- a/src/categories/dto/category.dto.ts +++ b/src/categories/dto/category.dto.ts @@ -72,6 +72,10 @@ export class CreateCategoryDto { @Type(() => TechnicalFieldDto) technicalFields?: TechnicalFieldDto[]; + @IsOptional() + @IsString() + seoDescription?: string; + @IsOptional() @IsString() storeId?: string; @@ -117,4 +121,8 @@ export class UpdateCategoryDto { @ValidateNested({ each: true }) @Type(() => TechnicalFieldDto) technicalFields?: TechnicalFieldDto[]; + + @IsOptional() + @IsString() + seoDescription?: string; } diff --git a/src/public/public.controller.ts b/src/public/public.controller.ts index 11625ce..610b964 100644 --- a/src/public/public.controller.ts +++ b/src/public/public.controller.ts @@ -16,13 +16,17 @@ export class PublicController { @Query('storeSlug') storeSlug?: string, @Query('name') name?: string, @Query('categoryId') categoryId?: string, - @Query('minPrice') minPrice?: number, - @Query('maxPrice') maxPrice?: number, + @Query('minPrice') minPrice?: string, + @Query('maxPrice') maxPrice?: string, ) { const parsedCategoryId = categoryId !== undefined && categoryId !== '' ? Number(categoryId) : undefined; + const parsedMin = + minPrice !== undefined && minPrice !== '' ? Number(minPrice) : undefined; + const parsedMax = + maxPrice !== undefined && maxPrice !== '' ? Number(maxPrice) : undefined; return this.publicService.listProducts({ storeId, storeSlug, @@ -31,8 +35,14 @@ export class PublicController { parsedCategoryId !== undefined && Number.isFinite(parsedCategoryId) ? parsedCategoryId : undefined, - minPrice, - maxPrice, + minPrice: + parsedMin !== undefined && Number.isFinite(parsedMin) + ? parsedMin + : undefined, + maxPrice: + parsedMax !== undefined && Number.isFinite(parsedMax) + ? parsedMax + : undefined, }); } diff --git a/src/public/public.service.ts b/src/public/public.service.ts index c531518..74fd323 100644 --- a/src/public/public.service.ts +++ b/src/public/public.service.ts @@ -9,6 +9,7 @@ export type PublicCategoryTreeNode = { nameEn: string; sortOrder: number; imageUrl: string | null; + seoDescription: string; children: PublicCategoryTreeNode[]; }; @@ -126,6 +127,7 @@ export class PublicService { nameFa: true, nameEn: true, sortOrder: true, + seoDescription: true, products: { where: { published: true }, take: 1, @@ -156,6 +158,7 @@ export class PublicService { nameEn: node.nameEn, sortOrder: node.sortOrder, imageUrl: cover, + seoDescription: node.seoDescription ?? '', children: walk(node.id), }; });