This commit is contained in:
 Илья Шалонин 2025-10-29 13:43:43 +05:00
commit 8538ab969a
434 changed files with 44051 additions and 0 deletions

View file

@ -0,0 +1,17 @@
"use client";
import { MDXContent } from "@content-collections/mdx/react";
import { mdxComponents } from "../utils/mdx-components";
export function PostContent({ content }: { content: string }) {
return (
<div className="prose dark:prose-invert mx-auto mt-6 max-w-2xl">
<MDXContent
code={content}
components={{
a: mdxComponents.a,
}}
/>
</div>
);
}

View file

@ -0,0 +1,80 @@
"use client";
import { LocaleLink } from "@i18n/routing";
import type { Post } from "@marketing/blog/types";
import Image from "next/image";
export function PostListItem({ post }: { post: Post }) {
const { title, excerpt, authorName, image, date, path, authorImage, tags } =
post;
return (
<div className="rounded-2xl border bg-card/50 p-6">
{image && (
<div className="-mx-4 -mt-4 relative mb-4 aspect-16/9 overflow-hidden rounded-xl">
<Image
src={image}
alt={title}
fill
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
className="object-cover object-center"
/>
<LocaleLink
href={`/blog/${path}`}
className="absolute inset-0"
/>
</div>
)}
{tags && (
<div className="mb-2 flex flex-wrap gap-2">
{tags.map((tag) => (
<span
key={tag}
className="font-semibold text-primary text-xs uppercase tracking-wider"
>
#{tag}
</span>
))}
</div>
)}
<LocaleLink
href={`/blog/${path}`}
className="font-semibold text-xl"
>
{title}
</LocaleLink>
{excerpt && <p className="opacity-50">{excerpt}</p>}
<div className="mt-4 flex items-center justify-between">
{authorName && (
<div className="flex items-center">
{authorImage && (
<div className="relative mr-2 size-8 overflow-hidden rounded-full">
<Image
src={authorImage}
alt={authorName}
fill
sizes="96px"
className="object-cover object-center"
/>
</div>
)}
<div>
<p className="font-semibold text-sm opacity-50">
{authorName}
</p>
</div>
</div>
)}
<div className="mr-0 ml-auto">
<p className="text-sm opacity-30">
{Intl.DateTimeFormat("en-US").format(new Date(date))}
</p>
</div>
</div>
</div>
);
}

View file

@ -0,0 +1,3 @@
import type { Post as ContentCollectionsPost } from "content-collections";
export type Post = Omit<ContentCollectionsPost, "_meta">;

View file

@ -0,0 +1,25 @@
import type { Post } from "@marketing/blog/types";
import { allPosts } from "content-collections";
export async function getAllPosts(): Promise<Post[]> {
// ... add a custom loader here for your posts and map it to the post schema
return Promise.resolve(allPosts);
}
export async function getPostBySlug(
slug: string,
options?: {
locale?: string;
},
): Promise<Post | null> {
// ... add a custom loader here for your posts and map it to the post schema
return Promise.resolve(
allPosts.find(
(post) =>
post.path === slug &&
(!options?.locale || post.locale === options.locale),
) ?? null,
);
}

View file

@ -0,0 +1,103 @@
import { LocaleLink } from "@i18n/routing";
import { slugifyHeadline } from "@shared/lib/content";
import type { MDXComponents } from "mdx/types";
import type { ImageProps } from "next/image";
import Image from "next/image";
export const mdxComponents = {
a: (props) => {
const { href, children, ...rest } = props;
const isInternalLink =
href && (href.startsWith("/") || href.startsWith("#"));
return isInternalLink ? (
<LocaleLink href={href} {...rest}>
{children}
</LocaleLink>
) : (
<a target="_blank" rel="noopener noreferrer" href={href} {...rest}>
{children}
</a>
);
},
img: (props) =>
props.src ? (
<Image
{...(props as ImageProps)}
sizes="100vw"
style={{ width: "100%", height: "auto" }}
className="rounded-lg shadow"
loading="lazy"
/>
) : null,
h1: ({ children, ...rest }) => (
<h1
id={slugifyHeadline(children as string)}
className="mb-6 font-bold text-4xl"
{...rest}
>
{children}
</h1>
),
h2: ({ children, ...rest }) => (
<h2
id={slugifyHeadline(children as string)}
className="mb-4 font-bold text-2xl"
{...rest}
>
{children}
</h2>
),
h3: ({ children, ...rest }) => (
<h3
id={slugifyHeadline(children as string)}
className="mb-4 font-bold text-xl"
{...rest}
>
{children}
</h3>
),
h4: ({ children, ...rest }) => (
<h4
id={slugifyHeadline(children as string)}
className="mb-4 font-bold text-lg"
{...rest}
>
{children}
</h4>
),
h5: ({ children, ...rest }) => (
<h5
id={slugifyHeadline(children as string)}
className="mb-4 font-bold text-base"
{...rest}
>
{children}
</h5>
),
h6: ({ children, ...rest }) => (
<h6
id={slugifyHeadline(children as string)}
className="mb-4 font-bold text-sm"
{...rest}
>
{children}
</h6>
),
p: ({ children, ...rest }) => (
<p className="mb-6 text-foreground/60 leading-relaxed" {...rest}>
{children}
</p>
),
ul: ({ children, ...rest }) => (
<ul className="mb-6 list-inside list-disc space-y-2 pl-4" {...rest}>
{children}
</ul>
),
ol: ({ children, ...rest }) => (
<ol className="mb-6 list-inside list-decimal space-y-2 pl-4" {...rest}>
{children}
</ol>
),
li: ({ children, ...rest }) => <li {...rest}>{children}</li>,
} satisfies MDXComponents;