|
1 | | -import type { Context, Page } from '@/types' |
| 1 | +import type { Context, Page, ResolvedArticle } from '@/types' |
2 | 2 | import type { PageTransformer, TemplateData, Section, LinkData } from './types' |
3 | 3 | import { renderContent } from '@/content-render/index' |
4 | 4 | import { loadTemplate } from '@/article-api/lib/load-template' |
5 | 5 | import { getAllTocItems, flattenTocItems } from '@/article-api/lib/get-all-toc-items' |
6 | 6 |
|
7 | | -interface RecommendedItem { |
8 | | - href: string |
9 | | - title?: string |
10 | | - intro?: string |
11 | | -} |
12 | | - |
13 | 7 | interface BespokeLandingPage extends Omit<Page, 'featuredLinks'> { |
14 | 8 | featuredLinks?: Record<string, Array<string | { href: string; title: string; intro?: string }>> |
15 | 9 | children?: string[] |
16 | | - recommended?: RecommendedItem[] |
17 | | - rawRecommended?: string[] |
| 10 | + carousels?: Record<string, ResolvedArticle[]> |
| 11 | + rawCarousels?: Record<string, string[]> |
18 | 12 | includedCategories?: string[] |
19 | 13 | } |
20 | 14 |
|
21 | 15 | /** |
22 | 16 | * Transforms bespoke-landing pages into markdown format. |
23 | | - * Handles recommended carousel and full article listings. |
| 17 | + * Handles carousels and full article listings. |
24 | 18 | * Note: Unlike discovery-landing, bespoke-landing shows ALL articles |
25 | 19 | * regardless of includedCategories. |
26 | 20 | */ |
@@ -53,38 +47,48 @@ export class BespokeLandingTransformer implements PageTransformer { |
53 | 47 | const bespokePage = page as BespokeLandingPage |
54 | 48 | const sections: Section[] = [] |
55 | 49 |
|
56 | | - // Recommended carousel |
57 | | - const recommended = bespokePage.recommended ?? bespokePage.rawRecommended |
58 | | - if (recommended && recommended.length > 0) { |
| 50 | + // Process carousels (each carousel becomes a section) |
| 51 | + const carousels = bespokePage.carousels ?? bespokePage.rawCarousels |
| 52 | + if (carousels && typeof carousels === 'object') { |
59 | 53 | const { default: getLearningTrackLinkData } = await import( |
60 | 54 | '@/learning-track/lib/get-link-data' |
61 | 55 | ) |
62 | 56 |
|
63 | | - let links: LinkData[] |
64 | | - if (typeof recommended[0] === 'object' && 'title' in recommended[0]) { |
65 | | - links = recommended.map((item) => ({ |
66 | | - href: typeof item === 'string' ? item : item.href, |
67 | | - title: (typeof item === 'object' && item.title) || '', |
68 | | - intro: (typeof item === 'object' && item.intro) || '', |
69 | | - })) |
70 | | - } else { |
71 | | - const linkData = await getLearningTrackLinkData(recommended as string[], context, { |
72 | | - title: true, |
73 | | - intro: true, |
74 | | - }) |
75 | | - links = (linkData || []).map((item: { href: string; title?: string; intro?: string }) => ({ |
76 | | - href: item.href, |
77 | | - title: item.title || '', |
78 | | - intro: item.intro || '', |
79 | | - })) |
80 | | - } |
81 | | - |
82 | | - const validLinks = links.filter((l) => l.href && l.title) |
83 | | - if (validLinks.length > 0) { |
84 | | - sections.push({ |
85 | | - title: 'Recommended', |
86 | | - groups: [{ title: null, links: validLinks }], |
87 | | - }) |
| 57 | + for (const [carouselKey, articles] of Object.entries(carousels)) { |
| 58 | + if (!Array.isArray(articles) || articles.length === 0) continue |
| 59 | + |
| 60 | + let links: LinkData[] |
| 61 | + if (typeof articles[0] === 'object' && 'title' in articles[0]) { |
| 62 | + // Already resolved articles |
| 63 | + links = articles.map((item) => ({ |
| 64 | + href: typeof item === 'string' ? item : item.href, |
| 65 | + title: (typeof item === 'object' && item.title) || '', |
| 66 | + intro: (typeof item === 'object' && item.intro) || '', |
| 67 | + })) |
| 68 | + } else { |
| 69 | + // Raw paths that need resolution |
| 70 | + const linkData = await getLearningTrackLinkData(articles as string[], context, { |
| 71 | + title: true, |
| 72 | + intro: true, |
| 73 | + }) |
| 74 | + links = (linkData || []).map( |
| 75 | + (item: { href: string; title?: string; intro?: string }) => ({ |
| 76 | + href: item.href, |
| 77 | + title: item.title || '', |
| 78 | + intro: item.intro || '', |
| 79 | + }), |
| 80 | + ) |
| 81 | + } |
| 82 | + |
| 83 | + const validLinks = links.filter((l) => l.href && l.title) |
| 84 | + if (validLinks.length > 0) { |
| 85 | + // Use carousel key as title (capitalize first letter) |
| 86 | + const sectionTitle = carouselKey.charAt(0).toUpperCase() + carouselKey.slice(1) |
| 87 | + sections.push({ |
| 88 | + title: sectionTitle, |
| 89 | + groups: [{ title: null, links: validLinks }], |
| 90 | + }) |
| 91 | + } |
88 | 92 | } |
89 | 93 | } |
90 | 94 |
|
|
0 commit comments