fix dynamic slug to editorials and news
This commit is contained in:
parent
a22e86ef20
commit
8bae1f35e8
|
|
@ -38,6 +38,16 @@ export const routeTranslations = {
|
|||
}
|
||||
} as const;
|
||||
|
||||
export function getRouteKeyFromSlug(slug: string): keyof typeof routeTranslations {
|
||||
for (const [key, translations] of Object.entries(routeTranslations)) {
|
||||
const values = Object.values(translations) as string[];
|
||||
if (values.includes(slug)) {
|
||||
return key as keyof typeof routeTranslations;
|
||||
}
|
||||
}
|
||||
return "news";
|
||||
}
|
||||
|
||||
export function getLocalizedRoute(route: keyof typeof routeTranslations, locale: string | undefined): string {
|
||||
const l = (locale in routeTranslations[route] ? locale : "es") as Locale;
|
||||
return routeTranslations[route][l as keyof (typeof routeTranslations)[typeof route]] || routeTranslations[route]["en" as keyof (typeof routeTranslations)[typeof route]];
|
||||
|
|
|
|||
|
|
@ -1,58 +0,0 @@
|
|||
---
|
||||
import MainLayout from "@/layouts/MainLayout.astro"
|
||||
import Header from "@/components/Header.astro"
|
||||
import NewsList from "@/components/cards/NewsList.astro";
|
||||
import { getCollection } from "astro:content";
|
||||
import FooterSection from "@/components/section/FooterSection.astro";
|
||||
|
||||
import { createTranslator, getLocalizedRoute, routeTranslations } from '@/i18n';
|
||||
const tl = createTranslator(Astro.currentLocale);
|
||||
|
||||
export function getStaticPaths() {
|
||||
const locales = Object.keys(routeTranslations.editorial);
|
||||
return locales.map((locale) => ({
|
||||
params: {
|
||||
locale,
|
||||
editorial_slug: getLocalizedRoute('editorial', locale)
|
||||
},
|
||||
}));
|
||||
}
|
||||
|
||||
const { locale, editorial_slug } = Astro.params;
|
||||
|
||||
const items = await getCollection("editorial", (post)=>{
|
||||
const currentLocale = Astro.currentLocale;
|
||||
return post.data.locale == currentLocale
|
||||
});
|
||||
|
||||
const sortedPosts = [...items]
|
||||
.sort((a, b) => {
|
||||
const dateDiff = new Date(b.data.date).getTime() - new Date(a.data.date).getTime()
|
||||
if (dateDiff !== 0) return dateDiff
|
||||
return (a.data.order ?? 0) - (b.data.order ?? 0)
|
||||
});
|
||||
---
|
||||
|
||||
<MainLayout >
|
||||
<div class="top-16 relative mb container mx-auto">
|
||||
<Header />
|
||||
</div>
|
||||
<div class="container mx-auto mt-4">
|
||||
<div class="flex flex-col lg:w-1/2 items-center mx-auto py-8">
|
||||
<h1 class="text-white text-2xl uppercase font-bold text-center mb-4 font-primary md:mt-20 mt-10">{tl("editorial.title")}</h1>
|
||||
<h2 class="text-white text-3xl lg:text-2xl font-bold text-center font-secondary mb-4 md:p-0 px-2">{tl("editorial.text")}</h2>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col md:gap-0 gap-2 lg:max-w-4xl mx-auto bg-white p-4 md:p-8">
|
||||
{
|
||||
sortedPosts.map((item) => (
|
||||
<div class="news-item">
|
||||
<NewsList data={item} content={{ body: item.body }} routeKey="editorial" />
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<FooterSection />
|
||||
|
||||
</MainLayout>
|
||||
|
|
@ -1,143 +0,0 @@
|
|||
---
|
||||
import { YouTube } from "astro-embed";
|
||||
import MainLayout from "../../../layouts/MainLayout.astro";
|
||||
import Header from "../../../components/Header.astro";
|
||||
import CarouselSection from "../../../components/section/CarouselSection.astro";
|
||||
import { Image } from "@unpic/astro";
|
||||
import { getCollection, render } from "astro:content";
|
||||
import TitleSection from "../../../components/section/TitleSection.astro";
|
||||
import FooterSection from "../../../components/section/FooterSection.astro";
|
||||
import { getLocalizedRoute } from "@/i18n";
|
||||
export const prerender = true;
|
||||
// 1. Generate a new path for every collection entry
|
||||
export async function getStaticPaths() {
|
||||
const posts = await getCollection("news");
|
||||
return posts.map((post) => ({
|
||||
params: {
|
||||
id: post.id,
|
||||
locale: post.data.locale,
|
||||
news_slug: getLocalizedRoute("news", post.data.locale),
|
||||
},
|
||||
props: { post },
|
||||
}));
|
||||
}
|
||||
|
||||
const { locale, news_slug } = Astro.params;
|
||||
|
||||
// 2. For your template, you can get the entry directly from the prop
|
||||
const { post } = Astro.props;
|
||||
const { Content } = await render(post);
|
||||
|
||||
const baseSlug = news_slug;
|
||||
|
||||
const rawContent = post.body || "";
|
||||
const plainText = rawContent
|
||||
.replace(/^#.*$/gm, "")
|
||||
.replace(/^###.*$/gm, "")
|
||||
.replace(/\*\*([^*]+)\*\*/g, "$1")
|
||||
.replace(/\*([^*]+)\*/g, "$1")
|
||||
.replace(/_([^_]+)_/g, "$1")
|
||||
.replace(/\[([^\]]+)\]\([^)]+\)/g, "$1")
|
||||
.replace(/^>.*$/gm, "")
|
||||
.replace(/`[^`]+`/g, "")
|
||||
.replace(/^[-*]\s+/gm, "")
|
||||
.trim();
|
||||
const words = plainText
|
||||
.split(/\s+/)
|
||||
.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);
|
||||
const y = post.data.date.getFullYear();
|
||||
const m = String(post.data.date.getMonth() + 1).padStart(2, "0");
|
||||
const d = String(post.data.date.getDate()).padStart(2, "0");
|
||||
const dateISO = `${y}-${m}-${d}`;
|
||||
---
|
||||
|
||||
<MainLayout
|
||||
title={post.data.title}
|
||||
description={excerpt}
|
||||
image={post.data.thumbnail}
|
||||
url={canonicalUrl.toString()}
|
||||
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>
|
||||
|
||||
<a class="block">
|
||||
<TitleSection title={post.data.title} />
|
||||
</a>
|
||||
|
||||
<div class="container mx-auto">
|
||||
<a class="block">
|
||||
{
|
||||
post.data.gallery?.length ? (
|
||||
<CarouselSection images={post.data.gallery} />
|
||||
) : post.data.thumbnail ? (
|
||||
<Image
|
||||
src={post.data.thumbnail}
|
||||
alt={post.data.title}
|
||||
class="hover:opacity-90 transition-opacity"
|
||||
/>
|
||||
) : null
|
||||
}
|
||||
</a>
|
||||
</div>
|
||||
<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"
|
||||
>
|
||||
<article id="article-body">
|
||||
<time
|
||||
id="article-date"
|
||||
class="block text-center text-[#003421]/60 text-sm mb-8 hidden"
|
||||
datetime={dateISO}
|
||||
>
|
||||
{localeDate}
|
||||
</time>
|
||||
<Content />
|
||||
</article>
|
||||
</div>
|
||||
<div class="md:col-span-3 bg-tertiary md:sticky top-0 self-start">
|
||||
{post.data.youtube && <YouTube id={post.data.youtube} />}
|
||||
|
||||
{
|
||||
post.data.gallery &&
|
||||
post.data.gallery.map((galleryImage, i) => (
|
||||
<button type="button" data-lightbox-index={i} data-lightbox-src={galleryImage.image} data-lightbox-alt={galleryImage.text} class="cursor-pointer block w-full text-left">
|
||||
<Image src={galleryImage.image} alt={galleryImage.text} />
|
||||
</button>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</MainLayout>
|
||||
|
||||
<FooterSection />
|
||||
|
|
@ -9,27 +9,30 @@ import TitleSection from "../../../components/section/TitleSection.astro";
|
|||
import FooterSection from "../../../components/section/FooterSection.astro";
|
||||
import { getLocalizedRoute } from "@/i18n";
|
||||
export const prerender = true;
|
||||
// 1. Generate a new path for every collection entry
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const posts = await getCollection("editorial");
|
||||
return posts.map((post) => ({
|
||||
params: {
|
||||
id: post.id,
|
||||
locale: post.data.locale,
|
||||
editorial_slug: getLocalizedRoute("editorial", post.data.locale),
|
||||
},
|
||||
props: { post },
|
||||
}));
|
||||
const paths = [];
|
||||
for (const collection of ["news", "editorial"]) {
|
||||
const posts = await getCollection(collection);
|
||||
for (const post of posts) {
|
||||
paths.push({
|
||||
params: {
|
||||
id: post.id,
|
||||
locale: post.data.locale,
|
||||
section: getLocalizedRoute(collection, post.data.locale),
|
||||
},
|
||||
props: { post },
|
||||
});
|
||||
}
|
||||
}
|
||||
return paths;
|
||||
}
|
||||
|
||||
const { locale, editorial_slug } = Astro.params;
|
||||
|
||||
// 2. For your template, you can get the entry directly from the prop
|
||||
const { locale, section } = Astro.params;
|
||||
const { post } = Astro.props;
|
||||
const routeKey = post.collection;
|
||||
const { Content } = await render(post);
|
||||
|
||||
const baseSlug = editorial_slug;
|
||||
|
||||
const rawContent = post.body || "";
|
||||
const plainText = rawContent
|
||||
.replace(/^#.*$/gm, "")
|
||||
|
|
@ -48,7 +51,7 @@ const words = plainText
|
|||
.slice(0, 35);
|
||||
const excerpt = words.join(" ") + (words.length === 35 ? "..." : "");
|
||||
|
||||
const canonicalUrl = new URL(`/${locale}/${editorial_slug}/${post.id}`, Astro.site);
|
||||
const canonicalUrl = new URL(`/${locale}/${section}/${post.id}`, Astro.site);
|
||||
const imageUrl = post.data.thumbnail
|
||||
? new URL(post.data.thumbnail, Astro.site).toString()
|
||||
: null;
|
||||
|
|
@ -61,6 +64,8 @@ const y = post.data.date.getFullYear();
|
|||
const m = String(post.data.date.getMonth() + 1).padStart(2, "0");
|
||||
const d = String(post.data.date.getDate()).padStart(2, "0");
|
||||
const dateISO = `${y}-${m}-${d}`;
|
||||
|
||||
const schemaType = routeKey === "news" ? "NewsArticle" : "Article";
|
||||
---
|
||||
|
||||
<MainLayout
|
||||
|
|
@ -74,7 +79,7 @@ const dateISO = `${y}-${m}-${d}`;
|
|||
type="application/ld+json"
|
||||
set:html={JSON.stringify({
|
||||
"@context": "https://schema.org",
|
||||
"@type": "Article",
|
||||
"@type": schemaType,
|
||||
headline: post.data.title,
|
||||
datePublished: post.data.date,
|
||||
description: excerpt,
|
||||
|
|
@ -2,47 +2,37 @@
|
|||
import MainLayout from "@/layouts/MainLayout.astro"
|
||||
import Header from "@/components/Header.astro"
|
||||
import NewsList from "@/components/cards/NewsList.astro";
|
||||
import { getCollection, getEntry } from "astro:content";
|
||||
import { getCollection } from "astro:content";
|
||||
import FooterSection from "@/components/section/FooterSection.astro";
|
||||
|
||||
|
||||
import { createTranslator, getLocalizedRoute, routeTranslations } from '@/i18n';
|
||||
import { createTranslator, getRouteKeyFromSlug } from '@/i18n';
|
||||
const tl = createTranslator(Astro.currentLocale);
|
||||
|
||||
export function getStaticPaths() {
|
||||
const locales = Object.keys(routeTranslations.news);
|
||||
return locales.map((locale) => ({
|
||||
params: {
|
||||
locale,
|
||||
news_slug: getLocalizedRoute('news', locale)
|
||||
},
|
||||
}));
|
||||
}
|
||||
const { locale, section } = Astro.params;
|
||||
const routeKey = getRouteKeyFromSlug(section);
|
||||
|
||||
const { locale, news_slug } = Astro.params;
|
||||
|
||||
const newsItems = await getCollection("news", (post)=>{
|
||||
const items = await getCollection(routeKey, (post)=>{
|
||||
const currentLocale = Astro.currentLocale;
|
||||
return post.data.locale == currentLocale
|
||||
});
|
||||
|
||||
const sortedPosts = [...newsItems]
|
||||
const sortedPosts = [...items]
|
||||
.sort((a, b) => {
|
||||
const dateDiff = new Date(b.data.date).getTime() - new Date(a.data.date).getTime()
|
||||
if (dateDiff !== 0) return dateDiff
|
||||
return (a.data.order ?? 0) - (b.data.order ?? 0)
|
||||
});
|
||||
|
||||
const allTags = [...new Set(
|
||||
const allTags = routeKey === "news" ? [...new Set(
|
||||
sortedPosts
|
||||
.filter(p => p.data.tags && p.data.tags.length > 0)
|
||||
.flatMap(p => p.data.tags)
|
||||
.filter((tag): tag is string => tag !== undefined)
|
||||
)].sort();
|
||||
)].sort() : [];
|
||||
|
||||
const allYears = [...new Set(
|
||||
const allYears = routeKey === "news" ? [...new Set(
|
||||
sortedPosts.map(p => new Date(p.data.date).getFullYear())
|
||||
)].sort((a, b) => b - a);
|
||||
)].sort((a, b) => b - a) : [];
|
||||
---
|
||||
|
||||
<MainLayout >
|
||||
|
|
@ -51,49 +41,49 @@ const allYears = [...new Set(
|
|||
</div>
|
||||
<div class="container mx-auto mt-4">
|
||||
<div class="flex flex-col lg:w-1/2 items-center mx-auto py-8">
|
||||
<h1 class="text-white text-2xl uppercase font-bold text-center mb-4 font-primary md:mt-20 mt-10">{tl("news.title")}</h1>
|
||||
<h2 class="text-white text-3xl lg:text-5xl font-bold text-center font-secondary mb-4 md:p-0 px-2">{tl("news.text")}</h2>
|
||||
</div>
|
||||
<h1 class="text-white text-2xl uppercase font-bold text-center mb-4 font-primary md:mt-20 mt-10">{tl(routeKey + ".title")}</h1>
|
||||
<h2 class="text-white text-3xl lg:text-5xl font-bold text-center font-secondary mb-4 md:p-0 px-2">{tl(routeKey + ".text")}</h2>
|
||||
</div>
|
||||
|
||||
|
||||
{allTags.length > 0 && (
|
||||
<div class="container mx-auto mb-8 px-4 md:px-0">
|
||||
<div class="flex flex-nowrap md:flex-wrap gap-2 md:justify-center overflow-x-auto md:overflow-visible pb-2 md:pb-0">
|
||||
<button class="filter-btn px-4 py-2 font-primary text-sm cursor-pointer transition-colors whitespace-nowrap" data-tag="all">
|
||||
{tl("news.all")}
|
||||
</button>
|
||||
{allTags.map((tag) => (
|
||||
<button class="filter-btn px-4 py-2 font-primary text-sm cursor-pointer transition-colors whitespace-nowrap" data-tag={tag!}>
|
||||
{tag}
|
||||
{routeKey === "news" && allTags.length > 0 && (
|
||||
<div class="container mx-auto mb-8 px-4 md:px-0">
|
||||
<div class="flex flex-nowrap md:flex-wrap gap-2 md:justify-center overflow-x-auto md:overflow-visible pb-2 md:pb-0">
|
||||
<button class="filter-btn px-4 py-2 font-primary text-sm cursor-pointer transition-colors whitespace-nowrap" data-tag="all">
|
||||
{tl("news.all")}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div class="flex flex-col md:gap-0 gap-2 lg:max-w-4xl mx-auto bg-white p-4 md:p-8">
|
||||
{allYears.length > 0 && (
|
||||
<div class="container mb-4">
|
||||
<div class="flex justify-start md:justify-end">
|
||||
<select id="year-filter" class="bg-[#003421] text-[#EBE5D0] px-4 py-2 font-primary text-sm cursor-pointer border-none outline-none">
|
||||
<option value="all">{tl("news.allYears")}</option>
|
||||
{allYears.map((year) => (
|
||||
<option value={year}>{year}</option>
|
||||
{allTags.map((tag) => (
|
||||
<button class="filter-btn px-4 py-2 font-primary text-sm cursor-pointer transition-colors whitespace-nowrap" data-tag={tag!}>
|
||||
{tag}
|
||||
</button>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div class="flex flex-col md:gap-0 gap-2 lg:max-w-4xl mx-auto bg-white p-4 md:p-8">
|
||||
{routeKey === "news" && allYears.length > 0 && (
|
||||
<div class="container mb-4">
|
||||
<div class="flex justify-start md:justify-end">
|
||||
<select id="year-filter" class="bg-[#003421] text-[#EBE5D0] px-4 py-2 font-primary text-sm cursor-pointer border-none outline-none">
|
||||
<option value="all">{tl("news.allYears")}</option>
|
||||
{allYears.map((year) => (
|
||||
<option value={year}>{year}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{
|
||||
sortedPosts.map((item) => (
|
||||
<div class="news-item" data-tags={routeKey === "news" ? JSON.stringify(item.data.tags || []) : "[]"} data-year={routeKey === "news" ? new Date(item.data.date).getFullYear() : ""}>
|
||||
<NewsList data={item} content={{ body: item.body }} routeKey={routeKey} />
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
)}
|
||||
{
|
||||
sortedPosts.map((item) => (
|
||||
<div class="news-item" data-tags={JSON.stringify(item.data.tags || [])} data-year={new Date(item.data.date).getFullYear()}>
|
||||
<NewsList data={item} content={{ body: item.body }} />
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<FooterSection />
|
||||
</div>
|
||||
<FooterSection />
|
||||
|
||||
</MainLayout>
|
||||
|
||||
Loading…
Reference in New Issue