Add website static image slots with page grouping and bilingual captions.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Alireza Hassani
2026-08-16 23:56:16 +03:30
co-authored by Cursor
parent b991a60cbc
commit 1c7678ce67
18 changed files with 1098 additions and 6 deletions
+3 -1
View File
@@ -265,6 +265,8 @@ All routes are prefixed with `/api/v1`.
| GET | `/tenants/:host/website/category-groups` | Homepage category rows |
| GET | `/tenants/:host/website/brand-groups` | Homepage brand rows |
| GET | `/tenants/:host/website/sliders` | Homepage sliders with slides |
| GET | `/tenants/:host/website/static-images` | Named static image slots (`?pageKey=` optional) |
| GET | `/tenants/:host/website/static-images/:key` | One static image slot by key |
### Authenticated (JWT)
@@ -294,7 +296,7 @@ Pattern: `/businesses/:businessId/<resource>`
| Media | `/media` | `media.*` |
| Categories | `/categories` | `categories.*` |
| Products | `/products` | `products.*` |
| Website | `/website/category-groups`, `/website/brand-groups`, `/website/sliders` | `website.*` |
| Website | `/website/category-groups`, `/website/brand-groups`, `/website/sliders`, `/website/static-images` | `website.*` |
#### Categories — notable sub-routes
+9 -1
View File
@@ -30,7 +30,7 @@ You are building a **Meshkee business website (storefront)**. You must use the M
### Typical bootstrap sequence
1. `GET /tenants/{domain}` → branding + `businessId`
2. Homepage: business-info, sliders, category-groups, brand-groups, store-specials
2. Homepage: business-info, **static-images**, sliders, category-groups, brand-groups, store-specials
3. Catalog: categories, products, store-items (`name` instant search: in-stock first, then `updatedAt`), **user-products** (customer stock listings)
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`
@@ -46,6 +46,14 @@ Public marketplace listings owned by customers — not catalog `products`.
- `GET /tenants/{domain}/user-products/{slug}/technical-info` — category form + values
Use product categories from `GET /tenants/{domain}/categories?entityType=product` for filters. Creating/editing listings is customer-dashboard only (`/businesses/.../my-user-products`), not website-facing.
### Static images
Named slots the business dashboard can replace. Fetch once per page:
- `GET /tenants/{domain}/website/static-images` — all slots
- `GET /tenants/{domain}/website/static-images?pageKey=home` — slots for one page (`home`, `products`, `about`, …)
- `GET /tenants/{domain}/website/static-images/{key}` — one slot (e.g. `home-hero`)
Use `slot.key` in the placeholder. Match `pageKey` to the website page. For a single image: `images[0]?.url`. For a list: map `images` (if `itemCount` is set, that many images are expected). Each image may include `titleFa`, `titleEn`, `subtext`, and `linkUrl`. If `linkUrl` is set, wrap in `<a href={linkUrl}>`. Pick `titleFa` or `titleEn` from the site locale. If `images` is empty, keep the local fallback.
If OpenAPI and this brief conflict, **OpenAPI wins**.
---
@@ -1489,6 +1489,20 @@
"url": "{{baseUrl}}/tenants/{{domain}}/website/sliders"
}
},
{
"name": "List static images (website)",
"request": {
"method": "GET",
"url": "{{baseUrl}}/tenants/{{domain}}/website/static-images?pageKey=home"
}
},
{
"name": "Get static image slot by key (website)",
"request": {
"method": "GET",
"url": "{{baseUrl}}/tenants/{{domain}}/website/static-images/home-hero"
}
},
{
"name": "List store specials (website)",
"request": {
+59
View File
@@ -214,6 +214,65 @@
}
}
},
"/tenants/{domain}/website/static-images": {
"get": {
"tags": [
"Homepage"
],
"summary": "Named static image slots (single or list)",
"description": "Each slot has a stable `key` used in website placeholders and a `pageKey` (`home`, `products`, `about`, …). Filter with `?pageKey=home`. `kind` is `single` (one image) or `list` (ordered images). `itemCount` is the expected number of images when fixed (e.g. 2 for a two-banner row); omit/null means an unbounded list. Every image may include optional `titleFa`, `titleEn`, `subtext`, and `linkUrl`. Empty `images` means show the site fallback.",
"parameters": [
{
"$ref": "#/components/parameters/domain"
},
{
"name": "pageKey",
"in": "query",
"required": false,
"schema": {
"type": "string",
"example": "home"
},
"description": "If set, only slots for that website page are returned."
}
],
"responses": {
"200": {
"description": "{ items: StaticImageSlot[] } — key, pageKey, kind, label, aspectRatio, recommendedWidth, itemCount, images[{ id, url, titleFa, titleEn, subtext, linkUrl, width, height, sortOrder }]"
}
}
}
},
"/tenants/{domain}/website/static-images/{key}": {
"get": {
"tags": [
"Homepage"
],
"summary": "One static image slot by key",
"parameters": [
{
"$ref": "#/components/parameters/domain"
},
{
"name": "key",
"in": "path",
"required": true,
"schema": {
"type": "string",
"example": "home-hero"
}
}
],
"responses": {
"200": {
"description": "StaticImageSlot"
},
"404": {
"description": "Unknown slot key for this domain"
}
}
}
},
"/tenants/{domain}/website/category-groups": {
"get": {
"tags": [