@reblu/site-contracts 1.12.0 → 1.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # @reblu/site-contracts
2
2
 
3
+ ## 1.14.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 92e0f6f: Add the `header` slot to `Cta['slot']` (`'header' | 'hero' | 'blog-end' | 'services' | 'contact' | 'sticky-mobile' | 'footer'`), so a SITE can drive its persistent header CTA from nexus via `GET /api/site/ctas?slot=header`.
8
+ - b75f066: Deprecate `ShopCheckEmailInput` and `ShopEmailCheck` (REB-834). `POST /api/site/shop/check-email` was an account-enumeration oracle on the tenant's customer list; it now always answers `{ exists: false }`. Never branch UI on `exists`. Both types are removed in the next major.
9
+
10
+ ## 1.13.0
11
+
12
+ ### Minor Changes
13
+
14
+ - 7a00471: Featured pages per page category (REB-869). `@reblu/site-contracts` adds `PagesListQuery`, the query of `GET /api/site/pages`, including the new `featured` flag. `@reblu/site-client`: `pages.list({ category, featured: true })` returns that category's featured pages (max 3) in the tenant's order, skipping unpublished, deleted or moved pages, and an empty list when nothing is featured; the new `pages.featured(category, { limit?, fallback?, cache? })` helper returns the featured pages, or the category's first `limit` pages in normal order when none are featured, so a landing teaser is never empty. `limit` must be an integer >= 1: above 3 it is clamped to 3; 0, negative, fractional or NaN throw a `RangeError` before any request. Against an API that predates `featured`, the flag is ignored and `pages.featured()` silently degrades to the category's first `limit` pages. `@reblu/site-blocks` adds `PageCardGrid`, the page-card grid `ParentPage` already rendered for its child pages, as a drop-in (`pages`, `pageHref`, `heading`); cards are keyed by category + slug, an empty `heading` renders no `<h2>`. `ParentPage` now renders through it with unchanged markup (including the empty `<h2>` for `childPagesLabel=''`).
15
+
3
16
  ## 1.12.0
4
17
 
5
18
  ### Minor Changes
