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;
|
} 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 {
|
export function getLocalizedRoute(route: keyof typeof routeTranslations, locale: string | undefined): string {
|
||||||
const l = (locale in routeTranslations[route] ? locale : "es") as Locale;
|
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]];
|
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 FooterSection from "../../../components/section/FooterSection.astro";
|
||||||
import { getLocalizedRoute } from "@/i18n";
|
import { getLocalizedRoute } from "@/i18n";
|
||||||
export const prerender = true;
|
export const prerender = true;
|
||||||
// 1. Generate a new path for every collection entry
|
|
||||||
export async function getStaticPaths() {
|
export async function getStaticPaths() {
|
||||||
const posts = await getCollection("editorial");
|
const paths = [];
|
||||||
return posts.map((post) => ({
|
for (const collection of ["news", "editorial"]) {
|
||||||
params: {
|
const posts = await getCollection(collection);
|
||||||
id: post.id,
|
for (const post of posts) {
|
||||||
locale: post.data.locale,
|
paths.push({
|
||||||
editorial_slug: getLocalizedRoute("editorial", post.data.locale),
|
params: {
|
||||||
},
|
id: post.id,
|
||||||
props: { post },
|
locale: post.data.locale,
|
||||||
}));
|
section: getLocalizedRoute(collection, post.data.locale),
|
||||||
|
},
|
||||||
|
props: { post },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return paths;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { locale, editorial_slug } = Astro.params;
|
const { locale, section } = Astro.params;
|
||||||
|
|
||||||
// 2. For your template, you can get the entry directly from the prop
|
|
||||||
const { post } = Astro.props;
|
const { post } = Astro.props;
|
||||||
|
const routeKey = post.collection;
|
||||||
const { Content } = await render(post);
|
const { Content } = await render(post);
|
||||||
|
|
||||||
const baseSlug = editorial_slug;
|
|
||||||
|
|
||||||
const rawContent = post.body || "";
|
const rawContent = post.body || "";
|
||||||
const plainText = rawContent
|
const plainText = rawContent
|
||||||
.replace(/^#.*$/gm, "")
|
.replace(/^#.*$/gm, "")
|
||||||
|
|
@ -48,7 +51,7 @@ const words = plainText
|
||||||
.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}/${editorial_slug}/${post.id}`, Astro.site);
|
const canonicalUrl = new URL(`/${locale}/${section}/${post.id}`, Astro.site);
|
||||||
const imageUrl = post.data.thumbnail
|
const imageUrl = post.data.thumbnail
|
||||||
? new URL(post.data.thumbnail, Astro.site).toString()
|
? new URL(post.data.thumbnail, Astro.site).toString()
|
||||||
: null;
|
: null;
|
||||||
|
|
@ -61,6 +64,8 @@ const y = post.data.date.getFullYear();
|
||||||
const m = String(post.data.date.getMonth() + 1).padStart(2, "0");
|
const m = String(post.data.date.getMonth() + 1).padStart(2, "0");
|
||||||
const d = String(post.data.date.getDate()).padStart(2, "0");
|
const d = String(post.data.date.getDate()).padStart(2, "0");
|
||||||
const dateISO = `${y}-${m}-${d}`;
|
const dateISO = `${y}-${m}-${d}`;
|
||||||
|
|
||||||
|
const schemaType = routeKey === "news" ? "NewsArticle" : "Article";
|
||||||
---
|
---
|
||||||
|
|
||||||
<MainLayout
|
<MainLayout
|
||||||
|
|
@ -74,7 +79,7 @@ const dateISO = `${y}-${m}-${d}`;
|
||||||
type="application/ld+json"
|
type="application/ld+json"
|
||||||
set:html={JSON.stringify({
|
set:html={JSON.stringify({
|
||||||
"@context": "https://schema.org",
|
"@context": "https://schema.org",
|
||||||
"@type": "Article",
|
"@type": schemaType,
|
||||||
headline: post.data.title,
|
headline: post.data.title,
|
||||||
datePublished: post.data.date,
|
datePublished: post.data.date,
|
||||||
description: excerpt,
|
description: excerpt,
|
||||||
|
|
@ -2,47 +2,37 @@
|
||||||
import MainLayout from "@/layouts/MainLayout.astro"
|
import MainLayout from "@/layouts/MainLayout.astro"
|
||||||
import Header from "@/components/Header.astro"
|
import Header from "@/components/Header.astro"
|
||||||
import NewsList from "@/components/cards/NewsList.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 FooterSection from "@/components/section/FooterSection.astro";
|
||||||
|
|
||||||
|
import { createTranslator, getRouteKeyFromSlug } from '@/i18n';
|
||||||
import { createTranslator, getLocalizedRoute, routeTranslations } from '@/i18n';
|
|
||||||
const tl = createTranslator(Astro.currentLocale);
|
const tl = createTranslator(Astro.currentLocale);
|
||||||
|
|
||||||
export function getStaticPaths() {
|
const { locale, section } = Astro.params;
|
||||||
const locales = Object.keys(routeTranslations.news);
|
const routeKey = getRouteKeyFromSlug(section);
|
||||||
return locales.map((locale) => ({
|
|
||||||
params: {
|
|
||||||
locale,
|
|
||||||
news_slug: getLocalizedRoute('news', locale)
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
const { locale, news_slug } = Astro.params;
|
const items = await getCollection(routeKey, (post)=>{
|
||||||
|
|
||||||
const newsItems = await getCollection("news", (post)=>{
|
|
||||||
const currentLocale = Astro.currentLocale;
|
const currentLocale = Astro.currentLocale;
|
||||||
return post.data.locale == currentLocale
|
return post.data.locale == currentLocale
|
||||||
});
|
});
|
||||||
|
|
||||||
const sortedPosts = [...newsItems]
|
const sortedPosts = [...items]
|
||||||
.sort((a, b) => {
|
.sort((a, b) => {
|
||||||
const dateDiff = new Date(b.data.date).getTime() - new Date(a.data.date).getTime()
|
const dateDiff = new Date(b.data.date).getTime() - new Date(a.data.date).getTime()
|
||||||
if (dateDiff !== 0) return dateDiff
|
if (dateDiff !== 0) return dateDiff
|
||||||
return (a.data.order ?? 0) - (b.data.order ?? 0)
|
return (a.data.order ?? 0) - (b.data.order ?? 0)
|
||||||
});
|
});
|
||||||
|
|
||||||
const allTags = [...new Set(
|
const allTags = routeKey === "news" ? [...new Set(
|
||||||
sortedPosts
|
sortedPosts
|
||||||
.filter(p => p.data.tags && p.data.tags.length > 0)
|
.filter(p => p.data.tags && p.data.tags.length > 0)
|
||||||
.flatMap(p => p.data.tags)
|
.flatMap(p => p.data.tags)
|
||||||
.filter((tag): tag is string => tag !== undefined)
|
.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())
|
sortedPosts.map(p => new Date(p.data.date).getFullYear())
|
||||||
)].sort((a, b) => b - a);
|
)].sort((a, b) => b - a) : [];
|
||||||
---
|
---
|
||||||
|
|
||||||
<MainLayout >
|
<MainLayout >
|
||||||
|
|
@ -51,49 +41,49 @@ const allYears = [...new Set(
|
||||||
</div>
|
</div>
|
||||||
<div class="container mx-auto mt-4">
|
<div class="container mx-auto mt-4">
|
||||||
<div class="flex flex-col lg:w-1/2 items-center mx-auto py-8">
|
<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>
|
<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("news.text")}</h2>
|
<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>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
{allTags.length > 0 && (
|
{routeKey === "news" && allTags.length > 0 && (
|
||||||
<div class="container mx-auto mb-8 px-4 md:px-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">
|
<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">
|
<button class="filter-btn px-4 py-2 font-primary text-sm cursor-pointer transition-colors whitespace-nowrap" data-tag="all">
|
||||||
{tl("news.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}
|
|
||||||
</button>
|
</button>
|
||||||
))}
|
{allTags.map((tag) => (
|
||||||
</div>
|
<button class="filter-btn px-4 py-2 font-primary text-sm cursor-pointer transition-colors whitespace-nowrap" data-tag={tag!}>
|
||||||
</div>
|
{tag}
|
||||||
)}
|
</button>
|
||||||
|
|
||||||
<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>
|
|
||||||
))}
|
))}
|
||||||
</select>
|
</div>
|
||||||
</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>
|
</div>
|
||||||
)}
|
</div>
|
||||||
{
|
<FooterSection />
|
||||||
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 />
|
|
||||||
|
|
||||||
</MainLayout>
|
</MainLayout>
|
||||||
|
|
||||||
Loading…
Reference in New Issue