Fix: Refactor feeds to Server Components, fix filters/counts, and add news detail route
This commit is contained in:
parent
5e7a28878f
commit
43dd3770b9
@ -1,36 +1,43 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useEffect } from 'react';
|
||||
import { databases, APPWRITE_ALMEIRA_CONFIG, Query } from '@/lib/appwrite';
|
||||
import { APPWRITE_ALMEIRA_CONFIG } from '@/lib/appwrite';
|
||||
import { createAdminClient } from '@/lib/appwrite.server';
|
||||
import { Query } from 'node-appwrite';
|
||||
import { NewsCard } from '@/components/NewsCard';
|
||||
import { Loader2 } from 'lucide-react';
|
||||
import { Loader2, Sparkles } from 'lucide-react';
|
||||
|
||||
export default function ClasificadosPage() {
|
||||
const [posts, setPosts] = useState<any[]>([]);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
async function fetchClasificadosData() {
|
||||
try {
|
||||
const client = await createAdminClient();
|
||||
|
||||
// 1. Settings for count
|
||||
const settingsRes = await client.databases.listDocuments(APPWRITE_ALMEIRA_CONFIG.databaseId, 'settings', []);
|
||||
const postsCount = settingsRes.documents.length > 0 ? (settingsRes.documents[0].classifieds_count || 9) : 9;
|
||||
|
||||
useEffect(() => {
|
||||
const fetchLatestPosts = async () => {
|
||||
try {
|
||||
const response = await databases.listDocuments(
|
||||
APPWRITE_ALMEIRA_CONFIG.databaseId,
|
||||
APPWRITE_ALMEIRA_CONFIG.collections.posts,
|
||||
[
|
||||
Query.equal('tipo', 'clasificado'),
|
||||
Query.orderDesc('$createdAt'),
|
||||
Query.limit(6)
|
||||
]
|
||||
);
|
||||
setPosts(response.documents);
|
||||
} catch (error) {
|
||||
console.error("Error fetching posts:", error);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
// 2. Get CLASIFICADO type ID
|
||||
const typeRes = await client.databases.listDocuments(APPWRITE_ALMEIRA_CONFIG.databaseId, 'publication_types', [
|
||||
Query.equal('codigo', 'CLASIFICADO')
|
||||
]);
|
||||
|
||||
let posts: any[] = [];
|
||||
if (typeRes.documents.length > 0) {
|
||||
const typeId = typeRes.documents[0].$id;
|
||||
const postsRes = await client.databases.listDocuments(APPWRITE_ALMEIRA_CONFIG.databaseId, 'posts', [
|
||||
Query.equal('tipo_publicacion_id', typeId),
|
||||
Query.equal('estado', 'Publicado'),
|
||||
Query.orderDesc('fecha_publicacion'),
|
||||
Query.limit(postsCount)
|
||||
]);
|
||||
posts = postsRes.documents;
|
||||
}
|
||||
|
||||
fetchLatestPosts();
|
||||
}, []);
|
||||
return { posts };
|
||||
} catch (e) {
|
||||
console.error("Clasificados fetch error", e);
|
||||
return { posts: [] };
|
||||
}
|
||||
}
|
||||
|
||||
export default async function ClasificadosPage() {
|
||||
const { posts } = await fetchClasificadosData();
|
||||
|
||||
return (
|
||||
<main className="animate-fade-in relative min-h-screen">
|
||||
@ -49,13 +56,13 @@ export default function ClasificadosPage() {
|
||||
Compra, venta y servicios en tu comunidad al instante con total seguridad.
|
||||
</p>
|
||||
|
||||
<button
|
||||
onClick={() => document.getElementById('noticias')?.scrollIntoView({ behavior: 'smooth'})}
|
||||
<a
|
||||
href="#noticias"
|
||||
className="group relative inline-flex items-center justify-center gap-3 px-8 py-3 rounded-full font-bold text-white overflow-hidden transition-all hover:scale-105"
|
||||
>
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-purple-600 to-blue-600 opacity-90 group-hover:opacity-100 transition-opacity"></div>
|
||||
<span className="relative z-10 tracking-widest uppercase text-sm">Explorar Mercado</span>
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@ -70,12 +77,7 @@ export default function ClasificadosPage() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{isLoading ? (
|
||||
<div className="w-full py-40 flex flex-col items-center justify-center text-purple-500">
|
||||
<Loader2 size={50} className="animate-spin mb-6" />
|
||||
<p className="font-outfit text-xl font-bold tracking-widest uppercase animate-pulse">Sincronizando Noticias...</p>
|
||||
</div>
|
||||
) : posts.length === 0 ? (
|
||||
{posts.length === 0 ? (
|
||||
<div className="w-full py-20 flex flex-col items-center justify-center border border-white/5 rounded-3xl glass-panel relative overflow-hidden">
|
||||
<div className="p-6 rounded-full bg-slate-800/80 mb-6 border border-white/10 relative z-10">
|
||||
<Sparkles size={48} className="text-purple-500" />
|
||||
@ -85,18 +87,27 @@ export default function ClasificadosPage() {
|
||||
</div>
|
||||
) : (
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{posts.map((post) => (
|
||||
<NewsCard
|
||||
key={post.$id}
|
||||
id={post.$id}
|
||||
slug={post.slug || post.$id}
|
||||
titulo={post.titulo}
|
||||
resumen={post.resumen_redes || post.contenido?.substring(0, 100) + '...'}
|
||||
tipo={post.tipo}
|
||||
fecha={post.fecha_publicacion || post.$createdAt}
|
||||
imagenUrl={post.imagen_destacada_id || post.og_image_url}
|
||||
/>
|
||||
))}
|
||||
{posts.map((post) => {
|
||||
const PROJECT_ID = process.env.NEXT_PUBLIC_APPWRITE_PROJECT_ID || '69b84df5003bd8957c5c';
|
||||
const ENDPOINT = process.env.NEXT_PUBLIC_APPWRITE_ENDPOINT || 'https://appwrite.almeira.com.co/v1';
|
||||
let imagenUrl = null;
|
||||
if (post.imagen_destacada_id) {
|
||||
imagenUrl = `${ENDPOINT}/storage/buckets/${post.imagen_destacada_id.bucket_id}/files/${post.imagen_destacada_id.file_id}/preview?project=${PROJECT_ID}&width=800&height=500&quality=85`;
|
||||
}
|
||||
|
||||
return (
|
||||
<NewsCard
|
||||
key={post.$id}
|
||||
id={post.$id}
|
||||
slug={post.slug || post.$id}
|
||||
titulo={post.titulo}
|
||||
resumen={post.resumen_redes || post.contenido?.substring(0, 100).replace(/<[^>]*>?/gm, '') + '...'}
|
||||
tipo="Aviso"
|
||||
fecha={post.fecha_publicacion || post.$createdAt}
|
||||
imagenUrl={imagenUrl}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
|
||||
@ -1,36 +1,43 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useEffect } from 'react';
|
||||
import { databases, APPWRITE_ALMEIRA_CONFIG, Query } from '@/lib/appwrite';
|
||||
import { APPWRITE_ALMEIRA_CONFIG } from '@/lib/appwrite';
|
||||
import { createAdminClient } from '@/lib/appwrite.server';
|
||||
import { Query } from 'node-appwrite';
|
||||
import { NewsCard } from '@/components/NewsCard';
|
||||
import { Loader2 } from 'lucide-react';
|
||||
import { Loader2, Calendar } from 'lucide-react';
|
||||
|
||||
export default function EventosPage() {
|
||||
const [posts, setPosts] = useState<any[]>([]);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
async function fetchEventosData() {
|
||||
try {
|
||||
const client = await createAdminClient();
|
||||
|
||||
// 1. Settings for count
|
||||
const settingsRes = await client.databases.listDocuments(APPWRITE_ALMEIRA_CONFIG.databaseId, 'settings', []);
|
||||
const postsCount = settingsRes.documents.length > 0 ? (settingsRes.documents[0].events_count || 9) : 9;
|
||||
|
||||
useEffect(() => {
|
||||
const fetchLatestPosts = async () => {
|
||||
try {
|
||||
const response = await databases.listDocuments(
|
||||
APPWRITE_ALMEIRA_CONFIG.databaseId,
|
||||
APPWRITE_ALMEIRA_CONFIG.collections.posts,
|
||||
[
|
||||
Query.equal('tipo', 'evento'),
|
||||
Query.orderDesc('$createdAt'),
|
||||
Query.limit(6)
|
||||
]
|
||||
);
|
||||
setPosts(response.documents);
|
||||
} catch (error) {
|
||||
console.error("Error fetching posts:", error);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
// 2. Get EVENTO type ID
|
||||
const typeRes = await client.databases.listDocuments(APPWRITE_ALMEIRA_CONFIG.databaseId, 'publication_types', [
|
||||
Query.equal('codigo', 'EVENTO')
|
||||
]);
|
||||
|
||||
let posts: any[] = [];
|
||||
if (typeRes.documents.length > 0) {
|
||||
const typeId = typeRes.documents[0].$id;
|
||||
const postsRes = await client.databases.listDocuments(APPWRITE_ALMEIRA_CONFIG.databaseId, 'posts', [
|
||||
Query.equal('tipo_publicacion_id', typeId),
|
||||
Query.equal('estado', 'Publicado'),
|
||||
Query.orderDesc('fecha_publicacion'),
|
||||
Query.limit(postsCount)
|
||||
]);
|
||||
posts = postsRes.documents;
|
||||
}
|
||||
|
||||
fetchLatestPosts();
|
||||
}, []);
|
||||
return { posts };
|
||||
} catch (e) {
|
||||
console.error("Eventos fetch error", e);
|
||||
return { posts: [] };
|
||||
}
|
||||
}
|
||||
|
||||
export default async function EventosPage() {
|
||||
const { posts } = await fetchEventosData();
|
||||
|
||||
return (
|
||||
<main className="animate-fade-in relative min-h-screen">
|
||||
@ -49,13 +56,13 @@ export default function EventosPage() {
|
||||
Los mejores eventos artísticos, culturales y sociales en un solo lugar.
|
||||
</p>
|
||||
|
||||
<button
|
||||
onClick={() => document.getElementById('noticias')?.scrollIntoView({ behavior: 'smooth'})}
|
||||
<a
|
||||
href="#noticias"
|
||||
className="group relative inline-flex items-center justify-center gap-3 px-8 py-3 rounded-full font-bold text-white overflow-hidden transition-all hover:scale-105"
|
||||
>
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-cyan-500 to-blue-600 opacity-90 group-hover:opacity-100 transition-opacity"></div>
|
||||
<span className="relative z-10 tracking-widest uppercase text-sm">Explorar Agenda</span>
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@ -70,12 +77,7 @@ export default function EventosPage() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{isLoading ? (
|
||||
<div className="w-full py-40 flex flex-col items-center justify-center text-cyan-500">
|
||||
<Loader2 size={50} className="animate-spin mb-6" />
|
||||
<p className="font-outfit text-xl font-bold tracking-widest uppercase animate-pulse">Sincronizando Noticias...</p>
|
||||
</div>
|
||||
) : posts.length === 0 ? (
|
||||
{posts.length === 0 ? (
|
||||
<div className="w-full py-20 flex flex-col items-center justify-center border border-white/5 rounded-3xl glass-panel relative overflow-hidden">
|
||||
<div className="p-6 rounded-full bg-slate-800/80 mb-6 border border-white/10 relative z-10">
|
||||
<Calendar size={48} className="text-cyan-500" />
|
||||
@ -85,18 +87,27 @@ export default function EventosPage() {
|
||||
</div>
|
||||
) : (
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{posts.map((post) => (
|
||||
<NewsCard
|
||||
key={post.$id}
|
||||
id={post.$id}
|
||||
slug={post.slug || post.$id}
|
||||
titulo={post.titulo}
|
||||
resumen={post.resumen_redes || post.contenido?.substring(0, 100) + '...'}
|
||||
tipo={post.tipo}
|
||||
fecha={post.fecha_publicacion || post.$createdAt}
|
||||
imagenUrl={post.imagen_destacada_id || post.og_image_url}
|
||||
/>
|
||||
))}
|
||||
{posts.map((post) => {
|
||||
const PROJECT_ID = process.env.NEXT_PUBLIC_APPWRITE_PROJECT_ID || '69b84df5003bd8957c5c';
|
||||
const ENDPOINT = process.env.NEXT_PUBLIC_APPWRITE_ENDPOINT || 'https://appwrite.almeira.com.co/v1';
|
||||
let imagenUrl = null;
|
||||
if (post.imagen_destacada_id) {
|
||||
imagenUrl = `${ENDPOINT}/storage/buckets/${post.imagen_destacada_id.bucket_id}/files/${post.imagen_destacada_id.file_id}/preview?project=${PROJECT_ID}&width=800&height=500&quality=85`;
|
||||
}
|
||||
|
||||
return (
|
||||
<NewsCard
|
||||
key={post.$id}
|
||||
id={post.$id}
|
||||
slug={post.slug || post.$id}
|
||||
titulo={post.titulo}
|
||||
resumen={post.resumen_redes || post.contenido?.substring(0, 100).replace(/<[^>]*>?/gm, '') + '...'}
|
||||
tipo="Evento"
|
||||
fecha={post.fecha_publicacion || post.$createdAt}
|
||||
imagenUrl={imagenUrl}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
|
||||
102
src/app/noticia/[slug]/page.tsx
Normal file
102
src/app/noticia/[slug]/page.tsx
Normal file
@ -0,0 +1,102 @@
|
||||
import { APPWRITE_ALMEIRA_CONFIG } from '@/lib/appwrite';
|
||||
import { createAdminClient } from '@/lib/appwrite.server';
|
||||
import { Query } from 'node-appwrite';
|
||||
import { GlobalSidebar } from '@/components/AdWrappers';
|
||||
import { Calendar, User, ArrowLeft } from 'lucide-react';
|
||||
import Link from 'next/link';
|
||||
|
||||
async function fetchPostData(slug: string) {
|
||||
try {
|
||||
const client = await createAdminClient();
|
||||
|
||||
const res = await client.databases.listDocuments(APPWRITE_ALMEIRA_CONFIG.databaseId, 'posts', [
|
||||
Query.equal('slug', slug),
|
||||
Query.limit(1)
|
||||
]);
|
||||
|
||||
if (res.documents.length === 0) return null;
|
||||
return res.documents[0];
|
||||
} catch (e) {
|
||||
console.error("Post detail fetch error", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export default async function NoticiaDetail({ params }: { params: { slug: string } }) {
|
||||
const post = await fetchPostData(params.slug);
|
||||
|
||||
if (!post) {
|
||||
return (
|
||||
<div className="min-h-screen flex flex-col items-center justify-center bg-slate-950 text-white p-4">
|
||||
<h1 className="text-4xl font-black font-outfit mb-4">404 - SEÑAL PERDIDA</h1>
|
||||
<p className="text-slate-400 mb-8 text-center">No hemos podido localizar la transmisión solicitada en el núcleo central.</p>
|
||||
<Link href="/" className="px-6 py-3 rounded-full bg-cyan-600 font-bold hover:bg-cyan-500 transition-colors">VOLVER AL FEED</Link>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const PROJECT_ID = process.env.NEXT_PUBLIC_APPWRITE_PROJECT_ID || '69b84df5003bd8957c5c';
|
||||
const ENDPOINT = process.env.NEXT_PUBLIC_APPWRITE_ENDPOINT || 'https://appwrite.almeira.com.co/v1';
|
||||
let imagenUrl = null;
|
||||
if (post.imagen_destacada_id) {
|
||||
imagenUrl = `${ENDPOINT}/storage/buckets/${post.imagen_destacada_id.bucket_id}/files/${post.imagen_destacada_id.file_id}/preview?project=${PROJECT_ID}&width=1200&height=600&quality=90`;
|
||||
}
|
||||
|
||||
const fechaObj = new Date(post.fecha_publicacion || post.$createdAt);
|
||||
const fechaFormateada = fechaObj.toLocaleDateString('es-ES', { day: '2-digit', month: 'long', year: 'numeric' });
|
||||
|
||||
return (
|
||||
<main className="animate-fade-in relative min-h-screen pt-24 pb-20">
|
||||
<div className="container mx-auto px-4 lg:px-8">
|
||||
<div className="flex flex-col lg:flex-row gap-12">
|
||||
<article className="flex-1 max-w-4xl overflow-hidden">
|
||||
<Link href="/" className="inline-flex items-center gap-2 text-cyan-400 font-bold hover:text-cyan-300 transition-colors mb-8 group">
|
||||
<ArrowLeft size={18} className="transition-transform group-hover:-translate-x-1" />
|
||||
VOLVER A NOTICIAS
|
||||
</Link>
|
||||
|
||||
<header className="mb-10">
|
||||
<div className="flex items-center gap-4 mb-6">
|
||||
<span className="px-3 py-1 rounded-full bg-cyan-500/10 border border-cyan-500/30 text-cyan-400 text-xs font-black tracking-widest uppercase">
|
||||
{post.tipo?.toUpperCase() || 'NOTICIA'}
|
||||
</span>
|
||||
<div className="flex items-center gap-2 text-slate-500 text-sm font-medium">
|
||||
<Calendar size={16} />
|
||||
<span>{fechaFormateada}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h1 className="text-4xl md:text-6xl font-outfit font-black text-white leading-tight mb-6">
|
||||
{post.titulo}
|
||||
</h1>
|
||||
|
||||
{post.resumen_redes && (
|
||||
<p className="text-xl text-slate-400 font-medium leading-relaxed italic border-l-4 border-cyan-500 pl-6 py-2">
|
||||
{post.resumen_redes}
|
||||
</p>
|
||||
)}
|
||||
</header>
|
||||
|
||||
{imagenUrl && (
|
||||
<div className="relative aspect-[21/9] w-full rounded-3xl overflow-hidden mb-12 border border-white/10 shadow-2xl">
|
||||
<img src={imagenUrl} alt={post.titulo} className="w-full h-full object-cover" />
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-slate-950/50 to-transparent"></div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div
|
||||
className="prose prose-invert prose-cyan max-w-none text-slate-300 leading-relaxed text-lg
|
||||
prose-headings:font-outfit prose-headings:font-black prose-headings:text-white
|
||||
prose-p:mb-6 prose-strong:text-cyan-400"
|
||||
dangerouslySetInnerHTML={{ __html: post.contenido }}
|
||||
/>
|
||||
</article>
|
||||
|
||||
<aside className="w-full lg:w-[320px] xl:w-[360px] shrink-0 sticky top-24 h-fit">
|
||||
<GlobalSidebar />
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
119
src/app/page.tsx
119
src/app/page.tsx
@ -1,37 +1,45 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useEffect } from 'react';
|
||||
import { databases, APPWRITE_ALMEIRA_CONFIG, Query } from '@/lib/appwrite';
|
||||
import { APPWRITE_ALMEIRA_CONFIG } from '@/lib/appwrite';
|
||||
import { createAdminClient } from '@/lib/appwrite.server';
|
||||
import { Query } from 'node-appwrite';
|
||||
import { NewsCard } from '@/components/NewsCard';
|
||||
import { GlobalSidebar } from '@/components/AdWrappers';
|
||||
import { Loader2, RadioTower, Sparkles, ChevronDown } from 'lucide-react';
|
||||
import { RadioTower, Sparkles, ChevronDown, Loader2 } from 'lucide-react';
|
||||
import { Suspense } from 'react';
|
||||
|
||||
export default function Home() {
|
||||
const [posts, setPosts] = useState<any[]>([]);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
async function fetchHomeData() {
|
||||
try {
|
||||
const client = await createAdminClient();
|
||||
|
||||
// 1. Settings for count
|
||||
const settingsRes = await client.databases.listDocuments(APPWRITE_ALMEIRA_CONFIG.databaseId, 'settings', []);
|
||||
const postsCount = settingsRes.documents.length > 0 ? (settingsRes.documents[0].feed_count || settingsRes.documents[0].post_count || 9) : 9;
|
||||
|
||||
useEffect(() => {
|
||||
const fetchLatestPosts = async () => {
|
||||
try {
|
||||
const response = await databases.listDocuments(
|
||||
APPWRITE_ALMEIRA_CONFIG.databaseId,
|
||||
APPWRITE_ALMEIRA_CONFIG.collections.posts,
|
||||
[
|
||||
Query.equal('tipo', 'noticia'),
|
||||
Query.orderDesc('$createdAt'),
|
||||
Query.limit(6)
|
||||
]
|
||||
);
|
||||
setPosts(response.documents);
|
||||
} catch (error) {
|
||||
console.error("Error fetching posts:", error);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
// 2. Get NOTICIA type ID
|
||||
const typeRes = await client.databases.listDocuments(APPWRITE_ALMEIRA_CONFIG.databaseId, 'publication_types', [
|
||||
Query.equal('codigo', 'NOTICIA')
|
||||
]);
|
||||
|
||||
let posts: any[] = [];
|
||||
if (typeRes.documents.length > 0) {
|
||||
const typeId = typeRes.documents[0].$id;
|
||||
const postsRes = await client.databases.listDocuments(APPWRITE_ALMEIRA_CONFIG.databaseId, 'posts', [
|
||||
Query.equal('tipo_publicacion_id', typeId),
|
||||
Query.equal('estado', 'Publicado'),
|
||||
Query.orderDesc('fecha_publicacion'),
|
||||
Query.limit(postsCount)
|
||||
]);
|
||||
posts = postsRes.documents;
|
||||
}
|
||||
|
||||
fetchLatestPosts();
|
||||
}, []);
|
||||
return { posts };
|
||||
} catch (e) {
|
||||
console.error("Home fetch error", e);
|
||||
return { posts: [] };
|
||||
}
|
||||
}
|
||||
|
||||
export default async function Home() {
|
||||
const { posts } = await fetchHomeData();
|
||||
|
||||
return (
|
||||
<main className="animate-fade-in relative">
|
||||
@ -56,22 +64,22 @@ export default function Home() {
|
||||
El primer portal transaccional hiperconectado. Sumérgete en la actualidad de la región en tiempo real con una interfaz sin precedentes.
|
||||
</p>
|
||||
|
||||
<button
|
||||
onClick={() => document.getElementById('noticias')?.scrollIntoView({ behavior: 'smooth'})}
|
||||
<a
|
||||
href="#noticias"
|
||||
className="group relative inline-flex items-center justify-center gap-3 px-8 py-4 rounded-full font-bold text-white text-lg overflow-hidden transition-all hover:scale-105"
|
||||
>
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-cyan-500 to-blue-600 opacity-90 group-hover:opacity-100 transition-opacity"></div>
|
||||
<div className="absolute -inset-1 bg-cyan-400 blur-xl opacity-40 group-hover:opacity-70 transition-opacity rounded-full animate-pulse"></div>
|
||||
<Sparkles size={20} className="relative z-10" />
|
||||
<span className="relative z-10 tracking-wide">EXPLORAR CONTENIDO</span>
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{/* Scroll Indicator */}
|
||||
<div className="absolute bottom-10 left-1/2 -translate-x-1/2 flex flex-col items-center gap-2 text-slate-500 animate-bounce cursor-pointer hover:text-cyan-400 transition-colors" onClick={() => document.getElementById('noticias')?.scrollIntoView({ behavior: 'smooth'})}>
|
||||
<a href="#noticias" className="absolute bottom-10 left-1/2 -translate-x-1/2 flex flex-col items-center gap-2 text-slate-500 animate-bounce cursor-pointer hover:text-cyan-400 transition-colors">
|
||||
<span className="text-xs font-bold uppercase tracking-widest">Descubrir</span>
|
||||
<ChevronDown size={24} className="text-cyan-500" />
|
||||
</div>
|
||||
</a>
|
||||
</section>
|
||||
|
||||
{/* Feed Section */}
|
||||
@ -88,15 +96,7 @@ export default function Home() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{isLoading ? (
|
||||
<div className="w-full py-40 flex flex-col items-center justify-center text-cyan-500">
|
||||
<div className="relative">
|
||||
<div className="absolute inset-0 bg-cyan-500 blur-xl opacity-40 animate-pulse rounded-full"></div>
|
||||
<Loader2 size={60} className="relative animate-spin mb-6" />
|
||||
</div>
|
||||
<p className="font-outfit text-xl font-bold tracking-widest uppercase animate-pulse">Sincronizando Noticias...</p>
|
||||
</div>
|
||||
) : posts.length === 0 ? (
|
||||
{posts.length === 0 ? (
|
||||
<div className="w-full py-32 flex flex-col items-center justify-center border border-white/5 rounded-3xl glass-panel relative overflow-hidden">
|
||||
<div className="absolute inset-0 bg-gradient-to-b from-cyan-500/5 to-transparent"></div>
|
||||
<div className="p-6 rounded-full bg-slate-800/80 mb-6 border border-white/10 shadow-[0_0_40px_rgba(6,182,212,0.15)] relative z-10">
|
||||
@ -107,18 +107,27 @@ export default function Home() {
|
||||
</div>
|
||||
) : (
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-2 xl:grid-cols-3 gap-6">
|
||||
{posts.map((post) => (
|
||||
<NewsCard
|
||||
key={post.$id}
|
||||
id={post.$id}
|
||||
slug={post.slug || post.$id}
|
||||
titulo={post.titulo}
|
||||
resumen={post.resumen_redes || post.contenido?.substring(0, 100) + '...'}
|
||||
tipo={post.tipo}
|
||||
fecha={post.fecha_publicacion || post.$createdAt}
|
||||
imagenUrl={post.imagen_destacada_id || post.og_image_url}
|
||||
/>
|
||||
))}
|
||||
{posts.map((post) => {
|
||||
const PROJECT_ID = process.env.NEXT_PUBLIC_APPWRITE_PROJECT_ID || '69b84df5003bd8957c5c';
|
||||
const ENDPOINT = process.env.NEXT_PUBLIC_APPWRITE_ENDPOINT || 'https://appwrite.almeira.com.co/v1';
|
||||
let imagenUrl = null;
|
||||
if (post.imagen_destacada_id) {
|
||||
imagenUrl = `${ENDPOINT}/storage/buckets/${post.imagen_destacada_id.bucket_id}/files/${post.imagen_destacada_id.file_id}/preview?project=${PROJECT_ID}&width=800&height=500&quality=85`;
|
||||
}
|
||||
|
||||
return (
|
||||
<NewsCard
|
||||
key={post.$id}
|
||||
id={post.$id}
|
||||
slug={post.slug || post.$id}
|
||||
titulo={post.titulo}
|
||||
resumen={post.resumen_redes || post.contenido?.substring(0, 100).replace(/<[^>]*>?/gm, '') + '...'}
|
||||
tipo="Noticia"
|
||||
fecha={post.fecha_publicacion || post.$createdAt}
|
||||
imagenUrl={imagenUrl}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user