package/README.md CHANGED
@@ -30,6 +30,13 @@ async function getPosts(): Promise<{ data: BlogPostListItem[] }> {
30
30
  Pair it with `@reblu/site-client` for a typed client that already knows these
31
31
  shapes.
32
32
 
33
+ ### Editable CTAs — slots
34
+
35
+ `Cta['slot']` is the union of placements a CTA can be configured for in nexus:
36
+ `'header' | 'hero' | 'blog-end' | 'services' | 'contact' | 'sticky-mobile' |
37
+ 'footer'`. `GET /api/site/ctas?slot=header` returns the active CTAs of the
38
+ persistent header; `@reblu/site-next`'s `EditableCta` renders them drop-in.
39
+
33
40
  ### Page blocks — canonical shape (REB-849 / REB-851 / REB-852 / REB-854)
34
41
 
35
42
  `GET /api/site/pages/[slug]` (`PageDetail`) returns BOTH `sections[]` (legacy,
@@ -132,6 +139,16 @@ may exist in both `tratamientos` and `especialidades`. Consequences for a SITE:
132
139
  legacy slug-only reference **only when that slug is unique** — otherwise
133
140
  `resolved` is `null`. Build the embed's href from `resolved.category`.
134
141
 
142
+ ### Featured pages per category (REB-869)
143
+
144
+ `PagesListQuery` is the query of `GET /api/site/pages`. With `category` set,
145
+ `featured: true` returns that category's featured pages (max 3) in the order
146
+ the tenant chose in nexus, instead of the normal listing. Unpublished,
147
+ deleted or moved pages are skipped; nothing featured → empty `data` (the API
148
+ never falls back). For a teaser that falls back to the category's first pages,
149
+ use `@reblu/site-client`'s `pages.featured(category)` with
150
+ `@reblu/site-blocks`' `PageCardGrid`.
151
+
135
152
  ### Shop — product trust items (REB-858)
136
153
 
137
154
  `ShopConfig.productTrustItems` is the store-wide list of short trust messages
package/dist/index.d.cts CHANGED
@@ -71,7 +71,7 @@ export type ContactFormSubmission = { id: string; createdAt: string;}
71
71
 
72
72
  export type CookieBanner = { enabled: boolean; title: string | null; body: string | null; acceptLabel: string | null; rejectLabel: string | null; manageLabel: string | null; policyUrl: string | null;}
73
73
 
74
- export type Cta = { id: string; slot: "hero" | "blog-end" | "services" | "contact" | "sticky-mobile" | "footer"; label: string; href: string; sortOrder: number; abVariant: string | null; analyticsId: string;}
74
+ export type Cta = { id: string; slot: "header" | "hero" | "blog-end" | "services" | "contact" | "sticky-mobile" | "footer"; label: string; href: string; sortOrder: number; abVariant: string | null; analyticsId: string;}
75
75
 
76
76
  export type CustomFieldDefinition = { id: string; entityType: "page" | "product"; key: string; label: string; valueType: "boolean" | "text" | "select" | "multiselect"; options: { value: string; label: string; }[]; isFilterable: boolean; showInDetail: boolean; unit: string | null; sortOrder: number;}
77
77
 
@@ -111,6 +111,8 @@ export type PageSlug = { slug: string; type: "SERVICE" | "CUSTOM"; upda
111
111
 
112
112
  export type PageType = "custom" | "service" | "treatments" | "specialties" | "team" | "generic"
113
113
 
114
+ export type PagesListQuery = { type?: ("custom" | "service" | "treatments" | "specialties" | "team" | "generic")[] | undefined; category?: string | undefined; page?: unknown; pageSize?: unknown; sort?: "order" | "-publishedAt" | undefined; featured?: boolean | undefined;}
115
+
114
116
  export type PagesListSort = "order" | "-publishedAt"
115
117
 
116
118
  export type PaginationMeta = { total?: number | undefined; page?: number | undefined; limit?: number | undefined; pages?: number | undefined;}
@@ -157,6 +159,10 @@ export type ShopCartValidationItem = { productId: string; name: string;
157
159
 
158
160
  export type ShopCategory = { id: string; name: string; slug: string; description: string | null; imageUrl: string | null; parentId: string | null; sortOrder: number;}
159
161
 
162
+ /**
163
+ * @deprecated REB-834 — `POST /api/site/shop/check-email` no longer reveals
164
+ * whether an account exists. Do not call it; removed in the next major.
165
+ */
160
166
  export type ShopCheckEmailInput = { email: string;}
161
167
 
162
168
  export type ShopCheckoutInput = { items: { productId: string; quantity: number; variantId?: string | null | undefined; }[]; customerEmail: string; customerName: string; successUrl: string; cancelUrl: string; customerPhone?: string | null | undefined; discountCode?: string | undefined; shippingAddress?: { firstName: string; lastName: string; street: string; city: string; postalCode: string; region?: string | undefined; country?: string | undefined; phone?: string | null | undefined; } | undefined; billing?: { billingName: string; billingNif?: string | undefined; billingEmail?: string | undefined; billingAddress?: { firstName: string; lastName: string; street: string; city: string; postalCode: string; region?: string | undefined; country?: string | undefined; phone?: string | null | undefined; } | undefined; } | undefined;}
@@ -167,6 +173,12 @@ export type ShopConfig = { shopName: string | null; defaultLowStockThresho
167
173
 
168
174
  export type ShopDiscountApplication = { valid: boolean; discount: number; type: "PERCENTAGE" | "FIXED" | null; code: string | null; error?: string | undefined;}
169
175
 
176
+ /**
177
+ * @deprecated REB-834 — `exists` is always `false`. Never branch UI on it:
178
+ * the checkout always offers «Iniciar sesión o continuar como invitado» and
179
+ * the server links the order to an existing account after payment. Removed
180
+ * in the next major.
181
+ */
170
182
  export type ShopEmailCheck = { exists: boolean;}
171
183
 
172
184
  export type ShopErrorCode = "STOCK_CHANGED" | "PRICE_CHANGED" | "PRODUCT_UNAVAILABLE" | "DISCOUNT_INVALID" | "SHIPPING_UNAVAILABLE" | "INVALID_INPUT" | "STORE_DISABLED" | "PAYMENT_PROVIDER_ERROR" | "INTERNAL_ERROR"
package/dist/index.d.ts CHANGED
@@ -71,7 +71,7 @@ export type ContactFormSubmission = { id: string; createdAt: string;}
71
71
 
72
72
  export type CookieBanner = { enabled: boolean; title: string | null; body: string | null; acceptLabel: string | null; rejectLabel: string | null; manageLabel: string | null; policyUrl: string | null;}
73
73
 
74
- export type Cta = { id: string; slot: "hero" | "blog-end" | "services" | "contact" | "sticky-mobile" | "footer"; label: string; href: string; sortOrder: number; abVariant: string | null; analyticsId: string;}
74
+ export type Cta = { id: string; slot: "header" | "hero" | "blog-end" | "services" | "contact" | "sticky-mobile" | "footer"; label: string; href: string; sortOrder: number; abVariant: string | null; analyticsId: string;}
75
75
 
76
76
  export type CustomFieldDefinition = { id: string; entityType: "page" | "product"; key: string; label: string; valueType: "boolean" | "text" | "select" | "multiselect"; options: { value: string; label: string; }[]; isFilterable: boolean; showInDetail: boolean; unit: string | null; sortOrder: number;}
77
77
 
@@ -111,6 +111,8 @@ export type PageSlug = { slug: string; type: "SERVICE" | "CUSTOM"; upda
111
111
 
112
112
  export type PageType = "custom" | "service" | "treatments" | "specialties" | "team" | "generic"
113
113
 
114
+ export type PagesListQuery = { type?: ("custom" | "service" | "treatments" | "specialties" | "team" | "generic")[] | undefined; category?: string | undefined; page?: unknown; pageSize?: unknown; sort?: "order" | "-publishedAt" | undefined; featured?: boolean | undefined;}
115
+
114
116
  export type PagesListSort = "order" | "-publishedAt"
115
117
 
116
118
  export type PaginationMeta = { total?: number | undefined; page?: number | undefined; limit?: number | undefined; pages?: number | undefined;}
@@ -157,6 +159,10 @@ export type ShopCartValidationItem = { productId: string; name: string;
157
159
 
158
160
  export type ShopCategory = { id: string; name: string; slug: string; description: string | null; imageUrl: string | null; parentId: string | null; sortOrder: number;}
159
161
 
162
+ /**
163
+ * @deprecated REB-834 — `POST /api/site/shop/check-email` no longer reveals
164
+ * whether an account exists. Do not call it; removed in the next major.
165
+ */
160
166
  export type ShopCheckEmailInput = { email: string;}
161
167
 
162
168
  export type ShopCheckoutInput = { items: { productId: string; quantity: number; variantId?: string | null | undefined; }[]; customerEmail: string; customerName: string; successUrl: string; cancelUrl: string; customerPhone?: string | null | undefined; discountCode?: string | undefined; shippingAddress?: { firstName: string; lastName: string; street: string; city: string; postalCode: string; region?: string | undefined; country?: string | undefined; phone?: string | null | undefined; } | undefined; billing?: { billingName: string; billingNif?: string | undefined; billingEmail?: string | undefined; billingAddress?: { firstName: string; lastName: string; street: string; city: string; postalCode: string; region?: string | undefined; country?: string | undefined; phone?: string | null | undefined; } | undefined; } | undefined;}
@@ -167,6 +173,12 @@ export type ShopConfig = { shopName: string | null; defaultLowStockThresho
167
173
 
168
174
  export type ShopDiscountApplication = { valid: boolean; discount: number; type: "PERCENTAGE" | "FIXED" | null; code: string | null; error?: string | undefined;}
169
175
 
176
+ /**
177
+ * @deprecated REB-834 — `exists` is always `false`. Never branch UI on it:
178
+ * the checkout always offers «Iniciar sesión o continuar como invitado» and
179
+ * the server links the order to an existing account after payment. Removed
180
+ * in the next major.
181
+ */
170
182
  export type ShopEmailCheck = { exists: boolean;}
171
183
 
172
184
  export type ShopErrorCode = "STOCK_CHANGED" | "PRICE_CHANGED" | "PRODUCT_UNAVAILABLE" | "DISCOUNT_INVALID" | "SHIPPING_UNAVAILABLE" | "INVALID_INPUT" | "STORE_DISABLED" | "PAYMENT_PROVIDER_ERROR" | "INTERNAL_ERROR"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reblu/site-contracts",
3
- "version": "1.12.0",
3
+ "version": "1.14.0",
4
4
  "private": false,
5
5
  "description": "Public, dependency-free TypeScript types for the Reblu SITE API (types only — no Zod, no Prisma at runtime).",
6
6
  "license": "Apache-2.0",