Initial commit from remix

This commit is contained in:
gpt-engineer-app[bot]
2025-09-25 16:01:00 +00:00
commit 5ddc52658d
149 changed files with 32798 additions and 0 deletions

View File

@@ -0,0 +1,484 @@
import React, { useState } from 'react';
import { Outlet, Link, useLocation } from 'react-router-dom';
import { useAuth } from '@/contexts/AuthContext';
import { useLanguage } from '@/contexts/LanguageContext';
import DashboardStyles from '@/components/layouts/DashboardStyles';
import {
Home,
Plus,
Wallet,
List,
MessageSquare,
Star,
BookOpen,
Heart,
FileText,
User,
Users,
Settings,
LogOut,
Search,
Bell,
Menu,
Sun,
Moon,
Maximize,
CreditCard,
BarChart3,
MapPin,
DollarSign,
AlertTriangle,
RefreshCw,
Navigation,
Car,
Target,
Zap,
Eye,
Megaphone,
ChevronDown,
ChevronRight
} from 'lucide-react';
const DashboardLayout = ({ children }: { children: React.ReactNode }) => {
const { user, logout } = useAuth();
const { t } = useLanguage();
const location = useLocation();
const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
const [expandedItems, setExpandedItems] = useState<Record<string, boolean>>({});
const currentTab = new URLSearchParams(location.search).get('tab') || 'overview';
const toggleExpanded = (itemTab: string) => {
setExpandedItems(prev => ({
...prev,
[itemTab]: !prev[itemTab]
}));
};
const menuItems = [
{ icon: Home, label: 'Dashboard', path: '/dashboard' },
{ icon: Settings, label: 'Admin Panel', path: '/dashboard/admin' },
{ icon: Plus, label: 'Add listing', path: '/dashboard/add-listing' },
{ icon: Wallet, label: 'Wallet', path: '/dashboard/wallet' },
{ icon: MessageSquare, label: 'Message', path: '/dashboard/messages', badge: '2' },
];
const adminSubmenu = [
{ icon: BarChart3, label: 'Resumen General', tab: 'overview' },
{ icon: Users, label: 'Gestión de Usuarios', tab: 'users' },
{ icon: MapPin, label: 'Proveedores de Servicios', tab: 'services' },
{ icon: DollarSign, label: 'Gestión Financiera', tab: 'financial' },
{
icon: FileText,
label: 'Contenido Turístico',
tab: 'content',
subItems: [
{ icon: MapPin, label: 'Destinos', tab: 'content-destinations' },
{ icon: Star, label: 'Lugares', tab: 'content-places' },
{ icon: Users, label: 'Guías', tab: 'content-guides' },
{ icon: Car, label: 'Taxis', tab: 'content-taxis' },
{
icon: Navigation,
label: 'Geolocalización',
tab: 'content-geolocation',
subItems: [
{ icon: MapPin, label: 'Geofences', tab: 'geofences' },
{ icon: BarChart3, label: 'Analíticas', tab: 'analytics' },
{ icon: Target, label: 'Pruebas', tab: 'testing' },
{ icon: AlertTriangle, label: 'Emergencias', tab: 'emergency-geo' },
{ icon: Navigation, label: 'Navegación', tab: 'navigation' }
]
},
{ icon: Megaphone, label: 'Promocional', tab: 'content-promotional' },
{ icon: Zap, label: 'Guías IA', tab: 'content-ai-guides' },
{ icon: Eye, label: 'Realidad AR', tab: 'content-ar' }
]
},
{ icon: AlertTriangle, label: 'Emergencias', tab: 'emergency' },
{ icon: MessageSquare, label: 'Soporte', tab: 'support' },
...(user?.role === 'super_admin' ? [{ icon: Settings, label: 'Configuración', tab: 'config' }] : []),
];
const listingItems = [
{ icon: List, label: 'My Listing', path: '/dashboard/my-listings', hasSubmenu: true },
{ icon: Star, label: 'Reviews', path: '/dashboard/reviews' },
{ icon: BookOpen, label: 'Bookings', path: '/dashboard/bookings' },
{ icon: Heart, label: 'Bookmark', path: '/dashboard/bookmarks' },
{ icon: FileText, label: 'Invoice', path: '/dashboard/invoices' },
];
const accountItems = [
{ icon: User, label: 'Edit Profile', path: '/dashboard/profile' },
{ icon: CreditCard, label: 'Wallet', path: '/dashboard/wallet' },
{ icon: Settings, label: 'Setting', path: '/dashboard/settings' },
];
return (
<>
<DashboardStyles />
<div className="min-h-screen" style={{ fontFamily: '"Wix Madefor Display", sans-serif', backgroundColor: '#f8f4f3' }}>
{/* Background decorations */}
<div className="decoration blur-2"></div>
<div className="decoration blur-3"></div>
{/* Sidebar */}
<nav className={`fixed top-0 left-0 z-50 h-screen bg-white border-r-4 border-white transition-all duration-300 ${sidebarCollapsed ? 'w-16' : 'w-80'}`} style={{ minWidth: sidebarCollapsed ? '64px' : '320px', maxWidth: sidebarCollapsed ? '64px' : '320px', backdropFilter: 'blur(15px)', backgroundColor: 'rgba(255, 255, 255, 0.9)' }}>
{/* Sidebar Header */}
<div className="flex items-center justify-between p-6 h-20 border-b border-gray-100">
<Link to="/dashboard" className={`flex items-center space-x-2 ${sidebarCollapsed ? 'justify-center' : ''}`}>
<div className="w-24 h-24 rounded-lg flex items-center justify-center">
<img
src="https://karibeo.com/desktop/assets/images/logo.png"
alt="Karibeo"
className="w-full h-full object-contain rounded-lg"
/>
</div>
</Link>
</div>
{/* Sidebar Navigation */}
<div className="p-4 h-full overflow-y-auto" style={{ height: 'calc(100vh - 80px)' }}>
{/* Main Menu */}
<div className="mb-6">
{!sidebarCollapsed && (
<div className="flex items-center justify-between mb-4 px-4 py-3">
<span className="text-xs font-semibold text-gray-600 uppercase tracking-wider">Main Menu</span>
<div className="flex space-x-1">
<div className="w-1 h-1 bg-gray-400 rounded-full"></div>
<div className="w-1 h-1 bg-gray-400 rounded-full"></div>
<div className="w-1 h-1 bg-gray-400 rounded-full"></div>
</div>
</div>
)}
<div className="space-y-1 pr-8">
{menuItems.map((item) => {
const Icon = item.icon;
const isActive = location.pathname === item.path;
const isAdminPanel = item.path === '/dashboard/admin';
return (
<div key={item.path}>
<Link
to={item.path}
className={`flex items-center space-x-3 px-7 py-2.5 rounded-none transition-all border-l-4 ${
isActive
? 'text-white border-l-4 rounded-r-full'
: 'text-gray-700 hover:text-red-500 border-transparent rounded-r-full'
} ${sidebarCollapsed ? 'justify-center px-2' : ''}`}
style={{
backgroundColor: isActive ? 'rgba(248, 69, 37, 0.1)' : 'transparent',
borderLeftColor: isActive ? '#F84525' : 'transparent',
color: isActive ? '#F84525' : '#433c3a'
}}
>
<Icon className="w-5 h-5 flex-shrink-0" />
{!sidebarCollapsed && (
<div className="flex items-center justify-between w-full">
<span className="font-medium">{item.label}</span>
{item.badge && (
<span className="bg-green-500 text-white text-xs px-2 py-0.5 rounded-full">
{item.badge}
</span>
)}
</div>
)}
</Link>
{/* Admin submenus */}
{!sidebarCollapsed && isAdminPanel && location.pathname.startsWith('/dashboard/admin') && (
<div className="mt-1 ml-10 space-y-1">
{adminSubmenu.map((sub) => {
const SubIcon = sub.icon;
const activeSub = currentTab === sub.tab;
const hasSubItems = sub.subItems && sub.subItems.length > 0;
const isExpanded = expandedItems[sub.tab] || (hasSubItems && sub.subItems.some(item => currentTab === item.tab || (item.subItems && item.subItems.some(subItem => currentTab === subItem.tab))));
return (
<div key={sub.tab}>
<div className={`flex items-center justify-between px-3 py-2 rounded-md text-sm ${activeSub ? 'bg-orange-50 text-orange-600' : 'text-gray-600 hover:text-orange-600 hover:bg-gray-50'}`}>
<Link
to={`/dashboard/admin?tab=${sub.tab}`}
className="flex items-center space-x-2 flex-1"
>
<SubIcon className="w-4 h-4" />
<span>{sub.label}</span>
</Link>
{hasSubItems && (
<button
onClick={() => toggleExpanded(sub.tab)}
className="p-1 hover:bg-gray-200 rounded"
>
{isExpanded ? (
<ChevronDown className="w-3 h-3" />
) : (
<ChevronRight className="w-3 h-3" />
)}
</button>
)}
</div>
{/* Sub-submenus */}
{hasSubItems && isExpanded && (
<div className="ml-6 mt-1 space-y-1">
{sub.subItems.map((subItem) => {
const SubSubIcon = subItem.icon;
const activeSubSub = currentTab === subItem.tab;
const hasSubSubItems = subItem.subItems && subItem.subItems.length > 0;
const isSubExpanded = expandedItems[subItem.tab] || (hasSubSubItems && subItem.subItems.some(item => currentTab === item.tab));
return (
<div key={subItem.tab}>
<div className={`flex items-center justify-between px-3 py-1.5 rounded-md text-xs ${activeSubSub ? 'bg-orange-100 text-orange-700' : 'text-gray-500 hover:text-orange-600 hover:bg-gray-50'}`}>
<Link
to={`/dashboard/admin?tab=${subItem.tab}`}
className="flex items-center space-x-2 flex-1"
>
<SubSubIcon className="w-3 h-3" />
<span>{subItem.label}</span>
</Link>
{hasSubSubItems && (
<button
onClick={() => toggleExpanded(subItem.tab)}
className="p-1 hover:bg-gray-200 rounded"
>
{isSubExpanded ? (
<ChevronDown className="w-3 h-3" />
) : (
<ChevronRight className="w-3 h-3" />
)}
</button>
)}
</div>
{/* Sub-sub-submenus */}
{hasSubSubItems && isSubExpanded && (
<div className="ml-4 mt-1 space-y-1">
{subItem.subItems.map((subSubItem) => {
const SubSubSubIcon = subSubItem.icon;
const activeSubSubSub = currentTab === subSubItem.tab;
return (
<Link
key={subSubItem.tab}
to={`/dashboard/admin?tab=${subSubItem.tab}`}
className={`flex items-center space-x-2 px-3 py-1 rounded-md text-xs ${activeSubSubSub ? 'bg-orange-200 text-orange-800' : 'text-gray-400 hover:text-orange-600 hover:bg-gray-50'}`}
>
<SubSubSubIcon className="w-3 h-3" />
<span>{subSubItem.label}</span>
</Link>
);
})}
</div>
)}
</div>
);
})}
</div>
)}
</div>
);
})}
</div>
)}
</div>
);
})}
</div>
</div>
{/* Listing Section */}
<div className="mb-6">
{!sidebarCollapsed && (
<div className="flex items-center justify-between mb-4 px-4 py-3">
<span className="text-xs font-semibold text-gray-600 uppercase tracking-wider">Listing</span>
<div className="flex space-x-1">
<div className="w-1 h-1 bg-gray-400 rounded-full"></div>
<div className="w-1 h-1 bg-gray-400 rounded-full"></div>
<div className="w-1 h-1 bg-gray-400 rounded-full"></div>
</div>
</div>
)}
<div className="space-y-1 pr-8">
{listingItems.map((item) => {
const Icon = item.icon;
const isActive = location.pathname === item.path;
return (
<Link
key={item.path}
to={item.path}
className={`flex items-center space-x-3 px-7 py-2.5 rounded-none transition-all border-l-4 ${
isActive
? 'text-white border-l-4 rounded-r-full'
: 'text-gray-700 hover:text-red-500 border-transparent rounded-r-full'
} ${sidebarCollapsed ? 'justify-center px-2' : ''}`}
style={{
backgroundColor: isActive ? 'rgba(248, 69, 37, 0.1)' : 'transparent',
borderLeftColor: isActive ? '#F84525' : 'transparent',
color: isActive ? '#F84525' : '#433c3a'
}}
>
<Icon className="w-5 h-5 flex-shrink-0" />
{!sidebarCollapsed && <span className="font-medium">{item.label}</span>}
</Link>
);
})}
</div>
</div>
{/* Account Section */}
<div className="mb-6">
{!sidebarCollapsed && (
<div className="flex items-center justify-between mb-4 px-4 py-3">
<span className="text-xs font-semibold text-gray-600 uppercase tracking-wider">Account</span>
<div className="flex space-x-1">
<div className="w-1 h-1 bg-gray-400 rounded-full"></div>
<div className="w-1 h-1 bg-gray-400 rounded-full"></div>
<div className="w-1 h-1 bg-gray-400 rounded-full"></div>
</div>
</div>
)}
<div className="space-y-1 pr-8">
{accountItems.map((item) => {
const Icon = item.icon;
const isActive = location.pathname === item.path;
return (
<Link
key={item.path}
to={item.path}
className={`flex items-center space-x-3 px-7 py-2.5 rounded-none transition-all border-l-4 ${
isActive
? 'text-white border-l-4 rounded-r-full'
: 'text-gray-700 hover:text-red-500 border-transparent rounded-r-full'
} ${sidebarCollapsed ? 'justify-center px-2' : ''}`}
style={{
backgroundColor: isActive ? 'rgba(248, 69, 37, 0.1)' : 'transparent',
borderLeftColor: isActive ? '#F84525' : 'transparent',
color: isActive ? '#F84525' : '#433c3a'
}}
>
<Icon className="w-5 h-5 flex-shrink-0" />
{!sidebarCollapsed && <span className="font-medium">{item.label}</span>}
</Link>
);
})}
<button
onClick={logout}
className={`flex items-center space-x-3 px-7 py-2.5 rounded-r-full transition-all text-gray-700 hover:text-red-600 w-full border-l-4 border-transparent ${sidebarCollapsed ? 'justify-center px-2' : ''}`}
>
<LogOut className="w-5 h-5 flex-shrink-0" />
{!sidebarCollapsed && <span className="font-medium">Logout</span>}
</button>
</div>
</div>
</div>
</nav>
{/* Main Content Area */}
<div className={`transition-all duration-300 ${sidebarCollapsed ? 'ml-16' : 'ml-80'}`}>
{/* Top Navigation */}
<nav className="h-20 px-6 py-4 z-10 relative" style={{ backgroundColor: 'rgba(255, 255, 255, 0.7)', backdropFilter: 'blur(15px)' }}>
<div className="flex items-center justify-between">
{/* Left Side */}
<div className="flex items-center space-x-4">
<button
onClick={() => setSidebarCollapsed(!sidebarCollapsed)}
className="p-2 rounded-full text-white transition-colors"
style={{ backgroundColor: '#F84525' }}
>
<Menu className="w-5 h-5" />
</button>
{/* Search */}
<div className="relative">
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<Search className="h-4 w-4" style={{ color: '#69534f' }} />
</div>
<input
type="text"
placeholder="Search (Ctrl+/)"
className="block w-80 pl-12 pr-16 py-3 border border-white rounded-xl text-sm placeholder-gray-500 focus:outline-none focus:ring-1 focus:border-red-500"
style={{
backgroundColor: '#fff',
borderColor: '#fff',
height: '48px',
borderRadius: '0.8rem'
}}
/>
<span className="absolute inset-y-0 right-0 pr-3 flex items-center text-xs font-bold px-2 py-1 rounded" style={{ backgroundColor: '#f8f4f3', borderColor: '#f8f4f3', top: '50%', transform: 'translateY(-50%)', right: '8px', fontSize: '12px' }}>
(Ctrl+/)
</span>
</div>
</div>
{/* Right Side */}
<div className="flex items-center space-x-4">
{/* Refresh (Admin) */}
<button
onClick={() => window.dispatchEvent(new CustomEvent('admin:refresh'))}
className="p-2 rounded-xl transition-colors"
style={{ backgroundColor: '#21272f', borderColor: '#21272f', color: '#F84525' }}
title="Actualizar datos"
>
<RefreshCw className="w-5 h-5" />
</button>
{/* Notifications */}
<button className="p-2 rounded-xl transition-colors relative" style={{ backgroundColor: '#21272f', borderColor: '#21272f', color: '#F84525' }} title="Notificaciones">
<Bell className="w-5 h-5" />
<span className="absolute -top-1 -right-1 bg-red-500 text-white text-[10px] px-1 py-0.5 rounded-full">!</span>
</button>
{/* Theme Toggle */}
<button className="p-2 rounded-xl transition-colors" style={{ backgroundColor: '#21272f', borderColor: '#21272f', color: '#F84525' }}>
<Sun className="w-5 h-5" />
</button>
{/* Fullscreen */}
<button className="p-2 rounded-xl transition-colors" style={{ backgroundColor: '#21272f', borderColor: '#21272f', color: '#F84525' }}>
<Maximize className="w-5 h-5" />
</button>
{/* User Profile */}
<div className="flex items-center space-x-3 pl-4">
<div className="flex items-center space-x-3">
<div className="w-10 h-10 bg-gradient-to-br from-orange-400 to-red-500 rounded-full flex items-center justify-center relative">
<span className="text-white font-semibold text-sm">
{user?.name?.[0] || user?.email?.[0] || 'U'}
</span>
<div className="absolute -bottom-1 -right-1 w-4 h-4 bg-green-500 border-2 border-white rounded-full"></div>
</div>
<div className="text-left">
<div className="font-semibold text-gray-800 text-sm flex items-center space-x-2">
<span>{user?.name || 'Usuario'}</span>
{user?.role === 'super_admin' && (
<span className="bg-gradient-to-r from-orange-400 to-red-500 text-white text-[10px] px-1.5 py-0.5 rounded">Super Admin</span>
)}
</div>
<div className="text-xs text-gray-500">
{user?.email || 'example@gmail.com'}
</div>
</div>
</div>
</div>
</div>
</div>
</nav>
{/* Page Content */}
<main className="p-6">
{children}
</main>
</div>
</div>
</>
);
};
export default DashboardLayout;