diff --git a/apps/website/src/app/(home)/NewBlogPosts/NewBlogPosts.tsx b/apps/website/src/app/(home)/NewBlogPosts/NewBlogPosts.tsx
new file mode 100644
index 0000000..0e8e17e
--- /dev/null
+++ b/apps/website/src/app/(home)/NewBlogPosts/NewBlogPosts.tsx
@@ -0,0 +1,13 @@
+import { BlogTeaser } from "../../../components/BlogTeaser/BlogTeaser";
+import { getBlogPosts } from "../../../lib/queries";
+
+export const NewBlogPosts = async () => {
+ const blogPosts = await getBlogPosts();
+ return (
+
+ );
+};
diff --git a/apps/website/src/app/(home)/page.tsx b/apps/website/src/app/(home)/page.tsx
index ddf65d1..ae37be4 100644
--- a/apps/website/src/app/(home)/page.tsx
+++ b/apps/website/src/app/(home)/page.tsx
@@ -1,9 +1,9 @@
import { MyClients } from "../../components/MyClients/MyClients";
import { createGenerateMetadata } from "../../lib/metadata";
-import { BlogTeaser } from "./BlogTeaser/BlogTeaser";
import { Header } from "./Header/Header";
import { HomeAccordion } from "./HomeAccordion/HomeAccordion";
import { LcdTeaser } from "./LcdTeaser/LcdTeaser";
+import { NewBlogPosts } from "./NewBlogPosts/NewBlogPosts";
export const generateMetadata = createGenerateMetadata(async () => ({
other: {
@@ -17,7 +17,7 @@ const Home = async () => {
-
+
);
diff --git a/apps/website/src/app/blog/[slug]/page.tsx b/apps/website/src/app/blog/[slug]/page.tsx
index dc69a29..6ebed2f 100644
--- a/apps/website/src/app/blog/[slug]/page.tsx
+++ b/apps/website/src/app/blog/[slug]/page.tsx
@@ -1,37 +1,16 @@
import { format } from "date-fns";
import { CalendarDays, Clock, Feather, User } from "lucide-react";
-import { groq } from "next-sanity";
import readingTime from "reading-time";
-import { z } from "zod";
import { ArticleHeader } from "../../../components/ArticleHeader/ArticleHeader";
import { MDXContent } from "../../../components/MDXContent/MDXContent";
import { StructuredData } from "../../../components/StructuredData/StructuredData";
import { Container } from "../../../design-system/Container/Container";
import { createGenerateMetadata, ogImage } from "../../../lib/metadata";
-import { queryContent } from "../../../lib/sanity";
+import { getBlogPost, getBlogPosts } from "../../../lib/queries";
export const generateMetadata = createGenerateMetadata(async ({ params }) => {
const { slug } = params;
- const { title, summary, date, image, content } = await queryContent(
- groq`
- *[_type == 'blogPost' && slug.current == '${slug}'][0]
- {
- title,
- date,
- summary,
- 'image': image.asset->url,
- content,
- }
- `,
- z.object({
- title: z.string(),
- date: z.string(),
- summary: z.string().nullable(),
- image: z.string(),
- content: z.string(),
- }),
- );
-
+ const { title, summary, date, image, content } = await getBlogPost(slug);
const stats = readingTime(content);
return {
@@ -50,7 +29,7 @@ export const generateMetadata = createGenerateMetadata(async ({ params }) => {
url: ogImage({
overline: "Blog • Katharina Clasen",
headline: title,
- image,
+ image: image.url,
readingTime: `${Math.ceil(stats.minutes)} min`,
date: format(new Date(date), "LLLL dd, yyyy"),
}),
@@ -70,20 +49,7 @@ export const generateMetadata = createGenerateMetadata(async ({ params }) => {
});
export const generateStaticParams = async () => {
- const blogPosts = await queryContent(
- groq`
- *[_type == 'blogPost']
- {
- 'slug': slug.current
- }
- `,
- z.array(
- z.object({
- slug: z.string(),
- }),
- ),
- );
-
+ const blogPosts = await getBlogPosts();
return blogPosts.map((blogPost) => ({
slug: blogPost.slug,
}));
@@ -97,52 +63,7 @@ interface Props {
const BlogPostPage = async ({ params }: Props) => {
const { slug } = params;
- const blogPost = await queryContent(
- groq`
- *[_type == 'blogPost' && slug.current == '${slug}'][0]
- {
- _id,
- 'slug': slug.current,
- title,
- summary,
- image{'url': asset->url, alt, border},
- author,
- date,
- services[]->{title},
- topics[]->{title},
- content
- }
- `,
- z.object({
- _id: z.string(),
- slug: z.string(),
- title: z.string(),
- summary: z.string().nullable(),
- image: z.object({
- url: z.string(),
- alt: z.string(),
- border: z.boolean().nullable(),
- }),
- author: z.string(),
- date: z.string(),
- services: z
- .array(
- z.object({
- title: z.string(),
- }),
- )
- .nullable(),
- topics: z
- .array(
- z.object({
- title: z.string(),
- }),
- )
- .nullable(),
- content: z.string(),
- }),
- );
-
+ const blogPost = await getBlogPost(slug);
const stats = readingTime(blogPost.content);
return (
<>
diff --git a/apps/website/src/app/blog/page.tsx b/apps/website/src/app/blog/page.tsx
index 919b196..a9d99ae 100644
--- a/apps/website/src/app/blog/page.tsx
+++ b/apps/website/src/app/blog/page.tsx
@@ -1,14 +1,11 @@
import { format } from "date-fns";
import { CalendarDays, Clock, Feather, User } from "lucide-react";
-import { groq } from "next-sanity";
import Link from "next/link";
import readingTime from "reading-time";
-import { z } from "zod";
import { ArticlePreview } from "../../components/ArticlePreview/ArticlePreview";
import { Container } from "../../design-system/Container/Container";
import { createGenerateMetadata, ogImage } from "../../lib/metadata";
-import { getMetadata } from "../../lib/queries";
-import { queryContent } from "../../lib/sanity";
+import { getBlogPosts, getMetadata } from "../../lib/queries";
export const generateMetadata = createGenerateMetadata(async () => {
const {
@@ -43,51 +40,7 @@ export const generateMetadata = createGenerateMetadata(async () => {
});
const BlogPage = async () => {
- const blogPosts = await queryContent(
- groq`
- *[_type == 'blogPost']
- {
- _id,
- title,
- 'slug': slug.current,
- image{'url': asset->url, alt, border},
- author,
- date,
- services[]->{title},
- topics[]->{title},
- content
- } | order(date desc)
- `,
- z.array(
- z.object({
- _id: z.string(),
- title: z.string(),
- slug: z.string(),
- image: z.object({
- url: z.string(),
- alt: z.string(),
- border: z.boolean().nullable(),
- }),
- author: z.string(),
- date: z.string(),
- services: z
- .array(
- z.object({
- title: z.string(),
- }),
- )
- .nullable(),
- topics: z
- .array(
- z.object({
- title: z.string(),
- }),
- )
- .nullable(),
- content: z.string(),
- }),
- ),
- );
+ const blogPosts = await getBlogPosts();
return (
diff --git a/apps/website/src/app/lifecentereddesign/LcdBlogPosts/LcdBlogPosts.tsx b/apps/website/src/app/lifecentereddesign/LcdBlogPosts/LcdBlogPosts.tsx
new file mode 100644
index 0000000..d3d5595
--- /dev/null
+++ b/apps/website/src/app/lifecentereddesign/LcdBlogPosts/LcdBlogPosts.tsx
@@ -0,0 +1,18 @@
+import { BlogTeaser } from "../../../components/BlogTeaser/BlogTeaser";
+import { getBlogPosts } from "../../../lib/queries";
+
+export const LcdBlogPosts = async () => {
+ const blogPosts = await getBlogPosts();
+ const lcdBlogPosts = blogPosts.filter(
+ (blogPost) =>
+ blogPost.topics?.some((topic) => topic.title === "Life-centered Design"),
+ );
+
+ return (
+
+ );
+};
diff --git a/apps/website/src/app/lifecentereddesign/page.tsx b/apps/website/src/app/lifecentereddesign/page.tsx
index df41bcf..236bcc3 100644
--- a/apps/website/src/app/lifecentereddesign/page.tsx
+++ b/apps/website/src/app/lifecentereddesign/page.tsx
@@ -2,6 +2,7 @@ import { createGenerateMetadata, ogImage } from "../../lib/metadata";
import { getMetadata } from "../../lib/queries";
import { Header } from "./Header/Header";
import { LcdAccordion } from "./LcdAccordion/LcdAccordion";
+import { LcdBlogPosts } from "./LcdBlogPosts/LcdBlogPosts";
import { LcdJourney } from "./LcdJourney/LcdJourney";
import { LcdPrinciples } from "./LcdPrinciples/LcdPrinciples";
import { LcdThinking } from "./LcdThinking/LcdThinking";
@@ -50,6 +51,7 @@ const LcdPage = () => {
+
diff --git a/apps/website/src/app/projects/ProjectList/ProjectList.tsx b/apps/website/src/app/projects/ProjectList/ProjectList.tsx
index 03d1ab2..0492446 100644
--- a/apps/website/src/app/projects/ProjectList/ProjectList.tsx
+++ b/apps/website/src/app/projects/ProjectList/ProjectList.tsx
@@ -4,10 +4,11 @@ import { ArticlePreview } from "../../../components/ArticlePreview/ArticlePrevie
import { AutoAnimate } from "../../../components/AutoAnimate/AutoAnimate";
import { Heading } from "../../../design-system/Heading/Heading";
import { context } from "../../../lib/projects";
-import { Filter, Projects, Sort } from "../page";
+import { Project } from "../../../lib/queries";
+import { Filter, Sort } from "../page";
interface Props {
- projects: Projects;
+ projects: Array;
filter?: Filter;
sort?: Sort;
}
diff --git a/apps/website/src/app/projects/[slug]/page.tsx b/apps/website/src/app/projects/[slug]/page.tsx
index eaa3228..ded143c 100644
--- a/apps/website/src/app/projects/[slug]/page.tsx
+++ b/apps/website/src/app/projects/[slug]/page.tsx
@@ -1,36 +1,14 @@
import { CalendarDays, Contact, Feather } from "lucide-react";
-import { groq } from "next-sanity";
-import { z } from "zod";
import { ArticleHeader } from "../../../components/ArticleHeader/ArticleHeader";
import { MDXContent } from "../../../components/MDXContent/MDXContent";
import { Container } from "../../../design-system/Container/Container";
import { createGenerateMetadata, ogImage } from "../../../lib/metadata";
-import { context, contexts } from "../../../lib/projects";
-import { queryContent } from "../../../lib/sanity";
+import { context } from "../../../lib/projects";
+import { getProject, getProjects } from "../../../lib/queries";
export const generateMetadata = createGenerateMetadata(async ({ params }) => {
const { slug } = params;
- const project = await queryContent(
- groq`
- *[_type == 'project' && slug.current == '${slug}'][0]
- {
- title,
- summary,
- date,
- context,
- 'client': client->shortName,
- 'image': image.asset->url,
- }
- `,
- z.object({
- title: z.string(),
- summary: z.string().nullable(),
- date: z.string(),
- context: z.enum(contexts),
- client: z.string().nullable(),
- image: z.string(),
- }),
- );
+ const project = await getProject(slug);
return {
title: project.title,
description: project.summary || "Project by Katharina Clasen",
@@ -47,7 +25,7 @@ export const generateMetadata = createGenerateMetadata(async ({ params }) => {
url: ogImage({
overline: "Project • Katharina Clasen",
headline: project.title,
- image: project.image,
+ image: project.image.url,
client: context(project.context, project.client || ""),
date: new Date(project.date).getFullYear().toString(),
}),
@@ -60,19 +38,7 @@ export const generateMetadata = createGenerateMetadata(async ({ params }) => {
});
export const generateStaticParams = async () => {
- const projects = await queryContent(
- groq`
- *[_type == 'project']
- {
- 'slug': slug.current
- }`,
- z.array(
- z.object({
- slug: z.string(),
- }),
- ),
- );
-
+ const projects = await getProjects();
return projects.map((project) => ({
slug: project.slug,
}));
@@ -86,58 +52,7 @@ interface Props {
const ProjectPage = async ({ params }: Props) => {
const { slug } = params;
- const project = await queryContent(
- groq`
- *[_type == 'project' && slug.current == '${slug}'][0]
- {
- _id,
- title,
- image{'url': asset->url, alt, border},
- context,
- 'client': client->shortName,
- date,
- period,
- externalLink{label, href},
- services[]->{title},
- topics[]->{title},
- content
- }
- `,
- z.object({
- _id: z.string(),
- title: z.string(),
- image: z.object({
- url: z.string(),
- alt: z.string(),
- border: z.boolean().nullable(),
- }),
- context: z.enum(contexts),
- client: z.string().nullable(),
- date: z.string(),
- period: z.string().nullable(),
- externalLink: z
- .object({
- label: z.string(),
- href: z.string(),
- })
- .nullable(),
- services: z
- .array(
- z.object({
- title: z.string(),
- }),
- )
- .nullable(),
- topics: z
- .array(
- z.object({
- title: z.string(),
- }),
- )
- .nullable(),
- content: z.string(),
- }),
- );
+ const project = await getProject(slug);
return (
diff --git a/apps/website/src/app/projects/page.tsx b/apps/website/src/app/projects/page.tsx
index 58ed3e0..47c56db 100644
--- a/apps/website/src/app/projects/page.tsx
+++ b/apps/website/src/app/projects/page.tsx
@@ -5,7 +5,7 @@ import { Container } from "../../design-system/Container/Container";
import { Heading } from "../../design-system/Heading/Heading";
import { createGenerateMetadata, ogImage } from "../../lib/metadata";
import { contexts } from "../../lib/projects";
-import { getMetadata } from "../../lib/queries";
+import { getMetadata, getProjects } from "../../lib/queries";
import { queryContent } from "../../lib/sanity";
import { ProjectFilter } from "./ProjectFilter/ProjectFilter";
import { ProjectList } from "./ProjectList/ProjectList";
@@ -44,58 +44,6 @@ export const generateMetadata = createGenerateMetadata(async () => {
};
});
-export type Projects = Awaited>;
-
-const getProjects = async () => {
- return await queryContent(
- groq`
- *[_type == 'project']
- {
- _id,
- title,
- 'slug': slug.current,
- image{'url': asset->url, alt, border},
- context,
- 'client': client->shortName,
- date,
- period,
- services[]->{title},
- topics[]->{title}
- } | order(date desc)
- `,
- z.array(
- z.object({
- _id: z.string(),
- title: z.string(),
- slug: z.string(),
- image: z.object({
- url: z.string(),
- alt: z.string(),
- border: z.boolean().nullable(),
- }),
- context: z.enum(contexts),
- client: z.string().nullable(),
- date: z.string(),
- period: z.string().nullable(),
- services: z
- .array(
- z.object({
- title: z.string(),
- }),
- )
- .nullable(),
- topics: z
- .array(
- z.object({
- title: z.string(),
- }),
- )
- .nullable(),
- }),
- ),
- );
-};
-
export type Filter = z.infer;
const filterSchema = z.object({
service: z.coerce.string().optional(),
diff --git a/apps/website/src/app/(home)/BlogTeaser/BlogTeaser.tsx b/apps/website/src/components/BlogTeaser/BlogTeaser.tsx
similarity index 59%
rename from apps/website/src/app/(home)/BlogTeaser/BlogTeaser.tsx
rename to apps/website/src/components/BlogTeaser/BlogTeaser.tsx
index deea96a..7c48aa1 100644
--- a/apps/website/src/app/(home)/BlogTeaser/BlogTeaser.tsx
+++ b/apps/website/src/components/BlogTeaser/BlogTeaser.tsx
@@ -1,72 +1,32 @@
import { format } from "date-fns";
import { ArrowRight, CalendarDays, Clock, User } from "lucide-react";
-import { groq } from "next-sanity";
import Link from "next/link";
import readingTime from "reading-time";
-import { z } from "zod";
-import { ArticlePreview } from "../../../components/ArticlePreview/ArticlePreview";
-import { Section } from "../../../components/Section/Section";
-import { Body } from "../../../design-system/Body/Body";
-import { Button } from "../../../design-system/Button/Button";
-import { Heading } from "../../../design-system/Heading/Heading";
-import { queryContent } from "../../../lib/sanity";
+import { Body } from "../../design-system/Body/Body";
+import { Button } from "../../design-system/Button/Button";
+import { Heading } from "../../design-system/Heading/Heading";
+import { BlogPost } from "../../lib/queries";
+import { ArticlePreview } from "../ArticlePreview/ArticlePreview";
+import { Section } from "../Section/Section";
-export const BlogTeaser = async () => {
- const blogPosts = await queryContent(
- groq`
- *[_type == 'blogPost']
- {
- _id,
- title,
- 'slug': slug.current,
- image{'url': asset->url, alt, border},
- author,
- date,
- services[]->{title},
- topics[]->{title},
- content
- } | order(date desc)[0..1]
- `,
- z.array(
- z.object({
- _id: z.string(),
- title: z.string(),
- slug: z.string(),
- image: z.object({
- url: z.string(),
- alt: z.string(),
- border: z.boolean().nullable(),
- }),
- author: z.string(),
- date: z.string(),
- services: z
- .array(
- z.object({
- title: z.string(),
- }),
- )
- .nullable(),
- topics: z
- .array(
- z.object({
- title: z.string(),
- }),
- )
- .nullable(),
- content: z.string(),
- }),
- ),
- );
+interface Props {
+ title: string;
+ description: string;
+ blogPosts: Array;
+}
+
+export const BlogTeaser = async ({ title, description, blogPosts }: Props) => {
+ const blogPostsToDisplay = blogPosts.slice(0, 2);
return (
- Blog
+ {title}
- Irregular writing on UX Design, Life-centered Design, and more...
+ {description}
- {blogPosts.map((blogPost) => {
+ {blogPostsToDisplay.map((blogPost) => {
const stats = readingTime(blogPost.content);
return (
-
diff --git a/apps/website/src/lib/queries.ts b/apps/website/src/lib/queries.ts
index e462844..8f3ecd1 100644
--- a/apps/website/src/lib/queries.ts
+++ b/apps/website/src/lib/queries.ts
@@ -5,6 +5,7 @@ import { backgroundColorsList, colorsList } from "./colors";
import { illustrationsList } from "../components/illustrations/illustrations";
import { markdownToHtml } from "./markdown";
import { queryContent } from "./sanity";
+import { contexts } from "./projects";
export type AccordeonItems = Awaited>;
@@ -229,3 +230,161 @@ export const getImage = async (id: string) => {
}),
);
};
+
+export type BlogPost = z.infer;
+const blogPostSchema = z.object({
+ _id: z.string(),
+ slug: z.string(),
+ title: z.string(),
+ summary: z.string().nullable(),
+ image: z.object({
+ url: z.string(),
+ alt: z.string(),
+ border: z.boolean().nullable(),
+ }),
+ author: z.string(),
+ date: z.string(),
+ services: z
+ .array(
+ z.object({
+ title: z.string(),
+ }),
+ )
+ .nullable(),
+ topics: z
+ .array(
+ z.object({
+ title: z.string(),
+ }),
+ )
+ .nullable(),
+ content: z.string(),
+});
+
+export const getBlogPosts = async () => {
+ return await queryContent(
+ groq`
+ *[_type == 'blogPost']
+ {
+ _id,
+ 'slug': slug.current,
+ title,
+ summary,
+ image{'url': asset->url, alt, border},
+ author,
+ date,
+ services[]->{title},
+ topics[]->{title},
+ content
+ } | order(date desc)
+ `,
+ z.array(blogPostSchema),
+ );
+};
+
+export const getBlogPost = async (slug: string) => {
+ return await queryContent(
+ groq`
+ *[_type == 'blogPost' && slug.current == '${slug}'][0]
+ {
+ _id,
+ 'slug': slug.current,
+ title,
+ summary,
+ image{'url': asset->url, alt, border},
+ author,
+ date,
+ services[]->{title},
+ topics[]->{title},
+ content
+ }
+ `,
+ blogPostSchema,
+ );
+};
+
+export type Project = z.infer;
+const projectSchema = z.object({
+ _id: z.string(),
+ slug: z.string(),
+ title: z.string(),
+ summary: z.string().nullable(),
+ image: z.object({
+ url: z.string(),
+ alt: z.string(),
+ border: z.boolean().nullable(),
+ }),
+ context: z.enum(contexts),
+ client: z.string().nullable(),
+ date: z.string(),
+ period: z.string().nullable(),
+ externalLink: z
+ .object({
+ label: z.string(),
+ href: z.string(),
+ })
+ .nullable(),
+ services: z
+ .array(
+ z.object({
+ title: z.string(),
+ }),
+ )
+ .nullable(),
+ topics: z
+ .array(
+ z.object({
+ title: z.string(),
+ }),
+ )
+ .nullable(),
+ content: z.string(),
+});
+
+export const getProjects = async () => {
+ return await queryContent(
+ groq`
+ *[_type == 'project']
+ {
+ _id,
+ 'slug': slug.current,
+ title,
+ summary,
+ image{'url': asset->url, alt, border},
+ context,
+ 'client': client->shortName,
+ date,
+ period,
+ externalLink{label, href},
+ services[]->{title},
+ topics[]->{title},
+ content
+ } | order(date desc)
+ `,
+ z.array(projectSchema),
+ );
+};
+
+export const getProject = async (slug: string) => {
+ return await queryContent(
+ groq`
+ *[_type == 'project' && slug.current == '${slug}'][0]
+ {
+ _id,
+ 'slug': slug.current,
+ title,
+ summary,
+ image{'url': asset->url, alt, border},
+ context,
+ 'client': client->shortName,
+ date,
+ period,
+ externalLink{label, href},
+ services[]->{title},
+ topics[]->{title},
+ content
+ }
+ `,
+ projectSchema,
+ );
+};