Add tag filter to blogs, portfolios, and videos list APIs.
Storefronts can filter published content by metadata.tags the same way as products; website API docs are updated to match. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
e0e122ed57
commit
1b16e79d3f
@@ -31,7 +31,7 @@ You are building a **Meshkee business website (storefront)**. You must use the M
|
||||
### Typical bootstrap sequence
|
||||
1. `GET /tenants/{domain}` → branding + `businessId` + `specialProductsSource` (`product` or `store_item`)
|
||||
2. Homepage: business-info, **static-images**, sliders, category-groups, brand-groups, store-specials (`source` repeats the tenant setting; items are store listings when `source` is `store_item`)
|
||||
3. Catalog: categories, products (`GET /products/{slug}` includes `relatedProducts`: same category then same brand, in-stock first), store-items (`name` instant search: in-stock first, then `updatedAt`), **user-products** (customer stock listings)
|
||||
3. Catalog: categories, products (`GET /products/{slug}` includes `relatedProducts`: same category then same brand, in-stock first), store-items (`name` instant search: in-stock first, then `updatedAt`), **user-products** (customer stock listings). List filters: products/blogs/portfolios/videos accept `?tag=` (exact match on `metadata.tags`).
|
||||
4. Auth: register/login → store tokens. Optional: `POST /auth/send-otp` then `POST /auth/login-otp` (passwordless) or `POST /auth/reset-password` (forgot password). `POST /auth/verify-otp` only marks the cell verified (no tokens).
|
||||
5. Cart checkout with `addressId` or inline `shippingAddress` + `payment`
|
||||
- For online pay: `payment.type = "e_payment_gate"`, `gatewayType` (e.g. `"mellat"` or `"zarinpal"`), and absolute `returnUrl`
|
||||
|
||||
@@ -715,6 +715,11 @@
|
||||
"key": "title",
|
||||
"value": "",
|
||||
"disabled": true
|
||||
},
|
||||
{
|
||||
"key": "tag",
|
||||
"value": "",
|
||||
"disabled": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -822,6 +827,11 @@
|
||||
"key": "title",
|
||||
"value": "",
|
||||
"disabled": true
|
||||
},
|
||||
{
|
||||
"key": "tag",
|
||||
"value": "",
|
||||
"disabled": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -924,6 +934,11 @@
|
||||
"key": "title",
|
||||
"value": "",
|
||||
"disabled": true
|
||||
},
|
||||
{
|
||||
"key": "tag",
|
||||
"value": "",
|
||||
"disabled": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -933,6 +933,14 @@
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "tag",
|
||||
"in": "query",
|
||||
"description": "Filter by exact tag in metadata.tags",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
@@ -1094,6 +1102,14 @@
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "tag",
|
||||
"in": "query",
|
||||
"description": "Filter by exact tag in metadata.tags",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
@@ -1244,6 +1260,14 @@
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "tag",
|
||||
"in": "query",
|
||||
"description": "Filter by exact tag in metadata.tags",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
|
||||
@@ -502,6 +502,14 @@ export class BlogsService {
|
||||
title: { contains: query.title.trim(), mode: 'insensitive' },
|
||||
}
|
||||
: {}),
|
||||
...(query.tag?.trim()
|
||||
? {
|
||||
metadata: {
|
||||
path: ['tags'],
|
||||
array_contains: query.tag.trim(),
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,10 @@ export class ListBlogsDto {
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
title?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
tag?: string;
|
||||
}
|
||||
|
||||
export class ListPublicBlogsDto {
|
||||
@@ -65,6 +69,10 @@ export class ListPublicBlogsDto {
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
title?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
tag?: string;
|
||||
}
|
||||
|
||||
export class CreateBlogDto {
|
||||
|
||||
@@ -35,6 +35,10 @@ export class ListPortfoliosDto {
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
title?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
tag?: string;
|
||||
}
|
||||
|
||||
export class ListPublicPortfoliosDto {
|
||||
@@ -57,6 +61,10 @@ export class ListPublicPortfoliosDto {
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
title?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
tag?: string;
|
||||
}
|
||||
|
||||
export class CreatePortfolioDto {
|
||||
|
||||
@@ -607,6 +607,14 @@ export class PortfoliosService {
|
||||
],
|
||||
}
|
||||
: {}),
|
||||
...(query.tag?.trim()
|
||||
? {
|
||||
metadata: {
|
||||
path: ['tags'],
|
||||
array_contains: query.tag.trim(),
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,10 @@ export class ListVideosDto {
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
title?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
tag?: string;
|
||||
}
|
||||
|
||||
export class ListPublicVideosDto {
|
||||
@@ -65,6 +69,10 @@ export class ListPublicVideosDto {
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
title?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
tag?: string;
|
||||
}
|
||||
|
||||
export class CreateVideoDto {
|
||||
|
||||
@@ -566,6 +566,14 @@ export class VideosService {
|
||||
],
|
||||
}
|
||||
: {}),
|
||||
...(query.tag?.trim()
|
||||
? {
|
||||
metadata: {
|
||||
path: ['tags'],
|
||||
array_contains: query.tag.trim(),
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ You are building a **Meshkee business website (storefront)**. You must use the M
|
||||
### Typical bootstrap sequence
|
||||
1. `GET /tenants/{domain}` → branding + `businessId` + `specialProductsSource` (`product` or `store_item`)
|
||||
2. Homepage: business-info, **static-images**, sliders, category-groups, brand-groups, store-specials (`source` repeats the tenant setting; items are store listings when `source` is `store_item`)
|
||||
3. Catalog: categories, products (`GET /products/{slug}` includes `relatedProducts`: same category then same brand, in-stock first), store-items (`name` instant search: in-stock first, then `updatedAt`), **user-products** (customer stock listings)
|
||||
3. Catalog: categories, products (`GET /products/{slug}` includes `relatedProducts`: same category then same brand, in-stock first), store-items (`name` instant search: in-stock first, then `updatedAt`), **user-products** (customer stock listings). List filters: products/blogs/portfolios/videos accept `?tag=` (exact match on `metadata.tags`).
|
||||
4. Auth: register/login → store tokens. Optional: `POST /auth/send-otp` then `POST /auth/login-otp` (passwordless) or `POST /auth/reset-password` (forgot password). `POST /auth/verify-otp` only marks the cell verified (no tokens).
|
||||
5. Cart checkout with `addressId` or inline `shippingAddress` + `payment`
|
||||
- For online pay: `payment.type = "e_payment_gate"`, `gatewayType` (e.g. `"mellat"` or `"zarinpal"`), and absolute `returnUrl`
|
||||
|
||||
@@ -715,6 +715,11 @@
|
||||
"key": "title",
|
||||
"value": "",
|
||||
"disabled": true
|
||||
},
|
||||
{
|
||||
"key": "tag",
|
||||
"value": "",
|
||||
"disabled": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -822,6 +827,11 @@
|
||||
"key": "title",
|
||||
"value": "",
|
||||
"disabled": true
|
||||
},
|
||||
{
|
||||
"key": "tag",
|
||||
"value": "",
|
||||
"disabled": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -924,6 +934,11 @@
|
||||
"key": "title",
|
||||
"value": "",
|
||||
"disabled": true
|
||||
},
|
||||
{
|
||||
"key": "tag",
|
||||
"value": "",
|
||||
"disabled": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -933,6 +933,14 @@
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "tag",
|
||||
"in": "query",
|
||||
"description": "Filter by exact tag in metadata.tags",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
@@ -1094,6 +1102,14 @@
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "tag",
|
||||
"in": "query",
|
||||
"description": "Filter by exact tag in metadata.tags",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
@@ -1244,6 +1260,14 @@
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "tag",
|
||||
"in": "query",
|
||||
"description": "Filter by exact tag in metadata.tags",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
|
||||
Reference in New Issue
Block a user