Skip to content

Commit

Permalink
feat: add DTO
Browse files Browse the repository at this point in the history
  • Loading branch information
fbuireu committed Jun 12, 2024
1 parent 69263f3 commit 5f8dc63
Show file tree
Hide file tree
Showing 28 changed files with 203 additions and 214 deletions.
Binary file modified .yarn/install-state.gz
Binary file not shown.
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,14 @@
"@astrojs/check": "^0.7.0",
"@astrojs/cloudflare": "^10.4.0",
"@astrojs/mdx": "^3.1.0",
"@astrojs/partytown": "^2.1.0",
"@astrojs/partytown": "^2.1.1",
"@astrojs/react": "^3.5.0",
"@astrojs/rss": "^4.0.6",
"@astrojs/sitemap": "^3.1.5",
"@fontsource-variable/nunito-sans": "^5.0.14",
"@fontsource/baskervville": "^5.0.20",
"@hookform/resolvers": "^3.6.0",
"astro": "^4.10.1",
"astro": "^4.10.2",
"clsx": "^2.1.1",
"firebase": "^10.12.2",
"firebase-admin": "^12.1.1",
Expand All @@ -64,7 +64,7 @@
"react-google-recaptcha-v3": "^1.10.1",
"react-hook-form": "^7.51.5",
"react-router-dom": "^6.23.1",
"resend": "^3.2.0",
"resend": "^3.3.0",
"swiper": "^11.1.4",
"three": "^0.165.0",
"vanilla-cookieconsent": "^3.0.1",
Expand All @@ -84,7 +84,7 @@
"@types/react-dom": "^18.3.0",
"@types/three": "^0.165.0",
"husky": "^9.0.11",
"lint-staged": "^15.2.5",
"lint-staged": "^15.2.6",
"stylelint": "^16.6.1",
"stylelint-config-recommended": "^14.0.0",
"stylelint-order": "^6.0.4",
Expand Down
45 changes: 45 additions & 0 deletions src/application/dto/articleDTO.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import type { CollectionEntry } from 'astro:content';
import type { BaseDTO } from '@shared/application/dto/baseDTO.ts';
import { DEFAULT_DATE_FORMAT } from 'src/consts';
import MarkdownIt from 'markdown-it';
import { generateExcerpt } from '@shared/application/utils/generateExcerpt';
import { getEntry } from "astro:content";

const parser: MarkdownIt = new MarkdownIt();

export enum ArticleType {
DEFAULT = "default",
NO_IMAGE = "no_image",
}

export interface ArticleData {
author: CollectionEntry<"authors">;
description: string;
publishDate: string;
variant: ArticleType;
}

export type ArticleDTO = {
data: ArticleData;
} & CollectionEntry<"articles">


