-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from Albert-mah/main
feat: add tutorials page
- Loading branch information
Showing
7 changed files
with
356 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
--- | ||
import FormattedDate from "../../../components/FormattedDate.astro"; | ||
import PageHeader from "../../../components/PageHeader.astro"; | ||
import Toc from "../../../components/Toc.astro"; | ||
import Layout from "../../../layouts/Layout.astro"; | ||
import { getTutorialArticle, url } from "../../../utils"; | ||
const { slug } = Astro.params; | ||
const { data, headings, html } = await getTutorialArticle(slug, 'cn'); | ||
if (!data) { | ||
return new Response("Not found", { status: 404 }); | ||
} | ||
const title = data.title_cn || data.title; | ||
// const keywords = (data.tags || []) | ||
// .map((tag: any) => tag.title_cn || tag.title) | ||
// .join(","); | ||
const description = data.description_cn || data.description; | ||
const basepath = "/cn/tutorials"; | ||
const tabs = [ | ||
{ | ||
title: "首页", | ||
link: "/cn/", | ||
}, | ||
{ | ||
title: "实战教程", | ||
link: basepath, | ||
}, | ||
{ | ||
title: '详情', | ||
active: true, | ||
}, | ||
]; | ||
--- | ||
|
||
<Layout title={title} description={description}> | ||
<PageHeader title={title} description={description} breadcrumb={tabs}> | ||
<div> | ||
{ | ||
data.author && ( | ||
<span title={data.author}> | ||
{data.author} | ||
<span style="opacity: 0.3; display:inline-block; padding: 0 .8em;"> | ||
| | ||
</span> | ||
</span> | ||
) | ||
} | ||
<FormattedDate locale={"zh-cn"} date={data.publishedAt || data.createdAt} /> | ||
</div> | ||
</PageHeader> | ||
<section class="section bg-white-color"> | ||
<main | ||
class={headings.length > 0 ? "container blog-container row" : "container"} | ||
> | ||
{ | ||
headings.length > 0 ? ( | ||
<> | ||
<div class="col-md-9 col-18 markdown-body"> | ||
<Fragment set:html={html} /> | ||
</div> | ||
<div class="col-md-3 col-6 markdown-toc"> | ||
<Toc headings={headings} /> | ||
</div> | ||
</> | ||
) : ( | ||
<> | ||
<Fragment set:html={html} /> | ||
</> | ||
) | ||
} | ||
</main> | ||
</section> | ||
</Layout> | ||
|
||
<style> | ||
main { | ||
max-width: 960px; | ||
margin: 0 auto; | ||
} | ||
</style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
--- | ||
import PageHeader from "../../../components/PageHeader.astro"; | ||
import Layout from "../../../layouts/Layout.astro"; | ||
import { getLastUpdatedAt, getPage, listTutorialArticles } from "../../../utils"; | ||
const page = await getPage("tutorials"); | ||
const title = page.title_cn || page.title; | ||
const description = page.description_cn || page.description; | ||
const keywords = page.keywords_cn || page.keywords; | ||
const tutorialsData = await listTutorialArticles({ pageSize: 100 }); | ||
// Group tutorials by series dynamically | ||
const tutorials = tutorialsData.data.reduce((acc, item) => { | ||
const seriesTitle = item.serials?.title_cn || item.serials?.title || 'Uncategorized'; | ||
const seriesDescription = item.serials?.description_cn || item.serials?.description || ""; | ||
// Initialize the series if not present in the accumulator | ||
if (!acc[seriesTitle]) { | ||
acc[seriesTitle] = { | ||
description: seriesDescription, | ||
articles: [], | ||
}; | ||
} | ||
// Add the tutorial to the respective series | ||
acc[seriesTitle].articles.push({ | ||
title: item.title_cn || item.title, | ||
link: `/cn/tutorials/${item.slug}`, | ||
description: item.description_cn || item.description || "", | ||
}); | ||
return acc; | ||
}, {}); | ||
// Convert the tutorials object into an array of series objects for easier mapping | ||
const tutorialSeries = Object.entries(tutorials).map(([seriesTitle, seriesData]) => ({ | ||
seriesTitle, | ||
description: seriesData.description, | ||
articles: seriesData.articles, | ||
})); | ||
--- | ||
|
||
<Layout title={title} description={description} keywords={keywords}> | ||
<PageHeader title={title} description={description} /> | ||
<section class="section"> | ||
<div class="container"> | ||
<div class="row justify-content-center"> | ||
<div class="col-lg-9"> | ||
<div class="card shadow border-0 rounded"> | ||
<div class="card-body"> | ||
{tutorialSeries.map(series => ( | ||
<> | ||
<h5 class="card-title">{series.seriesTitle}</h5> | ||
{series.description && <p class="text-muted">{series.description}</p>} | ||
<ul class="list-unstyled text-muted mb-4 pb-4"> | ||
{series.articles.map(article => ( | ||
<li> | ||
<i data-feather="arrow-right" class="fea icon-sm me-2"></i> | ||
<a href={article.link} target="_blank">{article.title}</a> | ||
{article.description && ` - ${article.description}`} | ||
</li> | ||
))} | ||
</ul> | ||
</> | ||
))} | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
</section> | ||
</Layout> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
--- | ||
import FormattedDate from "../../../components/FormattedDate.astro"; | ||
import PageHeader from "../../../components/PageHeader.astro"; | ||
import Toc from "../../../components/Toc.astro"; | ||
import Layout from "../../../layouts/Layout.astro"; | ||
import { getTutorialArticle, url } from "../../../utils"; | ||
const { slug } = Astro.params; | ||
const { data, headings, html } = await getTutorialArticle(slug); | ||
if (!data) { | ||
return new Response("Not found", { status: 404 }); | ||
} | ||
const title = data.title; | ||
const keywords = (data.tags || []).map((tag: any) => tag.title).join(","); | ||
const description = data.description; | ||
const basepath = "/en/tutorials"; | ||
const tabs = [ | ||
{ | ||
title: "Home", | ||
link: "/en/", | ||
}, | ||
{ | ||
title: "Tutorials", | ||
link: basepath, | ||
}, | ||
{ | ||
title: 'Detail', | ||
active: true, | ||
}, | ||
]; | ||
--- | ||
|
||
<Layout title={title} description={description} keywords={keywords}> | ||
<PageHeader title={title} description={description} breadcrumb={tabs}> | ||
<div> | ||
{ | ||
data.author && ( | ||
<span title={data.author}> | ||
{data.author} | ||
<span style="opacity: 0.3; display:inline-block; padding: 0 .8em;"> | ||
| | ||
</span> | ||
</span> | ||
) | ||
} | ||
<FormattedDate date={data.publishedAt || data.createdAt} /> | ||
</div> | ||
</PageHeader> | ||
<section class="section bg-white-color"> | ||
<main | ||
class={headings.length > 0 ? "container blog-container row" : "container"} | ||
> | ||
{ | ||
headings.length > 0 ? ( | ||
<> | ||
<div class="col-md-9 col-18 markdown-body"> | ||
<Fragment set:html={html} /> | ||
</div> | ||
<div class="col-md-3 col-6 markdown-toc"> | ||
<Toc headings={headings} /> | ||
</div> | ||
</> | ||
) : ( | ||
<> | ||
<Fragment set:html={html} /> | ||
</> | ||
) | ||
} | ||
</main> | ||
</section> | ||
</Layout> | ||
|
||
<style> | ||
main { | ||
max-width: 960px; | ||
margin: 0 auto; | ||
} | ||
</style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
--- | ||
import PageHeader from "../../../components/PageHeader.astro"; | ||
import Layout from "../../../layouts/Layout.astro"; | ||
import { getPage, listTutorialArticles } from "../../../utils"; | ||
const page = await getPage("tutorials"); | ||
const title = page.title; | ||
const description = page.description; | ||
const keywords = page.keywords; | ||
const tutorialsData = await listTutorialArticles({ pageSize: 100 }); | ||
// Group tutorials by series dynamically | ||
const tutorials = tutorialsData.data.reduce((acc, item) => { | ||
const seriesTitle = item.serials?.title || 'Uncategorized'; | ||
const seriesDescription = item.serials?.description || ""; | ||
// Initialize the series if not present in the accumulator | ||
if (!acc[seriesTitle]) { | ||
acc[seriesTitle] = { | ||
description: seriesDescription, | ||
articles: [], | ||
}; | ||
} | ||
// Add the tutorial to the respective series | ||
acc[seriesTitle].articles.push({ | ||
title: item.title, | ||
link: `/en/tutorials/${item.slug}`, //${item.serials?.slug}/ | ||
description: item.description || "", | ||
}); | ||
return acc; | ||
}, {}); | ||
// Convert the tutorials object into an array of series objects for easier mapping | ||
const tutorialSeries = Object.entries(tutorials).map(([seriesTitle, seriesData]) => ({ | ||
seriesTitle, | ||
description: seriesData.description, | ||
articles: seriesData.articles, | ||
})); | ||
--- | ||
|
||
<Layout title={title} description={description} keywords={keywords}> | ||
<PageHeader title={title} description={description} /> | ||
<section class="section"> | ||
<div class="container"> | ||
<div class="row justify-content-center"> | ||
<div class="col-lg-9"> | ||
<div class="card shadow border-0 rounded"> | ||
<div class="card-body"> | ||
{tutorialSeries.map(series => ( | ||
<> | ||
<h5 class="card-title">{series.seriesTitle}</h5> | ||
{series.description && <p class="text-muted">{series.description}</p>} | ||
<ul class="list-unstyled text-muted mb-4 pb-4"> | ||
{series.articles.map(article => ( | ||
<li> | ||
<i data-feather="arrow-right" class="fea icon-sm me-2"></i> | ||
<a href={article.link} target="_blank">{article.title}</a> | ||
{article.description && ` - ${article.description}`} | ||
</li> | ||
))} | ||
</ul> | ||
</> | ||
))} | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
</section> | ||
</Layout> |
Oops, something went wrong.