feat(F-048): completed feature
This commit is contained in:
6895
project/frontend/package-lock.json
generated
Normal file
6895
project/frontend/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -4,12 +4,12 @@ import { fetchPage } from '@/lib/api';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Quiénes somos',
|
||||
description: 'Conoce la historia, misión y valores de MercadoDeVida. Productos naturales y orgánicos de confianza.',
|
||||
description: 'Conoce la historia, misión y valores de mercadodevida. Productos naturales y orgánicos de confianza.',
|
||||
};
|
||||
|
||||
const FALLBACK_HTML = `
|
||||
<h2>Nuestra historia</h2>
|
||||
<p>MercadoDeVida nació de la convicción de que comer bien no debería ser complicado. Seleccionamos cuidadosamente cada producto para asegurar la máxima calidad y transparencia.</p>
|
||||
<p>mercadodevida nació de la convicción de que comer bien no debería ser complicado. Seleccionamos cuidadosamente cada producto para asegurar la máxima calidad y transparencia.</p>
|
||||
<h2>Nuestra misión</h2>
|
||||
<p>Facilitar el acceso a productos naturales y orgánicos de alta calidad, directamente desde productores certificados, sin intermediarios.</p>
|
||||
<h2>Valores</h2>
|
||||
@@ -31,7 +31,7 @@ export default async function AboutPage() {
|
||||
return (
|
||||
<ContentPage
|
||||
title={cms?.title ?? 'Quiénes somos'}
|
||||
description="Conoce la historia, misión y valores de MercadoDeVida. Productos naturales y orgánicos de confianza."
|
||||
description="Conoce la historia, misión y valores de mercadodevida. Productos naturales y orgánicos de confianza."
|
||||
>
|
||||
<div dangerouslySetInnerHTML={{ __html: body }} />
|
||||
</ContentPage>
|
||||
|
||||
@@ -211,7 +211,10 @@ export default function AccountPage() {
|
||||
}, [user]);
|
||||
|
||||
useEffect(() => {
|
||||
load();
|
||||
const timer = window.setTimeout(() => {
|
||||
void load();
|
||||
}, 0);
|
||||
return () => window.clearTimeout(timer);
|
||||
}, [load]);
|
||||
|
||||
const handleDelete = async (id: string) => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { Metadata } from 'next';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Admin Pedidos — MercadoDeVida',
|
||||
title: 'Admin Pedidos — mercadodevida',
|
||||
};
|
||||
|
||||
export default function AdminOrdersPage() {
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { Metadata } from 'next';
|
||||
import Link from 'next/link';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Admin — MercadoDeVida',
|
||||
title: 'Admin — mercadodevida',
|
||||
};
|
||||
|
||||
export default function AdminDashboard() {
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { Metadata } from 'next';
|
||||
import { fetchProducts } from '@/lib/api';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Admin Productos — MercadoDeVida',
|
||||
title: 'Admin Productos — mercadodevida',
|
||||
};
|
||||
|
||||
export default async function AdminProductsPage() {
|
||||
|
||||
@@ -13,7 +13,7 @@ export async function generateMetadata({ params }: Props): Promise<Metadata> {
|
||||
if (!brand) return { title: 'Marca no encontrada' };
|
||||
return {
|
||||
title: brand.seoTitle ?? brand.name,
|
||||
description: brand.seoDescription ?? `Productos ${brand.name} en MercadoDeVida.`,
|
||||
description: brand.seoDescription ?? `Productos ${brand.name} en mercadodevida.`,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { Metadata } from 'next';
|
||||
import { fetchBrands } from '@/lib/api';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Marcas — MercadoDeVida',
|
||||
title: 'Marcas — mercadodevida',
|
||||
description: 'Todas las marcas de productos naturales y ecológicos.',
|
||||
};
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { Metadata } from 'next';
|
||||
import CartPageContent from '@/components/cart/CartPageContent';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Carrito — MercadoDeVida',
|
||||
title: 'Carrito — mercadodevida',
|
||||
};
|
||||
|
||||
export default function CartPage() {
|
||||
|
||||
@@ -13,7 +13,7 @@ export async function generateMetadata({ params }: Props): Promise<Metadata> {
|
||||
if (!category) return { title: 'Categoría no encontrada' };
|
||||
return {
|
||||
title: category.seoTitle ?? category.name,
|
||||
description: category.seoDescription ?? `${category.name} — Productos naturales y orgánicos en MercadoDeVida.`,
|
||||
description: category.seoDescription ?? `${category.name} — Productos naturales y orgánicos en mercadodevida.`,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { Metadata } from 'next';
|
||||
import { fetchCategories } from '@/lib/api';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Categorías — MercadoDeVida',
|
||||
title: 'Categorías — mercadodevida',
|
||||
description: 'Explora todas las categorías de productos naturales y orgánicos.',
|
||||
};
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { Metadata } from 'next';
|
||||
import CheckoutClient from '@/components/checkout/CheckoutClient';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Checkout — MercadoDeVida',
|
||||
title: 'Checkout — mercadodevida',
|
||||
};
|
||||
|
||||
export default function CheckoutPage() {
|
||||
|
||||
@@ -4,7 +4,7 @@ import { fetchPage } from '@/lib/api';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Contacto',
|
||||
description: 'Ponte en contacto con el equipo de MercadoDeVida. Resolvemos tus dudas sobre productos, pedidos y envíos.',
|
||||
description: 'Ponte en contacto con el equipo de mercadodevida. Resolvemos tus dudas sobre productos, pedidos y envíos.',
|
||||
};
|
||||
|
||||
const FALLBACK_HTML = `
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { Metadata } from 'next';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Política de cookies',
|
||||
description: 'Información sobre el uso de cookies en MercadoDeVida.',
|
||||
description: 'Información sobre el uso de cookies en mercadodevida.',
|
||||
robots: { index: false, follow: false },
|
||||
};
|
||||
|
||||
|
||||
@@ -13,14 +13,14 @@ const opensans = Open_Sans({
|
||||
});
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'MercadoDeVida — Productos naturales y orgánicos',
|
||||
title: 'mercadodevida — Productos naturales y orgánicos',
|
||||
description:
|
||||
'Tienda online de productos naturales, orgánicos y saludables. Envío a toda España. Calidad certificada.',
|
||||
icons: {
|
||||
icon: '/images/favicon.png',
|
||||
},
|
||||
openGraph: {
|
||||
title: 'MercadoDeVida — Productos naturales y orgánicos',
|
||||
title: 'mercadodevida — Productos naturales y orgánicos',
|
||||
description: 'Tienda online de productos naturales, orgánicos y saludables.',
|
||||
type: 'website',
|
||||
},
|
||||
|
||||
@@ -9,8 +9,8 @@ export async function generateMetadata({ searchParams }: Props): Promise<Metadat
|
||||
const { orderId } = await searchParams;
|
||||
return {
|
||||
title: orderId
|
||||
? `Pedido ${orderId.slice(0, 8).toUpperCase()} confirmado — MercadoDeVida`
|
||||
: 'Pedido confirmado — MercadoDeVida',
|
||||
? `Pedido ${orderId.slice(0, 8).toUpperCase()} confirmado — mercadodevida`
|
||||
: 'Pedido confirmado — mercadodevida',
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { Metadata } from 'next';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Política de privacidad',
|
||||
description: 'Información sobre cómo MercadoDeVida recopila, usa y protege tus datos personales.',
|
||||
description: 'Información sobre cómo mercadodevida recopila, usa y protege tus datos personales.',
|
||||
robots: { index: false, follow: false },
|
||||
};
|
||||
|
||||
@@ -20,7 +20,7 @@ export default function PrivacyPage() {
|
||||
|
||||
<h2>Responsable del tratamiento</h2>
|
||||
<p>
|
||||
MercadoDeVida<br />
|
||||
mercadodevida<br />
|
||||
Email: hola@mercadodevida.es
|
||||
</p>
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import type { Metadata } from 'next';
|
||||
import { fetchProducts, fetchBrands, fetchCategories, formatPrice } from '@/lib/api';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Productos — MercadoDeVida',
|
||||
title: 'Productos — mercadodevida',
|
||||
description: 'Todos los productos naturales y orgánicos.',
|
||||
};
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ interface Props {
|
||||
export async function generateMetadata({ searchParams }: Props): Promise<Metadata> {
|
||||
const { q } = await searchParams;
|
||||
const title = q ? `Buscar: "${q}"` : 'Buscar productos';
|
||||
return { title, description: `${title} en MercadoDeVida.` };
|
||||
return { title, description: `${title} en mercadodevida.` };
|
||||
}
|
||||
|
||||
export default async function SearchPage({ searchParams }: Props) {
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { Metadata } from 'next';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Términos y condiciones',
|
||||
description: 'Condiciones generales de venta de MercadoDeVida. Lea atentamente antes de realizar su pedido.',
|
||||
description: 'Condiciones generales de venta de mercadodevida. Lea atentamente antes de realizar su pedido.',
|
||||
robots: { index: false, follow: false },
|
||||
};
|
||||
|
||||
@@ -20,7 +20,7 @@ export default function TermsPage() {
|
||||
|
||||
<h2>Identificación del vendedor</h2>
|
||||
<p>
|
||||
MercadoDeVida<br />
|
||||
mercadodevida<br />
|
||||
Email: hola@mercadodevida.es
|
||||
</p>
|
||||
|
||||
|
||||
@@ -101,7 +101,7 @@ export function Footer() {
|
||||
{/* Bottom bar */}
|
||||
<div className="mt-10 pt-6 flex flex-col sm:flex-row justify-between items-center gap-4" style={{ borderTop: '1px solid rgba(255,255,255,0.1)' }}>
|
||||
<p className="text-xs" style={{ color: 'rgba(232,245,224,0.4)' }}>
|
||||
© 2026 MercadoDeVida. Todos los derechos reservados.
|
||||
© 2026 mercadodevida. Todos los derechos reservados.
|
||||
</p>
|
||||
<div className="flex items-center gap-4">
|
||||
<a href="https://instagram.com/mercadodevida/" target="_blank" rel="noopener" aria-label="Instagram"
|
||||
|
||||
@@ -120,13 +120,7 @@ export function Header() {
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!q.trim()) {
|
||||
setResults([]);
|
||||
setSuggestions([]);
|
||||
setShowDropdown(false);
|
||||
setSearching(false);
|
||||
return;
|
||||
}
|
||||
if (!q.trim()) return;
|
||||
if (debounceRef.current) clearTimeout(debounceRef.current);
|
||||
debounceRef.current = setTimeout(() => {
|
||||
loadResults(q.trim());
|
||||
@@ -146,6 +140,16 @@ export function Header() {
|
||||
return () => document.removeEventListener('mousedown', handler);
|
||||
}, []);
|
||||
|
||||
const handleSearchChange = (value: string) => {
|
||||
setQ(value);
|
||||
if (!value.trim()) {
|
||||
setResults([]);
|
||||
setSuggestions([]);
|
||||
setShowDropdown(false);
|
||||
setSearching(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSuggestionClick = (term: string) => {
|
||||
setQ(term);
|
||||
setShowDropdown(false);
|
||||
@@ -168,7 +172,7 @@ export function Header() {
|
||||
<Link href="/" className="flex items-center shrink-0">
|
||||
<Image
|
||||
src="/images/logo-main.png"
|
||||
alt="MercadoDeVida"
|
||||
alt="mercadodevida"
|
||||
width={56}
|
||||
height={56}
|
||||
className="h-14 w-auto object-contain"
|
||||
@@ -183,7 +187,7 @@ export function Header() {
|
||||
<input
|
||||
type="search"
|
||||
value={q}
|
||||
onChange={(e) => setQ(e.target.value)}
|
||||
onChange={(e) => handleSearchChange(e.target.value)}
|
||||
onFocus={() => { if (q.trim() && (results.length > 0 || suggestions.length > 0)) setShowDropdown(true); }}
|
||||
placeholder="Buscar productos, marcas..."
|
||||
className="w-full pl-10 pr-10 py-2.5 border border-gray-200 rounded-full text-sm
|
||||
|
||||
@@ -26,15 +26,13 @@ export function CartProvider({ children }: { children: React.ReactNode }) {
|
||||
const [items, setItems] = useState<CartItem[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
try {
|
||||
const stored = localStorage.getItem('mdv_cart');
|
||||
if (stored) setItems(JSON.parse(stored));
|
||||
} catch {}
|
||||
}, []);
|
||||
|
||||
const persist = useCallback((newItems: CartItem[]) => {
|
||||
setItems(newItems);
|
||||
localStorage.setItem('mdv_cart', JSON.stringify(newItems));
|
||||
const timer = window.setTimeout(() => {
|
||||
try {
|
||||
const stored = localStorage.getItem('mdv_cart');
|
||||
if (stored) setItems(JSON.parse(stored));
|
||||
} catch {}
|
||||
}, 0);
|
||||
return () => window.clearTimeout(timer);
|
||||
}, []);
|
||||
|
||||
const addItem = useCallback((item: CartItem) => {
|
||||
|
||||
Reference in New Issue
Block a user