export const articleDTO: BaseDTO<CollectionEntry<"articles">, ArticleDTO, Promise<ArticleDTO>> = {
render: async (raw: CollectionEntry<"articles">): Promise<ArticleDTO> => {
const author = await getEntry(raw.data.author.collection, raw.data.author.slug)
const description = raw.data.description ?? generateExcerpt({ parser, content: raw.body }).excerpt
const publishDate = new Date(raw.data.publishDate).toLocaleDateString("en", DEFAULT_DATE_FORMAT)
const variant: ArticleType = raw.data.featuredImage ? ArticleType.DEFAULT : ArticleType.NO_IMAGE

return {
...raw,
data: {
...raw.data,
author,
description,
publishDate,
variant,
},
}
},
};
4 changes: 2 additions & 2 deletions src/pages/articles/[...slug].astro
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import { Image } from "astro:assets";
import BaseLayout from "@components/templates/baseLayout/BaseLayout.astro";
import "./_article.css";
import { DEFAULT_DATE_FORMAT } from "src/consts";
import { slugify } from "@shared/utils/slugify";
import { slugify } from "../../ui/shared/ui/utils/slugify";
import type { ImageMetadata } from "astro";
import Breadcrumbs from "@components/molecules/breadcrumbs/Breadcrumbs.astro";
import { generateExcerpt } from "@shared/utils/generateExcerpt";
import { generateExcerpt } from "../../ui/shared/application/utils/generateExcerpt";
export const prerender = true;
Expand Down
90 changes: 36 additions & 54 deletions src/pages/articles/index.astro
Original file line number Diff line number Diff line change
@@ -1,45 +1,22 @@
---
import { getCollection, getEntry } from "astro:content";
import { getCollection } from "astro:content";
import BaseLayout from "@components/templates/baseLayout/BaseLayout.astro";
import { Image } from "astro:assets";
import MarkdownIt from "markdown-it";
import { generateExcerpt } from "src/ui/shared/utils/generateExcerpt";
import { slugify } from "@shared/utils/slugify";
import { DEFAULT_DATE_FORMAT } from "src/consts";
import { slugify } from "../../ui/shared/ui/utils/slugify";
import "./_articles.css";
import horizontalArrow from "@assets/images/svg/left-arrow.svg";
import type { ImageMetadata } from "astro";
import Breadcrumbs from "@components/molecules/breadcrumbs/Breadcrumbs.astro";
import { articleDTO } from "@application/dto/articleDTO";
import { getFeaturedArticle } from "../../ui/shared/application/utils/getFeaturedArticle";
import { ArticleType } from "../../application/dto/articleDTO";
enum ArticleType {
DEFAULT = "default",
NO_IMAGE = "no_image",
}
const articles = await getCollection("articles");
const images = import.meta.glob<{ default: ImageMetadata }>("/src/assets/**/*.{jpeg,jpg,png,gif}");
let articles = await getCollection("articles");
articles.sort((a, b) => new Date(b.data?.publishDate).valueOf() - new Date(a.data?.publishDate).valueOf());
const {
body: featuredArticleBody,
data: featuredArticle,
slug: featuredArticleSlug,
} = articles.find((article) => article.data?.isFeatured && article.data?.featuredImage) ??
articles.find((article) => article.data?.featuredImage);
const parser: MarkdownIt = MarkdownIt("default", {});
const featuredArticleDescription =
featuredArticle.description ??
generateExcerpt({
parser,
content: featuredArticleBody,
}).excerpt;
const publishedDate = featuredArticle.publishDate.toLocaleDateString("en", DEFAULT_DATE_FORMAT);
const featuredArticleHref = `/articles/${featuredArticleSlug}`;
const featuredArticleShareUrl = new URL(featuredArticleHref, Astro.url).href;
articles = await Promise.all(articles.map(articleDTO.render));
const featuredArticle = await articleDTO.render(getFeaturedArticle(articles));
const featuredArticleShareUrl = new URL(`/articles/${featuredArticle.slug}`, Astro.url).href;
---

