昨天我們把 Blog 部分結束
今天接著看 Program 的部分
Program 分為「探索課程頁」和「課程內頁」
我們今天一樣先從探索頁開始
程式碼我們一樣先上
import { Button as ChakraButton, Icon, SkeletonText } from '@chakra-ui/react'
// ~中間略~
export const StyledButton = styled(ChakraButton)`
// ~中間略~
`
const ProgramCollectionPage: React.VFC = () => {
const { formatMessage } = useIntl()
const [defaultActive] = useQueryParam('active', StringParam)
const [queryTitle] = useQueryParam('title', StringParam)
const [noSelector] = useQueryParam('noSelector', BooleanParam)
const [noBanner] = useQueryParam('noBanner', BooleanParam)
const [permitted] = useQueryParam('permitted', BooleanParam)
const { settings } = useApp()
const { pageTitle } = useNav()
const { currentLocale } = useContext(LocaleContext)
const { loadingPrograms, errorPrograms, programs } = usePublishedProgramCollection({
isPrivate: permitted ? undefined : false,
categoryId: defaultActive || undefined,
})
const [selectedCategoryId, setSelectedCategoryId] = useState<string | null>(defaultActive || null)
const categories: Category[] = sortBy(prop('position'))(
uniqBy(category => category.id, flatten(programs.map(program => program.categories).filter(notEmpty))),
)
const filteredPrograms = programs.filter(
program =>
(!selectedCategoryId || program.categories?.some(category => category.id === selectedCategoryId)) &&
(!program.supportLocales || program.supportLocales.find(locale => locale === currentLocale)),
)
useEffect(() => {
if (defaultActive) {
setSelectedCategoryId(defaultActive)
}
}, [defaultActive])
useEffect(() => {
ReactGA.ga('send', 'pageview')
}, [])
const programCollectionPageTitle = queryTitle || pageTitle || formatMessage(productMessages.program.title.explore)
return (
<DefaultLayout white>
<ProgramCollectionPageHelmet title={programCollectionPageTitle} programs={filteredPrograms} />
<StyledBanner>
<div className="container">
<StyledBannerTitle>
<Icon as={AiFillAppstore} className="mr-3" />
<span>{programCollectionPageTitle}</span>
</StyledBannerTitle>
{!noSelector && (
<StyledButton
colorScheme="primary"
variant={selectedCategoryId === null ? 'solid' : 'outline'}
className="mb-2"
onClick={() => setSelectedCategoryId(null)}
>
{formatMessage(commonMessages.button.allCategory)}
</StyledButton>
)}
{!noSelector &&
categories.map(category => (
<StyledButton
key={category.id}
colorScheme="primary"
variant={selectedCategoryId === category.id ? 'solid' : 'outline'}
className="ml-2 mb-2"
onClick={() => setSelectedCategoryId(category.id)}
>
{category.name}
</StyledButton>
))}
</div>
</StyledBanner>
<StyledCollection>
<div className="container">
{!noBanner && settings['program_collection_banner.enabled'] === 'true' && (
<ProgramCollectionBanner
link={settings['program_collection_banner.link']}
imgUrls={{
0: settings['program_collection_banner.img_url@0'],
425: settings['program_collection_banner.img_url@425'],
}}
/>
)}
{loadingPrograms ? (
<SkeletonText mt="1" noOfLines={4} spacing="4" />
) : !!errorPrograms ? (
<div>{formatMessage(commonMessages.status.readingFail)}</div>
) : (
<ProgramCollection programs={filteredPrograms} />
)}
</div>
</StyledCollection>
</DefaultLayout>
)
}
export default ProgramCollectionPage
和文章探索頁的排版不同
在課程探索頁的排版相較更為簡潔俐落
不囉唆,上圖
最上方為 Banner 區
但如果仔細看程式碼的話
Banner 區其實只有放「標題」和「分類按鈕」
在 ProgramCollectionPage 使用了非常多「useQueryParam」
用來判斷,當有不同的 URL 參數時,顯示對應條件的畫面
下方顯示的是各個課程的列表
他這邊有做 conditional rendering
如果今天「program_collection_banner.enabled」的設定是打開的
則顯示「ProgramCollectionBanner」這個元件
但以目前這個範例,它的設定使沒有被啟用的
故顯示「ProgramCollection」這個元件
但在專案中目前都是以「ProgramCollection」為主
「ProgramCollectionBanner」已經不怎麼使用
我們明天重點看一下「ProgramCollection」和 「ProgramCard」這兩個元件