Rework layout and refactors (#4)

Reviewed-on: https://git.juancman.dev/juancmandev/website/pulls/4
Co-authored-by: juancmandev <juancmandev@protonmail.com>
Co-committed-by: juancmandev <juancmandev@protonmail.com>
This commit is contained in:
juancmandev
2025-03-14 00:56:54 -04:00
committed by Juan Manzanero
parent 9b4a54f702
commit b4447f0e38
47 changed files with 161 additions and 636 deletions

View File

@@ -6,7 +6,7 @@ import Layout from '@/layouts/Layout.astro';
title='Not found'
description='Error 404: Not found.'
>
<div class='prose prose-invert'>
<div class='prose prose-invert max-w-[800px] mx-auto'>
<h1 class=''>Error 404: Not found</h1>
</div>
</Layout>

View File

@@ -32,7 +32,7 @@ const { Content, remarkPluginFrontmatter: data } = await render(project);
title={data.title}
description={data.description}
>
<article class='prose prose-invert'>
<article class='prose prose-invert max-w-[800px] mx-auto'>
<Content components={{ ...components }} />
</article>
</Layout>

View File

@@ -36,7 +36,7 @@ const { Content, remarkPluginFrontmatter: data } = await render(post);
title={data.title}
description={data.description}
>
<article class='prose prose-invert'>
<article class='prose prose-invert max-w-[800px] mx-auto'>
<h1>{data.title}</h1>
<Content components={{ ...components }} />
<hr />

View File

@@ -1,5 +1,5 @@
---
import PostItem from '@/components/post-item';
import PostItemList from '@/components/post-items-list';
import { getLangFromUrl } from '@/i18n/utils';
import Layout from '@/layouts/Layout.astro';
import { sortContentByDate } from '@/utils/sorts';
@@ -27,26 +27,12 @@ const lang = getLangFromUrl(Astro.url);
---
<Layout {...pageData}>
<section class='prose prose-invert'>
<div class='prose prose-invert max-w-[800px] mx-auto'>
<h1>{pageData.title}</h1>
<p>{pageData.description}</p>
</section>
<ul class='mt-4 flex flex-col gap-4'>
{
filterEnPosts.map(
(blogpost) =>
blogpost && (
<li>
<PostItem
type='blog'
lang={lang}
id={blogpost.id}
date={blogpost.data.date!}
title={blogpost.data.title!}
/>
</li>
)
)
}
</ul>
<PostItemList
items={filterEnPosts}
lang={lang}
/>
</div>
</Layout>

View File

@@ -32,7 +32,7 @@ const { Content, remarkPluginFrontmatter: data } = await render(page);
title={data.title}
description={data.description}
>
<article class='prose prose-invert'>
<article class='prose prose-invert max-w-[800px] mx-auto'>
<Content components={{ ...components }} />
</article>
</Layout>

View File

@@ -36,7 +36,7 @@ const { Content, remarkPluginFrontmatter: data } = await render(blog);
title={data.title}
description={data.description}
>
<article class='prose prose-invert'>
<article class='prose prose-invert max-w-[800px] mx-auto'>
<h1>{data.title}</h1>
<Content components={{ ...components }} />
<hr />

View File

@@ -27,7 +27,7 @@ const lang = getLangFromUrl(Astro.url);
---
<Layout {...pageData}>
<section class='prose prose-invert'>
<section class='prose prose-invert max-w-[800px] mx-auto'>
<h1>{pageData.title}</h1>
<p>{pageData.description}</p>
</section>

View File

@@ -12,9 +12,6 @@ const markdownParser = new MarkdownIt();
const imagesBlog = import.meta.glob<{ default: ImageMetadata }>(
'/src/assets/blog/**/**/*.{jpeg,jpg,png,gif,webp}'
);
const imagesPortfolio = import.meta.glob<{ default: ImageMetadata }>(
'/src/assets/portfolio/**/**/*.{jpeg,jpg,png,gif,webp}'
);
export async function GET(context: any) {
const items: RSSFeedItem[] = [];
@@ -29,16 +26,6 @@ export async function GET(context: any) {
return lang === 'es' && post;
});
const portfolio = await getCollection(
'portfolio',
({ data }) => data.draft !== true && data.rss === true
);
const filterPortfolio = portfolio.filter((project) => {
const [lang] = project.id.split('/');
return lang === 'es' && project;
});
for await (const post of filterBlog) {
const body = markdownParser.render(post.body!);
const html = htmlParser.parse(body);
@@ -79,47 +66,6 @@ export async function GET(context: any) {
});
}
for await (const project of filterPortfolio) {
const body = markdownParser.render(project.body!);
const html = htmlParser.parse(body);
const images = html.querySelectorAll('img');
for await (const img of images) {
const src = img.getAttribute('src')!;
if (src.startsWith('@/')) {
const prefixRemoved = src.replace('@/', '');
const imagePathPrefix = `/src/${prefixRemoved}`;
const imagePath = await imagesPortfolio[imagePathPrefix]?.()?.then(
(res: any) => res.default
);
if (imagePath) {
const optimizedImg = await getImage({ src: imagePath });
img.setAttribute(
'src',
context.site + optimizedImg.src.replace('/', '')
);
}
} else if (src.startsWith('/images')) {
// images starting with `/images/` is the public dir
img.setAttribute('src', context.site + src.replace('/', ''));
} else {
throw Error('src unknown');
}
}
items.push({
title: project.data.title,
pubDate: project.data.date,
description: project.data.description,
link: `/portfolio/${project.id.split('.')[0]}/`,
content: sanitizeHtml(html.toString(), {
allowedTags: sanitizeHtml.defaults.allowedTags.concat(['img']),
}),
});
}
return rss({
xmlns: { atom: 'http://www.w3.org/2005/Atom' },
title: 'juancmandev',

View File

@@ -1,6 +1,6 @@
---
import LinkButton from '@/components/link-button';
import PostItem from '@/components/post-item';
import PostItemList from '@/components/post-items-list';
import { getLangFromUrl } from '@/i18n/utils';
import Layout from '@/layouts/Layout.astro';
import { sortContentByDate } from '@/utils/sorts';
@@ -26,28 +26,11 @@ const allEsPosts = allPosts.map((post) => {
sortContentByDate(allEsPosts);
const last3Blogs = allEsPosts.slice(0, 3);
const allProjects = await getCollection(
'portfolio',
({ data }) => data.draft !== true
);
const allEnProjects = allProjects.map((project) => {
const [lang, id] = project.id.split('/');
if (lang !== 'en')
return {
...project,
id: id.split('.')[0],
};
else null;
});
sortContentByDate(allEnProjects);
const last3Projects = allEnProjects.slice(0, 3);
const lang = getLangFromUrl(Astro.url);
---
<Layout {...pageData}>
<div class='prose prose-invert'>
<div class='prose prose-invert max-w-[800px] mx-auto'>
<h1 class='text-primary'>Bienvenido a mi dominio, extraño.</h1>
<p>
Soy <strong class='text-primary'>juancmandev</strong>; <strong
@@ -63,24 +46,10 @@ const lang = getLangFromUrl(Astro.url);
</p>
<section>
<h2>Últimos posts</h2>
<ul class='mt-0 p-0 list-none'>
{
last3Blogs.map(
(blogpost) =>
blogpost && (
<li class='p-0'>
<PostItem
type='blog'
lang={lang}
id={blogpost.id}
date={blogpost.data.date}
title={blogpost.data.title}
/>
</li>
)
)
}
</ul>
<PostItemList
items={last3Blogs}
lang={lang}
/>
<LinkButton
variant='secondary'
href='/es/blog'
@@ -88,32 +57,5 @@ const lang = getLangFromUrl(Astro.url);
>Más posts</LinkButton
>
</section>
<section>
<h2>Últimos proyectos</h2>
<ul class='mt-0 p-0 list-none'>
{
last3Projects.map(
(project) =>
project && (
<li class='p-0'>
<PostItem
lang={lang}
type='portfolio'
id={project.id}
date={project.data.date!}
title={project.data.title!}
/>
</li>
)
)
}
</ul>
<LinkButton
variant='secondary'
href='/es/portfolio'
className='no-underline'
>Más proyectos</LinkButton
>
</section>
</div>
</Layout>

View File

@@ -1,48 +0,0 @@
---
import Layout from '@/layouts/Layout.astro';
import components from '@/components/mdx/wrapper';
import formatDate from '@/utils/format-date';
import { getLangFromUrl } from '@/i18n/utils';
import { getCollection, getEntry, render } from 'astro:content';
export async function getStaticPaths() {
const allProjects = await getCollection(
'portfolio',
({ data }) => data.draft !== true
);
const filterEnProjects = allProjects.map((project) => {
const [lang, id] = project.id.split('/');
if (lang === 'es')
return {
...project,
id: id.split('.')[0],
};
else null;
});
return filterEnProjects.map((project) => ({
params: { slug: project?.id },
}));
}
const lang = getLangFromUrl(Astro.url);
const { slug } = Astro.params;
const project = await getEntry('portfolio', `${lang}/${slug}`)!;
const { Content, remarkPluginFrontmatter: data } = await render(project);
---
<Layout
title={data.title}
description={data.description}
>
<article class='prose prose-invert'>
<h1>{data.title}</h1>
<Content components={{ ...components }} />
<hr />
<p>
<strong>Publicado: </strong>
{data.date && formatDate(new Date(data.date), lang)}
</p>
</article>
</Layout>

View File

@@ -1,55 +0,0 @@
---
import PostItem from '@/components/post-item';
import { getLangFromUrl } from '@/i18n/utils';
import Layout from '@/layouts/Layout.astro';
import { sortContentByDate } from '@/utils/sorts';
import { getCollection } from 'astro:content';
const pageData = {
title: 'Portfolio',
description: 'Revisa mis proyectos.',
};
const allProjects = await getCollection(
'portfolio',
({ data }) => data.draft !== true
);
const allEsProjects = allProjects.map((project) => {
const [lang, id] = project.id.split('/');
if (lang === 'es')
return {
...project,
id: id.split('.')[0],
};
else null;
});
sortContentByDate(allEsProjects);
const lang = getLangFromUrl(Astro.url);
---
<Layout {...pageData}>
<section class='prose prose-invert'>
<h1>{pageData.title}</h1>
<p>{pageData.description}</p>
</section>
<ul class='mt-4 flex flex-col gap-4'>
{
allEsProjects.map(
(project) =>
project && (
<li>
<PostItem
lang={lang}
type='portfolio'
id={project.id}
date={project.data.date!}
title={project.data.title!}
/>
</li>
)
)
}
</ul>
</Layout>

View File

@@ -34,7 +34,7 @@ const { Content, remarkPluginFrontmatter: data } = await render(video);
title={data.title}
description={data.description}
>
<article class='prose prose-invert'>
<article class='prose prose-invert max-w-[800px] mx-auto'>
<h1>{data.title}</h1>
<Content components={{ ...components }} />
<hr />

View File

@@ -1,5 +1,5 @@
---
import PostItem from '@/components/post-item';
import PostItemList from '@/components/post-items-list';
import { getLangFromUrl } from '@/i18n/utils';
import Layout from '@/layouts/Layout.astro';
import { sortContentByDate } from '@/utils/sorts';
@@ -20,23 +20,12 @@ const lang = getLangFromUrl(Astro.url);
---
<Layout {...pageData}>
<section class='prose prose-invert'>
<div class='prose prose-invert max-w-[800px] mx-auto'>
<h1>{pageData.title}</h1>
<p>{pageData.description}</p>
</section>
<ul class='mt-4 flex flex-col gap-4'>
{
allVideos.map((video: any) => (
<li>
<PostItem
lang={lang}
type='videos'
id={video.id.split('.')[0]}
date={video.data.date!}
title={video.data.title!}
/>
</li>
))
}
</ul>
<PostItemList
items={allVideos}
lang={lang}
/>
</div>
</Layout>

View File

@@ -12,9 +12,6 @@ const markdownParser = new MarkdownIt();
const imagesBlog = import.meta.glob<{ default: ImageMetadata }>(
'/src/assets/blog/**/**/*.{jpeg,jpg,png,gif,webp}'
);
const imagesPortfolio = import.meta.glob<{ default: ImageMetadata }>(
'/src/assets/portfolio/**/**/*.{jpeg,jpg,png,gif,webp}'
);
export async function GET(context: any) {
const items: RSSFeedItem[] = [];
@@ -29,16 +26,6 @@ export async function GET(context: any) {
return lang !== 'es' && post;
});
const portfolio = await getCollection(
'portfolio',
({ data }) => data.draft !== true && data.rss === true
);
const filterPortfolio = portfolio.filter((project) => {
const [lang] = project.id.split('/');
return lang !== 'es' && project;
});
for await (const post of filterBlog) {
const body = markdownParser.render(post.body!);
const html = htmlParser.parse(body);
@@ -79,47 +66,6 @@ export async function GET(context: any) {
});
}
for await (const project of filterPortfolio) {
const body = markdownParser.render(project.body!);
const html = htmlParser.parse(body);
const images = html.querySelectorAll('img');
for await (const img of images) {
const src = img.getAttribute('src')!;
if (src.startsWith('@/')) {
const prefixRemoved = src.replace('@/', '');
const imagePathPrefix = `/src/${prefixRemoved}`;
const imagePath = await imagesPortfolio[imagePathPrefix]?.()?.then(
(res: any) => res.default
);
if (imagePath) {
const optimizedImg = await getImage({ src: imagePath });
img.setAttribute(
'src',
context.site + optimizedImg.src.replace('/', '')
);
}
} else if (src.startsWith('/images')) {
// images starting with `/images/` is the public dir
img.setAttribute('src', context.site + src.replace('/', ''));
} else {
throw Error('src unknown');
}
}
items.push({
title: project.data.title,
pubDate: project.data.date,
description: project.data.description,
link: `/portfolio/${project.id.split('.')[0]}/`,
content: sanitizeHtml(html.toString(), {
allowedTags: sanitizeHtml.defaults.allowedTags.concat(['img']),
}),
});
}
return rss({
xmlns: { atom: 'http://www.w3.org/2005/Atom' },
title: 'juancmandev',

View File

@@ -2,9 +2,10 @@
import Layout from '@/layouts/Layout.astro';
import LinkButton from '@/components/link-button';
import { getCollection } from 'astro:content';
import PostItem from '@/components/post-item';
import { sortContentByDate } from '@/utils/sorts';
import { getLangFromUrl } from '@/i18n/utils';
import PostItemList from '@/components/post-items-list';
import Random from '@/components/random.astro';
const pageData = {
title: 'juancmandev',
@@ -26,31 +27,15 @@ const allEnPosts = allPosts.map((post) => {
sortContentByDate(allEnPosts);
const last3Blogs = allEnPosts.slice(0, 3);
const allProjects = await getCollection(
'portfolio',
({ data }) => data.draft !== true
);
const allEnProjects = allProjects.map((project) => {
const [lang, id] = project.id.split('/');
if (lang !== 'es')
return {
...project,
id: id.split('.')[0],
};
else null;
});
sortContentByDate(allEnProjects);
const last3Projects = allEnProjects.slice(0, 3);
const lang = getLangFromUrl(Astro.url);
---
<Layout {...pageData}>
<div class='prose prose-invert'>
<div class='prose prose-invert max-w-[800px] mx-auto'>
<h1 class='text-primary'>Welcome to my domain, stranger.</h1>
<!-- <Random /> -->
<p>
I am <strong class='text-primary'>juancmandev</strong>; <strong
I am <strong class='text-primary'>Juan Manzanero</strong>; <strong
>Web Developer</strong
>, <strong>Linux</strong> enthusiast, and <strong>privacy</strong> defender.
</p>
@@ -61,21 +46,10 @@ const lang = getLangFromUrl(Astro.url);
</p>
<section>
<h2>Latest posts</h2>
<ul class='mt-0 p-0 list-none'>
{
last3Blogs.map((blogpost: any) => (
<li class='p-0'>
<PostItem
type='blog'
lang={lang}
id={blogpost.id}
date={blogpost.data.date!}
title={blogpost.data.title!}
/>
</li>
))
}
</ul>
<PostItemList
items={last3Blogs}
lang={lang}
/>
<LinkButton
variant='secondary'
href='/blog'
@@ -83,32 +57,5 @@ const lang = getLangFromUrl(Astro.url);
>More posts</LinkButton
>
</section>
<section>
<h2>Latest projects</h2>
<ul class='mt-0 p-0 list-none'>
{
last3Projects.map(
(project) =>
project && (
<li class='p-0'>
<PostItem
lang={lang}
type='portfolio'
id={project.id}
date={project.data.date!}
title={project.data.title!}
/>
</li>
)
)
}
</ul>
<LinkButton
variant='secondary'
href='/portfolio'
className='no-underline'
>More projects</LinkButton
>
</section>
</div>
</Layout>

View File

@@ -14,7 +14,7 @@ const data = await pb.collection('microblogs').getFullList({
title='Microblog'
description='Short-format writing. Instead of using shitty social media.'
>
<div class='prose prose-invert'>
<div class='prose prose-invert max-w-[800px] mx-auto'>
<h1>Microblog</h1>
<p>Short-format writing.</p>
<p>Instead of using shitty social media.</p>

View File

@@ -1,48 +0,0 @@
---
import Layout from '@/layouts/Layout.astro';
import components from '@/components/mdx/wrapper';
import formatDate from '@/utils/format-date';
import { getLangFromUrl } from '@/i18n/utils';
import { getCollection, getEntry, render } from 'astro:content';
export async function getStaticPaths() {
const allProjects = await getCollection(
'portfolio',
({ data }) => data.draft !== true
);
const filterEnProjects = allProjects.map((project) => {
const [lang, id] = project.id.split('/');
if (lang === 'en')
return {
...project,
id: id.split('.')[0],
};
else null;
});
return filterEnProjects.map((project) => ({
params: { slug: project?.id },
}));
}
const lang = getLangFromUrl(Astro.url);
const { slug } = Astro.params;
const project = await getEntry('portfolio', `${lang}/${slug}`)!;
const { Content, remarkPluginFrontmatter: data } = await render(project);
---
<Layout
title={data.title}
description={data.description}
>
<article class='prose prose-invert'>
<h1>{data.title}</h1>
<Content components={{ ...components }} />
<hr />
<p>
<strong>Posted: </strong>
{data.date && formatDate(new Date(data.date), lang)}
</p>
</article>
</Layout>

View File

@@ -1,55 +0,0 @@
---
import PostItem from '@/components/post-item';
import { getLangFromUrl } from '@/i18n/utils';
import Layout from '@/layouts/Layout.astro';
import { sortContentByDate } from '@/utils/sorts';
import { getCollection } from 'astro:content';
const pageData = {
title: 'Portfolio',
description: 'Check my projects.',
};
const allProjects = await getCollection(
'portfolio',
({ data }) => data.draft !== true
);
const allEnProjects = allProjects.map((project) => {
const [lang, id] = project.id.split('/');
if (lang === 'en')
return {
...project,
id: id.split('.')[0],
};
else null;
});
sortContentByDate(allEnProjects);
const lang = getLangFromUrl(Astro.url);
---
<Layout {...pageData}>
<section class='prose prose-invert'>
<h1>{pageData.title}</h1>
<p>{pageData.description}</p>
</section>
<ul class='mt-4 flex flex-col gap-4'>
{
allEnProjects.map(
(project) =>
project && (
<li>
<PostItem
lang={lang}
type='portfolio'
id={project.id}
date={project.data.date!}
title={project.data.title!}
/>
</li>
)
)
}
</ul>
</Layout>

View File

@@ -4,7 +4,7 @@ const robotsTxt = `
User-agent: *
Allow: /
Sitemap: ${new URL('sitemap-index.xml', import.meta.env.SITE).href}
Sitemap: ${new URL('sitemap-index.xml', 'https://juancman.dev').href}
`.trim();
export const GET: APIRoute = () => {