<!-- todo: isolate sections -->
Expand All @@ -49,32 +26,32 @@ const featuredArticleShareUrl = new URL(featuredArticleHref, Astro.url).href;
<div class="articles__wrapper">
<section class="articles__wrapper__inner common-wrapper">
<div class="featured-article__wrapper">
<a href={featuredArticleHref}>
<a href={`/articles/${featuredArticle.slug}`}>
<Image
class="featured-article__image"
src={images[featuredArticle.featuredImage]()}
alt={featuredArticle.title}
src={images[featuredArticle.data.featuredImage]()}
alt={featuredArticle.data.title}
/>
</a>
<div class="featured-article__details__wrapper flex row-wrap">
<div class="featured-article__details flex column-wrap">
<time class="featured-article__publish-date font-sans-serif" datetime={publishedDate}>
{publishedDate}
<time class="featured-article__publish-date font-sans-serif" datetime={featuredArticle.data.publishDate}>
{featuredArticle.data.publishDate}
</time>
<h2 class="featured-article__title">{featuredArticle.title}</h2>
<p class="featured-article__description">{featuredArticleDescription}</p>
<h2 class="featured-article__title">{featuredArticle.data.title}</h2>
<p class="featured-article__description">{featuredArticle.data.description}</p>
{
featuredArticle.tags?.length > 0 && (
<ul class="featured-article__tags__list">
{featuredArticle.tags.map((tag) => (
{featuredArticle.data.tags.map((tag) => (
<a class="featured-article__tag__item" href={`/tags/${slugify(tag)}`}>
#{tag}
</a>
))}
</ul>
)
}
<a href={featuredArticleHref} class="featured-article__link flex align-center">
<a href={`/articles/${featuredArticle.slug}`} class="featured-article__link flex align-center">
Read More <img src={horizontalArrow.src} alt="Readt more" />
</a>
</div>
Expand Down Expand Up @@ -108,41 +85,37 @@ const featuredArticleShareUrl = new URL(featuredArticleHref, Astro.url).href;
"itemListElement": {
"@type": "ListItem",
"position": 0,
"url": new URL(featuredArticleSlug, Astro.url).href
"url": new URL(`/articles/${featuredArticle.slug}`, Astro.url).href
}
})}/>
</section>
</div>
<section class="articles__grid common-wrapper">
<ul class="articles__grid__list flex row-wrap justify-space-between">
{
articles.filter(({ slug }) => slug !== featuredArticleSlug).map(async ({ body, data: article, slug }) => {
const author = await getEntry(article.author.collection, article.author.slug)
const description = article.description ?? generateExcerpt({ parser, content: body }).excerpt;
const publishedDate = article.publishDate.toLocaleDateString('en', DEFAULT_DATE_FORMAT);
const variant: ArticleType = article.featuredImage ? ArticleType.DEFAULT : ArticleType.NO_IMAGE;
articles.filter(({ slug }) => slug !== featuredArticle.slug).map(async ({ data: article, slug }, index) => {

return (
<li class="articles__grid__item">
<a class="article__card__link" href={`/articles/${slug}`} aria-label={article.title} />
<article
class:list={[`article__card__wrapper`, {
'--default-variant': variant === ArticleType.DEFAULT,
'--no-image-variant': variant === ArticleType.NO_IMAGE
'--default-variant': article.variant === ArticleType.DEFAULT,
'--no-image-variant': article.variant === ArticleType.NO_IMAGE
}]}
>
{article.featuredImage && (
<Image class="article__card__image" src={images[article.featuredImage]()} alt={article.title} />
)}
<time class="article__card__publish-date font-sans-serif" datetime={publishedDate}>
{publishedDate}
<time class="article__card__publish-date font-sans-serif" datetime={article.publishedDate}>
{article.publishedDate}
</time>
<h2 class="article__card__title font-serif">{article.title}</h2>
<p class="article__card__author">
by <a href={`/tags/${author.data.id}`}>{author.data.name}</a>
by <a href={`/tags/${article.author.data.id}`}>{article.author.data.name}</a>
</p>
<p class="article__card__excerpt">{description}</p>
{(articles.tags && article.tags?.length > 0) && (
<p class="article__card__excerpt">{article.description}</p>
{article.tags?.length > 0 && (
<ul class="article__card__tags__list">
{article.tags.map((tag) => (
<a class="article__card__tag__item" href={`/tags/${slugify(tag)}`}>
Expand All @@ -152,6 +125,15 @@ const featuredArticleShareUrl = new URL(featuredArticleHref, Astro.url).href;
</ul>
)}
</article>
<script type="application/ld+json" set:html={JSON.stringify({
"@context": "https://schema.org",
"@type": "ItemList",
"itemListElement": {
"@type": "ListItem",
"position": index + 1,
"url": new URL(`/articles/${slug}`, Astro.url).href
}
})}/>
</li>
);
})
Expand Down
2 changes: 1 addition & 1 deletion src/pages/projects.astro
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const images = import.meta.glob<{ default: ImageMetadata }>("/src/assets/**/*.{j

<BaseLayout title="" description="">
<ProjectsIntro />
<ProjectSection id="stories-of-impact" sl>
<ProjectSection id="stories-of-impact">
<div class="project-description flex column-wrap justify-center align-center" slot="project-information">
<h2 class="project-title">Stories of Impact</h2>
<p>Lorem ipsum dolor sit amet</p>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
import { type CollectionEntry, getEntry } from "astro:content";
import type { CollectionEntry } from "astro:content";
import { AboutLatestArticlesSliderNavigation } from "@components/molecules/aboutLatestArticlesSlider/components/aboutLatestArticlesSliderNavigation";
import { generateExcerpt } from "src/ui/shared/utils/generateExcerpt";
import { slugify } from "@shared/utils/slugify";
import MarkdownIt from "markdown-it";
import { DEFAULT_DATE_FORMAT } from "src/consts.ts";
import { slugify } from "src/ui/shared/ui/utils/slugify";
import { A11y, Keyboard, Navigation, Autoplay, Virtual } from "swiper/modules";
import { Swiper, SwiperSlide } from "swiper/react";
import type { SwiperOptions } from "swiper/types";
Expand Down Expand Up @@ -41,7 +38,6 @@ const SLIDER_CONFIG: SwiperOptions = {
},
containerModifierClass: "latest-articles-",
};
const parser: MarkdownIt = MarkdownIt("default", {});

export const AboutLatestArticlesSlider = ({ articles }: AboutLatestArticlesSLiderProps) => {
return (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { deSlugify } from "@shared/utils/deSlugify";
import { deSlugify } from "src/ui/shared/ui/utils/deSlugify";

interface GenerateBreadcrumbsProps {
currentPath: string;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,14 @@
import type { CollectionEntry } from "astro:content";
import { generateExcerpt } from "src/ui/shared/utils/generateExcerpt";
import { slugify } from "@shared/utils/slugify";
import MarkdownIt from "markdown-it";
import { DEFAULT_DATE_FORMAT } from "src/consts.ts";
import { slugify } from "src/ui/shared/ui/utils/slugify";
import { A11y, Keyboard, Navigation, Virtual, Autoplay } from "swiper/modules";
import { Swiper, SwiperSlide } from "swiper/react";
import type { SwiperOptions } from "swiper/types";
import "./latest-articles-slider.css";
import { LatestArticlesSliderNavigation } from "./components/latestArticlesSliderNavigation";
import clsx from "clsx";
import { type ArticleDTO, ArticleType } from "@application/dto/articleDTO.ts";

interface LatestArticlesSLiderProps {
articles: CollectionEntry<"articles">[];
}

enum ArticleType {
DEFAULT = "default",
NO_IMAGE = "no_image",
articles: ArticleDTO[];
}

const SLIDER_CONFIG: SwiperOptions = {
Expand Down Expand Up @@ -51,18 +43,17 @@ export const LatestArticlesSlider = ({ articles }: LatestArticlesSLiderProps) =>
<div className="latest-articles__slider common-wrapper">
<Swiper {...SLIDER_CONFIG}>
<ul className="latest__articles__list flex row-wrap justify-space-between">
{articles.map(({ slug, data: article, ...content }) => {
const variant: ArticleType = article.featuredImage ? ArticleType.DEFAULT : ArticleType.NO_IMAGE;
{articles.map(({ slug, data: article }) => {
const href = `/articles/${slug}`;

console.log("article", article);
return (
<li key={slug} className="latest__article__item__wrapper article__item clickable">
<SwiperSlide key={slug}>
<a className="latest__article__link-card" href={href} aria-label={article.title} />
<article
className={clsx("latest__article__item", {
"--default-variant": variant === ArticleType.DEFAULT,
"--no-image-variant": variant === ArticleType.NO_IMAGE,
"--default-variant": article.variant === ArticleType.DEFAULT,
"--no-image-variant": article.variant === ArticleType.NO_IMAGE,
})}
>
{article.featuredImage && (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,33 +1,13 @@
---
import MarkdownIt from "markdown-it";
import { generateExcerpt } from "../../../shared/utils/generateExcerpt";
import { DEFAULT_DATE_FORMAT } from "../../../../consts";
import { getCollection, getEntry } from "astro:content";
import { articleDTO } from "@application/dto/articleDTO";
import { getCollection } from "astro:content";
import "./about-latest-articles.css";
import { AboutLatestArticlesSlider } from "@components/molecules/aboutLatestArticlesSlider";
const parser: MarkdownIt = MarkdownIt("default", {});
const images = import.meta.glob("/src/assets/**/*.{jpeg,jpg,png,gif}");
let articles = await getCollection("articles");
articles.sort((a, b) => new Date(b.data.publishDate).valueOf() - new Date(a.data.publishDate).valueOf()).splice(4);
articles = await Promise.all(
articles.map(async (article) => {
const author = await getEntry(article.data.author.collection, article.data.author.slug);
const description = article.data.description ?? generateExcerpt({ parser, content: article.body }).excerpt;
const publishDate = new Date(article.data.publishDate).toLocaleDateString("en", DEFAULT_DATE_FORMAT);
return {
...article,
data: {
...article.data,
author,
description,
publishDate,
},
};
}),
);
articles = await Promise.all(articles.map(articleDTO.render));
---

<section class="about-latest-articles__wrapper common-wrapper">
Expand Down
Loading

0 comments on commit 5f8dc63

Please sign in to comment.