mirror of
https://github.com/10h30/blog-balodeplao.git
synced 2026-05-12 15:21:15 +09:00
Update structure
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
---
|
---
|
||||||
import Tags from "@/components/ui/Tags.astro";
|
import Tags from "@/components/ui/Tags.astro";
|
||||||
|
import Categories from "@/components/ui/Categories.astro";
|
||||||
import { type CollectionEntry, render } from "astro:content";
|
import { type CollectionEntry, render } from "astro:content";
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
@@ -10,7 +11,7 @@ const { post } = Astro.props;
|
|||||||
const { remarkPluginFrontmatter } = await render(post);
|
const { remarkPluginFrontmatter } = await render(post);
|
||||||
|
|
||||||
const readingTime = remarkPluginFrontmatter?.minutesRead
|
const readingTime = remarkPluginFrontmatter?.minutesRead
|
||||||
? `${Math.ceil(remarkPluginFrontmatter.minutesRead)} min read`
|
? `${Math.ceil(remarkPluginFrontmatter.minutesRead)} phút đọc`
|
||||||
: "";
|
: "";
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -34,7 +35,7 @@ const readingTime = remarkPluginFrontmatter?.minutesRead
|
|||||||
{post.data.author}
|
{post.data.author}
|
||||||
</p>
|
</p>
|
||||||
<a
|
<a
|
||||||
href={`/blog/${post.id}`}
|
href={`/${post.id}`}
|
||||||
class="mt-2 block before:absolute before:inset-0 before:z-10"
|
class="mt-2 block before:absolute before:inset-0 before:z-10"
|
||||||
>
|
>
|
||||||
<p class="text-xl font-semibold text-foreground group-hover:underline">
|
<p class="text-xl font-semibold text-foreground group-hover:underline">
|
||||||
@@ -55,7 +56,7 @@ const readingTime = remarkPluginFrontmatter?.minutesRead
|
|||||||
<div class="flex space-x-1 text-sm text-muted-foreground">
|
<div class="flex space-x-1 text-sm text-muted-foreground">
|
||||||
<time datetime={post.data.pubDate.toISOString()}>
|
<time datetime={post.data.pubDate.toISOString()}>
|
||||||
{
|
{
|
||||||
post.data.pubDate.toLocaleDateString("en-US", {
|
post.data.pubDate.toLocaleDateString("vi-VN", {
|
||||||
year: "numeric",
|
year: "numeric",
|
||||||
month: "long",
|
month: "long",
|
||||||
day: "numeric",
|
day: "numeric",
|
||||||
@@ -69,6 +70,14 @@ const readingTime = remarkPluginFrontmatter?.minutesRead
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{
|
||||||
|
post.data.categories && (
|
||||||
|
<div class="mt-4 relative z-20 flex flex-wrap gap-2">
|
||||||
|
<Categories categories={post.data.categories} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
post.data.tags && (
|
post.data.tags && (
|
||||||
<div class="mt-4 relative z-20">
|
<div class="mt-4 relative z-20">
|
||||||
|
|||||||
@@ -13,25 +13,18 @@ import { siteConfig } from "@/config/site";
|
|||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-6">
|
<div class="flex items-center gap-6">
|
||||||
<a
|
<a
|
||||||
href={siteConfig.socialLinks.twitter}
|
href={siteConfig.socialLinks.instagram}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
class="text-muted-foreground transition-colors hover:text-primary"
|
class="text-muted-foreground transition-colors hover:text-primary"
|
||||||
>Twitter</a
|
>Instagram</a
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
href={siteConfig.socialLinks.github}
|
href={siteConfig.socialLinks.facebook}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
class="text-muted-foreground transition-colors hover:text-primary"
|
class="text-muted-foreground transition-colors hover:text-primary"
|
||||||
>GitHub</a
|
>Facebook</a
|
||||||
>
|
|
||||||
<a
|
|
||||||
href={siteConfig.socialLinks.discord}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
class="text-muted-foreground transition-colors hover:text-primary"
|
|
||||||
>Discord</a
|
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -39,7 +32,7 @@ import { siteConfig } from "@/config/site";
|
|||||||
class="mt-8 border-t border-border pt-8 text-center text-xs text-muted-foreground"
|
class="mt-8 border-t border-border pt-8 text-center text-xs text-muted-foreground"
|
||||||
>
|
>
|
||||||
© {new Date().getFullYear()}
|
© {new Date().getFullYear()}
|
||||||
{siteConfig.author}. All rights reserved.
|
{siteConfig.name}. All rights reserved.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ if (type === "WebSite") {
|
|||||||
"@type": "Person",
|
"@type": "Person",
|
||||||
name: siteConfig.author,
|
name: siteConfig.author,
|
||||||
},
|
},
|
||||||
sameAs: [siteConfig.socialLinks.twitter, siteConfig.socialLinks.github],
|
sameAs: [siteConfig.socialLinks.instagram, siteConfig.socialLinks.facebook],
|
||||||
};
|
};
|
||||||
} else if (type === "BlogPosting") {
|
} else if (type === "BlogPosting") {
|
||||||
schema = {
|
schema = {
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
---
|
||||||
|
export interface Props {
|
||||||
|
categories: string[];
|
||||||
|
class?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { categories, class: className = "text-sm" } = Astro.props;
|
||||||
|
---
|
||||||
|
|
||||||
|
{
|
||||||
|
categories && Array.isArray(categories) && (
|
||||||
|
<ul class:list={["flex flex-wrap gap-2", className]}>
|
||||||
|
{categories.map((category) => (
|
||||||
|
<li class="inline-block relative">
|
||||||
|
<a
|
||||||
|
href={`/category/${category}`}
|
||||||
|
class="inline-block bg-primary/10 hover:bg-primary/20 text-primary hover:text-primary px-3 py-1 rounded-full border border-primary/20 transition-colors duration-200"
|
||||||
|
>
|
||||||
|
{category}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -28,7 +28,7 @@ const { prev, next } = url;
|
|||||||
<span />
|
<span />
|
||||||
)}
|
)}
|
||||||
<div class="flex items-center text-sm text-muted-foreground">
|
<div class="flex items-center text-sm text-muted-foreground">
|
||||||
Página {currentPage} de {lastPage}
|
Trang {currentPage} / {lastPage}
|
||||||
</div>
|
</div>
|
||||||
{next ? (
|
{next ? (
|
||||||
<a
|
<a
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ const { tags, class: className = "text-sm" } = Astro.props;
|
|||||||
{tags.map((tag) => (
|
{tags.map((tag) => (
|
||||||
<li class="inline-block relative">
|
<li class="inline-block relative">
|
||||||
<a
|
<a
|
||||||
href={`/blog/tags/${tag}`}
|
href={`/tags/${tag}`}
|
||||||
class="inline-block bg-muted/50 hover:bg-muted text-muted-foreground hover:text-foreground px-3 py-1 rounded-full border border-border transition-colors duration-200"
|
class="inline-block bg-muted/50 hover:bg-muted text-muted-foreground hover:text-foreground px-3 py-1 rounded-full border border-border transition-colors duration-200"
|
||||||
>
|
>
|
||||||
#{tag}
|
#{tag}
|
||||||
|
|||||||
+8
-12
@@ -2,25 +2,21 @@ import ogImage from "@/assets/og-image.png";
|
|||||||
|
|
||||||
export const siteConfig = {
|
export const siteConfig = {
|
||||||
name: "Astro Starter Pro",
|
name: "Astro Starter Pro",
|
||||||
description:
|
description: "Just another Astro blog",
|
||||||
"Starter template optimized for SEO and performance. A solid foundation to start your projects with best practices.",
|
url: "https://thuanbui.me",
|
||||||
url: "https://astrostarterpro.com",
|
lang: "vi",
|
||||||
lang: "en",
|
locale: "vi_VN",
|
||||||
locale: "en_US",
|
author: "Thuan Bui",
|
||||||
author: "Devgelo",
|
twitter: "@10h30",
|
||||||
twitter: "@Devgelo",
|
|
||||||
ogImage: ogImage,
|
ogImage: ogImage,
|
||||||
socialLinks: {
|
socialLinks: {
|
||||||
twitter: "https://twitter.com",
|
instagram: "https://instagram.com/",
|
||||||
github: "https://github.com/devgelo-labs/astro-starter-pro",
|
facebook: "https://facebook.com/",
|
||||||
discord: "https://discord.com",
|
|
||||||
},
|
},
|
||||||
navLinks: [
|
navLinks: [
|
||||||
{ text: "Home", href: "/" },
|
{ text: "Home", href: "/" },
|
||||||
{ text: "About", href: "/about" },
|
{ text: "About", href: "/about" },
|
||||||
{ text: "Services", href: "/services" },
|
|
||||||
{ text: "Blog", href: "/blog" },
|
{ text: "Blog", href: "/blog" },
|
||||||
{ text: "Contact", href: "/contact" },
|
{ text: "Contact", href: "/contact" },
|
||||||
{ text: "Widgets", href: "/widgets" },
|
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,12 +5,12 @@ const blog = defineCollection({
|
|||||||
loader: glob({ pattern: "**/*.{md,mdx}", base: "./src/content/blog" }),
|
loader: glob({ pattern: "**/*.{md,mdx}", base: "./src/content/blog" }),
|
||||||
schema: z.object({
|
schema: z.object({
|
||||||
title: z.string(),
|
title: z.string(),
|
||||||
description: z.string(),
|
description: z.string().optional(),
|
||||||
pubDate: z.coerce.date(),
|
pubDate: z.coerce.date(),
|
||||||
author: z.string(),
|
author: z.string(),
|
||||||
image: z.string().optional(),
|
image: z.string().optional(),
|
||||||
tags: z.array(z.string()).optional(),
|
tags: z.array(z.string()).optional(),
|
||||||
category: z.string().optional(),
|
categories: z.array(z.string()).optional(),
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -16,14 +16,14 @@ const { post } = Astro.props;
|
|||||||
const { Content, remarkPluginFrontmatter } = await render(post);
|
const { Content, remarkPluginFrontmatter } = await render(post);
|
||||||
const { title, description, pubDate, author, image } = post.data;
|
const { title, description, pubDate, author, image } = post.data;
|
||||||
|
|
||||||
const formattedDate = pubDate.toLocaleDateString("es-ES", {
|
const formattedDate = pubDate.toLocaleDateString("vi-VN", {
|
||||||
year: "numeric",
|
year: "numeric",
|
||||||
month: "long",
|
month: "long",
|
||||||
day: "numeric",
|
day: "numeric",
|
||||||
});
|
});
|
||||||
|
|
||||||
const readingTime = remarkPluginFrontmatter?.minutesRead
|
const readingTime = remarkPluginFrontmatter?.minutesRead
|
||||||
? `${Math.ceil(remarkPluginFrontmatter.minutesRead)} min de lectura`
|
? `${Math.ceil(remarkPluginFrontmatter.minutesRead)} phút đọc`
|
||||||
: "";
|
: "";
|
||||||
|
|
||||||
const metadata = {
|
const metadata = {
|
||||||
@@ -62,7 +62,11 @@ const metadata = {
|
|||||||
{
|
{
|
||||||
image && (
|
image && (
|
||||||
<div class="reveal relative mb-8 h-96 w-full overflow-hidden rounded-xl shadow-lg">
|
<div class="reveal relative mb-8 h-96 w-full overflow-hidden rounded-xl shadow-lg">
|
||||||
<img src={image} alt={title} class="h-full w-full object-cover" />
|
<img
|
||||||
|
src={`/images/${image}`}
|
||||||
|
alt={title}
|
||||||
|
class="h-full w-full object-cover"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -10,9 +10,7 @@ export async function getStaticPaths() {
|
|||||||
|
|
||||||
const categories = new Set();
|
const categories = new Set();
|
||||||
posts.forEach((post) => {
|
posts.forEach((post) => {
|
||||||
if (post.data.category) {
|
post.data.categories?.forEach((tag) => categories.add(tag));
|
||||||
categories.add(post.data.category);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return Array.from(categories).map((category) => {
|
return Array.from(categories).map((category) => {
|
||||||
@@ -20,7 +18,9 @@ export async function getStaticPaths() {
|
|||||||
params: { category: category as string },
|
params: { category: category as string },
|
||||||
props: {
|
props: {
|
||||||
category,
|
category,
|
||||||
posts: posts.filter((post) => post.data.category === category),
|
posts: posts.filter((post) =>
|
||||||
|
post.data.categories?.includes(category as string),
|
||||||
|
),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@@ -50,7 +50,7 @@ const metadata = {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<div class="mb-8 text-center">
|
<div class="mb-8 text-center">
|
||||||
<a href="/blog" class="text-primary hover:underline">← Back to blog</a>
|
<a href="/blog" class="text-primary hover:underline">← Quay lại blog</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="grid gap-8 md:grid-cols-2 lg:grid-cols-3">
|
<div class="grid gap-8 md:grid-cols-2 lg:grid-cols-3">
|
||||||
@@ -48,7 +48,7 @@ const metadata = {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<div class="mb-8 text-center">
|
<div class="mb-8 text-center">
|
||||||
<a href="/blog" class="text-primary hover:underline">← Back to blog</a>
|
<a href="/blog" class="text-primary hover:underline">← Quay lại blog</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="grid gap-8 md:grid-cols-2 lg:grid-cols-3">
|
<div class="grid gap-8 md:grid-cols-2 lg:grid-cols-3">
|
||||||
Reference in New Issue
Block a user