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 = "",
|
description = "",
|
||||||
image = null,
|
image = null,
|
||||||
url = null,
|
url = null,
|
||||||
|
date = null,
|
||||||
} = Astro.props;
|
} = Astro.props;
|
||||||
|
|
||||||
const imageUrl = image ? new URL(image, Astro.site).toString() : null;
|
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:width" content="1200">
|
||||||
<meta property="og:image:height" content="630">
|
<meta property="og:image:height" content="630">
|
||||||
|
|
||||||
|
{date && <meta property="article:published_time" content={date instanceof Date ? date.toISOString() : date} />}
|
||||||
|
|
||||||
<!-- Twitter -->
|
<!-- Twitter -->
|
||||||
|
|
||||||
<meta name="twitter:card" content="summary_large_image" />
|
<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">
|
<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">
|
<li class="border-b pb-3">
|
||||||
<a :href="twitterUrl" target="_blank">
|
<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>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="border-b pb-3">
|
<li class="border-b pb-3">
|
||||||
<a :href="facebookUrl" target="_blank">
|
<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>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="border-b pb-3">
|
<li class="border-b pb-3">
|
||||||
<a :href="whatsappUrl" target="_blank">
|
<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>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="border-b pb-3">
|
<li class="border-b pb-3">
|
||||||
<a :href="linkedinUrl" target="_blank">
|
<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>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<button @click="copyLink" class="cursor-pointer">
|
<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>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ const { title } = Astro.props;
|
||||||
<div class="md:py-16 p-4 bg-white">
|
<div class="md:py-16 p-4 bg-white">
|
||||||
<div class="container mx-auto">
|
<div class="container mx-auto">
|
||||||
<div class="flex justify-between px-4">
|
<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">
|
<img class="md:w-20 md:h-20 w-10 h-10" src="/img/lion.svg" alt="Leon">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,8 @@ const {
|
||||||
title,
|
title,
|
||||||
description,
|
description,
|
||||||
image,
|
image,
|
||||||
url
|
url,
|
||||||
|
date
|
||||||
} = Astro.props;
|
} = Astro.props;
|
||||||
|
|
||||||
const currentLocale = Astro.currentLocale ?? 'es';
|
const currentLocale = Astro.currentLocale ?? 'es';
|
||||||
|
|
@ -29,6 +30,7 @@ const isNewsPage = newsSegments.some(segment => Astro.url.pathname.includes(`/${
|
||||||
description={description}
|
description={description}
|
||||||
image={image}
|
image={image}
|
||||||
url={url}
|
url={url}
|
||||||
|
date={date}
|
||||||
/>
|
/>
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener('contextmenu', (event) => {
|
document.addEventListener('contextmenu', (event) => {
|
||||||
|
|
|
||||||
|
|
@ -47,14 +47,41 @@ const words = plainText
|
||||||
.filter((w) => w.length > 0)
|
.filter((w) => w.length > 0)
|
||||||
.slice(0, 35);
|
.slice(0, 35);
|
||||||
const excerpt = words.join(" ") + (words.length === 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
|
<MainLayout
|
||||||
title={post.data.title}
|
title={post.data.title}
|
||||||
description={excerpt}
|
description={excerpt}
|
||||||
image={post.data.thumbnail}
|
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">
|
<div class="container mx-auto md:py-16 py-8">
|
||||||
<Header />
|
<Header />
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -78,14 +105,21 @@ const excerpt = words.join(" ") + (words.length === 35 ? "..." : "");
|
||||||
}
|
}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid md:grid-cols-10">
|
<div class="grid md:grid-cols-10 container mx-auto">
|
||||||
<div
|
<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"
|
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">
|
<article id="article-body">
|
||||||
{excerpt}
|
<time
|
||||||
</p> -->
|
id="article-date"
|
||||||
|
class="text-center text-[#003421]/60 text-sm hidden"
|
||||||
|
datetime={post.data.date.toISOString()}
|
||||||
|
>
|
||||||
|
{localeDate}
|
||||||
|
</time>
|
||||||
<Content />
|
<Content />
|
||||||
|
</article>
|
||||||
</div>
|
</div>
|
||||||
<div class="md:col-span-3 bg-tertiary md:sticky top-0 h-fit">
|
<div class="md:col-span-3 bg-tertiary md:sticky top-0 h-fit">
|
||||||
{post.data.youtube && <YouTube id={post.data.youtube} />}
|
{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 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>
|
</style>
|
||||||
|
|
||||||
<MainLayout>
|
<MainLayout
|
||||||
|
title={post.data.title}
|
||||||
|
date={post.data.date}
|
||||||
|
>
|
||||||
<Fragment slot="head">
|
<Fragment slot="head">
|
||||||
<!-- Título -->
|
<!-- Título -->
|
||||||
<title>{post.data.title}</title>
|
<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="og:image:type" content={`image/${post.data.gallery[0].image.format}`} />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
<meta property="article:published_time" content={post.data.date.toISOString()} />
|
||||||
</Fragment>
|
</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">
|
<div class="container mx-auto py-16">
|
||||||
<Header />
|
<Header />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<TitleSection title={post.data.title} />
|
<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">
|
<div class="container mx-auto">
|
||||||
{post.data.gallery && <CarouselSection images={post.data.gallery} />}
|
{post.data.gallery && <CarouselSection images={post.data.gallery} />}
|
||||||
<div class="grid md:grid-cols-10">
|
<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]">
|
<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 />
|
<Content />
|
||||||
|
</article>
|
||||||
</div>
|
</div>
|
||||||
<div class="md:col-span-3 bg-tertiary md:sticky top-0 h-fit">
|
<div class="md:col-span-3 bg-tertiary md:sticky top-0 h-fit">
|
||||||
{ post.data.youtube && (
|
{ post.data.youtube && (
|
||||||
|
|
|
||||||
|
|
@ -24,24 +24,24 @@ body {
|
||||||
background-color: var(--background);
|
background-color: var(--background);
|
||||||
}
|
}
|
||||||
|
|
||||||
.content h2{
|
.content h2 {
|
||||||
font-size: 22px;
|
font-size: 22px;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
line-height: 100%;
|
line-height: 100%;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content h3{
|
.content h3 {
|
||||||
font-size: 20px;
|
font-size: 19px;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
line-height: 100%;
|
line-height: 100%;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content h1{
|
.content h1 {
|
||||||
font-size: 32px;
|
font-size: 32px;
|
||||||
margin-bottom: 30px;
|
margin-bottom: 30px;
|
||||||
line-height: 100%;
|
line-height: 150%;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
font-family: var(--font-secondary);
|
font-family: var(--font-secondary);
|
||||||
|
|
@ -60,6 +60,7 @@ body {
|
||||||
font-size: 22px;
|
font-size: 22px;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
line-height: 120%;
|
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