@reblu/site-blocks 0.23.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 +14 -0
- package/README.md +28 -6
- package/dist/index.cjs +37 -2
- 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 +37 -2
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
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
|
+
|
|
3
17
|
## 0.23.0
|
|
4
18
|
|
|
5
19
|
### 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
|
@@ -14824,6 +14824,8 @@ var DEFAULT_COURSE_PRICE_BANDS = [
|
|
|
14824
14824
|
{ min: 3e4 }
|
|
14825
14825
|
];
|
|
14826
14826
|
var DEFAULT_COURSE_CATALOG_FILTERS_LABELS = {
|
|
14827
|
+
category: "Categor\xEDa",
|
|
14828
|
+
allCategories: "Todas",
|
|
14827
14829
|
instructor: "Docente",
|
|
14828
14830
|
allInstructors: "Todos",
|
|
14829
14831
|
price: "Precio",
|
|
@@ -14847,6 +14849,8 @@ function bandLabel(band, t, currencyCode, locale) {
|
|
|
14847
14849
|
}
|
|
14848
14850
|
function CourseCatalogFilters({
|
|
14849
14851
|
instructors,
|
|
14852
|
+
categories = [],
|
|
14853
|
+
enabled = {},
|
|
14850
14854
|
hrefs,
|
|
14851
14855
|
active = {},
|
|
14852
14856
|
onChange,
|
|
@@ -14858,9 +14862,39 @@ function CourseCatalogFilters({
|
|
|
14858
14862
|
}) {
|
|
14859
14863
|
const t = { ...DEFAULT_COURSE_CATALOG_FILTERS_LABELS, ...labels };
|
|
14860
14864
|
const named = instructors.filter((i) => instructorName(i) !== "");
|
|
14861
|
-
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;
|
|
14862
14870
|
const isFree = Boolean(active.free);
|
|
14871
|
+
if (!showCategories && !showInstructors && !showPrice) return null;
|
|
14863
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
|
+
] }),
|
|
14864
14898
|
showInstructors && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(FilterFieldset, { legend: t.instructor, children: [
|
|
14865
14899
|
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
14866
14900
|
OptionLink,
|
|
@@ -14886,7 +14920,7 @@ function CourseCatalogFilters({
|
|
|
14886
14920
|
);
|
|
14887
14921
|
})
|
|
14888
14922
|
] }),
|
|
14889
|
-
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(FilterFieldset, { legend: t.price, children: [
|
|
14923
|
+
showPrice && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(FilterFieldset, { legend: t.price, children: [
|
|
14890
14924
|
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
14891
14925
|
OptionLink,
|
|
14892
14926
|
{
|
|
@@ -14921,6 +14955,7 @@ function CourseCatalogFilters({
|
|
|
14921
14955
|
onClick: onChange ? (event) => {
|
|
14922
14956
|
event.preventDefault();
|
|
14923
14957
|
onChange({
|
|
14958
|
+
category: void 0,
|
|
14924
14959
|
instructor: void 0,
|
|
14925
14960
|
priceMin: void 0,
|
|
14926
14961
|
priceMax: void 0,
|