feat: implement NewsList component and configure TinaCMS news and documentary collections

This commit is contained in:
Esteban 2026-05-05 00:21:23 -05:00
parent 70d963770e
commit 7598808f60
12 changed files with 100 additions and 54 deletions

View File

@ -1,85 +1,111 @@
---
import { Image } from "astro:assets"
import { Image } from "astro:assets";
import { Icon } from "astro-icon/components";
import "dayjs/locale/es";
import dayjs from "dayjs";
import utc from "dayjs/plugin/utc";
const regionNames = new Intl.DisplayNames(['es'], { type: 'region' });
const regionNames = new Intl.DisplayNames(["es"], { type: "region" });
const locale = Astro.currentLocale;
import { createTranslator } from '@/i18n';
import { createTranslator } from "@/i18n";
const tl = createTranslator(Astro.currentLocale);
dayjs.extend(utc);
dayjs.locale(locale);
const { data, content } = Astro.props;
const nicedate = dayjs.utc(data.data.date).format("D MMMM YYYY");
const countryName = data?.data?.country ? regionNames.of(data.data.country) : "";
const countryName = data?.data?.country
? regionNames.of(data.data.country)
: "";
const locationArray = [data.data.city,countryName]
const location = locationArray.filter(Boolean).join(', ');
const locationArray = [data.data.city, countryName];
const location = locationArray.filter(Boolean).join(", ");
const newsUrl = `/${locale}/news/${data.id}`;
const rawContent = content?.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, '')
.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, 40);
const excerpt = words.join(' ') + (words.length === 40 ? '...' : '');
const words = plainText
.split(/\s+/)
.filter((w) => w.length > 0)
.slice(0, 40);
const excerpt = words.join(" ") + (words.length === 40 ? "..." : "");
---
<a href={newsUrl} class="block group">
<article class="flex flex-col md:flex-row gap-6 md:gap-8 p-4 md:p-6 border-b border-tertiary/20 hover:bg-tertiary/5 transition-colors">
<article
class="flex flex-col md:flex-row gap-6 md:gap-8 p-4 md:p-6 border-b border-tertiary/20 hover:bg-tertiary/5 transition-colors"
>
<div class="md:w-1/3 flex-shrink-0 overflow-hidden">
<Image
src={data.data.thumbnail}
src={data.data.thumbnail_square || data.data.thumbnail}
alt={data.data.title}
width={400}
height={300}
width={480}
height={340}
class="w-full h-48 md:h-full object-cover transform group-hover:scale-105 transition-transform duration-300"
/>
</div>
<div class="md:w-2/3 flex flex-col">
<div class="flex items-center gap-2 mb-2">
<span class="font-normal font-primary text-sm text-tertiary/70">{nicedate}</span>
{location && (
<span class="font-normal font-primary text-sm text-tertiary/70"
>{nicedate}
</span>
{
location && (
<>
<span class="text-tertiary/40">|</span>
<span class="font-normal font-primary text-sm text-tertiary/70">{location}</span>
<span class="font-normal font-primary text-sm text-tertiary/70">
{location}
</span>
</>
)}
)
}
</div>
<h3 class="text-xl md:text-2xl font-bold font-secondary text-tertiary group-hover:text-tertiary/80 transition-colors mb-2">
<h3
class="text-xl md:text-2xl font-bold font-secondary text-tertiary group-hover:text-tertiary/80 transition-colors mb-2"
>
{data.data.title}
</h3>
<p class="font-primary text-base text-tertiary/80 mb-4 line-clamp-3">
<p
class="font-primary text-base text-tertiary/80 mb-4 line-clamp-3"
>
{excerpt}
</p>
{data.data.tags && data.data.tags.length > 0 && (
{
data.data.tags && data.data.tags.length > 0 && (
<div class="flex flex-nowrap md:flex-wrap gap-2 overflow-x-auto md:overflow-visible pb-2 md:pb-0 mb-4">
{data.data.tags.map((tag: string) => (
<span class="badge rounded-none bg-[#EBE6D2] border-none text-[#003421] whitespace-nowrap">{tag}</span>
<span class="badge rounded-none bg-[#EBE6D2] border-none text-[#003421] whitespace-nowrap">
{tag}
</span>
))}
</div>
)}
)
}
<div class="mt-auto">
<span class="inline-flex items-center gap-1 text-sm font-primary text-tertiary font-semibold group-hover:underline">
<span
class="inline-flex items-center gap-1 text-sm font-primary text-tertiary font-semibold group-hover:underline"
>
{tl("news.seemore")}
<Icon name="ph:arrow-right" class="transform group-hover:translate-x-1 transition-transform" />
<Icon
name="ph:arrow-right"
class="transform group-hover:translate-x-1 transition-transform"
/>
</span>
</div>
</div>

