11---
22import StarlightPage from " @astrojs/starlight/components/StarlightPage.astro" ;
3- import { getCollection } from " astro:content" ;
3+ import { getCollection , type CollectionEntry } from " astro:content" ;
44import { marked } from " marked" ;
55import { CardGrid , LinkTitleCard , Description } from " ~/components" ;
66
@@ -12,21 +12,201 @@ const frontmatter = {
1212} as const ;
1313
1414const learningPaths = await getCollection (" learning-paths" );
15+
16+ function lpGroups(paths : Array <CollectionEntry <" learning-paths" >>) {
17+ const groups = paths .flatMap ((p ) => p .data .product_group ?? []);
18+ const additional = paths .flatMap ((p ) => p .data .additional_groups ?? []);
19+
20+ const unique = [... new Set (groups .concat (additional ))];
21+
22+ return unique .sort ();
23+ }
24+
25+ function lpProducts(paths : Array <CollectionEntry <" learning-paths" >>) {
26+ const products = paths .flatMap ((p ) => p .data .products ?? []);
27+
28+ const unique = [... new Set (products )];
29+
30+ return unique .sort ();
31+ }
32+
33+ const groups = lpGroups (learningPaths );
34+ const products = lpProducts (learningPaths );
1535---
1636
1737<StarlightPage frontmatter ={ frontmatter } >
1838 <Description >
1939 { frontmatter .description }
2040 </Description >
21- <CardGrid >
22- {
23- learningPaths .map ((lp ) => (
24- <LinkTitleCard
25- title = { lp .data .title }
26- href = { lp .data .path }
27- set :html = { marked .parse (lp .data .description )}
28- />
29- ))
30- }
31- </CardGrid >
41+ <div class =" flex" >
42+ <div class =" w-1/4 pr-4 hidden lg:block" >
43+ <div class =" border-b-2 border-accent-600 dark:border-accent-200" >
44+ <h2 >Filters</h2 >
45+ </div >
46+ <div id =" areas-filter" >
47+ <p >
48+ <strong >Product areas</strong >
49+ </p >
50+ {
51+ groups .map ((group ) => (
52+ <>
53+ <input type = " checkbox" id = { group } />
54+ <label for = { group } >{ group } </label >
55+ <br />
56+ </>
57+ ))
58+ }
59+ </div >
60+ <div id =" products-filter" >
61+ <p >
62+ <strong >Products</strong >
63+ </p >
64+ {
65+ products .map ((product ) => (
66+ <>
67+ <input type = " checkbox" id = { product } />
68+ <label for = { product } >{ product } </label >
69+ <br />
70+ </>
71+ ))
72+ }
73+ </div >
74+ </div >
75+ <div id =" lp-grid" class =" w-full" >
76+ <CardGrid >
77+ {
78+ learningPaths
79+ .sort ((a , b ) => a .data .priority - b .data .priority )
80+ .map ((lp ) => (
81+ <div
82+ class = " [&>article]:h-full [&>article]:w-full"
83+ data-groups = { JSON .stringify (
84+ [lp .data .product_group ].concat (
85+ lp .data .additional_groups ?? [],
86+ ),
87+ )}
88+ data-products = { JSON .stringify (lp .data .products )}
89+ >
90+ <LinkTitleCard
91+ title = { lp .data .title }
92+ href = { lp .data .path }
93+ set :html = { marked .parse (lp .data .description )}
94+ />
95+ </div >
96+ ))
97+ }
98+ </CardGrid >
99+ </div >
100+ </div >
32101</StarlightPage >
102+
103+ <script >
104+ function getAreaFilters(): NodeListOf<HTMLInputElement> | undefined {
105+ const groupFiltersContainer =
106+ document.querySelector<HTMLDivElement>("#areas-filter");
107+
108+ if (!groupFiltersContainer) return undefined;
109+
110+ const groupFilters =
111+ groupFiltersContainer.querySelectorAll<HTMLInputElement>("input");
112+
113+ if (!groupFilters) return undefined;
114+
115+ return groupFilters;
116+ }
117+
118+ function getProductFilters(): NodeListOf<HTMLInputElement> | undefined {
119+ const groupFiltersContainer =
120+ document.querySelector<HTMLDivElement>("#products-filter");
121+
122+ if (!groupFiltersContainer) return undefined;
123+
124+ const groupFilters =
125+ groupFiltersContainer.querySelectorAll<HTMLInputElement>("input");
126+
127+ if (!groupFilters) return undefined;
128+
129+ return groupFilters;
130+ }
131+
132+ function getLearningPaths(): NodeListOf<HTMLDivElement> | undefined {
133+ const learningPathsContainer =
134+ document.querySelector<HTMLDivElement>("#lp-grid");
135+
136+ if (!learningPathsContainer) return undefined;
137+
138+ const learningPaths =
139+ learningPathsContainer.querySelectorAll<HTMLDivElement>("[data-groups]");
140+
141+ if (!learningPaths) return undefined;
142+
143+ return learningPaths;
144+ }
145+
146+ function filterProducts() {
147+ const areaFilters = getAreaFilters();
148+ const productFilters = getProductFilters();
149+
150+ if (!areaFilters || !productFilters) return;
151+
152+ const checkedAreas: Array<string> = [];
153+ const checkedProducts: Array<string> = [];
154+
155+ for (const filter of areaFilters) {
156+ if (filter.checked) {
157+ checkedAreas.push(filter.id);
158+ }
159+ }
160+
161+ for (const filter of productFilters) {
162+ if (filter.checked) {
163+ checkedProducts.push(filter.id);
164+ }
165+ }
166+
167+ const learningPaths = getLearningPaths();
168+
169+ if (!learningPaths) return;
170+
171+ if (checkedAreas.length === 0 && checkedProducts.length === 0) {
172+ learningPaths.forEach((card) => {
173+ card.style.display = "";
174+ });
175+ return;
176+ }
177+
178+ for (const learningPath of learningPaths) {
179+ if (!learningPath.dataset.groups || !learningPath.dataset.products)
180+ continue;
181+
182+ const groups: Array<string> = JSON.parse(learningPath.dataset.groups);
183+ const products: Array<string> = JSON.parse(learningPath.dataset.products);
184+
185+ let show = true;
186+
187+ show = checkedAreas.some((v) => {
188+ return groups.includes(v);
189+ });
190+
191+ if (!show) {
192+ show = checkedProducts.some((v) => {
193+ return products.includes(v);
194+ });
195+ }
196+
197+ if (show) {
198+ learningPath.style.display = "";
199+ } else {
200+ learningPath.style.display = "none";
201+ }
202+ }
203+ }
204+
205+ getAreaFilters()?.forEach((input) => {
206+ input.addEventListener("change", filterProducts);
207+ });
208+
209+ getProductFilters()?.forEach((input) => {
210+ input.addEventListener("change", filterProducts);
211+ });
212+ </script >
0 commit comments