Use id+slug paths for user products and count their visits.

Align storefront/sitemap URLs with catalog products, add by-id public APIs, and include user-product detail views in the product visit chart.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Alireza Hassani
2026-09-15 15:35:46 +03:30
co-authored by Cursor
parent 3aef62c1b8
commit 6517877bed
19 changed files with 392 additions and 86 deletions
+63 -4
View File
@@ -39,7 +39,7 @@
},
{
"name": "User Products",
"description": "Customer marketplace listings. Detail returns `technicalValues` without labels; use `GET .../user-products/{slug}/technical-info` for form field labels + values."
"description": "Customer marketplace listings. Prefer `GET .../user-products/by-id/{id}` for storefront pages. Detail returns `technicalValues` without labels; use `.../technical-info` for form field labels + values."
},
{
"name": "Store"
@@ -434,7 +434,7 @@
"SEO"
],
"summary": "User products sitemap urlset",
"description": "Published customer marketplace listings. Default paths: `/user-products/{slug}`. Included in the index when the `customer_products` module is enabled. Override path via sitemap-config `templates.userProduct`.",
"description": "Published customer marketplace listings. Default paths: `/user-products/{id}/{slug}` (slug from titleFa/titleEn). Included in the index when the `customer_products` module is enabled. Override path via sitemap-config `templates.userProduct`.",
"parameters": [
{
"$ref": "#/components/parameters/domain"
@@ -659,6 +659,8 @@
"blog_detail",
"video_list",
"video_detail",
"user_product_list",
"user_product_detail",
"website",
"product",
"portfolio",
@@ -668,7 +670,7 @@
},
"entityId": {
"type": "string",
"description": "Required for *_detail kinds (product, portfolio, blog, video, store item)."
"description": "Required for *_detail kinds (product, user product, portfolio, blog, video, store item)."
},
"path": {
"type": "string",
@@ -1262,7 +1264,64 @@
],
"responses": {
"200": {
"description": "{ items: UserProductListItem[], total, page, pageSize }. Each item includes id, slug, titleFa/titleEn, price, priceCurrency, condition, city/country names, imageUrl, category*, promoted, publishedAt."
"description": "{ items: UserProductListItem[], total, page, pageSize }. Each item includes id, slug, pathSlug, titleFa/titleEn, price, priceCurrency, condition, city/country names, imageUrl, category*, promoted, publishedAt."
}
}
}
},
"/tenants/{domain}/user-products/by-id/{productId}": {
"get": {
"tags": [
"User Products"
],
"summary": "User product by id (preferred for /user-products/{id}/{pathSlug} pages)",
"description": "Full published listing resolved by id. Prefer this for storefront detail pages — the path slug segment is SEO-only.\n\n**Important:** `technicalValues` items are **values only**. For a label→value table, call `GET /tenants/{domain}/user-products/by-id/{productId}/technical-info`.",
"parameters": [
{
"$ref": "#/components/parameters/domain"
},
{
"name": "productId",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "{ product } with gallery, pathSlug, technicalValues (values only), countryId, cityId"
},
"404": {
"description": "Not found or not published"
}
}
}
},
"/tenants/{domain}/user-products/by-id/{productId}/technical-info": {
"get": {
"tags": [
"User Products"
],
"summary": "User product technical form + values by id",
"description": "**Use this for the listing specs UI** when the page is resolved by id. Same shape as the slug technical-info route.",
"parameters": [
{
"$ref": "#/components/parameters/domain"
},
{
"name": "productId",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "{ form: { id, categoryId, fields: [...] } | null, values: [...] }"
}
}
}