View File

@ -15,6 +15,7 @@ const news = defineCollection({
state: z.string().optional(),
country: z.string().optional(),
thumbnail: image().optional().describe("Main news thumbnail image"),
thumbnail_square: image().optional().describe("Main news thumbnail square image"),
youtube: z.string().optional(),
tags: z.array(z.string()).optional().describe("News tags"),
gallery: z.array(z.object({

View File

@ -5,7 +5,8 @@ date: 2025-01-04
slug: 2025-01-04-jerusalen-en-el-centro-del-2025-advertencias-sobre-un-ano-clave-para-el-cambio-global
place: ''
country: 'PR'
city: 'Puerto Rico'
city: 'Cayey '
thumbnail_square: 'https://ik.imagekit.io/crpy/7K0B4402-1.webp?tr=w-1280,q-auto,f-auto'
thumbnail: 'https://ik.imagekit.io/crpy/7K0B4402.webp?tr=w-1280,q-auto,f-auto'
tags: [Israel, Puerto Rico]
gallery: [

View File

@ -5,7 +5,7 @@ date: 2025-02-16
slug: 2025-02-16-llamado-a-la-accion-internacional-el-dr-jose-benjamin-perez-matos-exige-respuestas-concretas-ante-las-crisis-en-israel-y-venezuela
place: ''
country: 'PR'
city: 'Puerto Rico'
city: 'Cayey'
thumbnail: 'https://ik.imagekit.io/crpy/tr:w-1280/comunicado-1.webp'
tags: [Puerto Rico, Venezuela, Israel]
gallery: [

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
{"version":{"fullVersion":"2.2.5","major":"2","minor":"2","patch":"5"},"meta":{"flags":["experimentalData"]},"collections":[{"name":"news","label":"News","path":"src/content/news","format":"md","fields":[{"type":"string","name":"locale","label":"Language","options":[{"label":"Español","value":"es"},{"label":"English","value":"en"},{"label":"Português","value":"pt"},{"label":"Kinyarwanda","value":"rw"}],"namespace":["news","locale"],"searchable":true,"uid":false},{"type":"string","name":"title","label":"Title","isTitle":true,"required":true,"namespace":["news","title"],"searchable":true,"uid":false},{"type":"datetime","name":"date","label":"Date","required":true,"namespace":["news","date"],"searchable":true,"uid":false},{"type":"string","name":"slug","label":"Slug","namespace":["news","slug"],"searchable":true,"uid":false},{"type":"string","name":"place","label":"Place","namespace":["news","place"],"searchable":true,"uid":false},{"type":"string","name":"thumbnail","label":"Thumbnail URL","namespace":["news","thumbnail"],"searchable":true,"uid":false},{"type":"string","name":"youtube","label":"YouTube ID","namespace":["news","youtube"],"searchable":true,"uid":false},{"type":"string","name":"tags","label":"Tags (comma separated)","namespace":["news","tags"],"searchable":true,"uid":false},{"type":"boolean","name":"draft","label":"Draft","namespace":["news","draft"],"searchable":true,"uid":false},{"type":"rich-text","name":"body","label":"Content","isBody":true,"namespace":["news","body"],"searchable":true,"parser":{"type":"markdown"},"uid":false}],"namespace":["news"]},{"name":"documentaries","label":"Documentaries","path":"src/content/documentaries","format":"md","fields":[{"type":"string","name":"locale","label":"Language","options":[{"label":"Español","value":"es"},{"label":"English","value":"en"},{"label":"Português","value":"pt"},{"label":"Kinyarwanda","value":"rw"}],"namespace":["documentaries","locale"],"searchable":true,"uid":false},{"type":"string","name":"title","label":"Title","isTitle":true,"required":true,"namespace":["documentaries","title"],"searchable":true,"uid":false},{"type":"string","name":"video_yt","label":"YouTube ID","namespace":["documentaries","video_yt"],"searchable":true,"uid":false},{"type":"datetime","name":"date","label":"Date","required":true,"namespace":["documentaries","date"],"searchable":true,"uid":false}],"namespace":["documentaries"]}],"config":{"media":{"tina":{"publicFolder":"public","mediaRoot":"public/images"}}}}
{"version":{"fullVersion":"2.2.5","major":"2","minor":"2","patch":"5"},"meta":{"flags":["experimentalData"]},"collections":[{"name":"news","label":"News","path":"src/content/news","format":"md","fields":[{"type":"string","name":"locale","label":"Language","options":[{"label":"Español","value":"es"},{"label":"English","value":"en"},{"label":"Português","value":"pt"},{"label":"Kinyarwanda","value":"rw"}],"namespace":["news","locale"],"searchable":true,"uid":false},{"type":"string","name":"title","label":"Title","isTitle":true,"required":true,"namespace":["news","title"],"searchable":true,"uid":false},{"type":"datetime","name":"date","label":"Date","required":true,"namespace":["news","date"],"searchable":true,"uid":false},{"type":"string","name":"slug","label":"Slug","namespace":["news","slug"],"searchable":true,"uid":false},{"type":"string","name":"place","label":"Place","namespace":["news","place"],"searchable":true,"uid":false},{"type":"string","name":"thumbnail","label":"Thumbnail URL","namespace":["news","thumbnail"],"searchable":true,"uid":false},{"type":"string","name":"thumbnail_square","label":"Thumbnail Square URL","namespace":["news","thumbnail_square"],"searchable":true,"uid":false},{"type":"string","name":"youtube","label":"YouTube ID","namespace":["news","youtube"],"searchable":true,"uid":false},{"type":"string","name":"tags","label":"Tags (comma separated)","namespace":["news","tags"],"searchable":true,"uid":false},{"type":"boolean","name":"draft","label":"Draft","namespace":["news","draft"],"searchable":true,"uid":false},{"type":"rich-text","name":"body","label":"Content","isBody":true,"namespace":["news","body"],"searchable":true,"parser":{"type":"markdown"},"uid":false}],"namespace":["news"]},{"name":"documentaries","label":"Documentaries","path":"src/content/documentaries","format":"md","fields":[{"type":"string","name":"locale","label":"Language","options":[{"label":"Español","value":"es"},{"label":"English","value":"en"},{"label":"Português","value":"pt"},{"label":"Kinyarwanda","value":"rw"}],"namespace":["documentaries","locale"],"searchable":true,"uid":false},{"type":"string","name":"title","label":"Title","isTitle":true,"required":true,"namespace":["documentaries","title"],"searchable":true,"uid":false},{"type":"string","name":"video_yt","label":"YouTube ID","namespace":["documentaries","video_yt"],"searchable":true,"uid":false},{"type":"datetime","name":"date","label":"Date","required":true,"namespace":["documentaries","date"],"searchable":true,"uid":false}],"namespace":["documentaries"]}],"config":{"media":{"tina":{"publicFolder":"public","mediaRoot":"public/images"}}}}

View File

@ -62,6 +62,11 @@ var config_default = defineConfig({
name: "thumbnail",
label: "Thumbnail URL"
},
{
type: "string",
name: "thumbnail_square",
label: "Thumbnail Square URL"
},
{
type: "string",
name: "youtube",

View File

@ -6,6 +6,7 @@ fragment NewsParts on News {
slug
place
thumbnail
thumbnail_square
youtube
tags
draft

View File

@ -97,6 +97,7 @@ type News implements Node & Document {
slug: String
place: String
thumbnail: String
thumbnail_square: String
youtube: String
tags: String
draft: Boolean
@ -139,6 +140,7 @@ input NewsFilter {
slug: StringFilter
place: StringFilter
thumbnail: StringFilter
thumbnail_square: StringFilter
youtube: StringFilter
tags: StringFilter
draft: BooleanFilter
@ -214,6 +216,7 @@ input NewsMutation {
slug: String
place: String
thumbnail: String
thumbnail_square: String
youtube: String
tags: String
draft: Boolean

View File

@ -191,6 +191,7 @@ export type News = Node & Document & {
slug?: Maybe<Scalars['String']['output']>;
place?: Maybe<Scalars['String']['output']>;
thumbnail?: Maybe<Scalars['String']['output']>;
thumbnail_square?: Maybe<Scalars['String']['output']>;
youtube?: Maybe<Scalars['String']['output']>;
tags?: Maybe<Scalars['String']['output']>;
draft?: Maybe<Scalars['Boolean']['output']>;
@ -233,6 +234,7 @@ export type NewsFilter = {
slug?: InputMaybe<StringFilter>;
place?: InputMaybe<StringFilter>;
thumbnail?: InputMaybe<StringFilter>;
thumbnail_square?: InputMaybe<StringFilter>;
youtube?: InputMaybe<StringFilter>;
tags?: InputMaybe<StringFilter>;
draft?: InputMaybe<BooleanFilter>;
@ -371,6 +373,7 @@ export type NewsMutation = {
slug?: InputMaybe<Scalars['String']['input']>;
place?: InputMaybe<Scalars['String']['input']>;
thumbnail?: InputMaybe<Scalars['String']['input']>;
thumbnail_square?: InputMaybe<Scalars['String']['input']>;
youtube?: InputMaybe<Scalars['String']['input']>;
tags?: InputMaybe<Scalars['String']['input']>;
draft?: InputMaybe<Scalars['Boolean']['input']>;
@ -384,7 +387,7 @@ export type DocumentariesMutation = {
date?: InputMaybe<Scalars['String']['input']>;
};
export type NewsPartsFragment = { __typename: 'News', locale?: string | null, title: string, date: string, slug?: string | null, place?: string | null, thumbnail?: string | null, youtube?: string | null, tags?: string | null, draft?: boolean | null, body?: any | null };
export type NewsPartsFragment = { __typename: 'News', locale?: string | null, title: string, date: string, slug?: string | null, place?: string | null, thumbnail?: string | null, thumbnail_square?: string | null, youtube?: string | null, tags?: string | null, draft?: boolean | null, body?: any | null };
export type DocumentariesPartsFragment = { __typename: 'Documentaries', locale?: string | null, title: string, video_yt?: string | null, date: string };
@ -393,7 +396,7 @@ export type NewsQueryVariables = Exact<{
}>;
export type NewsQuery = { __typename?: 'Query', news: { __typename: 'News', id: string, locale?: string | null, title: string, date: string, slug?: string | null, place?: string | null, thumbnail?: string | null, youtube?: string | null, tags?: string | null, draft?: boolean | null, body?: any | null, _sys: { __typename?: 'SystemInfo', filename: string, basename: string, hasReferences?: boolean | null, breadcrumbs: Array<string>, path: string, relativePath: string, extension: string } } };
export type NewsQuery = { __typename?: 'Query', news: { __typename: 'News', id: string, locale?: string | null, title: string, date: string, slug?: string | null, place?: string | null, thumbnail?: string | null, thumbnail_square?: string | null, youtube?: string | null, tags?: string | null, draft?: boolean | null, body?: any | null, _sys: { __typename?: 'SystemInfo', filename: string, basename: string, hasReferences?: boolean | null, breadcrumbs: Array<string>, path: string, relativePath: string, extension: string } } };
export type NewsConnectionQueryVariables = Exact<{
before?: InputMaybe<Scalars['String']['input']>;
@ -405,7 +408,7 @@ export type NewsConnectionQueryVariables = Exact<{
}>;
export type NewsConnectionQuery = { __typename?: 'Query', newsConnection: { __typename?: 'NewsConnection', totalCount: number, pageInfo: { __typename?: 'PageInfo', hasPreviousPage: boolean, hasNextPage: boolean, startCursor: string, endCursor: string }, edges?: Array<{ __typename?: 'NewsConnectionEdges', cursor: string, node?: { __typename: 'News', id: string, locale?: string | null, title: string, date: string, slug?: string | null, place?: string | null, thumbnail?: string | null, youtube?: string | null, tags?: string | null, draft?: boolean | null, body?: any | null, _sys: { __typename?: 'SystemInfo', filename: string, basename: string, hasReferences?: boolean | null, breadcrumbs: Array<string>, path: string, relativePath: string, extension: string } } | null } | null> | null } };
export type NewsConnectionQuery = { __typename?: 'Query', newsConnection: { __typename?: 'NewsConnection', totalCount: number, pageInfo: { __typename?: 'PageInfo', hasPreviousPage: boolean, hasNextPage: boolean, startCursor: string, endCursor: string }, edges?: Array<{ __typename?: 'NewsConnectionEdges', cursor: string, node?: { __typename: 'News', id: string, locale?: string | null, title: string, date: string, slug?: string | null, place?: string | null, thumbnail?: string | null, thumbnail_square?: string | null, youtube?: string | null, tags?: string | null, draft?: boolean | null, body?: any | null, _sys: { __typename?: 'SystemInfo', filename: string, basename: string, hasReferences?: boolean | null, breadcrumbs: Array<string>, path: string, relativePath: string, extension: string } } | null } | null> | null } };
export type DocumentariesQueryVariables = Exact<{
relativePath: Scalars['String']['input'];
@ -435,6 +438,7 @@ export const NewsPartsFragmentDoc = gql`
slug
place
thumbnail
thumbnail_square
youtube
tags
draft

View File

@ -63,6 +63,11 @@ export default defineConfig({
name: "thumbnail",
label: "Thumbnail URL",
},
{
type: "string",
name: "thumbnail_square",
label: "Thumbnail Square URL",
},
{
type: "string",
name: "youtube",

File diff suppressed because one or more lines are too long