From 0c3ebd125c5504d8dc3924479e5ec056f101f049 Mon Sep 17 00:00:00 2001 From: ben196888 Date: Wed, 27 Sep 2023 01:26:26 +0000 Subject: [PATCH 01/13] =?UTF-8?q?Create=20Page=20=E2=80=9Ccards=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- homepage/_pages/en/cards.md | 4 ++++ homepage/_pages/zh-tw/cards.md | 4 ++++ 2 files changed, 8 insertions(+) create mode 100644 homepage/_pages/en/cards.md create mode 100644 homepage/_pages/zh-tw/cards.md diff --git a/homepage/_pages/en/cards.md b/homepage/_pages/en/cards.md new file mode 100644 index 00000000..e58dd05f --- /dev/null +++ b/homepage/_pages/en/cards.md @@ -0,0 +1,4 @@ +--- +unique_slug: cards +name: Cards +--- diff --git a/homepage/_pages/zh-tw/cards.md b/homepage/_pages/zh-tw/cards.md new file mode 100644 index 00000000..6d06dee0 --- /dev/null +++ b/homepage/_pages/zh-tw/cards.md @@ -0,0 +1,4 @@ +--- +unique_slug: cards +name: 卡片頁 +--- From 74fbcb1a0eba644e52526d45e7542e96e5771081 Mon Sep 17 00:00:00 2001 From: Ben Liu Date: Tue, 17 Oct 2023 10:56:36 +0700 Subject: [PATCH 02/13] feat(homepage): add content mapper in card page --- homepage/src/pages/cards.jsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/homepage/src/pages/cards.jsx b/homepage/src/pages/cards.jsx index 04dc9aff..f621e522 100644 --- a/homepage/src/pages/cards.jsx +++ b/homepage/src/pages/cards.jsx @@ -1,7 +1,9 @@ import Head from 'next/head'; import Headline from '../components/headline'; import CardsGrid from '../components/cardsGrid'; +import contentMapper from '../layouts/contentMapper'; +import { fetchPage } from '../lib/fetchPage'; import { fetchCards } from '../lib/fetchCards'; import { getNavigationList } from '../lib/getNavigationList'; @@ -33,6 +35,7 @@ export const getStaticProps = async ({ locale }) => { }); const navigationList = await getNavigationList(locale); + const page = fetchPage(locale, 'cards'); const title = { en: 'Card Introduction', @@ -92,6 +95,7 @@ export const getStaticProps = async ({ locale }) => { 'open data': projectCardSubtitle['open data'][locale], 'open source': projectCardSubtitle['open source'][locale], }, + page, }, }; }; @@ -104,6 +108,7 @@ const cards = ({ jobCardTitle, eventCardTitle, projectCardSubtitle, + page, }) => { return ( <> @@ -111,6 +116,7 @@ const cards = ({ {headInfo.title} + {page.data['layout_list']?.map(contentMapper)} Date: Tue, 17 Oct 2023 12:40:55 +0700 Subject: [PATCH 03/13] feat(homepage): add cards layout in page collection --- homepage/_pages/en/cards.md | 24 ++++++++++++++++ homepage/_pages/zh-tw/cards.md | 24 ++++++++++++++++ homepage/src/pages/admin/config.json | 41 ++++++++++++++++++++++++++++ 3 files changed, 89 insertions(+) diff --git a/homepage/_pages/en/cards.md b/homepage/_pages/en/cards.md index e58dd05f..105cffda 100644 --- a/homepage/_pages/en/cards.md +++ b/homepage/_pages/en/cards.md @@ -1,4 +1,28 @@ --- unique_slug: cards name: Cards +layout_list: + - type: layout_headline + title: Card Introduction + - type: layout_cards + card_type: project + title: Project Card | Open Government + card_tags: + - open gov + - type: layout_cards + title: Project Card | Open Data + card_type: project + card_tags: + - open data + - type: layout_cards + title: Project Card | Open Source + card_type: project + card_tags: + - open source + - type: layout_cards + title: Job Card + card_type: job + - type: layout_cards + title: Event Card + card_type: event --- diff --git a/homepage/_pages/zh-tw/cards.md b/homepage/_pages/zh-tw/cards.md index 6d06dee0..3f6e3957 100644 --- a/homepage/_pages/zh-tw/cards.md +++ b/homepage/_pages/zh-tw/cards.md @@ -1,4 +1,28 @@ --- unique_slug: cards name: 卡片頁 +layout_list: + - type: layout_headline + title: 卡片介紹 + - type: layout_cards + card_type: project + title: 專案卡 | 開放政府專案 + card_tags: + - open gov + - type: layout_cards + title: 專案卡 | 開放資料專案 + card_type: project + card_tags: + - open data + - type: layout_cards + title: 專案卡 | 開放原始碼專案 + card_type: project + card_tags: + - open source + - type: layout_cards + title: 人力卡 + card_type: job + - type: layout_cards + title: 事件卡 + card_type: event --- diff --git a/homepage/src/pages/admin/config.json b/homepage/src/pages/admin/config.json index 21076ccd..b35aad64 100644 --- a/homepage/src/pages/admin/config.json +++ b/homepage/src/pages/admin/config.json @@ -151,6 +151,47 @@ ] } ] + }, + { + "label": "Layout - Cards", + "name": "layout_cards", + "widget": "object", + "summary": "{{fields.title}}", + "fields": [ + { "label": "Title", "name": "title", "widget": "string" }, + { + "label": "Card type", + "name": "card_type", + "widget": "select", + "options": [ + { "label": "專案", "value": "project" }, + { "label": "人力", "value": "job" }, + { "label": "事件", "value": "event" } + ], + "required": false + }, + { + "label": "Card tags", + "name": "card_tags", + "widget": "select", + "multiple": true, + "options": [ + { "label": "開放政府", "value": "open gov" }, + { "label": "開放資料", "value": "open data" }, + { "label": "開放原始碼", "value": "open source" }, + { "label": "基礎", "value": "basic" }, + { "label": "進階", "value": "advance" }, + { "label": "工程師", "value": "engineer" }, + { "label": "美術設計", "value": "designer" }, + { "label": "文字工作者", "value": "writer" }, + { "label": "行銷公關", "value": "marketing" }, + { "label": "議題工作者", "value": "advocator" }, + { "label": "公務員", "value": "civil servants" }, + { "label": "法務人員", "value": "legal" } + ], + "required": false + } + ] } ] } From fe9f7cf2a3061c276eff9fa990c7963823d382b7 Mon Sep 17 00:00:00 2001 From: Ben Liu Date: Wed, 18 Oct 2023 18:27:09 +0700 Subject: [PATCH 04/13] feat(homepage): add cms page collection layout summary --- homepage/src/pages/admin/config.json | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/homepage/src/pages/admin/config.json b/homepage/src/pages/admin/config.json index b35aad64..9df2bf99 100644 --- a/homepage/src/pages/admin/config.json +++ b/homepage/src/pages/admin/config.json @@ -43,6 +43,7 @@ "label": "Layout - Banner", "name": "layout_banner", "widget": "object", + "summary": "{{fields.title}}", "fields": [ { "label": "Background Hero Image", @@ -68,6 +69,7 @@ "label": "Layout - Headline", "name": "layout_headline", "widget": "object", + "summary": "{{fields.title}}", "fields": [ { "label": "Title", "name": "title", "widget": "string" }, { @@ -82,6 +84,7 @@ "label": "Layout - Image & Text", "name": "layout_image_text", "widget": "object", + "summary": "{{fields.title}}", "fields": [ { "label": "Image", "name": "image", "widget": "image" }, { "label": "Title", "name": "title", "widget": "string" }, @@ -122,6 +125,7 @@ "label": "Layout - Section", "name": "layout_section", "widget": "list", + "summary": "{{fields.title}}", "fields": [ { "label": "Title", "name": "title", "widget": "string" }, { @@ -329,9 +333,10 @@ "label": "Links", "label_singular": "Link", "widget": "list", + "summary": "{{fields.type} - {{fields.url}}", "fields": [ { - "label": "Type`", + "label": "Type", "name": "type", "widget": "select", "options": [ @@ -378,6 +383,7 @@ "label_singular": "Link", "widget": "list", "i18n": true, + "summary": "{{fields.display_text}} - {{fields.url}}", "fields": [ { "label": "Display Text", From d92fae1551a7435be4894ee79005a8ac90c5504d Mon Sep 17 00:00:00 2001 From: Ben Liu Date: Wed, 18 Oct 2023 23:09:35 +0700 Subject: [PATCH 05/13] refactor(homepage): replace data object with content list for content mapper --- homepage/src/lib/fetchPage.js | 9 ++++++--- homepage/src/pages/cards.jsx | 2 +- homepage/src/pages/index.jsx | 2 +- homepage/src/pages/resource.jsx | 2 +- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/homepage/src/lib/fetchPage.js b/homepage/src/lib/fetchPage.js index 68b737e6..237f0840 100644 --- a/homepage/src/lib/fetchPage.js +++ b/homepage/src/lib/fetchPage.js @@ -16,12 +16,15 @@ export function fetchPage(lang, page) { const fileContent = fs.readFileSync(filePath, 'utf8'); const { data } = matter(fileContent); - data['layout_list']?.forEach((layout) => { - layout.id = layout.id || titleToAnchorId(layout.title); + const contentList = data['layout_list']?.map((layout) => { + return { + id: layout.id || titleToAnchorId(layout.title), + ...layout, + }; }); return { - data, + contentList, }; } catch (error) { console.log(error); diff --git a/homepage/src/pages/cards.jsx b/homepage/src/pages/cards.jsx index f621e522..ecef2766 100644 --- a/homepage/src/pages/cards.jsx +++ b/homepage/src/pages/cards.jsx @@ -116,7 +116,7 @@ const cards = ({ {headInfo.title} - {page.data['layout_list']?.map(contentMapper)} + {page.contentList?.map(contentMapper)} ( `, }} /> - {page.data['layout_list']?.map(contentMapper)} + {page.contentList.map(contentMapper)} ); diff --git a/homepage/src/pages/resource.jsx b/homepage/src/pages/resource.jsx index 24dbeee8..263650b6 100644 --- a/homepage/src/pages/resource.jsx +++ b/homepage/src/pages/resource.jsx @@ -19,5 +19,5 @@ export const getStaticProps = async ({ locale }) => { }; export default function Resource({ page }) { - return <>{page.data['layout_list']?.map(contentMapper)}; + return <>{page.contentList.map(contentMapper)}; } From 6d5d5b67823a372c73ccf19a9fa0b25d8f3f1aa8 Mon Sep 17 00:00:00 2001 From: Ben Liu Date: Wed, 18 Oct 2023 23:48:50 +0700 Subject: [PATCH 06/13] refactor(homepage): decouple layout to component props and rendering logic --- homepage/src/layouts/contentMapper.jsx | 84 ++++------------------ homepage/src/lib/componentMapper.js | 97 ++++++++++++++++++++++++++ homepage/src/lib/fetchPage.js | 9 +-- homepage/src/pages/index.jsx | 2 +- homepage/src/pages/resource.jsx | 2 +- 5 files changed, 116 insertions(+), 78 deletions(-) create mode 100644 homepage/src/lib/componentMapper.js diff --git a/homepage/src/layouts/contentMapper.jsx b/homepage/src/layouts/contentMapper.jsx index a1723ac8..1e4c8b08 100644 --- a/homepage/src/layouts/contentMapper.jsx +++ b/homepage/src/layouts/contentMapper.jsx @@ -4,76 +4,22 @@ import ThreeColumns from '../components/threeColumns'; import ImageAndText from '../components/imageAndText'; import Section from '../components/section'; import Headline from '../components/headline'; +import { componentTypes } from '../lib/componentMapper'; -const contentMapper = (layout) => { - switch (layout.type) { - case 'layout_banner': - return ( - - ); - case 'layout_section': - if (layout.columns?.length === 2) { - return ( - - ); - } else if (layout.columns?.length === 3) { - return ( - - ); - } else { - return ( -
- ); - } - case 'layout_image_text': - return ( - - ); - case 'layout_headline': - return ( - - ); +const contentMapper = (component) => { + switch (component.type) { + case componentTypes.Banner: + return ; + case componentTypes.Headline: + return ; + case componentTypes.ImageAndText: + return ; + case componentTypes.OneColumn: + return
; + case componentTypes.TwoColumns: + return ; + case componentTypes.ThreeColumns: + return ; default: return null; } diff --git a/homepage/src/lib/componentMapper.js b/homepage/src/lib/componentMapper.js new file mode 100644 index 00000000..d84ca318 --- /dev/null +++ b/homepage/src/lib/componentMapper.js @@ -0,0 +1,97 @@ +import { titleToAnchorId } from './titleToAnchorId'; + +const removeUndefined = (obj) => { + const newObj = {}; + Object.keys(obj).forEach((key) => { + if (obj[key] !== undefined) { + newObj[key] = obj[key]; + } + }); + return newObj; +} + +export const componentTypes = { + Banner: 'Banner', + Headline: 'Headline', + OneColumn: 'OneColumn', + TwoColumns: 'TwoColumns', + ThreeColumns: 'ThreeColumns', + ImageAndText: 'ImageAndText', +} + +export const componentMapper = (layout) => { + let type = ''; + let props = {}; + switch (layout.type) { + case 'layout_banner': { + type = componentTypes.Banner; + props = { + id: titleToAnchorId(layout.title), + title: layout.title, + subtitle: layout.subtitle, + heroImage: layout.hero_image, + highlights: layout.highlights, + }; + break; + } + case 'layout_headline': { + type = componentTypes.Headline; + props = { + id: titleToAnchorId(layout.title), + title: layout.title, + subtitle: layout.subtitle, + }; + break; + } + case 'layout_image_text': { + type = componentTypes.ImageAndText; + props = { + id: titleToAnchorId(layout.title), + title: layout.title, + subtitle: layout.subtitle, + image: layout.image, + content: layout.text, + highlights: layout.highlights, + markdown: true, + }; + break; + } + case 'layout_section': { + if (layout.columns?.length === 1) { + type = componentTypes.OneColumn; + props = { + id: titleToAnchorId(layout.title), + title: layout.title, + subtitle: layout.columns?.[0]?.title, + image: layout.columns?.[0]?.image, + content: layout.columns?.[0]?.text, + markdown: true, + }; + break; + } else if (layout.columns?.length === 2) { + type = componentTypes.TwoColumns; + props = { + id: titleToAnchorId(layout.title), + title: layout.title, + columns: layout.columns, + markdown: true, + }; + break; + } else if (layout.columns?.length === 3) { + type = componentTypes.ThreeColumns; + props = { + id: titleToAnchorId(layout.title), + title: layout.title, + columns: layout.columns, + markdown: true, + }; + break; + } + } + } + + return { + type, + props: removeUndefined(props), + }; +} diff --git a/homepage/src/lib/fetchPage.js b/homepage/src/lib/fetchPage.js index 237f0840..2a4f48f8 100644 --- a/homepage/src/lib/fetchPage.js +++ b/homepage/src/lib/fetchPage.js @@ -1,7 +1,7 @@ import fs from 'fs'; import { join } from 'path'; import matter from 'gray-matter'; -import { titleToAnchorId } from './titleToAnchorId'; +import { componentMapper } from './componentMapper'; const pagesDirectory = join(process.cwd(), '_pages'); @@ -16,12 +16,7 @@ export function fetchPage(lang, page) { const fileContent = fs.readFileSync(filePath, 'utf8'); const { data } = matter(fileContent); - const contentList = data['layout_list']?.map((layout) => { - return { - id: layout.id || titleToAnchorId(layout.title), - ...layout, - }; - }); + const contentList = data['layout_list']?.map(componentMapper); return { contentList, diff --git a/homepage/src/pages/index.jsx b/homepage/src/pages/index.jsx index f2142a43..9c6529a5 100644 --- a/homepage/src/pages/index.jsx +++ b/homepage/src/pages/index.jsx @@ -63,7 +63,7 @@ const Index = ({ headInfo, page }) => ( `, }} /> - {page.contentList.map(contentMapper)} + {page.contentList?.map(contentMapper)} ); diff --git a/homepage/src/pages/resource.jsx b/homepage/src/pages/resource.jsx index 263650b6..806e2ae5 100644 --- a/homepage/src/pages/resource.jsx +++ b/homepage/src/pages/resource.jsx @@ -19,5 +19,5 @@ export const getStaticProps = async ({ locale }) => { }; export default function Resource({ page }) { - return <>{page.contentList.map(contentMapper)}; + return <>{page.contentList?.map(contentMapper)}; } From 7f380f387d5fec03bdfd6162aa112c6e880ba815 Mon Sep 17 00:00:00 2001 From: Ben Liu Date: Thu, 19 Oct 2023 07:17:35 +0700 Subject: [PATCH 07/13] refactor(homepage): decouple cards process, update logic from fetch logic --- homepage/src/lib/fetchCards.js | 17 ++++----------- homepage/src/lib/markdownToHtml.js | 7 +++++++ homepage/src/pages/cards.jsx | 33 +++++++++++++++--------------- 3 files changed, 27 insertions(+), 30 deletions(-) create mode 100644 homepage/src/lib/markdownToHtml.js diff --git a/homepage/src/lib/fetchCards.js b/homepage/src/lib/fetchCards.js index aa44a7f8..d101ea94 100644 --- a/homepage/src/lib/fetchCards.js +++ b/homepage/src/lib/fetchCards.js @@ -2,9 +2,6 @@ import fs from 'fs'; import { join } from 'path'; import matter from 'gray-matter'; -import { remark } from 'remark'; -import html from 'remark-html'; - const cardsDirectory = join(process.cwd(), '_cards'); /** @@ -16,24 +13,18 @@ function getCardDirectoryPath(lang) { return join(cardsDirectory, lang); } -export async function fetchCards(lang) { +export function fetchCards(lang) { const cardsDirectory = getCardDirectoryPath(lang); const filesInCards = fs.readdirSync(cardsDirectory); - const cards = filesInCards.map(async (filename) => { + const cards = filesInCards.map((filename) => { const fullPath = join(cardsDirectory, filename); const file = fs.readFileSync(fullPath, 'utf8'); const matterFile = matter(file); const { data, content } = matterFile; - const processedContent = await remark().use(html).process(content); - const contentHtml = processedContent.toString(); - - return { - frontMatter: data, - content: contentHtml, - }; + return { data, content }; }); - return Promise.all(cards); + return cards; } diff --git a/homepage/src/lib/markdownToHtml.js b/homepage/src/lib/markdownToHtml.js new file mode 100644 index 00000000..5139f40d --- /dev/null +++ b/homepage/src/lib/markdownToHtml.js @@ -0,0 +1,7 @@ +import { remark } from 'remark'; +import html from 'remark-html'; + +export async function markdownToHtml(markdown) { + const result = await remark().use(html).process(markdown); + return result.toString(); +} diff --git a/homepage/src/pages/cards.jsx b/homepage/src/pages/cards.jsx index ecef2766..a76ca28b 100644 --- a/homepage/src/pages/cards.jsx +++ b/homepage/src/pages/cards.jsx @@ -2,7 +2,6 @@ import Head from 'next/head'; import Headline from '../components/headline'; import CardsGrid from '../components/cardsGrid'; import contentMapper from '../layouts/contentMapper'; - import { fetchPage } from '../lib/fetchPage'; import { fetchCards } from '../lib/fetchCards'; import { getNavigationList } from '../lib/getNavigationList'; @@ -12,29 +11,29 @@ import { getNavigationList } from '../lib/getNavigationList'; * @type {import('next').GetStaticProps} */ export const getStaticProps = async ({ locale }) => { - const cards = await fetchCards(locale); - - // Get correct image path - const updatedCards = cards.map((card) => { - const updatedImage = card.frontMatter.image - ? card.frontMatter.image.replace('/homepage/public', '') - : '/images/uploads/初階專案卡封面-01.png'; + const rawCards = fetchCards(locale); + const cardTasks = rawCards.map(async ({ data, content }) => { + let image = data.image; + const defaultImage = '/images/uploads/初階專案卡封面-01.png'; + image = image ? image.replace('/homepage/public', '') : defaultImage; // Workaround for image prefix path. Will be removed after image path is fixed in all cards. - const image = !updatedImage.startsWith('/') - ? `/${updatedImage}` - : updatedImage; + image = !image.startsWith('/') ? `/${image}` : image; + + data.image = image; + + const { markdownToHtml } = await import('../lib/markdownToHtml'); + const processedContent = await markdownToHtml(content); return { - ...card, - frontMatter: { - ...card.frontMatter, - image, - }, + frontMatter: data, + content: processedContent, }; }); + const cards = await Promise.all(cardTasks); const navigationList = await getNavigationList(locale); + const page = fetchPage(locale, 'cards'); const title = { @@ -81,7 +80,7 @@ export const getStaticProps = async ({ locale }) => { return { props: { - cards: updatedCards, + cards, navigationList, headInfo: { title: headInfo.title[locale], From 61f024a60e247ce65fd0a4b9951160ebddcdffc6 Mon Sep 17 00:00:00 2001 From: Ben Liu Date: Thu, 19 Oct 2023 07:44:39 +0700 Subject: [PATCH 08/13] refactor(homepage): decouple page process and page fetch --- homepage/src/lib/fetchPage.js | 5 +---- homepage/src/pages/cards.jsx | 9 ++++++++- homepage/src/pages/index.jsx | 6 +++++- homepage/src/pages/resource.jsx | 6 +++++- 4 files changed, 19 insertions(+), 7 deletions(-) diff --git a/homepage/src/lib/fetchPage.js b/homepage/src/lib/fetchPage.js index 2a4f48f8..28697f63 100644 --- a/homepage/src/lib/fetchPage.js +++ b/homepage/src/lib/fetchPage.js @@ -1,7 +1,6 @@ import fs from 'fs'; import { join } from 'path'; import matter from 'gray-matter'; -import { componentMapper } from './componentMapper'; const pagesDirectory = join(process.cwd(), '_pages'); @@ -16,10 +15,8 @@ export function fetchPage(lang, page) { const fileContent = fs.readFileSync(filePath, 'utf8'); const { data } = matter(fileContent); - const contentList = data['layout_list']?.map(componentMapper); - return { - contentList, + data, }; } catch (error) { console.log(error); diff --git a/homepage/src/pages/cards.jsx b/homepage/src/pages/cards.jsx index a76ca28b..06a79d9d 100644 --- a/homepage/src/pages/cards.jsx +++ b/homepage/src/pages/cards.jsx @@ -5,6 +5,7 @@ import contentMapper from '../layouts/contentMapper'; import { fetchPage } from '../lib/fetchPage'; import { fetchCards } from '../lib/fetchCards'; import { getNavigationList } from '../lib/getNavigationList'; +import { componentMapper } from '../lib/componentMapper'; /** * @@ -36,6 +37,10 @@ export const getStaticProps = async ({ locale }) => { const page = fetchPage(locale, 'cards'); + const contentList = page.data['layout_list']?.map((layout) => + componentMapper(layout, rawCards), + ); + const title = { en: 'Card Introduction', 'zh-tw': '卡片介紹', @@ -94,7 +99,9 @@ export const getStaticProps = async ({ locale }) => { 'open data': projectCardSubtitle['open data'][locale], 'open source': projectCardSubtitle['open source'][locale], }, - page, + page: { + contentList, + }, }, }; }; diff --git a/homepage/src/pages/index.jsx b/homepage/src/pages/index.jsx index 9c6529a5..21077df4 100644 --- a/homepage/src/pages/index.jsx +++ b/homepage/src/pages/index.jsx @@ -3,6 +3,7 @@ import Script from 'next/script'; import { fetchPage } from '../lib/fetchPage'; import { getNavigationList } from '../lib/getNavigationList'; import contentMapper from '../layouts/contentMapper'; +import { componentMapper } from '../lib/componentMapper'; /** * @@ -23,6 +24,7 @@ export const getStaticProps = async ({ locale }) => { }; const page = fetchPage(locale, 'index'); + const contentList = page.data['layout_list']?.map(componentMapper); return { props: { @@ -31,7 +33,9 @@ export const getStaticProps = async ({ locale }) => { title: headInfo.title[locale], description: headInfo.description[locale], }, - page, + page: { + contentList, + }, }, }; }; diff --git a/homepage/src/pages/resource.jsx b/homepage/src/pages/resource.jsx index 806e2ae5..9b90fa0a 100644 --- a/homepage/src/pages/resource.jsx +++ b/homepage/src/pages/resource.jsx @@ -1,4 +1,5 @@ import contentMapper from '../layouts/contentMapper'; +import { componentMapper } from '../lib/componentMapper'; import { fetchPage } from '../lib/fetchPage'; import { getNavigationList } from '../lib/getNavigationList'; @@ -8,11 +9,14 @@ import { getNavigationList } from '../lib/getNavigationList'; */ export const getStaticProps = async ({ locale }) => { const page = fetchPage(locale, 'resource'); + const contentList = page.data['layout_list']?.map(componentMapper); const navigationList = await getNavigationList(locale); return { props: { - page, + page: { + contentList, + }, navigationList, }, }; From 0a84be815b340acf315f90a5ace652a76d5b96d2 Mon Sep 17 00:00:00 2001 From: Ben Liu Date: Thu, 19 Oct 2023 09:09:37 +0700 Subject: [PATCH 09/13] refactor(homepage): migrate hard coded cards component to cms cards layout component --- homepage/src/components/cards.jsx | 35 ++++++++ homepage/src/layouts/contentMapper.jsx | 3 + homepage/src/lib/componentMapper.js | 32 +++++++- homepage/src/pages/cards.jsx | 106 ++----------------------- homepage/src/pages/index.jsx | 4 +- homepage/src/pages/resource.jsx | 4 +- 6 files changed, 77 insertions(+), 107 deletions(-) create mode 100644 homepage/src/components/cards.jsx diff --git a/homepage/src/components/cards.jsx b/homepage/src/components/cards.jsx new file mode 100644 index 00000000..dad06bd9 --- /dev/null +++ b/homepage/src/components/cards.jsx @@ -0,0 +1,35 @@ +import { ParseMarkdownAndHtml } from './parseMarkdownAndHtml'; + +const Cards = ({ id, title, cards }) => { + return ( +
+
+
+

{title}

+
+
+ {cards.map((card) => ( + + ))} +
+
+
+ ); +}; + +const Card = ({ card }) => ( +
+
+

{card.data.title}

+
+ {card.data.title} + {card.data.description} + + {card.content} + +
+
+
+); + +export default Cards; diff --git a/homepage/src/layouts/contentMapper.jsx b/homepage/src/layouts/contentMapper.jsx index 1e4c8b08..e703bbe6 100644 --- a/homepage/src/layouts/contentMapper.jsx +++ b/homepage/src/layouts/contentMapper.jsx @@ -4,6 +4,7 @@ import ThreeColumns from '../components/threeColumns'; import ImageAndText from '../components/imageAndText'; import Section from '../components/section'; import Headline from '../components/headline'; +import Cards from '../components/cards'; import { componentTypes } from '../lib/componentMapper'; const contentMapper = (component) => { @@ -20,6 +21,8 @@ const contentMapper = (component) => { return ; case componentTypes.ThreeColumns: return ; + case componentTypes.Cards: + return ; default: return null; } diff --git a/homepage/src/lib/componentMapper.js b/homepage/src/lib/componentMapper.js index d84ca318..18b4d24f 100644 --- a/homepage/src/lib/componentMapper.js +++ b/homepage/src/lib/componentMapper.js @@ -8,7 +8,7 @@ const removeUndefined = (obj) => { } }); return newObj; -} +}; export const componentTypes = { Banner: 'Banner', @@ -17,9 +17,10 @@ export const componentTypes = { TwoColumns: 'TwoColumns', ThreeColumns: 'ThreeColumns', ImageAndText: 'ImageAndText', -} + Cards: 'Cards', +}; -export const componentMapper = (layout) => { +export const componentMapper = (layout, cards = []) => { let type = ''; let props = {}; switch (layout.type) { @@ -88,10 +89,33 @@ export const componentMapper = (layout) => { break; } } + case 'layout_cards': { + let filteredCards = cards; + // filter cards by card_type + if (layout.card_type) { + filteredCards = cards.filter( + (card) => layout.card_type === card.data.type, + ); + } + // filter cards by card_tags + if (layout.card_tags && layout.card_tags.length > 0) { + filteredCards = filteredCards.filter((card) => + layout.card_tags.every((tag) => card.data.tags?.includes(tag)), + ); + } + + type = componentTypes.Cards; + props = { + id: titleToAnchorId(layout.title), + title: layout.title, + cards: filteredCards, + }; + break; + } } return { type, props: removeUndefined(props), }; -} +}; diff --git a/homepage/src/pages/cards.jsx b/homepage/src/pages/cards.jsx index 06a79d9d..50d12014 100644 --- a/homepage/src/pages/cards.jsx +++ b/homepage/src/pages/cards.jsx @@ -1,6 +1,4 @@ import Head from 'next/head'; -import Headline from '../components/headline'; -import CardsGrid from '../components/cardsGrid'; import contentMapper from '../layouts/contentMapper'; import { fetchPage } from '../lib/fetchPage'; import { fetchCards } from '../lib/fetchCards'; @@ -23,12 +21,9 @@ export const getStaticProps = async ({ locale }) => { data.image = image; - const { markdownToHtml } = await import('../lib/markdownToHtml'); - const processedContent = await markdownToHtml(content); - return { - frontMatter: data, - content: processedContent, + data, + content, }; }); const cards = await Promise.all(cardTasks); @@ -38,44 +33,9 @@ export const getStaticProps = async ({ locale }) => { const page = fetchPage(locale, 'cards'); const contentList = page.data['layout_list']?.map((layout) => - componentMapper(layout, rawCards), + componentMapper(layout, cards), ); - const title = { - en: 'Card Introduction', - 'zh-tw': '卡片介紹', - }; - - const projectCardTitle = { - en: 'Project Card', - 'zh-tw': '專案卡', - }; - - const jobCardTitle = { - en: 'Job Card', - 'zh-tw': '人力卡', - }; - - const eventCardTitle = { - en: 'Event Card', - 'zh-tw': '事件卡', - }; - - const projectCardSubtitle = { - 'open gov': { - en: 'Open Government', - 'zh-tw': '開放政府專案', - }, - 'open data': { - en: 'Open Data', - 'zh-tw': '開放資料專案', - }, - 'open source': { - en: 'Open Source', - 'zh-tw': '開放原始碼專案', - }, - }; - const headInfo = { title: { en: `OpenStarTerVillage - Card Introduction`, @@ -85,20 +45,10 @@ export const getStaticProps = async ({ locale }) => { return { props: { - cards, navigationList, headInfo: { title: headInfo.title[locale], }, - title: title[locale], - projectCardTitle: projectCardTitle[locale], - jobCardTitle: jobCardTitle[locale], - eventCardTitle: eventCardTitle[locale], - projectCardSubtitle: { - 'open gov': projectCardSubtitle['open gov'][locale], - 'open data': projectCardSubtitle['open data'][locale], - 'open source': projectCardSubtitle['open source'][locale], - }, page: { contentList, }, @@ -106,60 +56,14 @@ export const getStaticProps = async ({ locale }) => { }; }; -const cards = ({ - cards, - headInfo, - title, - projectCardTitle, - jobCardTitle, - eventCardTitle, - projectCardSubtitle, - page, -}) => { +const cards = ({ headInfo, page }) => { return ( <> {headInfo.title} - + {page.contentList?.map(contentMapper)} - - - - - - ); }; diff --git a/homepage/src/pages/index.jsx b/homepage/src/pages/index.jsx index 21077df4..570b199a 100644 --- a/homepage/src/pages/index.jsx +++ b/homepage/src/pages/index.jsx @@ -24,7 +24,9 @@ export const getStaticProps = async ({ locale }) => { }; const page = fetchPage(locale, 'index'); - const contentList = page.data['layout_list']?.map(componentMapper); + const contentList = page.data['layout_list']?.map((layout) => + componentMapper(layout, []), + ); return { props: { diff --git a/homepage/src/pages/resource.jsx b/homepage/src/pages/resource.jsx index 9b90fa0a..5e15f2e9 100644 --- a/homepage/src/pages/resource.jsx +++ b/homepage/src/pages/resource.jsx @@ -9,7 +9,9 @@ import { getNavigationList } from '../lib/getNavigationList'; */ export const getStaticProps = async ({ locale }) => { const page = fetchPage(locale, 'resource'); - const contentList = page.data['layout_list']?.map(componentMapper); + const contentList = page.data['layout_list']?.map((layout) => + componentMapper(layout, []), + ); const navigationList = await getNavigationList(locale); return { From 69e7f3f769dcc3720a1c5624fa506e9a6fc066cb Mon Sep 17 00:00:00 2001 From: Ben Liu Date: Thu, 19 Oct 2023 09:10:35 +0700 Subject: [PATCH 10/13] feat(homepage): add view filters in cms card collection --- homepage/src/pages/admin/config.json | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/homepage/src/pages/admin/config.json b/homepage/src/pages/admin/config.json index 9df2bf99..277215b2 100644 --- a/homepage/src/pages/admin/config.json +++ b/homepage/src/pages/admin/config.json @@ -23,6 +23,8 @@ "description": "Pages in the website", "folder": "homepage/_pages", "slug": "{{unique_slug}}", + "identifier_field": "unique_slug", + "preview_path": "{{unique_slug}}", "create": true, "i18n": true, "fields": [ @@ -209,10 +211,26 @@ "folder": "homepage/_cards", "create": true, "i18n": true, - "view_groups": [ - { "label": "Drafts", "field": "draft" }, - { "label": "Type", "field": "type" } + "view_groups": [{ "label": "Type", "field": "type" }], + "view_filters": [ + { "label": "專案", "field": "type", "pattern": "project" }, + { "label": "人力", "field": "type", "pattern": "job" }, + { "label": "事件", "field": "type", "pattern": "event" }, + + { "label": "開放政府", "field": "tags", "pattern": "open gov" }, + { "label": "開放資料", "field": "tags", "pattern": "open data" }, + { "label": "開放原始碼", "field": "tags", "pattern": "open source" }, + { "label": "基礎", "field": "tags", "pattern": "basic" }, + { "label": "進階", "field": "tags", "pattern": "advance" }, + { "label": "工程師", "field": "tags", "pattern": "engineer" }, + { "label": "美術設計", "field": "tags", "pattern": "designer" }, + { "label": "文字工作者", "field": "tags", "pattern": "writer" }, + { "label": "行銷公關", "field": "tags", "pattern": "marketing" }, + { "label": "議題工作者", "field": "tags", "pattern": "advocator" }, + { "label": "公務員", "field": "tags", "pattern": "civil servants" }, + { "label": "法務人員", "field": "tags", "pattern": "legal" } ], + "summary": "{{fields.type}} / {{fields.title}}", "fields": [ { "label": "Image", From 76726d84ec7a597adf712be4af0fa5244aae4a9d Mon Sep 17 00:00:00 2001 From: Ben Liu Date: Thu, 19 Oct 2023 09:45:27 +0700 Subject: [PATCH 11/13] refactor(homepage): replace cms component mapper w/ unified component mapper --- homepage/src/lib/componentMapper.js | 4 +- homepage/src/lib/titleToAnchorId.js | 2 +- homepage/src/pages/admin/index.jsx | 109 +--------------------------- 3 files changed, 7 insertions(+), 108 deletions(-) diff --git a/homepage/src/lib/componentMapper.js b/homepage/src/lib/componentMapper.js index 18b4d24f..2e233082 100644 --- a/homepage/src/lib/componentMapper.js +++ b/homepage/src/lib/componentMapper.js @@ -58,7 +58,7 @@ export const componentMapper = (layout, cards = []) => { break; } case 'layout_section': { - if (layout.columns?.length === 1) { + if (layout.columns?.length <= 1) { type = componentTypes.OneColumn; props = { id: titleToAnchorId(layout.title), @@ -78,7 +78,7 @@ export const componentMapper = (layout, cards = []) => { markdown: true, }; break; - } else if (layout.columns?.length === 3) { + } else if (layout.columns?.length >= 3) { type = componentTypes.ThreeColumns; props = { id: titleToAnchorId(layout.title), diff --git a/homepage/src/lib/titleToAnchorId.js b/homepage/src/lib/titleToAnchorId.js index d55bdc23..2728c4f2 100644 --- a/homepage/src/lib/titleToAnchorId.js +++ b/homepage/src/lib/titleToAnchorId.js @@ -6,7 +6,7 @@ const cleanTitle = (title) => { .trim(); }; -export const titleToAnchorId = (title) => { +export const titleToAnchorId = (title = '') => { let anchorId = title.toLowerCase(); // lowercase anchorId = cleanTitle(anchorId); // remove special characters anchorId = anchorId.normalize('NFD').replace(/[\u0300-\u036f]/g, ''); // remove accents diff --git a/homepage/src/pages/admin/index.jsx b/homepage/src/pages/admin/index.jsx index f0e95736..de4e855e 100644 --- a/homepage/src/pages/admin/index.jsx +++ b/homepage/src/pages/admin/index.jsx @@ -3,116 +3,15 @@ import Head from 'next/head'; import Script from 'next/script'; import config from './config.json'; -import Banner from '../../components/banner'; -import ImageAndText from '../../components/imageAndText'; -import Headline from '../../components/headline'; -import Section from '../../components/section'; -import TwoColumns from '../../components/twoColumns'; -import ThreeColumns from '../../components/threeColumns'; import FooterLinks from '../../layouts/footer/footerLinks'; +import { componentMapper } from '../../lib/componentMapper'; +import contentMapper from '../../layouts/contentMapper'; const PagePreview = ({ entry }) => { const layoutList = entry.getIn(['data', 'layout_list']); const sections = layoutList?.map((layout) => { - const layoutType = layout.get('type')?.toString(); - if (layoutType === 'layout_banner') { - return ( - - ); - } else if (layoutType === 'layout_image_text') { - const highlights = layout - .get('highlights') - ?.map((highlight) => { - return { - item: highlight.get('item')?.toString(), - description: highlight.get('description')?.toString(), - }; - }) - ?.toArray(); - return ( - - ); - } else if (layoutType === 'layout_headline') { - return ( - - ); - } else if (layoutType === 'layout_section') { - const columnSize = layout.get('columns')?.size ?? 0; - if (columnSize === 0 || columnSize === 1) { - return ( -
- ); - } else if (columnSize === 2) { - const columns = layout - .get('columns') - ?.map((column) => { - return { - title: column.get('title')?.toString(), - image: column.get('image')?.toString(), - text: column.get('text')?.toString(), - }; - }) - .toArray(); - return ( - - ); - } else if (columnSize >= 3) { - const columns = layout - .get('columns') - ?.map((column) => { - return { - title: column.get('title')?.toString(), - image: column.get('image')?.toString(), - text: column.get('text')?.toString(), - }; - }) - .toArray(); - return ( - - ); - } - } else { - return
; - } + const component = componentMapper(layout.toJS(), []); + return contentMapper(component); }); return
{sections}
; }; From 9112ceace17904e63e17f027127ee42338ada1f9 Mon Sep 17 00:00:00 2001 From: Ben Liu Date: Thu, 19 Oct 2023 10:17:14 +0700 Subject: [PATCH 12/13] refactor(homepage): remove hard coded cards page link --- homepage/src/lib/getNavigationList.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/homepage/src/lib/getNavigationList.js b/homepage/src/lib/getNavigationList.js index ec67ce8b..1b01536b 100644 --- a/homepage/src/lib/getNavigationList.js +++ b/homepage/src/lib/getNavigationList.js @@ -29,10 +29,6 @@ export async function getPagesList(lang) { export async function getNavigationList(lang) { const pagesList = await getPagesList(lang); - const cardsPage = { - en: 'Cards', - 'zh-tw': '卡片頁', - }; // * dynamic page navigation const navigationList = pagesList @@ -41,8 +37,6 @@ export async function getNavigationList(lang) { page.path = page.path.replace('index', ''); return { link: `/${page.path}`, text: page.name }; }); - // add hard coded cards page - navigationList.push({ link: `/cards`, text: cardsPage[lang] }); return navigationList; } From 2718084f7e2a596f10fdd06925a60619a661f2d4 Mon Sep 17 00:00:00 2001 From: Ben Liu Date: Thu, 19 Oct 2023 11:14:13 +0700 Subject: [PATCH 13/13] feat(homepage): add id in each cards --- homepage/src/components/cards.jsx | 4 ++-- homepage/src/pages/cards.jsx | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/homepage/src/components/cards.jsx b/homepage/src/components/cards.jsx index dad06bd9..12122913 100644 --- a/homepage/src/components/cards.jsx +++ b/homepage/src/components/cards.jsx @@ -9,7 +9,7 @@ const Cards = ({ id, title, cards }) => {
{cards.map((card) => ( - + ))}
@@ -18,7 +18,7 @@ const Cards = ({ id, title, cards }) => { }; const Card = ({ card }) => ( -
+

{card.data.title}

diff --git a/homepage/src/pages/cards.jsx b/homepage/src/pages/cards.jsx index 50d12014..73b97c70 100644 --- a/homepage/src/pages/cards.jsx +++ b/homepage/src/pages/cards.jsx @@ -4,6 +4,7 @@ import { fetchPage } from '../lib/fetchPage'; import { fetchCards } from '../lib/fetchCards'; import { getNavigationList } from '../lib/getNavigationList'; import { componentMapper } from '../lib/componentMapper'; +import { titleToAnchorId } from '../lib/titleToAnchorId'; /** * @@ -20,6 +21,7 @@ export const getStaticProps = async ({ locale }) => { image = !image.startsWith('/') ? `/${image}` : image; data.image = image; + data.id = data.id || titleToAnchorId(data.title); return { data,