Documentation Index
Fetch the complete documentation index at: https://mintlify.com/No-Country-simulation/S02-26-Equipo-33-Web-App/llms.txt
Use this file to discover all available pages before exploring further.
Component Architecture
The Horse Trust frontend uses a modular component architecture organized by feature and function. Components are located in the /component directory and are built with React, TypeScript, and Tailwind CSS.
Component Directory Structure
component/
├── GetServerData/ # Server data fetching components
│ ├── index.tsx
│ └── RenderData.tsx
├── home/ # Homepage components
│ ├── FeaturedHorses.tsx
│ ├── HeroSection.tsx
│ └── SecuritySection.tsx
├── layout/ # Layout components
│ ├── Header.tsx
│ └── Footer.tsx
└── marketplace/ # Marketplace components
├── FilterSideBar.tsx
├── HorseCard.tsx
├── HorseDetailClient.tsx
└── MarketplaceClient.tsx
Layout Components
The main navigation header with authentication state management.
Location: component/layout/Header.tsx
Type: Client Component ("use client")
Interface
interface HeaderProps {
initialIsLoggedIn: boolean;
}
Key Features
- Responsive navigation with mobile menu
- User authentication state display
- Dynamic navigation links based on login status
- User dropdown menu with avatar
- Sticky positioning with backdrop blur
Usage Example
import Header from '@/component/layout/Header';
import { cookies } from 'next/headers';
export default async function RootLayout({ children }) {
const cookieStore = await cookies();
const isLoggedIn = cookieStore.has('horse_trust_token');
return (
<html>
<body>
<Header initialIsLoggedIn={isLoggedIn} />
{children}
</body>
</html>
);
}
Code Snippet
component/layout/Header.tsx
export default function Header({ initialIsLoggedIn }: { initialIsLoggedIn: boolean }) {
const pathname = usePathname();
const [isMenuOpen, setIsMenuOpen] = useState(false);
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
const [isLoggedIn, setIsLoggedIn] = useState(initialIsLoggedIn);
const [userName, setUserName] = useState("Usuario");
const navLinks = isLoggedIn
? [
{ name: 'Mi Panel', href: '/dashboard' },
{ name: 'Mis Publicaciones', href: '/mis-publicaciones' },
{ name: 'Catálogo', href: '/marketplace' },
{ name: 'Mensajes', href: '/chat' },
]
: [
{ name: 'Catálogo', href: '/marketplace' },
{ name: 'Cómo Funciona', href: '/como-funciona' },
{ name: 'Haras Top', href: '/haras-top' },
];
return (
<header className="sticky top-0 z-50 w-full bg-white/95 backdrop-blur">
{/* Header content */}
</header>
);
}
Site-wide footer with navigation links and branding.
Location: component/layout/Footer.tsx
Type: Server Component
Features
- Multi-column link organization
- Social media links
- Copyright information
- Responsive grid layout
Code Example
component/layout/Footer.tsx
export default function Footer() {
return (
<footer className="bg-white border-t border-slate-200 pt-24 pb-12">
<div className="max-w-7xl mx-auto px-4 grid grid-cols-2 md:grid-cols-4 lg:grid-cols-6 gap-12">
<div className="col-span-2">
<Link href="/" className="flex items-center gap-3">
<Image src="/img/logo-icon.png" alt="HorseTrust" width={48} height={48} />
<span className="text-2xl font-semibold">HorseTrust</span>
</Link>
</div>
{/* Footer columns */}
</div>
</footer>
);
}
Home Components
HeroSection
The landing page hero with search functionality.
Location: component/home/HeroSection.tsx
Type: Server Component
Features
- Full-width hero with background image
- Search inputs for breed and discipline
- Gradient overlay for text readability
- Call-to-action button
component/home/HeroSection.tsx
export default function HeroSection() {
return (
<section className="relative h-[600px] flex items-center justify-center overflow-hidden">
<div className="absolute inset-0">
<Image
src="/img/hero-bg.webp"
alt="Fondo ecuestre premium"
fill
priority
className="object-cover object-center"
/>
</div>
<div className="absolute inset-0 hero-gradient" />
<div className="relative z-10 max-w-4xl mx-auto px-4 text-center">
<h1 className="text-5xl md:text-7xl font-serif italic text-white leading-tight mb-6">
Confianza <span className="text-equestrian-gold">Inquebrantable</span>
</h1>
{/* Search form */}
</div>
</section>
);
}
FeaturedHorses
Displays featured horses fetched from the API.
Location: component/home/FeaturedHorses.tsx
Type: Async Server Component
Features
- Server-side data fetching
- Error handling for API failures
- Responsive grid layout
- Verified badge display
component/home/FeaturedHorses.tsx
async function getHorses() {
try {
const res = await fetch('https://s02-26-e33-horse-trust-api.vercel.app/api/horses', {
next: { revalidate: 60 }
});
if (!res.ok) throw new Error('Error al traer los caballos');
const jsonResponse = await res.json();
const horsesArray = Array.isArray(jsonResponse) ? jsonResponse : jsonResponse.data;
return horsesArray.slice(0, 3);
} catch (error) {
console.error("Error:", error);
return [];
}
}
export default async function FeaturedHorses() {
const realHorses = await getHorses();
return (
<section className="py-24 max-w-7xl mx-auto px-4">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-10">
{realHorses.map((horse: any) => (
<div key={horse.ID} className="luxury-card-hover group">
{/* Horse card content */}
</div>
))}
</div>
</section>
);
}
SecuritySection
Highlights platform security features.
Location: component/home/SecuritySection.tsx
Type: Server Component
Features
- Two-column layout with image and text
- Icon-based feature highlights
- Statistics display
- Call-to-action section
component/home/SecuritySection.tsx
export default function SecuritySection() {
return (
<>
<section className="bg-equestrian-navy py-32 relative overflow-hidden">
<div className="max-w-7xl mx-auto px-4 grid lg:grid-cols-2 gap-20 items-center">
<div>
<h3 className="text-4xl md:text-5xl font-serif text-white mb-8">
El Motor de Verificación de HorseTrust
</h3>
<div className="grid sm:grid-cols-2 gap-8">
<div className="space-y-4">
<div className="w-12 h-12 rounded-full border flex items-center justify-center">
<ShieldCheck className="w-6 h-6" />
</div>
<h4 className="text-white font-bold">Auditoría de Propiedad</h4>
<p className="text-slate-500 text-sm">Títulos legales verificados</p>
</div>
</div>
</div>
<div className="relative group">
<Image src="/img/caballo-security-section.webp" width={800} height={1000} />
</div>
</div>
</section>
</>
);
}
Marketplace Components
HorseCard
Individual horse listing card.
Location: component/marketplace/HorseCard.tsx
Type: Server Component
Interface
interface HorseProps {
horse: {
ID: number;
NAME: string;
AGE: number;
BREED: string;
DISCIPLINE: string;
LOCATION: string;
PRICE: number;
SELLER_VERIFIED: string;
MAIN_PHOTO: string;
};
}
Full Implementation
component/marketplace/HorseCard.tsx
export default function HorseCard({ horse }: HorseProps) {
const formattedPrice = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
maximumFractionDigits: 0,
}).format(horse.PRICE);
return (
<div className="bg-white rounded-xl overflow-hidden shadow-sm hover:shadow-md transition-shadow">
<div className="relative aspect-[4/3] overflow-hidden">
<img
src={horse.MAIN_PHOTO}
alt={horse.NAME}
className="h-full w-full object-cover group-hover:scale-105 transition-transform"
/>
{horse.SELLER_VERIFIED === 'verified' && (
<div className="absolute top-3 left-3 bg-white/90 backdrop-blur-sm px-2 py-1 rounded">
<ShieldCheck className="text-equestrian-navy w-4 h-4" />
<span className="text-[10px] font-bold">Verificado</span>
</div>
)}
</div>
<div className="p-5">
<div className="flex justify-between items-start mb-2">
<h3 className="text-lg font-bold">{horse.NAME}</h3>
<span className="text-lg font-black">{formattedPrice}</span>
</div>
<div className="flex flex-wrap gap-2 mb-4">
<span className="text-[11px] font-bold bg-slate-100 px-2 py-1 rounded-full">
{horse.BREED}
</span>
<span className="text-[11px] font-bold bg-slate-100 px-2 py-1 rounded-full">
{horse.AGE} años
</span>
</div>
<Link
href={`/marketplace/${horse.ID}`}
className="w-full block text-center bg-equestrian-navy text-white font-bold py-2 px-4 rounded"
>
Ver Detalles
</Link>
</div>
</div>
);
}
Sidebar with filtering options for marketplace.
Location: component/marketplace/FilterSideBar.tsx
Type: Client Component
Interface
interface FilterProps {
filters: {
minPrice: string;
maxPrice: string;
breed: string;
disciplines: string[];
location: string;
verifiedOnly: boolean;
};
setFilters: React.Dispatch<React.SetStateAction<any>>;
clearFilters: () => void;
}
Implementation
component/marketplace/FilterSideBar.tsx
"use client";
export default function FilterSideBar({ filters, setFilters, clearFilters }: FilterProps) {
const handleDisciplineToggle = (disc: string) => {
setFilters((prev: any) => {
const current = prev.disciplines;
const updated = current.includes(disc)
? current.filter((d: string) => d !== disc)
: [...current, disc];
return { ...prev, disciplines: updated };
});
};
return (
<aside className="hidden lg:block w-1/4 space-y-8">
<div className="sticky top-24">
<h3 className="text-lg font-bold">Filtros</h3>
{/* Price Range */}
<div className="space-y-4">
<label className="text-sm font-semibold">Inversión (USD)</label>
<div className="flex items-center gap-3">
<input
type="number"
placeholder="Mínimo"
value={filters.minPrice}
onChange={(e) => setFilters({ ...filters, minPrice: e.target.value })}
className="w-full rounded-lg border"
/>
<input
type="number"
placeholder="Máximo"
value={filters.maxPrice}
onChange={(e) => setFilters({ ...filters, maxPrice: e.target.value })}
className="w-full rounded-lg border"
/>
</div>
</div>
{/* Breed Selection */}
<div>
<select
value={filters.breed}
onChange={(e) => setFilters({ ...filters, breed: e.target.value })}
>
<option>Todas las Razas</option>
<option>Pura Sangre</option>
<option>Árabe</option>
</select>
</div>
{/* Discipline Checkboxes */}
<div>
{['Salto', 'Adiestramiento', 'Polo', 'Carreras'].map((disc) => (
<label key={disc}>
<input
type="checkbox"
checked={filters.disciplines.includes(disc)}
onChange={() => handleDisciplineToggle(disc)}
/>
{disc}
</label>
))}
</div>
</div>
</aside>
);
}
Component Best Practices
Server vs Client Components
- Use Server Components by default for better performance
- Add
"use client" only when needed (interactivity, state, effects)
- Server Components can fetch data directly
TypeScript Interfaces
Define clear interfaces for all component props:
interface ComponentProps {
data: DataType;
onAction?: () => void;
}
Styling Conventions
- Use Tailwind utility classes
- Leverage custom color variables from theme
- Apply responsive design with Tailwind breakpoints
Icon Usage
Import icons from lucide-react:
import { ShieldCheck, MapPin, Award } from 'lucide-react';
Reusability Guidelines
- Extract common UI patterns into reusable components
- Use composition over inheritance
- Keep components focused on a single responsibility
- Pass data through props, not global state
- Co-locate styles with components using Tailwind