@reblu/site-blocks 0.22.0 → 0.24.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 +25 -0
- package/README.md +28 -6
- package/dist/index.cjs +48 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +22 -2
- package/dist/index.d.ts +22 -2
- package/dist/index.js +48 -3
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
# @reblu/site-blocks
|
|
2
2
|
|
|
3
|
+
## 0.24.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- c7f3a43: Course catalogue filters are tenant config (REB-1234).
|
|
8
|
+
- `@reblu/site-contracts`: new `AcademyConfigPublic` and `AcademyCatalogFiltersPublic` types for `GET /api/site/academy/config`.
|
|
9
|
+
- `@reblu/site-client`: new `client.academy.config()`, returning `{ catalogFilters: { category, instructor, price } }` as switched on in nexus. A filter the tenant never set is `true`.
|
|
10
|
+
- `@reblu/site-blocks`: `CourseCatalogFilters` gains a category filter (`categories` + `hrefs.category`, `active.category`) and an `enabled` prop. Pass `catalogFilters` as `enabled` and only the switched-on filters render; with all off it renders nothing. Without `enabled` and `categories` the output is unchanged.
|
|
11
|
+
|
|
12
|
+
### Patch Changes
|
|
13
|
+
|
|
14
|
+
- Updated dependencies [c7f3a43]
|
|
15
|
+
- @reblu/site-contracts@1.24.0
|
|
16
|
+
|
|
17
|
+
## 0.23.0
|
|
18
|
+
|
|
19
|
+
### Minor Changes
|
|
20
|
+
|
|
21
|
+
- f2ed9e2: Default Spanish messages for the new checkout `ShopErrorCode`s (REB-1197, REB-1198): `PICKUP_UNAVAILABLE`, `PICKUP_LOCATION_INVALID`, `SHIPPING_ADDRESS_REQUIRED`, `ORDER_TOO_HEAVY`, `TERMS_NOT_ACCEPTED`, `TERMS_VERSION_CHANGED` and `DELIVERY_UNAVAILABLE`, in `DEFAULT_SHOP_ERROR_MESSAGES` (override any of them with `getShopErrorMessage`'s `overrides`).
|
|
22
|
+
|
|
23
|
+
### Patch Changes
|
|
24
|
+
|
|
25
|
+
- Updated dependencies [f2ed9e2]
|
|
26
|
+
- @reblu/site-contracts@1.23.0
|
|
27
|
+
|
|
3
28
|
## 0.22.0
|
|
4
29
|
|
|
5
30
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -29,7 +29,7 @@ colour on the red background. See `docs/theming/theming.md`.
|
|
|
29
29
|
| `Treatment`, `ParentPage`, `ProfileCard`, `ProductCard` | `.` | Presentational organisms over the SITE API contracts. RSC-safe. `Specialty`: deprecated alias, see below. |
|
|
30
30
|
| `ProfessionalFields` | `.` | The tenant's own professional fields (`professional.fields`) on a team card or profile. RSC-safe. See below. |
|
|
31
31
|
| `ShopFilters` | `.` | Plug-and-play shop filter chrome over `GET /api/site/shop/facets`. RSC-safe. See "Shop filters" below. |
|
|
32
|
-
| `CourseCatalogFilters`, `DEFAULT_COURSE_PRICE_BANDS` | `.` | Course catalogue filters by instructor and price (`client.academy.
|
|
32
|
+
| `CourseCatalogFilters`, `DEFAULT_COURSE_PRICE_BANDS` | `.` | Course catalogue filters by category, instructor and price, each switchable per tenant (`client.academy.config()`). RSC-safe. See "Course catalogue filters". |
|
|
33
33
|
| `CartLineList`, `CartSummary`, `CartAdjustmentsNotice`, `AddedToCartToast`, `CheckoutLoginPrompt`, `CheckoutForm`, `OrderConfirmation` | `.` | Cart & checkout organisms. RSC-safe by construction. See "Cart & checkout" below. |
|
|
34
34
|
| `PageBlocks`, `FaqAccordion` | `.` | Registry-block renderer (`PageDetail.blocks`) and native `<details>` FAQ. Server Components. See "Page blocks" below. |
|
|
35
35
|
| `ProductTrustItems`, `DEFAULT_TRUST_ICONS` | `.` | Store-wide trust messages from `ShopConfig.productTrustItems`. Server Component. See "Product trust items" below. |
|
|
@@ -991,13 +991,19 @@ import { CourseSyllabus } from '@reblu/site-blocks/client'
|
|
|
991
991
|
and `course-syllabus-preview`. No slot was renamed in REB-1135. It renders
|
|
992
992
|
nothing for a course without modules.
|
|
993
993
|
|
|
994
|
-
## Course catalogue filters — `CourseCatalogFilters` (REB-547)
|
|
994
|
+
## Course catalogue filters — `CourseCatalogFilters` (REB-547, REB-1234)
|
|
995
995
|
|
|
996
|
-
Drop-in filters for the academy catalogue:
|
|
996
|
+
Drop-in filters for the academy catalogue: a category filter fed by
|
|
997
|
+
`client.academy.categories()`, an instructor filter fed by
|
|
997
998
|
`client.academy.instructors()` (instructors of published courses, sorted by
|
|
998
999
|
name) and a price filter (bands in integer cents plus «Gratis»). Each option
|
|
999
|
-
maps 1:1 onto a `client.academy.courses()` param: `
|
|
1000
|
-
`priceMax`, `free`.
|
|
1000
|
+
maps 1:1 onto a `client.academy.courses()` param: `category`, `instructor`,
|
|
1001
|
+
`priceMin`, `priceMax`, `free`.
|
|
1002
|
+
|
|
1003
|
+
Which filters show is the tenant's choice, made in nexus › Formación ›
|
|
1004
|
+
Configuración › «Filtros del catálogo» and served by `client.academy.config()`
|
|
1005
|
+
as `catalogFilters`. Pass it as `enabled`: the component renders only the
|
|
1006
|
+
filters switched on, with no SITE code change when the tenant flips one. Same RSC-safe contract as `ShopFilters`: every option is a
|
|
1001
1007
|
real `<a href>`; pass `onChange` only from a `'use client'` boundary.
|
|
1002
1008
|
|
|
1003
1009
|
```tsx
|
|
@@ -1013,14 +1019,17 @@ export default async function CoursesPage({
|
|
|
1013
1019
|
const sp = await searchParams
|
|
1014
1020
|
const num = (v?: string) => (v ? Number(v) : undefined)
|
|
1015
1021
|
const active: AcademyCourseListQuery = {
|
|
1022
|
+
category: sp.category,
|
|
1016
1023
|
instructor: sp.instructor,
|
|
1017
1024
|
priceMin: num(sp.priceMin),
|
|
1018
1025
|
priceMax: num(sp.priceMax),
|
|
1019
1026
|
free: sp.free === 'true',
|
|
1020
1027
|
}
|
|
1021
|
-
const [courses, instructors] = await Promise.all([
|
|
1028
|
+
const [courses, categories, instructors, academy] = await Promise.all([
|
|
1022
1029
|
client.academy.courses(active),
|
|
1030
|
+
client.academy.categories(),
|
|
1023
1031
|
client.academy.instructors(),
|
|
1032
|
+
client.academy.config(),
|
|
1024
1033
|
])
|
|
1025
1034
|
|
|
1026
1035
|
// Keep the other params; `free` and a price band exclude each other.
|
|
@@ -1035,10 +1044,14 @@ export default async function CoursesPage({
|
|
|
1035
1044
|
|
|
1036
1045
|
return (
|
|
1037
1046
|
<>
|
|
1047
|
+
{/* Your search box stays: it is not a switchable filter. */}
|
|
1038
1048
|
<CourseCatalogFilters
|
|
1049
|
+
enabled={academy.catalogFilters}
|
|
1050
|
+
categories={categories.data}
|
|
1039
1051
|
instructors={instructors.data}
|
|
1040
1052
|
active={active}
|
|
1041
1053
|
hrefs={{
|
|
1054
|
+
category: (slug) => href({ category: slug }),
|
|
1042
1055
|
instructor: (id) => href({ instructor: id }),
|
|
1043
1056
|
price: (min, max) => href({ priceMin: min, priceMax: max, free: false }),
|
|
1044
1057
|
free: (next) => href({ free: next, priceMin: undefined, priceMax: undefined }),
|
|
@@ -1051,6 +1064,15 @@ export default async function CoursesPage({
|
|
|
1051
1064
|
}
|
|
1052
1065
|
```
|
|
1053
1066
|
|
|
1067
|
+
- **Tenant switches.** `enabled` takes `{ category, instructor, price }`; a
|
|
1068
|
+
key that is missing counts as on, so leaving out `enabled` shows every filter
|
|
1069
|
+
(the behaviour before REB-1234). A tenant that never set the switches gets
|
|
1070
|
+
all three `true` from `client.academy.config()`. With every filter off the
|
|
1071
|
+
component renders nothing, and the search box (yours) stays.
|
|
1072
|
+
- **Category needs data and an href.** The category group shows only with
|
|
1073
|
+
`categories` and `hrefs.category`; without them the component behaves as
|
|
1074
|
+
before. An active category slug that is no longer listed keeps the group
|
|
1075
|
+
visible, so it can be cleared.
|
|
1054
1076
|
- **Price is integer cents.** The default bands (`DEFAULT_COURSE_PRICE_BANDS`)
|
|
1055
1077
|
are EUR: up to 50, 50 to 150, 150 to 300, from 300. Pass `priceBands` for
|
|
1056
1078
|
another catalogue and `currencyCode` / `locale` for the labels.
|
package/dist/index.cjs
CHANGED
|
@@ -125,7 +125,17 @@ var DEFAULT_SHOP_ERROR_MESSAGES = {
|
|
|
125
125
|
INVOICING_NOT_READY: "No se puede completar la compra en este momento. Int\xE9ntalo m\xE1s tarde.",
|
|
126
126
|
FULL_INVOICE_UNAVAILABLE: "Esta tienda no emite facturas completas: contin\xFAa sin solicitarla.",
|
|
127
127
|
// REB-912 — the code used up its total uses; refused before any payment.
|
|
128
|
-
DISCOUNT_LIMIT_REACHED: "Este c\xF3digo ya ha alcanzado su l\xEDmite de usos"
|
|
128
|
+
DISCOUNT_LIMIT_REACHED: "Este c\xF3digo ya ha alcanzado su l\xEDmite de usos",
|
|
129
|
+
// REB-1197 — delivery refusals before any payment.
|
|
130
|
+
PICKUP_UNAVAILABLE: "La recogida no est\xE1 disponible en esta tienda. Elige el env\xEDo a domicilio.",
|
|
131
|
+
PICKUP_LOCATION_INVALID: "El punto de recogida elegido ya no est\xE1 disponible. Elige otro.",
|
|
132
|
+
SHIPPING_ADDRESS_REQUIRED: "Indica la direcci\xF3n de env\xEDo para recibir tu pedido.",
|
|
133
|
+
ORDER_TOO_HEAVY: "Tu pedido supera el peso m\xE1ximo. Reduce alguna cantidad o divide la compra.",
|
|
134
|
+
// REB-1198 — terms acceptance, before any payment.
|
|
135
|
+
TERMS_NOT_ACCEPTED: "Para continuar, acepta las condiciones generales de contrataci\xF3n.",
|
|
136
|
+
TERMS_VERSION_CHANGED: "Las condiciones de contrataci\xF3n se han actualizado. Rev\xEDsalas y vuelve a aceptarlas.",
|
|
137
|
+
// Owner, 2-Nov — no home delivery nor pickup for a cart that ships.
|
|
138
|
+
DELIVERY_UNAVAILABLE: "Ahora mismo no podemos entregar productos f\xEDsicos. Int\xE9ntalo m\xE1s tarde o contacta con la tienda."
|
|
129
139
|
};
|
|
130
140
|
var DEFAULT_SHOP_ERROR_FALLBACK_MESSAGE = "No se ha podido completar la operaci\xF3n. Int\xE9ntalo de nuevo.";
|
|
131
141
|
function getShopErrorMessage(code, overrides, fallback = DEFAULT_SHOP_ERROR_FALLBACK_MESSAGE) {
|
|
@@ -14814,6 +14824,8 @@ var DEFAULT_COURSE_PRICE_BANDS = [
|
|
|
14814
14824
|
{ min: 3e4 }
|
|
14815
14825
|
];
|
|
14816
14826
|
var DEFAULT_COURSE_CATALOG_FILTERS_LABELS = {
|
|
14827
|
+
category: "Categor\xEDa",
|
|
14828
|
+
allCategories: "Todas",
|
|
14817
14829
|
instructor: "Docente",
|
|
14818
14830
|
allInstructors: "Todos",
|
|
14819
14831
|
price: "Precio",
|
|
@@ -14837,6 +14849,8 @@ function bandLabel(band, t, currencyCode, locale) {
|
|
|
14837
14849
|
}
|
|
14838
14850
|
function CourseCatalogFilters({
|
|
14839
14851
|
instructors,
|
|
14852
|
+
categories = [],
|
|
14853
|
+
enabled = {},
|
|
14840
14854
|
hrefs,
|
|
14841
14855
|
active = {},
|
|
14842
14856
|
onChange,
|
|
@@ -14848,9 +14862,39 @@ function CourseCatalogFilters({
|
|
|
14848
14862
|
}) {
|
|
14849
14863
|
const t = { ...DEFAULT_COURSE_CATALOG_FILTERS_LABELS, ...labels };
|
|
14850
14864
|
const named = instructors.filter((i) => instructorName(i) !== "");
|
|
14851
|
-
const
|
|
14865
|
+
const sortedCategories = [...categories].sort((a, b2) => a.sortOrder - b2.sortOrder);
|
|
14866
|
+
const categoryHref = hrefs.category;
|
|
14867
|
+
const showCategories = enabled.category !== false && categoryHref !== void 0 && (sortedCategories.length > 0 || active.category !== void 0);
|
|
14868
|
+
const showInstructors = enabled.instructor !== false && (named.length > 0 || active.instructor !== void 0);
|
|
14869
|
+
const showPrice = enabled.price !== false;
|
|
14852
14870
|
const isFree = Boolean(active.free);
|
|
14871
|
+
if (!showCategories && !showInstructors && !showPrice) return null;
|
|
14853
14872
|
return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: (0, import_utils15.cn)("space-y-6", className), "data-slot": "course-catalog-filters", children: [
|
|
14873
|
+
showCategories && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(FilterFieldset, { legend: t.category, children: [
|
|
14874
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
14875
|
+
OptionLink,
|
|
14876
|
+
{
|
|
14877
|
+
href: categoryHref(void 0),
|
|
14878
|
+
active: active.category === void 0,
|
|
14879
|
+
label: t.allCategories,
|
|
14880
|
+
onActivate: onChange ? () => onChange({ category: void 0 }) : void 0
|
|
14881
|
+
}
|
|
14882
|
+
),
|
|
14883
|
+
sortedCategories.map((category) => {
|
|
14884
|
+
const isActive = active.category === category.slug;
|
|
14885
|
+
const next2 = isActive ? void 0 : category.slug;
|
|
14886
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
14887
|
+
OptionLink,
|
|
14888
|
+
{
|
|
14889
|
+
href: categoryHref(next2),
|
|
14890
|
+
active: isActive,
|
|
14891
|
+
label: category.name,
|
|
14892
|
+
onActivate: onChange ? () => onChange({ category: next2 }) : void 0
|
|
14893
|
+
},
|
|
14894
|
+
category.id
|
|
14895
|
+
);
|
|
14896
|
+
})
|
|
14897
|
+
] }),
|
|
14854
14898
|
showInstructors && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(FilterFieldset, { legend: t.instructor, children: [
|
|
14855
14899
|
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
14856
14900
|
OptionLink,
|
|
@@ -14876,7 +14920,7 @@ function CourseCatalogFilters({
|
|
|
14876
14920
|
);
|
|
14877
14921
|
})
|
|
14878
14922
|
] }),
|
|
14879
|
-
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(FilterFieldset, { legend: t.price, children: [
|
|
14923
|
+
showPrice && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(FilterFieldset, { legend: t.price, children: [
|
|
14880
14924
|
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
14881
14925
|
OptionLink,
|
|
14882
14926
|
{
|
|
@@ -14911,6 +14955,7 @@ function CourseCatalogFilters({
|
|
|
14911
14955
|
onClick: onChange ? (event) => {
|
|
14912
14956
|
event.preventDefault();
|
|
14913
14957
|
onChange({
|
|
14958
|
+
category: void 0,
|
|
14914
14959
|
instructor: void 0,
|
|
14915
14960
|
priceMin: void 0,
|
|
14916
14961
|
priceMax: void 0,
|