235 lines
9.5 KiB
TypeScript
235 lines
9.5 KiB
TypeScript
import {
|
|
mdiCalendarStar,
|
|
mdiDice5Outline,
|
|
mdiHomeVariantOutline,
|
|
mdiLightbulbOnOutline,
|
|
mdiLogin,
|
|
mdiMapMarkerPath,
|
|
mdiMapMarkerRadius,
|
|
mdiMapSearchOutline,
|
|
mdiSilverwareForkKnife,
|
|
mdiStarOutline,
|
|
mdiThemeLightDark,
|
|
} from '@mdi/js';
|
|
import Link from 'next/link';
|
|
import React, { ReactNode } from 'react';
|
|
import BaseIcon from '../BaseIcon';
|
|
import { guideStats } from '../../helpers/chinchorreoData';
|
|
import { useAppDispatch, useAppSelector } from '../../stores/hooks';
|
|
import { setDarkMode } from '../../stores/styleSlice';
|
|
|
|
type SectionKey =
|
|
| 'inicio'
|
|
| 'rutas'
|
|
| 'restaurantes'
|
|
| 'lugares'
|
|
| 'eventos'
|
|
| 'tips'
|
|
| 'favoritos'
|
|
| 'mapa';
|
|
|
|
type Props = {
|
|
activeSection: SectionKey;
|
|
children: ReactNode;
|
|
floatingAction?: {
|
|
label: string;
|
|
icon: string;
|
|
onClick: () => void;
|
|
};
|
|
};
|
|
|
|
const navigationItems: Array<{
|
|
key: SectionKey;
|
|
href: string;
|
|
label: string;
|
|
emoji: string;
|
|
icon: string;
|
|
}> = [
|
|
{ key: 'inicio', href: '/', label: 'Inicio', emoji: '🏠', icon: mdiHomeVariantOutline },
|
|
{ key: 'rutas', href: '/rutas', label: 'Rutas', emoji: '🗺️', icon: mdiMapMarkerPath },
|
|
{
|
|
key: 'restaurantes',
|
|
href: '/restaurantes',
|
|
label: 'Chinchorros',
|
|
emoji: '🍽️',
|
|
icon: mdiSilverwareForkKnife,
|
|
},
|
|
{
|
|
key: 'lugares',
|
|
href: '/lugares',
|
|
label: 'Lugares',
|
|
emoji: '📍',
|
|
icon: mdiMapMarkerRadius,
|
|
},
|
|
{ key: 'eventos', href: '/eventos', label: 'Eventos', emoji: '🗓️', icon: mdiCalendarStar },
|
|
{ key: 'tips', href: '/tips', label: 'Tips', emoji: '💡', icon: mdiLightbulbOnOutline },
|
|
{
|
|
key: 'favoritos',
|
|
href: '/mis-favoritos',
|
|
label: 'Favoritos',
|
|
emoji: '⭐',
|
|
icon: mdiStarOutline,
|
|
},
|
|
{ key: 'mapa', href: '/mapa', label: 'Mapa', emoji: '🧭', icon: mdiMapSearchOutline },
|
|
];
|
|
|
|
export default function PublicShell({ activeSection, children, floatingAction }: Props) {
|
|
const dispatch = useAppDispatch();
|
|
const darkMode = useAppSelector((state) => state.style.darkMode);
|
|
|
|
return (
|
|
<div className="relative min-h-screen overflow-hidden bg-[#07111f] text-white dark:bg-[#020712]">
|
|
<div className="pointer-events-none absolute inset-0 opacity-80">
|
|
<div className="absolute left-[-10rem] top-[-5rem] h-72 w-72 rounded-full bg-[#CE1126]/25 blur-3xl" />
|
|
<div className="absolute right-[-6rem] top-24 h-72 w-72 rounded-full bg-[#228B22]/20 blur-3xl" />
|
|
<div className="absolute bottom-[-8rem] left-1/4 h-80 w-80 rounded-full bg-[#002D62]/40 blur-3xl" />
|
|
</div>
|
|
|
|
<aside className="hidden lg:fixed lg:inset-y-0 lg:left-0 lg:flex lg:w-72 lg:flex-col lg:border-r lg:border-white/10 lg:bg-[#04111f]/85 lg:backdrop-blur-xl">
|
|
<div className="border-b border-white/10 px-6 py-8">
|
|
<div className="mb-4 inline-flex items-center gap-2 rounded-full border border-[#DAA520]/40 bg-[#DAA520]/10 px-3 py-1 text-xs font-bold uppercase tracking-[0.35em] text-[#FDE68A]">
|
|
Chinchorreo PR
|
|
</div>
|
|
<h1 className="text-3xl font-black tracking-tight text-white">Guía boricua para salir a chinchorrear</h1>
|
|
<p className="mt-3 text-sm leading-6 text-slate-300">
|
|
Rutas, chinchorros, lugares y favoritos para montar la próxima ruta con sazón.
|
|
</p>
|
|
<div className="mt-5 grid grid-cols-2 gap-3 text-sm">
|
|
<div className="rounded-2xl border border-white/10 bg-white/5 p-3">
|
|
<div className="text-2xl font-black text-[#FDE68A]">{guideStats.routes}</div>
|
|
<div className="text-slate-300">rutas listas</div>
|
|
</div>
|
|
<div className="rounded-2xl border border-white/10 bg-white/5 p-3">
|
|
<div className="text-2xl font-black text-[#A7F3D0]">{guideStats.chinchorros}</div>
|
|
<div className="text-slate-300">paradas</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<nav className="flex-1 space-y-1 overflow-y-auto px-4 py-5">
|
|
{navigationItems.map((item) => {
|
|
const isActive = item.key === activeSection;
|
|
return (
|
|
<Link
|
|
key={item.key}
|
|
href={item.href}
|
|
className={[
|
|
'group flex items-center gap-3 rounded-2xl border px-4 py-3 transition-all duration-200',
|
|
isActive
|
|
? 'border-[#DAA520]/40 bg-white/10 text-white shadow-lg shadow-black/20'
|
|
: 'border-transparent bg-transparent text-slate-300 hover:border-white/10 hover:bg-white/5 hover:text-white',
|
|
].join(' ')}
|
|
>
|
|
<div
|
|
className={[
|
|
'flex h-10 w-10 items-center justify-center rounded-2xl border',
|
|
isActive
|
|
? 'border-[#DAA520]/50 bg-[#DAA520]/15 text-[#FDE68A]'
|
|
: 'border-white/10 bg-white/5 text-slate-300',
|
|
].join(' ')}
|
|
>
|
|
<BaseIcon path={item.icon} size={18} />
|
|
</div>
|
|
<div className="min-w-0 flex-1">
|
|
<div className="text-sm font-semibold">
|
|
<span className="mr-2">{item.emoji}</span>
|
|
{item.label}
|
|
</div>
|
|
<div className="text-xs text-slate-400">Dale una vuelta</div>
|
|
</div>
|
|
</Link>
|
|
);
|
|
})}
|
|
</nav>
|
|
|
|
<div className="border-t border-white/10 px-4 py-4">
|
|
<div className="flex items-center gap-2">
|
|
<Link
|
|
href="/login"
|
|
className="flex flex-1 items-center justify-center gap-2 rounded-2xl border border-white/10 bg-white/5 px-3 py-3 text-sm font-semibold text-white transition hover:border-white/20 hover:bg-white/10"
|
|
>
|
|
<BaseIcon path={mdiLogin} size={18} />
|
|
Área privada
|
|
</Link>
|
|
<button
|
|
type="button"
|
|
onClick={() => dispatch(setDarkMode(null))}
|
|
className="flex h-12 w-12 items-center justify-center rounded-2xl border border-white/10 bg-white/5 text-white transition hover:border-white/20 hover:bg-white/10"
|
|
aria-label={darkMode ? 'Activar modo claro' : 'Activar modo oscuro'}
|
|
>
|
|
<BaseIcon path={mdiThemeLightDark} size={20} />
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</aside>
|
|
|
|
<main className="relative min-h-screen pb-28 lg:pl-72 lg:pb-16">
|
|
<header className="sticky top-0 z-30 border-b border-white/10 bg-[#04111f]/75 px-4 py-4 backdrop-blur-xl sm:px-6 lg:px-10">
|
|
<div className="mx-auto flex max-w-7xl items-center justify-between gap-4">
|
|
<div>
|
|
<div className="text-xs font-bold uppercase tracking-[0.3em] text-[#FDE68A]">Chinchorreo PR</div>
|
|
<div className="text-sm text-slate-300">Explora Puerto Rico con sabor, ritmo y ruta.</div>
|
|
</div>
|
|
|
|
<div className="flex items-center gap-2">
|
|
<Link
|
|
href="/login"
|
|
className="hidden items-center gap-2 rounded-full border border-white/10 bg-white/5 px-4 py-2 text-sm font-semibold text-white transition hover:border-white/20 hover:bg-white/10 sm:inline-flex"
|
|
>
|
|
<BaseIcon path={mdiLogin} size={16} />
|
|
Área privada
|
|
</Link>
|
|
<button
|
|
type="button"
|
|
onClick={() => dispatch(setDarkMode(null))}
|
|
className="inline-flex h-11 w-11 items-center justify-center rounded-full border border-white/10 bg-white/5 text-white transition hover:border-white/20 hover:bg-white/10"
|
|
aria-label={darkMode ? 'Activar modo claro' : 'Activar modo oscuro'}
|
|
>
|
|
<BaseIcon path={mdiThemeLightDark} size={18} />
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
<div className="mx-auto flex w-full max-w-7xl flex-col gap-8 px-4 py-6 sm:px-6 lg:px-10 lg:py-8">
|
|
{children}
|
|
</div>
|
|
</main>
|
|
|
|
{floatingAction ? (
|
|
<button
|
|
type="button"
|
|
onClick={floatingAction.onClick}
|
|
className="fixed bottom-24 right-4 z-40 inline-flex items-center gap-3 rounded-full border border-[#DAA520]/30 bg-gradient-to-r from-[#CE1126] to-[#DAA520] px-5 py-3 text-sm font-bold text-white shadow-2xl shadow-black/35 transition hover:translate-y-[-1px] hover:shadow-black/50 lg:bottom-8 lg:right-8"
|
|
>
|
|
<BaseIcon path={floatingAction.icon || mdiDice5Outline} size={18} />
|
|
{floatingAction.label}
|
|
</button>
|
|
) : null}
|
|
|
|
<nav className="fixed inset-x-0 bottom-0 z-40 border-t border-white/10 bg-[#04111f]/95 px-2 py-3 backdrop-blur-2xl lg:hidden">
|
|
<div className="flex items-center gap-2 overflow-x-auto pb-1">
|
|
{navigationItems.map((item) => {
|
|
const isActive = item.key === activeSection;
|
|
return (
|
|
<Link
|
|
key={item.key}
|
|
href={item.href}
|
|
className={[
|
|
'flex min-w-[96px] flex-col items-center justify-center rounded-2xl border px-3 py-2 text-center transition',
|
|
isActive
|
|
? 'border-[#DAA520]/40 bg-white/10 text-white'
|
|
: 'border-transparent bg-transparent text-slate-300',
|
|
].join(' ')}
|
|
>
|
|
<BaseIcon path={item.icon} size={18} />
|
|
<span className="mt-1 text-[11px] font-semibold">{item.label}</span>
|
|
</Link>
|
|
);
|
|
})}
|
|
</div>
|
|
</nav>
|
|
</div>
|
|
);
|
|
}
|