feat: display articles
Some checks failed
Deploy to SFTP Server / build (push) Failing after 1m38s

This commit is contained in:
Max Richter
2025-10-22 17:17:34 +02:00
parent 3a120e32fc
commit ae266dbdc5
10 changed files with 121 additions and 47 deletions

View File

@@ -1,5 +1,5 @@
---
import markdownToText from "@helpers/markdownToText";
import { markdownToText,readDuration } from "@helpers/markdown";
import { Card } from "./card";
import { useTranslatedPath, useTranslations } from "@i18n/utils";
import Image from "@components/Image.astro";
@@ -30,7 +30,7 @@ const link = translatePath(`/${collection}/${id.split("/")[0]}`);
<Card
classes={`grid gradient border-1 border-neutral overflow-hidden ${cover ? "grid-rows-[200px_1fr] xs:grid-rows-none xs:grid-cols-[1fr_200px]" : ""}`}>
<Card.Content classes="px-8 py-7 order-last xs:order-first">
{date && (<Card.Metadata date={date} />)}
{(date || body)&& <Card.Metadata date={date} readDuration={readDuration(body)} />}
<Card.Title classes="text-4xl flex items-center gap-2">
{
icon &&

View File

@@ -1,5 +1,5 @@
---
import markdownToText from "@helpers/markdownToText";
import { markdownToText } from "@helpers/markdown";
import { useTranslatedPath } from "@i18n/utils";
import type { InferEntrySchema } from "astro:content";

View File

@@ -1,5 +1,6 @@
<script lang="ts">
export let date: string | Date;
export let readDuration: string | undefined;
const toDate = (d: string | Date) =>
typeof d === "string" ? new Date(d) : d;
@@ -22,4 +23,8 @@
<time class="text-sm text-neutral" datetime={iso(date)}
>{formatDate(date)}</time>
{/if}
{#if readDuration}
<div class="text-sm text-neutral">{readDuration} mins read</div>
{/if}
</div>

View File

@@ -0,0 +1,25 @@
---
import * as memorium from "@helpers/memorium";
import { markdownToHtml, markdownToText } from "@helpers/markdown";
import Image from "@components/Image.astro";
const { resource } = Astro.props;
const ingredients = resource?.content?.recipeIngredient || [];
const instructions = resource?.content?.recipeInstructions || [];
---
<h1 class="text-4xl">{resource?.content?.headline}</h1>
<div>
{
resource?.content?.image && (
<Image
hash
src={{ src: memorium.getImageUrl(resource.content.image) }}
alt="Cover for {resource?.content?.name}"
class="rounded-2xl overflow-hidden"
pictureClass="rounded-2xl"
/>
)
}
</div>
<div set:html={markdownToHtml(resource?.content?.articleBody)} />

View File

@@ -0,0 +1,13 @@
---
import Recipe from "./Recipe.astro";
import Article from "./Article.astro";
const { resource } = Astro.props;
const type = resource?.content?._type ?? "unknown";
---
{type === "Recipe" && <Recipe resource={resource} />}
{type === "Article" && <Article resource={resource} />}
{type === "unknown" && <div>
<h3>Unknown resource type</h3>
</div>}

View File

@@ -0,0 +1,32 @@
---
import * as memorium from "@helpers/memorium";
import { markdownToHtml, markdownToText } from "@helpers/markdown";
import Image from "@components/Image.astro";
const { resource } = Astro.props
const ingredients = resource?.content?.recipeIngredient || [];
const instructions = resource?.content?.recipeInstructions || [];
---
<h1 class="text-4xl">{resource?.content?.name}</h1>
<div>
{resource?.content?.image && <Image hash src={{src: memorium.getImageUrl(resource.content.image)}} alt="Cover for {resource?.content?.name}" class="rounded-2xl overflow-hidden" pictureClass="rounded-2xl" />}
</div>
<p>{resource?.content?.description}</p>
<h2 class="text-2xl">Ingredients</h2>
<ul>
{
ingredients.map((ingredient) => (
<li set:html={markdownToHtml(ingredient)}/>
))
}
</ul>
<h2 class="text-2xl">Steps</h2>
<ol>
{
instructions.map((ingredient) => (
<li set:html={markdownToHtml(ingredient)}/>
))
}
</ol>

View File

@@ -1,8 +1,26 @@
import MarkdownIt from "markdown-it";
const parser = new MarkdownIt();
export default function markdownToText(markdown: string): string {
if(!markdown) return ''
export function readDuration(markdown: string): number | undefined {
const words = markdown.split(" ")?.filter(Boolean)?.length;
return words && Math.round(words / 250);
}
export function markdownToHtml(markdown: string): string {
const md = new MarkdownIt({
html: false, // set to true only if you trust the source
linkify: true,
typographer: true,
breaks: true,
});
// Convert -> sanitize
const unsafeHtml = md.render(markdown);
return unsafeHtml;
}
export function markdownToText(markdown: string): string {
if (!markdown) return "";
return parser
.render(markdown)
.split("\n")

View File

@@ -1,9 +1,10 @@
---
import Layout from "@layouts/Layout.astro";
import { useTranslatedPath } from "@i18n/utils";
import ResourceDisplay from "@components/resources/Display.astro";
import * as memorium from "@helpers/memorium";
import { resources as resourceTypes } from "../resources.ts";
import markdownToText from "@helpers/markdownToText";
import { markdownToText } from "@helpers/markdown";
import Image from "@components/Image.astro";
const { resourceType, resourceName } = Astro?.params;
@@ -35,9 +36,6 @@ export async function getStaticPaths() {
const resource = await memorium.listResource(
`${resourceType}/${resourceName}.md`,
);
const ingredients = resource?.content?.recipeIngredient || [];
const instructions = resource?.content?.recipeInstructions || [];
---
<Layout title="Max Richter">
@@ -49,7 +47,7 @@ const instructions = resource?.content?.recipeInstructions || [];
</a>
<div class="date opacity-50">
{
resource?.content.date?.toLocaleString("en-US", {
resource?.content.datePublished?.toLocaleString("en-US", {
month: "long",
day: "numeric",
year: "numeric",
@@ -57,28 +55,5 @@ const instructions = resource?.content?.recipeInstructions || [];
}
</div>
</div>
<!-- <pre>{JSON.stringify(resource, null, 2)}</pre> -->
<h1 class="text-4xl">{resource?.content?.name}</h1>
<div>
{resource?.content?.image && <Image hash src={{src: memorium.getImageUrl(resource.content.image)}} alt="Cover for {resource?.content?.name}" class="rounded-2xl overflow-hidden" pictureClass="rounded-2xl" />}
</div>
<p>{resource?.content?.description}</p>
<h2 class="text-2xl">Ingredients</h2>
<ul>
{
ingredients.map((ingredient) => (
<li>{markdownToText(ingredient)}</li>
))
}
</ul>
<h2 class="text-2xl">Steps</h2>
<ol>
{
instructions.map((ingredient) => (
<li>{markdownToText(ingredient)}</li>
))
}
</ol>
<ResourceDisplay resource={resource} />
</Layout>

View File

@@ -3,7 +3,7 @@ import Layout from "@layouts/Layout.astro";
import HeroCard from "@components/HeroCard.astro";
import * as memorium from "@helpers/memorium";
import { resources as resourceTypes } from "../resources.ts";
import markdownToText from "@helpers/markdownToText";
import { markdownToText } from "@helpers/markdown";
const { resourceType } = Astro.params;
@@ -28,19 +28,25 @@ export async function getStaticPaths() {
});
}
function isValidResource(res) {
if (res?.content?.name) return true;
if (res?.content?.headline) return true;
return false;
}
---
<Layout title="Max Richter">
{
resources.content
.filter((res) => res && res?.content && res?.content?.name)
.filter((res) => res && res?.content)
.map((resource: any) => (
<HeroCard
post={{
collection: "resources/" + resourceType,
id: resource.name.replace(/\.md$/, ""),
data: {
title: resource.content.name,
title: resource.content.name ?? resource.content.headline,
date: resource?.content?.datePublished,
cover: {
src: memorium.getImageUrl(resource.content.image),
},
@@ -50,3 +56,4 @@ export async function getStaticPaths() {
))
}
</Layout>
</Layout>

View File

@@ -19,15 +19,14 @@ type Resource = {
// },
// };
// const articles = {
// id: "Articles",
// collection,
// body: "Articles saved",
// data: {
// title: "Articles",
// icon: "📰",
// },
// };
const articles = {
id: "articles",
collection,
data: {
title: "Articles",
icon: "📰",
},
};
const recipes = {
id: "recipes",
@@ -58,4 +57,4 @@ const recipes = {
// },
// };
export const resources: Resource[] = [recipes];
export const resources: Resource[] = [recipes, articles];