feat: implement article detail pages with SEO and layout components
This commit is contained in:
parent
aa54417618
commit
6cea6fe48b
|
|
@ -7,6 +7,7 @@ const {
|
|||
description = "",
|
||||
image = null,
|
||||
url = null,
|
||||
date = null,
|
||||
} = Astro.props;
|
||||
|
||||
const imageUrl = image ? new URL(image, Astro.site).toString() : null;
|
||||
|
|
@ -35,6 +36,8 @@ const imageUrl = image ? new URL(image, Astro.site).toString() : null;
|
|||
<meta property="og:image:width" content="1200">
|
||||
<meta property="og:image:height" content="630">
|
||||
|
||||
{date && <meta property="article:published_time" content={date instanceof Date ? date.toISOString() : date} />}
|
||||
|
||||
<!-- Twitter -->
|
||||
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
|
|
|
|||
|
|
@ -15,27 +15,27 @@
|
|||
<ul class="flex flex-col gap-4 bg-white/90 backdrop-blur-md py-4 px-4 rounded-r-2xl shadow-xl border border-gray-200">
|
||||
<li class="border-b pb-3">
|
||||
<a :href="twitterUrl" target="_blank">
|
||||
<Icon icon="ph:x-logo-thin" class="text-2xl" />
|
||||
<Icon icon="ph:x-logo-thin" class="text-2xl text-black" />
|
||||
</a>
|
||||
</li>
|
||||
<li class="border-b pb-3">
|
||||
<a :href="facebookUrl" target="_blank">
|
||||
<Icon icon="ph:facebook-logo-thin" class="text-2xl" />
|
||||
<Icon icon="ph:facebook-logo-thin" class="text-2xl text-black" />
|
||||
</a>
|
||||
</li>
|
||||
<li class="border-b pb-3">
|
||||
<a :href="whatsappUrl" target="_blank">
|
||||
<Icon icon="ph:whatsapp-logo-thin" class="text-2xl" />
|
||||
<Icon icon="ph:whatsapp-logo-thin" class="text-2xl text-black" />
|
||||
</a>
|
||||
</li>
|
||||
<li class="border-b pb-3">
|
||||
<a :href="linkedinUrl" target="_blank">
|
||||
<Icon icon="ph:linkedin-logo-thin" class="text-2xl" />
|
||||
<Icon icon="ph:linkedin-logo-thin" class="text-2xl text-black" />
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<button @click="copyLink" class="cursor-pointer">
|
||||
<Icon :icon="copied ? 'ph:check-thin' : 'ph:link-thin'" class="text-2xl" />
|
||||
<Icon :icon="copied ? 'ph:check-thin' : 'ph:link-thin'" class="text-2xl text-black" />
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ const { title } = Astro.props;
|
|||
<div class="md:py-16 p-4 bg-white">
|
||||
<div class="container mx-auto">
|
||||
<div class="flex justify-between px-4">
|
||||
<h2 class="text-tertiary font-secondary text-xl sm:text-2xl md:text-3xl lg:text-5xl font-bold">{title}</h2>
|
||||
<h2 id="article-title" class="text-tertiary font-secondary text-xl sm:text-2xl md:text-3xl lg:text-5xl font-bold">{title}</h2>
|
||||
<img class="md:w-20 md:h-20 w-10 h-10" src="/img/lion.svg" alt="Leon">
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,8 @@ const {
|
|||
title,
|
||||
description,
|
||||
image,
|
||||
url
|
||||
url,
|
||||
date
|
||||
} = Astro.props;
|
||||
|
||||
const currentLocale = Astro.currentLocale ?? 'es';
|
||||
|
|
@ -29,6 +30,7 @@ const isNewsPage = newsSegments.some(segment => Astro.url.pathname.includes(`/${
|
|||
description={description}
|
||||
image={image}
|
||||
url={url}
|
||||
date={date}
|
||||
/>
|
||||
<script>
|
||||
document.addEventListener('contextmenu', (event) => {
|
||||
|
|
|
|||
|
|
@ -47,14 +47,41 @@ const words = plainText
|
|||
.filter((w) => w.length > 0)
|
||||
.slice(0, 35);
|
||||
const excerpt = words.join(" ") + (words.length === 35 ? "..." : "");
|
||||
|
||||
const canonicalUrl = new URL(`/${locale}/${news_slug}/${post.id}`, Astro.site);
|
||||
const imageUrl = post.data.thumbnail
|
||||
? new URL(post.data.thumbnail, Astro.site).toString()
|
||||
: null;
|
||||
const localeDate = new Intl.DateTimeFormat(locale || "es", {
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
}).format(post.data.date);
|
||||
---
|
||||
|
||||
<MainLayout
|
||||
title={post.data.title}
|
||||
description={excerpt}
|
||||
image={post.data.thumbnail}
|
||||
url={new URL(`/${locale}/${news_slug}/${post.id}`, Astro.site)}
|
||||
url={canonicalUrl}
|
||||
date={post.data.date}
|
||||
>
|
||||
<script
|
||||
type="application/ld+json"
|
||||
set:html={JSON.stringify({
|
||||
"@context": "https://schema.org",
|
||||
"@type": "NewsArticle",
|
||||
headline: post.data.title,
|
||||
datePublished: post.data.date,
|
||||
description: excerpt,
|
||||
image: imageUrl,
|
||||
url: canonicalUrl.toString(),
|
||||
author: {
|
||||
"@type": "Organization",
|
||||
name: "Centro del Reino de Paz y Justicia",
|
||||
},
|
||||
})}
|
||||
></script>
|
||||
<div class="container mx-auto md:py-16 py-8">
|
||||
<Header />
|
||||
</div>
|
||||
|
|
@ -78,14 +105,21 @@ const excerpt = words.join(" ") + (words.length === 35 ? "..." : "");
|
|||
}
|
||||
</a>
|
||||
</div>
|
||||
<div class="grid md:grid-cols-10">
|
||||
<div class="grid md:grid-cols-10 container mx-auto">
|
||||
<div
|
||||
id="article-content"
|
||||
class="md:col-span-7 content bg-white p-8 md:p-20 prose-p:mb-4 text-[#003421] text-justify"
|
||||
>
|
||||
<!-- <p class="text-lg font-semibold text-tertiary mb-8 pb-6 border-b border-tertiary/20 italic">
|
||||
{excerpt}
|
||||
</p> -->
|
||||
<Content />
|
||||
<article id="article-body">
|
||||
<time
|
||||
id="article-date"
|
||||
class="text-center text-[#003421]/60 text-sm hidden"
|
||||
datetime={post.data.date.toISOString()}
|
||||
>
|
||||
{localeDate}
|
||||
</time>
|
||||
<Content />
|
||||
</article>
|
||||
</div>
|
||||
<div class="md:col-span-3 bg-tertiary md:sticky top-0 h-fit">
|
||||
{post.data.youtube && <YouTube id={post.data.youtube} />}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@ const baseUrl = Astro.site ?? "https://mk8nrc8p-4321.brs.devtunnels.ms";
|
|||
|
||||
const pageUrl = new URL(`/${post.data.locale}/${news_slug}/${post.id}`, baseUrl).toString();
|
||||
|
||||
const imageUrl = post.data.thumbnail ? new URL(post.data.thumbnail, baseUrl).toString() : null;
|
||||
const localeDate = new Intl.DateTimeFormat(post.data.locale || 'es', { year: 'numeric', month: 'long', day: 'numeric' }).format(post.data.date);
|
||||
---
|
||||
|
||||
|
||||
|
|
@ -39,7 +41,10 @@ const pageUrl = new URL(`/${post.data.locale}/${news_slug}/${post.id}`, baseUrl)
|
|||
}
|
||||
</style>
|
||||
|
||||
<MainLayout>
|
||||
<MainLayout
|
||||
title={post.data.title}
|
||||
date={post.data.date}
|
||||
>
|
||||
<Fragment slot="head">
|
||||
<!-- Título -->
|
||||
<title>{post.data.title}</title>
|
||||
|
|
@ -60,18 +65,37 @@ const pageUrl = new URL(`/${post.data.locale}/${news_slug}/${post.id}`, baseUrl)
|
|||
<meta property="og:image:type" content={`image/${post.data.gallery[0].image.format}`} />
|
||||
</>
|
||||
)}
|
||||
<meta property="article:published_time" content={post.data.date.toISOString()} />
|
||||
</Fragment>
|
||||
<script type="application/ld+json" set:html={JSON.stringify({
|
||||
"@context": "https://schema.org",
|
||||
"@type": "NewsArticle",
|
||||
"headline": post.data.title,
|
||||
"datePublished": post.data.date,
|
||||
"image": imageUrl,
|
||||
"url": pageUrl,
|
||||
"author": {
|
||||
"@type": "Organization",
|
||||
"name": "Centro del Reino de Paz y Justicia"
|
||||
}
|
||||
})} />
|
||||
<div class="container mx-auto py-16">
|
||||
<Header />
|
||||
</div>
|
||||
|
||||
<TitleSection title={post.data.title} />
|
||||
|
||||
<time id="article-date" class="block text-center text-[#003421]/60 text-sm mt-4 mb-8" datetime={post.data.date.toISOString()}>
|
||||
{localeDate}
|
||||
</time>
|
||||
|
||||
<div class="container mx-auto">
|
||||
{post.data.gallery && <CarouselSection images={post.data.gallery} />}
|
||||
<div class="grid md:grid-cols-10">
|
||||
<div class="md:col-span-7 content bg-white p-8 md:p-20 prose-p:mb-4 text-[#003421]">
|
||||
<Content />
|
||||
<div id="article-content" class="md:col-span-7 content bg-white p-8 md:p-20 prose-p:mb-4 text-[#003421]">
|
||||
<article id="article-body">
|
||||
<Content />
|
||||
</article>
|
||||
</div>
|
||||
<div class="md:col-span-3 bg-tertiary md:sticky top-0 h-fit">
|
||||
{ post.data.youtube && (
|
||||
|
|
|
|||
|
|
@ -24,24 +24,24 @@ body {
|
|||
background-color: var(--background);
|
||||
}
|
||||
|
||||
.content h2{
|
||||
.content h2 {
|
||||
font-size: 22px;
|
||||
margin-bottom: 10px;
|
||||
line-height: 100%;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.content h3{
|
||||
font-size: 20px;
|
||||
.content h3 {
|
||||
font-size: 19px;
|
||||
margin-bottom: 10px;
|
||||
line-height: 100%;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.content h1{
|
||||
.content h1 {
|
||||
font-size: 32px;
|
||||
margin-bottom: 30px;
|
||||
line-height: 100%;
|
||||
line-height: 150%;
|
||||
font-weight: 700;
|
||||
text-align: left;
|
||||
font-family: var(--font-secondary);
|
||||
|
|
@ -60,6 +60,7 @@ body {
|
|||
font-size: 22px;
|
||||
margin-bottom: 20px;
|
||||
line-height: 120%;
|
||||
text-align: left; /* puedes cambiar a center si quieres */
|
||||
text-align: left;
|
||||
/* puedes cambiar a center si quieres */
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue