start to add spanish content again

This commit is contained in:
juancmandev 2024-07-30 22:37:03 -06:00
parent 56f20e167a
commit 16f07bf63b
19 changed files with 217 additions and 199 deletions

View File

@ -2,7 +2,11 @@ import { Code, RssIcon } from "lucide-react";
import { Button } from "@/components/ui/button";
import formatDate from "@/utils/format-date";
export default function Footer() {
type Props = {
lang: "en" | "es";
};
export default function Footer(props: Props) {
return (
<footer className="border-t border-secondary px-4 py-12 text-center text-sm md:px-16 prose prose-invert min-w-full">
<section>

View File

@ -3,6 +3,27 @@ import logo from "@/assets/logo.png";
import { Image } from "astro:assets";
import LinkButton from "@/components/link-button";
import { ChevronUp, Compass } from "lucide-react";
type Props = {
lang: "en" | "es";
};
const locales = {
en: {
to: "/es",
switch_language: "🇲🇽",
top: "Top",
navigation: "Navigation",
},
es: {
to: "/",
switch_language: "🇺🇸",
top: "Arriba",
navigation: "Navevación",
},
} as const;
const { lang } = Astro.props;
---
<header
@ -28,14 +49,21 @@ import { ChevronUp, Compass } from "lucide-react";
/>
</LinkButton>
</section>
<section class="flex items-center gap-4">
<LinkButton variant="link" className="p-0 gap-1" href="#">
<ChevronUp className="w-5" />
Top
<section class="flex items-center gap-2">
<LinkButton
variant="link"
href={locales[lang].to}
className="p-0 gap-1 text-base"
>
{locales[lang].switch_language}
</LinkButton>
<LinkButton variant="link" className="p-0 gap-1" href="#navigation">
<LinkButton variant="link" className="p-0 gap-0.5" href="#">
<ChevronUp className="w-5" />
{locales[lang].top}
</LinkButton>
<LinkButton variant="link" className="p-0 gap-0.5" href="#navigation">
<Compass className="w-5" />
Navigation
{locales[lang].navigation}
</LinkButton>
</section>
</div>

View File

@ -2,6 +2,7 @@ import { Button } from "@/components/ui/button";
type Props = {
children: React.ReactNode;
title?: string;
href: string;
variant?:
| "default"
@ -21,6 +22,7 @@ export default function LinkButton(props: Props) {
<Button
asChild
size={props.size}
title={props.title}
variant={props.variant}
className={props.className}
>

View File

@ -10,7 +10,7 @@ const { src, alt } = Astro.props;
alt={alt}
width={1092}
height={986}
class=".markdown-image w-auto h-auto rounded-md aspect-auto"
class="w-auto h-auto rounded-md aspect-auto"
/>
<script>

View File

@ -1,42 +0,0 @@
import { useCallback, useEffect, useState } from "react";
import { cn } from "@/lib/utils";
import { Button } from "@/components/ui/button";
import { CheckIcon, CopyIcon } from "lucide-react";
interface CopyButtonProps extends React.HTMLAttributes<HTMLButtonElement> {
value: string;
}
export default function CopyButton(props: CopyButtonProps) {
const [hasCopied, setHasCopied] = useState(false);
useEffect(() => {
setTimeout(() => {
setHasCopied(false);
}, 2000);
}, [hasCopied]);
const copyToClipboard = useCallback((value: string) => {
navigator.clipboard.writeText(value);
setHasCopied(true);
}, []);
return (
<Button
title={hasCopied ? "Copied!" : "Copy to clipboard"}
size={null}
variant="ghost"
className={cn("absolute right-2 top-1.5 p-2 ", props.className)}
onClick={() => copyToClipboard(props.value)}
{...props}
>
<span className="sr-only">Copy</span>
{hasCopied ? (
<CheckIcon className="h-4 w-4" />
) : (
<CopyIcon className="h-4 w-4" />
)}
</Button>
);
}

View File

@ -34,7 +34,7 @@ export const navItems: TNavItem[] = [
),
},
{
to: "/videos",
to: "es/videos",
child: (
<>
<MonitorPlay />
@ -80,9 +80,13 @@ export const navItems: TNavItem[] = [
},
];
export default function Navigation() {
type Props = {
lang: "en" | "es";
};
export default function Navigation(props: Props) {
return (
<nav className="px-4 sm:px-0 max-w-[65ch] mx-auto prose prose-invert py-20">
<nav className="px-4 sm:px-0 max-w-[65ch] mx-auto prose prose-invert pt-5 pb-20">
<h2 id="navigation">Navigation</h2>
<ul className="list-none p-0 flex flex-wrap gap-4">
{navItems.map((navItem, index) => (

View File

@ -5,7 +5,7 @@ type Props = {
slug: string;
date: Date | string;
title: string;
type: "blog" | "portfolio" | "videos";
type: "blog" | "portfolio" | "es/videos";
};
export default function PostItem(props: Props) {

View File

@ -1,20 +1,20 @@
---
import Header from "@/components/header.astro";
import Navigation from "@/components/ navigation";
import Navigation from "@/components/navigation";
import Footer from "@/components/footer";
import "@/styles/globals.css";
interface Props {
title: string;
description: string;
lang?: string;
lang: "en" | "es";
}
const { title, description, lang } = Astro.props;
---
<!doctype html>
<html lang={lang || "en"}>
<html lang={lang}>
<head>
<meta charset="UTF-8" />
<meta name="description" content={description} />
@ -37,11 +37,11 @@ const { title, description, lang } = Astro.props;
<title>{title}</title>
</head>
<body>
<Header />
<main class="px-4 sm:px-0 max-w-[65ch] pt-28 pb-14 mx-auto">
<Header lang={lang} />
<main class="px-4 sm:px-0 max-w-[65ch] pt-28 pb-5 mx-auto">
<slot />
</main>
<Navigation />
<Footer />
<Navigation lang={lang} />
<Footer lang={lang} />
</body>
</html>

View File

@ -3,12 +3,12 @@ import Layout from "@/layouts/Layout.astro";
import LinkButton from "@/components/link-button";
---
<Layout title="juancmandev" description="Error 404. Not found.">
<div class="prose prose-invert">
<h1 class="">Error 404: Not found</h1>
<p>Do not worry, you can <strong>go back to home</strong>.</p>
<LinkButton variant="default" href="/" className="no-underline"
>Home</LinkButton
>
</div>
<Layout lang="en" title="juancmandev" description="Error 404. Not found.">
<div class="prose prose-invert">
<h1 class="">Error 404: Not found</h1>
<p>Do not worry, you can <strong>go back to home</strong>.</p>
<LinkButton variant="default" href="/" className="no-underline"
>Home</LinkButton
>
</div>
</Layout>

View File

@ -5,24 +5,24 @@ import type { CollectionEntry } from "astro:content";
import components from "@/components/mdx/wrapper";
interface Props {
page: CollectionEntry<"pages">;
page: CollectionEntry<"pages">;
}
export async function getStaticPaths() {
const allPages = await getCollection("pages");
const allPages = await getCollection("pages");
return allPages.map((page: CollectionEntry<"pages">) => ({
params: { slug: page.slug },
props: { page },
}));
return allPages.map((page: CollectionEntry<"pages">) => ({
params: { slug: page.slug },
props: { page },
}));
}
const { page } = Astro.props;
const { Content } = await page.render();
---
<Layout title={page.data.title} description={page.data.description}>
<article class="prose prose-invert">
<Content components={{ ...components }} />
</article>
<Layout lang="en" title={page.data.title} description={page.data.description}>
<article class="prose prose-invert">
<Content components={{ ...components }} />
</article>
</Layout>

View File

@ -6,33 +6,33 @@ import components from "@/components/mdx/wrapper";
import formatDate from "@/utils/format-date";
interface Props {
post: CollectionEntry<"blog">;
post: CollectionEntry<"blog">;
}
export async function getStaticPaths() {
const allBlogPosts = await getCollection(
"blog",
({ data }) => data.draft !== true,
);
const allBlogPosts = await getCollection(
"blog",
({ data }) => data.draft !== true
);
return allBlogPosts.map((post) => ({
params: { slug: post.slug },
props: { post },
}));
return allBlogPosts.map((post) => ({
params: { slug: post.slug },
props: { post },
}));
}
const { post } = Astro.props;
const { Content } = await post.render();
---
<Layout title={post.data.title} description={post.data.description}>
<article class="prose prose-invert">
<h1>{post.data.title}</h1>
<Content components={{ ...components }} />
<hr />
<p>
<strong>Posted: </strong>
{post.data.date && formatDate(new Date(post.data.date))}
</p>
</article>
<Layout lang="en" title={post.data.title} description={post.data.description}>
<article class="prose prose-invert">
<h1>{post.data.title}</h1>
<Content components={{ ...components }} />
<hr />
<p>
<strong>Posted: </strong>
{post.data.date && formatDate(new Date(post.data.date))}
</p>
</article>
</Layout>

View File

@ -13,7 +13,7 @@ const allPosts = await getCollection("blog", ({ data }) => data.draft !== true);
sortContentByDate(allPosts);
---
<Layout {...pageData}>
<Layout {...pageData} lang="en">
<section class="prose prose-invert">
<h1>{pageData.title}</h1>
<p>{pageData.description}</p>

20
src/pages/es/index.astro Normal file
View File

@ -0,0 +1,20 @@
---
import Layout from "@/layouts/Layout.astro";
---
<Layout
lang="es"
title="juancmandev"
description="Bienvenido a mi dominio, extraño. Soy juancmandev; Desarrollador Web, entusiasta de Linux, y defensor de la privacidad."
>
<div class="prose prose-invert">
<h1 class="text-primary">Bienvenido a mi dominio, extraño.</h1>
<p>
Soy <strong class="text-primary">juancmandev</strong>; <strong
>Desarrollador Web</strong
>, entusiasta de <strong>Linux</strong> y defensor de la <strong
>privacidad.</strong
>
</p>
</div>
</Layout>

View File

@ -26,7 +26,7 @@ sortContentByDate(allVideos);
allVideos.map((video: any) => (
<li>
<PostItem
type="videos"
type="es/videos"
slug={video.slug}
date={video.data.date!}
title={video.data.title!}

View File

@ -10,72 +10,69 @@ sortContentByDate(allPosts);
const last3Blogs = allPosts.slice(0, 3);
const allProjects = await getCollection(
"portfolio",
({ data }) => data.draft !== true,
"portfolio",
({ data }) => data.draft !== true
);
sortContentByDate(allProjects);
const last3Projects = allProjects.slice(0, 3);
---
<Layout
title="juancmandev"
description="Welcome to my domain, stranger. I am juancmandev; Web Developer, Linux enthusiast, and privacy defender."
lang="en"
title="juancmandev"
description="Welcome to my domain, stranger. I am juancmandev; Web Developer, Linux enthusiast, and privacy defender."
>
<div class="prose prose-invert">
<h1 class="text-primary">Welcome to my domain, stranger.</h1>
<p>
I am <strong class="text-primary">juancmandev</strong>; <strong
>Web Developer</strong
>, <strong>Linux</strong> enthusiast, and <strong>privacy</strong> defender.
</p>
<p>
This is my <strong>website</strong>, a piece of the Internet that I
could call my <strong>home base</strong>. Here, I share my knowledge
about my career and talk about other topics.
</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"
slug={blogpost.slug}
date={blogpost.data.date!}
title={blogpost.data.title!}
/>
</li>
))
}
</ul>
<LinkButton
variant="secondary"
href="/blog"
className="no-underline">More posts</LinkButton
>
</section>
<section>
<h2>Latest projects</h2>
<ul class="mt-0 p-0 list-none">
{
last3Projects.map((project: any) => (
<li class="p-0">
<PostItem
type="portfolio"
slug={project.slug}
date={project.data.date!}
title={project.data.title!}
/>
</li>
))
}
</ul>
<LinkButton
variant="secondary"
href="/portfolio"
className="no-underline">More projects</LinkButton
>
</section>
</div>
<div class="prose prose-invert">
<h1 class="text-primary">Welcome to my domain, stranger.</h1>
<p>
I am <strong class="text-primary">juancmandev</strong>; <strong
>Web Developer</strong
>, <strong>Linux</strong> enthusiast, and <strong>privacy</strong> defender.
</p>
<p>
This is my <strong>website</strong>, a piece of the Internet that I could
call my <strong>home base</strong>. Here, I share my passion about open
source projects and other topics.
</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"
slug={blogpost.slug}
date={blogpost.data.date!}
title={blogpost.data.title!}
/>
</li>
))
}
</ul>
<LinkButton variant="secondary" href="/blog" className="no-underline"
>More posts</LinkButton
>
</section>
<section>
<h2>Latest projects</h2>
<ul class="mt-0 p-0 list-none">
{
last3Projects.map((project: any) => (
<li class="p-0">
<PostItem
type="portfolio"
slug={project.slug}
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

@ -5,27 +5,28 @@ import { createServerClient } from "@/utils/pocketbase";
const pb = createServerClient(import.meta.env.SECRET_POCKETBASE_API_URL);
const data = await pb.collection("microblogs").getFullList({
expand: "tags",
sort: "-published",
expand: "tags",
sort: "-published",
});
---
<Layout
title="Microblog"
description="Short-format writing. Instead of using shitty social media."
lang="en"
title="Microblog"
description="Short-format writing. Instead of using shitty social media."
>
<div class="prose prose-invert">
<h1>Microblog</h1>
<p>Short-format writing.</p>
<p>Instead of using shitty social media.</p>
<ul class="mx-auto p-0 mt-10 flex flex-col gap-10 list-none">
{
data.map((item: any) => (
<li>
<MicroblogItem {...item} />
</li>
))
}
</ul>
</div>
<div class="prose prose-invert">
<h1>Microblog</h1>
<p>Short-format writing.</p>
<p>Instead of using shitty social media.</p>
<ul class="mx-auto p-0 mt-10 flex flex-col gap-10 list-none">
{
data.map((item: any) => (
<li>
<MicroblogItem {...item} />
</li>
))
}
</ul>
</div>
</Layout>

View File

@ -6,33 +6,37 @@ import components from "@/components/mdx/wrapper";
import formatDate from "@/utils/format-date";
interface Props {
project: CollectionEntry<"portfolio">;
project: CollectionEntry<"portfolio">;
}
export async function getStaticPaths() {
const allProjects = await getCollection(
"portfolio",
({ data }) => data.draft !== true,
);
const allProjects = await getCollection(
"portfolio",
({ data }) => data.draft !== true
);
return allProjects.map((project) => ({
params: { slug: project.slug },
props: { project },
}));
return allProjects.map((project) => ({
params: { slug: project.slug },
props: { project },
}));
}
const { project } = Astro.props;
const { Content } = await project.render();
---
<Layout title={project.data.title} description={project.data.description}>
<article class="prose prose-invert">
<h1>{project.data.title}</h1>
<Content components={{ ...components }} />
<hr />
<p>
<strong>Posted: </strong>
{project.data.date && formatDate(new Date(project.data.date))}
</p>
</article>
<Layout
lang="en"
title={project.data.title}
description={project.data.description}
>
<article class="prose prose-invert">
<h1>{project.data.title}</h1>
<Content components={{ ...components }} />
<hr />
<p>
<strong>Posted: </strong>
{project.data.date && formatDate(new Date(project.data.date))}
</p>
</article>
</Layout>

View File

@ -16,7 +16,7 @@ const allProjects = await getCollection(
sortContentByDate(allProjects);
---
<Layout {...pageData}>
<Layout {...pageData} lang="en">
<section class="prose prose-invert">
<h1>{pageData.title}</h1>
<p>{pageData.description}</p>