Reverted to commit dd051b1e73
This commit is contained in:
76
src/App.tsx
76
src/App.tsx
@@ -39,16 +39,6 @@ import Security from "./pages/dashboard/Security";
|
|||||||
import VehicleManagement from "./pages/dashboard/VehicleManagement";
|
import VehicleManagement from "./pages/dashboard/VehicleManagement";
|
||||||
import Sustainability from "./pages/dashboard/Sustainability";
|
import Sustainability from "./pages/dashboard/Sustainability";
|
||||||
import Establishments from "./pages/dashboard/Establishments";
|
import Establishments from "./pages/dashboard/Establishments";
|
||||||
import EstablishmentsManagement from "./pages/dashboard/EstablishmentsManagement";
|
|
||||||
// Establishments pages
|
|
||||||
import EstablishmentPOS from "./pages/dashboard/establishments/POSTerminal";
|
|
||||||
import EstablishmentOrders from "./pages/dashboard/establishments/EstablishmentOrders";
|
|
||||||
import EstablishmentInventory from "./pages/dashboard/establishments/EstablishmentInventory";
|
|
||||||
import EstablishmentStaff from "./pages/dashboard/establishments/EstablishmentStaff";
|
|
||||||
import BusinessList from "./components/establishments/BusinessList";
|
|
||||||
import BusinessCategories from "./components/establishments/BusinessCategories";
|
|
||||||
import BusinessVerification from "./components/establishments/BusinessVerification";
|
|
||||||
import BusinessAnalytics from "./components/establishments/BusinessAnalytics";
|
|
||||||
// Hotel pages
|
// Hotel pages
|
||||||
import HotelRooms from "./pages/dashboard/hotel/Rooms";
|
import HotelRooms from "./pages/dashboard/hotel/Rooms";
|
||||||
import HotelCheckIn from "./pages/dashboard/hotel/CheckIn";
|
import HotelCheckIn from "./pages/dashboard/hotel/CheckIn";
|
||||||
@@ -298,71 +288,7 @@ const AppRouter = () => (
|
|||||||
<Route path="/dashboard/establishments" element={
|
<Route path="/dashboard/establishments" element={
|
||||||
<ProtectedRoute>
|
<ProtectedRoute>
|
||||||
<DashboardLayout>
|
<DashboardLayout>
|
||||||
<EstablishmentsManagement />
|
<Establishments />
|
||||||
</DashboardLayout>
|
|
||||||
</ProtectedRoute>
|
|
||||||
} />
|
|
||||||
|
|
||||||
<Route path="/dashboard/establishments/pos" element={
|
|
||||||
<ProtectedRoute>
|
|
||||||
<DashboardLayout>
|
|
||||||
<EstablishmentPOS />
|
|
||||||
</DashboardLayout>
|
|
||||||
</ProtectedRoute>
|
|
||||||
} />
|
|
||||||
|
|
||||||
<Route path="/dashboard/establishments/orders" element={
|
|
||||||
<ProtectedRoute>
|
|
||||||
<DashboardLayout>
|
|
||||||
<EstablishmentOrders />
|
|
||||||
</DashboardLayout>
|
|
||||||
</ProtectedRoute>
|
|
||||||
} />
|
|
||||||
|
|
||||||
<Route path="/dashboard/establishments/inventory" element={
|
|
||||||
<ProtectedRoute>
|
|
||||||
<DashboardLayout>
|
|
||||||
<EstablishmentInventory />
|
|
||||||
</DashboardLayout>
|
|
||||||
</ProtectedRoute>
|
|
||||||
} />
|
|
||||||
|
|
||||||
<Route path="/dashboard/establishments/staff" element={
|
|
||||||
<ProtectedRoute>
|
|
||||||
<DashboardLayout>
|
|
||||||
<EstablishmentStaff />
|
|
||||||
</DashboardLayout>
|
|
||||||
</ProtectedRoute>
|
|
||||||
} />
|
|
||||||
|
|
||||||
<Route path="/dashboard/establishments/businesses" element={
|
|
||||||
<ProtectedRoute>
|
|
||||||
<DashboardLayout>
|
|
||||||
<BusinessList />
|
|
||||||
</DashboardLayout>
|
|
||||||
</ProtectedRoute>
|
|
||||||
} />
|
|
||||||
|
|
||||||
<Route path="/dashboard/establishments/categories" element={
|
|
||||||
<ProtectedRoute>
|
|
||||||
<DashboardLayout>
|
|
||||||
<BusinessCategories />
|
|
||||||
</DashboardLayout>
|
|
||||||
</ProtectedRoute>
|
|
||||||
} />
|
|
||||||
|
|
||||||
<Route path="/dashboard/establishments/verification" element={
|
|
||||||
<ProtectedRoute>
|
|
||||||
<DashboardLayout>
|
|
||||||
<BusinessVerification />
|
|
||||||
</DashboardLayout>
|
|
||||||
</ProtectedRoute>
|
|
||||||
} />
|
|
||||||
|
|
||||||
<Route path="/dashboard/establishments/analytics" element={
|
|
||||||
<ProtectedRoute>
|
|
||||||
<DashboardLayout>
|
|
||||||
<BusinessAnalytics />
|
|
||||||
</DashboardLayout>
|
</DashboardLayout>
|
||||||
</ProtectedRoute>
|
</ProtectedRoute>
|
||||||
} />
|
} />
|
||||||
|
|||||||
@@ -1,63 +0,0 @@
|
|||||||
import { Link } from "react-router-dom";
|
|
||||||
import { useAuth } from "@/contexts/AuthContext";
|
|
||||||
import {
|
|
||||||
Sidebar,
|
|
||||||
SidebarContent,
|
|
||||||
SidebarGroup,
|
|
||||||
SidebarGroupContent,
|
|
||||||
SidebarGroupLabel,
|
|
||||||
SidebarMenu,
|
|
||||||
SidebarMenuItem,
|
|
||||||
SidebarMenuButton,
|
|
||||||
useSidebar,
|
|
||||||
} from "@/components/ui/sidebar";
|
|
||||||
import { LogOut } from "lucide-react";
|
|
||||||
import { menuGroups } from "@/config/sidebar";
|
|
||||||
import { SidebarMenuRenderer } from "@/components/sidebar/SidebarMenuRenderer";
|
|
||||||
|
|
||||||
export function AppSidebar() {
|
|
||||||
const { open, openMobile, isMobile } = useSidebar();
|
|
||||||
const collapsed = !open && !isMobile;
|
|
||||||
const { logout } = useAuth();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Sidebar className={collapsed ? "w-14" : "w-80"} collapsible="icon">
|
|
||||||
<SidebarContent className="overflow-y-auto">
|
|
||||||
{/* Logo */}
|
|
||||||
<div className="flex items-center justify-center p-6 border-b">
|
|
||||||
<Link to="/dashboard">
|
|
||||||
<img
|
|
||||||
src="https://karibeo.com/desktop/assets/images/logo.png"
|
|
||||||
alt="Karibeo"
|
|
||||||
className={collapsed ? "w-8 h-8" : "w-24 h-24"}
|
|
||||||
/>
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Menu Groups */}
|
|
||||||
{menuGroups.map((group) => (
|
|
||||||
<SidebarGroup key={group.label}>
|
|
||||||
{!collapsed && <SidebarGroupLabel>{group.label}</SidebarGroupLabel>}
|
|
||||||
<SidebarGroupContent>
|
|
||||||
<SidebarMenuRenderer items={group.items} />
|
|
||||||
</SidebarGroupContent>
|
|
||||||
</SidebarGroup>
|
|
||||||
))}
|
|
||||||
|
|
||||||
{/* Logout */}
|
|
||||||
<SidebarGroup>
|
|
||||||
<SidebarGroupContent>
|
|
||||||
<SidebarMenu>
|
|
||||||
<SidebarMenuItem>
|
|
||||||
<SidebarMenuButton onClick={logout} className={collapsed ? "justify-center" : ""}>
|
|
||||||
<LogOut className="h-4 w-4" />
|
|
||||||
{!collapsed && <span>Logout</span>}
|
|
||||||
</SidebarMenuButton>
|
|
||||||
</SidebarMenuItem>
|
|
||||||
</SidebarMenu>
|
|
||||||
</SidebarGroupContent>
|
|
||||||
</SidebarGroup>
|
|
||||||
</SidebarContent>
|
|
||||||
</Sidebar>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,47 +1,446 @@
|
|||||||
import React from 'react';
|
import React, { useState } from 'react';
|
||||||
import { useLocation } from 'react-router-dom';
|
import { Outlet, Link, useLocation } from 'react-router-dom';
|
||||||
import { useAuth } from '@/contexts/AuthContext';
|
import { useAuth } from '@/contexts/AuthContext';
|
||||||
import { useLanguage } from '@/contexts/LanguageContext';
|
import { useLanguage } from '@/contexts/LanguageContext';
|
||||||
import DashboardStyles from '@/components/layouts/DashboardStyles';
|
import DashboardStyles from '@/components/layouts/DashboardStyles';
|
||||||
import CurrencySelector from '@/components/CurrencySelector';
|
import CurrencySelector from '@/components/CurrencySelector';
|
||||||
import { AppSidebar } from '@/components/AppSidebar';
|
|
||||||
import { SidebarProvider, SidebarTrigger } from '@/components/ui/sidebar';
|
|
||||||
import {
|
import {
|
||||||
|
Home,
|
||||||
|
Plus,
|
||||||
|
Wallet,
|
||||||
|
List,
|
||||||
|
MessageSquare,
|
||||||
|
Star,
|
||||||
|
BookOpen,
|
||||||
|
Heart,
|
||||||
|
FileText,
|
||||||
|
User,
|
||||||
|
Users,
|
||||||
|
Settings,
|
||||||
|
LogOut,
|
||||||
Search,
|
Search,
|
||||||
Bell,
|
Bell,
|
||||||
Menu,
|
Menu,
|
||||||
Sun,
|
Sun,
|
||||||
|
Moon,
|
||||||
Maximize,
|
Maximize,
|
||||||
|
CreditCard,
|
||||||
|
BarChart3,
|
||||||
|
MapPin,
|
||||||
|
DollarSign,
|
||||||
|
AlertTriangle,
|
||||||
RefreshCw,
|
RefreshCw,
|
||||||
|
Navigation,
|
||||||
|
Car,
|
||||||
|
Target,
|
||||||
|
Zap,
|
||||||
|
Eye,
|
||||||
|
Megaphone,
|
||||||
|
ChevronDown,
|
||||||
|
ChevronRight,
|
||||||
|
Hotel,
|
||||||
|
UtensilsCrossed,
|
||||||
|
Brain,
|
||||||
|
DoorOpen,
|
||||||
|
BellRing,
|
||||||
|
Key,
|
||||||
|
Receipt,
|
||||||
|
ChefHat,
|
||||||
|
Grid3x3,
|
||||||
|
Package,
|
||||||
|
Globe,
|
||||||
|
Compass,
|
||||||
|
Map,
|
||||||
|
Shield,
|
||||||
|
Radio,
|
||||||
|
Sparkles,
|
||||||
|
Leaf,
|
||||||
|
Store
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
|
|
||||||
const DashboardLayout = ({ children }: { children: React.ReactNode }) => {
|
const DashboardLayout = ({ children }: { children: React.ReactNode }) => {
|
||||||
const { user } = useAuth();
|
const { user, logout } = useAuth();
|
||||||
const { t } = useLanguage();
|
const { t } = useLanguage();
|
||||||
const location = useLocation();
|
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',
|
||||||
|
subItems: [
|
||||||
|
{ icon: BarChart3, label: 'Resumen General', path: '/dashboard/admin?tab=overview' },
|
||||||
|
{ icon: Users, label: 'Usuarios', path: '/dashboard/admin?tab=users' },
|
||||||
|
{ icon: MapPin, label: 'Proveedores', path: '/dashboard/admin?tab=services' },
|
||||||
|
{ icon: DollarSign, label: 'Financiero', path: '/dashboard/admin?tab=financial' },
|
||||||
|
{
|
||||||
|
icon: FileText,
|
||||||
|
label: 'Contenido',
|
||||||
|
path: '/dashboard/admin?tab=content',
|
||||||
|
subItems: [
|
||||||
|
{ icon: Globe, label: 'Destinos', path: '/dashboard/admin?tab=content-destinations' },
|
||||||
|
{ icon: MapPin, label: 'Lugares', path: '/dashboard/admin?tab=content-places' },
|
||||||
|
{ icon: BookOpen, label: 'Guías', path: '/dashboard/admin?tab=content-guides' },
|
||||||
|
{ icon: Car, label: 'Taxis', path: '/dashboard/admin?tab=content-taxis' },
|
||||||
|
{ icon: Navigation, label: 'Geolocalización', path: '/dashboard/admin?tab=content-geolocation' },
|
||||||
|
{ icon: Megaphone, label: 'Promocional', path: '/dashboard/admin?tab=content-promotional' },
|
||||||
|
{ icon: Sparkles, label: 'AI Guías', path: '/dashboard/admin?tab=content-ai-guides' },
|
||||||
|
{ icon: Eye, label: 'AR Content', path: '/dashboard/admin?tab=content-ar' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: Map,
|
||||||
|
label: 'Geolocalización',
|
||||||
|
path: '/dashboard/admin?tab=geofences',
|
||||||
|
subItems: [
|
||||||
|
{ icon: Target, label: 'Geofences', path: '/dashboard/admin?tab=geofences' },
|
||||||
|
{ icon: BarChart3, label: 'Analytics', path: '/dashboard/admin?tab=analytics' },
|
||||||
|
{ icon: Zap, label: 'Testing', path: '/dashboard/admin?tab=testing' },
|
||||||
|
{ icon: Shield, label: 'Emergencia Geo', path: '/dashboard/admin?tab=emergency-geo' },
|
||||||
|
{ icon: Compass, label: 'Navegación', path: '/dashboard/admin?tab=navigation' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{ icon: AlertTriangle, label: 'Emergencias', path: '/dashboard/admin?tab=emergency' },
|
||||||
|
{ icon: MessageSquare, label: 'Soporte', path: '/dashboard/admin?tab=support' },
|
||||||
|
{ icon: Settings, label: 'Configuración', path: '/dashboard/admin?tab=config' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: Plus,
|
||||||
|
label: 'Channel Manager',
|
||||||
|
path: '/dashboard/channel-manager',
|
||||||
|
subItems: [
|
||||||
|
{ icon: BarChart3, label: 'Resumen', path: '/dashboard/channel-manager?tab=overview' },
|
||||||
|
{ icon: Zap, label: 'Canales', path: '/dashboard/channel-manager?tab=channels' },
|
||||||
|
{ icon: Home, label: 'Propiedades', path: '/dashboard/channel-manager?tab=listings' },
|
||||||
|
{ icon: BookOpen, label: 'Reservas', path: '/dashboard/channel-manager?tab=reservations' },
|
||||||
|
{ icon: BarChart3, label: 'Analytics', path: '/dashboard/channel-manager?tab=analytics' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: Hotel,
|
||||||
|
label: 'Hotel Management',
|
||||||
|
path: '/dashboard/hotel',
|
||||||
|
subItems: [
|
||||||
|
{ icon: Home, label: 'Habitaciones', path: '/dashboard/hotel/rooms' },
|
||||||
|
{ icon: DoorOpen, label: 'Check-in', path: '/dashboard/hotel/checkin' },
|
||||||
|
{ icon: BellRing, label: 'Room Service', path: '/dashboard/hotel/room-service' },
|
||||||
|
{ icon: Key, label: 'Acceso', path: '/dashboard/hotel/keyless' },
|
||||||
|
{ icon: Users, label: 'Personal', path: '/dashboard/hotel/staff' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: UtensilsCrossed,
|
||||||
|
label: 'Restaurant POS',
|
||||||
|
path: '/dashboard/restaurant',
|
||||||
|
subItems: [
|
||||||
|
{ icon: CreditCard, label: 'POS Terminal', path: '/dashboard/restaurant/pos' },
|
||||||
|
{ icon: Receipt, label: 'Pedidos', path: '/dashboard/restaurant/orders' },
|
||||||
|
{ icon: ChefHat, label: 'Cocina', path: '/dashboard/restaurant/kitchen' },
|
||||||
|
{ icon: Receipt, label: 'Cuentas', path: '/dashboard/restaurant/bills' },
|
||||||
|
{ icon: Grid3x3, label: 'Mesas', path: '/dashboard/restaurant/tables' },
|
||||||
|
{ icon: UtensilsCrossed, label: 'Menú', path: '/dashboard/restaurant/menu' },
|
||||||
|
{ icon: Package, label: 'Inventario', path: '/dashboard/restaurant/inventory' },
|
||||||
|
{ icon: Users, label: 'Personal', path: '/dashboard/restaurant/staff' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{ icon: Brain, label: 'Personalization', path: '/dashboard/personalization' },
|
||||||
|
{ icon: Shield, label: 'Security', path: '/dashboard/security' },
|
||||||
|
{ icon: Car, label: 'Vehicle Management', path: '/dashboard/vehicle-management' },
|
||||||
|
{ icon: Leaf, label: 'Sustainability', path: '/dashboard/sustainability' },
|
||||||
|
{ icon: Store, label: 'Comercios', path: '/dashboard/establishments' },
|
||||||
|
{ icon: Wallet, label: 'Wallet', path: '/dashboard/wallet' },
|
||||||
|
{ icon: MessageSquare, label: 'Message', path: '/dashboard/messages', badge: '2' },
|
||||||
|
];
|
||||||
|
|
||||||
|
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
<DashboardStyles />
|
<DashboardStyles />
|
||||||
<SidebarProvider>
|
<div className="min-h-screen" style={{ fontFamily: '"Wix Madefor Display", sans-serif', backgroundColor: '#f8f4f3' }}>
|
||||||
<div className="min-h-screen w-full flex" style={{ fontFamily: '"Wix Madefor Display", sans-serif', backgroundColor: '#f8f4f3' }}>
|
|
||||||
{/* Background decorations */}
|
{/* Background decorations */}
|
||||||
<div className="decoration blur-2"></div>
|
<div className="decoration blur-2"></div>
|
||||||
<div className="decoration blur-3"></div>
|
<div className="decoration blur-3"></div>
|
||||||
|
|
||||||
{/* Sidebar */}
|
{/* Sidebar */}
|
||||||
<AppSidebar />
|
<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 || location.pathname.startsWith(item.path + '/');
|
||||||
|
const hasDirectSubItems = item.subItems && item.subItems.length > 0;
|
||||||
|
const isExpanded = expandedItems[item.path] || (hasDirectSubItems && item.subItems.some(sub => location.pathname === sub.path || location.pathname.startsWith(sub.path + '/')));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div key={item.path}>
|
||||||
|
<div className={`flex items-center justify-between ${sidebarCollapsed ? '' : 'pr-2'}`}>
|
||||||
|
<Link
|
||||||
|
to={item.path}
|
||||||
|
className={`flex items-center space-x-3 px-7 py-2.5 rounded-none transition-all border-l-4 flex-1 ${
|
||||||
|
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, 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>
|
||||||
|
{!sidebarCollapsed && hasDirectSubItems && (
|
||||||
|
<button
|
||||||
|
onClick={() => toggleExpanded(item.path)}
|
||||||
|
className="p-1 hover:bg-gray-200 rounded mr-2"
|
||||||
|
>
|
||||||
|
{isExpanded ? (
|
||||||
|
<ChevronDown className="w-3 h-3" />
|
||||||
|
) : (
|
||||||
|
<ChevronRight className="w-3 h-3" />
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Sub-items */}
|
||||||
|
{!sidebarCollapsed && hasDirectSubItems && isExpanded && (
|
||||||
|
<div className="mt-1 ml-10 space-y-1">
|
||||||
|
{item.subItems.map((sub) => {
|
||||||
|
const SubIcon = sub.icon;
|
||||||
|
const activeSubPath = location.pathname === sub.path || location.pathname.startsWith(sub.path) || (sub.path.includes('?tab=') && location.search.includes(sub.path.split('?tab=')[1]));
|
||||||
|
const hasSubSubItems = sub.subItems && sub.subItems.length > 0;
|
||||||
|
const isSubExpanded = expandedItems[sub.path] || (hasSubSubItems && sub.subItems.some(subsub => location.search.includes(subsub.path.split('?tab=')[1])));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div key={sub.path}>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<Link
|
||||||
|
to={sub.path}
|
||||||
|
className={`flex items-center space-x-2 px-3 py-2 rounded-md text-sm flex-1 ${activeSubPath ? 'bg-orange-50 text-orange-800' : 'text-gray-600 hover:text-orange-600 hover:bg-gray-50'}`}
|
||||||
|
>
|
||||||
|
<SubIcon className="h-4 w-4" />
|
||||||
|
<span>{sub.label}</span>
|
||||||
|
</Link>
|
||||||
|
{hasSubSubItems && (
|
||||||
|
<button
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
toggleExpanded(sub.path);
|
||||||
|
}}
|
||||||
|
className="p-1 hover:bg-gray-200 rounded mr-1"
|
||||||
|
>
|
||||||
|
{isSubExpanded ? (
|
||||||
|
<ChevronDown className="w-3 h-3" />
|
||||||
|
) : (
|
||||||
|
<ChevronRight className="w-3 h-3" />
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Sub-sub-items */}
|
||||||
|
{hasSubSubItems && isSubExpanded && (
|
||||||
|
<div className="mt-1 ml-6 space-y-1">
|
||||||
|
{sub.subItems.map((subsub) => {
|
||||||
|
const SubSubIcon = subsub.icon;
|
||||||
|
const activeSubSubPath = location.search.includes(subsub.path.split('?tab=')[1]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
key={subsub.path}
|
||||||
|
to={subsub.path}
|
||||||
|
className={`flex items-center space-x-2 px-3 py-1.5 rounded-md text-xs ${activeSubSubPath ? 'bg-orange-50 text-orange-800' : 'text-gray-500 hover:text-orange-600 hover:bg-gray-50'}`}
|
||||||
|
>
|
||||||
|
<SubSubIcon className="h-3 w-3" />
|
||||||
|
<span>{subsub.label}</span>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</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 */}
|
{/* Main Content Area */}
|
||||||
<div className="flex-1 flex flex-col w-full">
|
<div className={`transition-all duration-300 ${sidebarCollapsed ? 'ml-16' : 'ml-80'}`}>
|
||||||
{/* Top Navigation */}
|
{/* Top Navigation */}
|
||||||
<nav className="h-20 px-6 py-4 z-10 relative" style={{ backgroundColor: 'rgba(255, 255, 255, 0.7)', backdropFilter: 'blur(15px)' }}>
|
<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">
|
<div className="flex items-center justify-between">
|
||||||
{/* Left Side */}
|
{/* Left Side */}
|
||||||
<div className="flex items-center space-x-4">
|
<div className="flex items-center space-x-4">
|
||||||
<SidebarTrigger className="p-2 rounded-full text-white transition-colors" style={{ backgroundColor: '#F84525' }}>
|
<button
|
||||||
|
onClick={() => setSidebarCollapsed(!sidebarCollapsed)}
|
||||||
|
className="p-2 rounded-full text-white transition-colors"
|
||||||
|
style={{ backgroundColor: '#F84525' }}
|
||||||
|
>
|
||||||
<Menu className="w-5 h-5" />
|
<Menu className="w-5 h-5" />
|
||||||
</SidebarTrigger>
|
</button>
|
||||||
|
|
||||||
{/* Search */}
|
{/* Search */}
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
@@ -56,20 +455,10 @@ const DashboardLayout = ({ children }: { children: React.ReactNode }) => {
|
|||||||
backgroundColor: '#fff',
|
backgroundColor: '#fff',
|
||||||
borderColor: '#fff',
|
borderColor: '#fff',
|
||||||
height: '48px',
|
height: '48px',
|
||||||
borderRadius: '0.8rem',
|
borderRadius: '0.8rem'
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<span
|
<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' }}>
|
||||||
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+/)
|
(Ctrl+/)
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -91,30 +480,18 @@ const DashboardLayout = ({ children }: { children: React.ReactNode }) => {
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
{/* Notifications */}
|
{/* Notifications */}
|
||||||
<button
|
<button className="p-2 rounded-xl transition-colors relative" style={{ backgroundColor: '#21272f', borderColor: '#21272f', color: '#F84525' }} title="Notificaciones">
|
||||||
className="p-2 rounded-xl transition-colors relative"
|
|
||||||
style={{ backgroundColor: '#21272f', borderColor: '#21272f', color: '#F84525' }}
|
|
||||||
title="Notificaciones"
|
|
||||||
>
|
|
||||||
<Bell className="w-5 h-5" />
|
<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 className="absolute -top-1 -right-1 bg-red-500 text-white text-[10px] px-1 py-0.5 rounded-full">!</span>
|
||||||
!
|
|
||||||
</span>
|
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{/* Theme Toggle */}
|
{/* Theme Toggle */}
|
||||||
<button
|
<button className="p-2 rounded-xl transition-colors" style={{ backgroundColor: '#21272f', borderColor: '#21272f', color: '#F84525' }}>
|
||||||
className="p-2 rounded-xl transition-colors"
|
|
||||||
style={{ backgroundColor: '#21272f', borderColor: '#21272f', color: '#F84525' }}
|
|
||||||
>
|
|
||||||
<Sun className="w-5 h-5" />
|
<Sun className="w-5 h-5" />
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{/* Fullscreen */}
|
{/* Fullscreen */}
|
||||||
<button
|
<button className="p-2 rounded-xl transition-colors" style={{ backgroundColor: '#21272f', borderColor: '#21272f', color: '#F84525' }}>
|
||||||
className="p-2 rounded-xl transition-colors"
|
|
||||||
style={{ backgroundColor: '#21272f', borderColor: '#21272f', color: '#F84525' }}
|
|
||||||
>
|
|
||||||
<Maximize className="w-5 h-5" />
|
<Maximize className="w-5 h-5" />
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
@@ -131,12 +508,12 @@ const DashboardLayout = ({ children }: { children: React.ReactNode }) => {
|
|||||||
<div className="font-semibold text-gray-800 text-sm flex items-center space-x-2">
|
<div className="font-semibold text-gray-800 text-sm flex items-center space-x-2">
|
||||||
<span>{user?.name || 'Usuario'}</span>
|
<span>{user?.name || 'Usuario'}</span>
|
||||||
{user?.role === 'super_admin' && (
|
{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">
|
<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>
|
||||||
Super Admin
|
|
||||||
</span>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-xs text-gray-500">{user?.email || 'example@gmail.com'}</div>
|
<div className="text-xs text-gray-500">
|
||||||
|
{user?.email || 'example@gmail.com'}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -145,10 +522,11 @@ const DashboardLayout = ({ children }: { children: React.ReactNode }) => {
|
|||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
{/* Page Content */}
|
{/* Page Content */}
|
||||||
<main className="p-6 flex-1">{children}</main>
|
<main className="p-6">
|
||||||
|
{children}
|
||||||
|
</main>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</SidebarProvider>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,185 +0,0 @@
|
|||||||
import { Link, useLocation } from "react-router-dom";
|
|
||||||
import {
|
|
||||||
SidebarMenu,
|
|
||||||
SidebarMenuItem,
|
|
||||||
SidebarMenuButton,
|
|
||||||
SidebarMenuSub,
|
|
||||||
SidebarMenuSubItem,
|
|
||||||
SidebarMenuSubButton,
|
|
||||||
useSidebar,
|
|
||||||
} from "@/components/ui/sidebar";
|
|
||||||
import {
|
|
||||||
Collapsible,
|
|
||||||
CollapsibleContent,
|
|
||||||
CollapsibleTrigger,
|
|
||||||
} from "@/components/ui/collapsible";
|
|
||||||
import { ChevronRight } from "lucide-react";
|
|
||||||
import { MenuItem } from "@/config/sidebar";
|
|
||||||
import { useState, useEffect } from "react";
|
|
||||||
|
|
||||||
interface SidebarMenuRendererProps {
|
|
||||||
items: MenuItem[];
|
|
||||||
level?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function SidebarMenuRenderer({ items, level = 0 }: SidebarMenuRendererProps) {
|
|
||||||
const { open, isMobile } = useSidebar();
|
|
||||||
const collapsed = !open && !isMobile;
|
|
||||||
const location = useLocation();
|
|
||||||
const [expandedItems, setExpandedItems] = useState<Record<string, boolean>>({});
|
|
||||||
|
|
||||||
const isActive = (path: string) => {
|
|
||||||
if (path.includes("?tab=")) {
|
|
||||||
const [basePath, tabQuery] = path.split("?tab=");
|
|
||||||
return (
|
|
||||||
location.pathname === basePath &&
|
|
||||||
location.search.includes(tabQuery)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return location.pathname === path || location.pathname.startsWith(path + "/");
|
|
||||||
};
|
|
||||||
|
|
||||||
const hasActiveChild = (items: MenuItem[]): boolean => {
|
|
||||||
return items.some((item) => {
|
|
||||||
if (isActive(item.path)) return true;
|
|
||||||
if (item.subItems) return hasActiveChild(item.subItems);
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const toggleExpanded = (itemPath: string) => {
|
|
||||||
setExpandedItems((prev) => ({
|
|
||||||
...prev,
|
|
||||||
[itemPath]: !prev[itemPath],
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
// Auto-expand groups with active children
|
|
||||||
useEffect(() => {
|
|
||||||
items.forEach((item) => {
|
|
||||||
if (item.subItems && hasActiveChild(item.subItems)) {
|
|
||||||
setExpandedItems((prev) => ({
|
|
||||||
...prev,
|
|
||||||
[item.path]: true,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, [location.pathname, location.search]);
|
|
||||||
|
|
||||||
const renderMenuItem = (item: MenuItem) => {
|
|
||||||
const Icon = item.icon;
|
|
||||||
const active = isActive(item.path);
|
|
||||||
const hasSubItems = item.subItems && item.subItems.length > 0;
|
|
||||||
const isExpanded = expandedItems[item.path] || (hasSubItems && hasActiveChild(item.subItems));
|
|
||||||
|
|
||||||
if (hasSubItems) {
|
|
||||||
return (
|
|
||||||
<Collapsible
|
|
||||||
key={item.path}
|
|
||||||
open={isExpanded}
|
|
||||||
onOpenChange={() => toggleExpanded(item.path)}
|
|
||||||
>
|
|
||||||
<SidebarMenuItem>
|
|
||||||
<CollapsibleTrigger asChild>
|
|
||||||
<SidebarMenuButton
|
|
||||||
className={`${
|
|
||||||
active ? "bg-primary text-primary-foreground" : ""
|
|
||||||
} ${collapsed && level === 0 ? "justify-center" : ""}`}
|
|
||||||
>
|
|
||||||
<Icon className="h-4 w-4" />
|
|
||||||
{!collapsed && <span>{item.label}</span>}
|
|
||||||
{!collapsed && (
|
|
||||||
<ChevronRight
|
|
||||||
className={`ml-auto h-4 w-4 transition-transform ${
|
|
||||||
isExpanded ? "rotate-90" : ""
|
|
||||||
}`}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</SidebarMenuButton>
|
|
||||||
</CollapsibleTrigger>
|
|
||||||
<CollapsibleContent>
|
|
||||||
<SidebarMenuSub>
|
|
||||||
{item.subItems!.map((subItem) =>
|
|
||||||
renderSubMenuItem(subItem)
|
|
||||||
)}
|
|
||||||
</SidebarMenuSub>
|
|
||||||
</CollapsibleContent>
|
|
||||||
</SidebarMenuItem>
|
|
||||||
</Collapsible>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<SidebarMenuItem key={item.path}>
|
|
||||||
<SidebarMenuButton asChild isActive={active}>
|
|
||||||
<Link to={item.path} className={collapsed && level === 0 ? "justify-center" : ""}>
|
|
||||||
<Icon className="h-4 w-4" />
|
|
||||||
{!collapsed && <span>{item.label}</span>}
|
|
||||||
{!collapsed && item.badge && (
|
|
||||||
<span className="ml-auto bg-green-500 text-white text-xs px-2 py-0.5 rounded-full">
|
|
||||||
{item.badge}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</Link>
|
|
||||||
</SidebarMenuButton>
|
|
||||||
</SidebarMenuItem>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const renderSubMenuItem = (item: MenuItem): JSX.Element => {
|
|
||||||
const Icon = item.icon;
|
|
||||||
const active = isActive(item.path);
|
|
||||||
const hasSubItems = item.subItems && item.subItems.length > 0;
|
|
||||||
const isExpanded = expandedItems[item.path] || (hasSubItems && hasActiveChild(item.subItems));
|
|
||||||
|
|
||||||
if (hasSubItems) {
|
|
||||||
return (
|
|
||||||
<Collapsible
|
|
||||||
key={item.path}
|
|
||||||
open={isExpanded}
|
|
||||||
onOpenChange={() => toggleExpanded(item.path)}
|
|
||||||
>
|
|
||||||
<SidebarMenuSubItem>
|
|
||||||
<CollapsibleTrigger asChild>
|
|
||||||
<SidebarMenuSubButton
|
|
||||||
className={active ? "bg-primary/10 text-primary" : ""}
|
|
||||||
>
|
|
||||||
<Icon className="h-4 w-4" />
|
|
||||||
<span>{item.label}</span>
|
|
||||||
<ChevronRight
|
|
||||||
className={`ml-auto h-4 w-4 transition-transform ${
|
|
||||||
isExpanded ? "rotate-90" : ""
|
|
||||||
}`}
|
|
||||||
/>
|
|
||||||
</SidebarMenuSubButton>
|
|
||||||
</CollapsibleTrigger>
|
|
||||||
<CollapsibleContent>
|
|
||||||
<SidebarMenuSub>
|
|
||||||
{item.subItems!.map((subItem) =>
|
|
||||||
renderSubMenuItem(subItem)
|
|
||||||
)}
|
|
||||||
</SidebarMenuSub>
|
|
||||||
</CollapsibleContent>
|
|
||||||
</SidebarMenuSubItem>
|
|
||||||
</Collapsible>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<SidebarMenuSubItem key={item.path}>
|
|
||||||
<SidebarMenuSubButton asChild isActive={active}>
|
|
||||||
<Link to={item.path}>
|
|
||||||
<Icon className="h-4 w-4" />
|
|
||||||
<span>{item.label}</span>
|
|
||||||
</Link>
|
|
||||||
</SidebarMenuSubButton>
|
|
||||||
</SidebarMenuSubItem>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<SidebarMenu>
|
|
||||||
{items.map((item) => renderMenuItem(item))}
|
|
||||||
</SidebarMenu>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,170 +0,0 @@
|
|||||||
import {
|
|
||||||
Home,
|
|
||||||
Plus,
|
|
||||||
Wallet,
|
|
||||||
List,
|
|
||||||
MessageSquare,
|
|
||||||
Star,
|
|
||||||
BookOpen,
|
|
||||||
Heart,
|
|
||||||
FileText,
|
|
||||||
User,
|
|
||||||
Users,
|
|
||||||
Settings,
|
|
||||||
LogOut,
|
|
||||||
CreditCard,
|
|
||||||
BarChart3,
|
|
||||||
MapPin,
|
|
||||||
DollarSign,
|
|
||||||
AlertTriangle,
|
|
||||||
Car,
|
|
||||||
Target,
|
|
||||||
Zap,
|
|
||||||
Eye,
|
|
||||||
Megaphone,
|
|
||||||
Hotel,
|
|
||||||
UtensilsCrossed,
|
|
||||||
Brain,
|
|
||||||
DoorOpen,
|
|
||||||
BellRing,
|
|
||||||
Key,
|
|
||||||
Receipt,
|
|
||||||
ChefHat,
|
|
||||||
Grid3x3,
|
|
||||||
Package,
|
|
||||||
Globe,
|
|
||||||
Compass,
|
|
||||||
Map,
|
|
||||||
Shield,
|
|
||||||
Sparkles,
|
|
||||||
Leaf,
|
|
||||||
Store,
|
|
||||||
Navigation,
|
|
||||||
} from "lucide-react";
|
|
||||||
|
|
||||||
export interface MenuItem {
|
|
||||||
icon: any;
|
|
||||||
label: string;
|
|
||||||
path: string;
|
|
||||||
badge?: string;
|
|
||||||
subItems?: MenuItem[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface MenuGroup {
|
|
||||||
label: string;
|
|
||||||
items: MenuItem[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export const menuGroups: MenuGroup[] = [
|
|
||||||
{
|
|
||||||
label: "Main Menu",
|
|
||||||
items: [
|
|
||||||
{ icon: Home, label: "Dashboard", path: "/dashboard" },
|
|
||||||
{
|
|
||||||
icon: Settings,
|
|
||||||
label: "Admin Panel",
|
|
||||||
path: "/dashboard/admin",
|
|
||||||
subItems: [
|
|
||||||
{ icon: BarChart3, label: "Resumen General", path: "/dashboard/admin?tab=overview" },
|
|
||||||
{ icon: Users, label: "Usuarios", path: "/dashboard/admin?tab=users" },
|
|
||||||
{ icon: MapPin, label: "Proveedores", path: "/dashboard/admin?tab=services" },
|
|
||||||
{ icon: DollarSign, label: "Financiero", path: "/dashboard/admin?tab=financial" },
|
|
||||||
{
|
|
||||||
icon: FileText,
|
|
||||||
label: "Contenido",
|
|
||||||
path: "/dashboard/admin?tab=content",
|
|
||||||
subItems: [
|
|
||||||
{ icon: Globe, label: "Destinos", path: "/dashboard/admin?tab=content-destinations" },
|
|
||||||
{ icon: MapPin, label: "Lugares", path: "/dashboard/admin?tab=content-places" },
|
|
||||||
{ icon: BookOpen, label: "Guías", path: "/dashboard/admin?tab=content-guides" },
|
|
||||||
{ icon: Car, label: "Taxis", path: "/dashboard/admin?tab=content-taxis" },
|
|
||||||
{ icon: Navigation, label: "Geolocalización", path: "/dashboard/admin?tab=content-geolocation" },
|
|
||||||
{ icon: Megaphone, label: "Promocional", path: "/dashboard/admin?tab=content-promotional" },
|
|
||||||
{ icon: Sparkles, label: "AI Guías", path: "/dashboard/admin?tab=content-ai-guides" },
|
|
||||||
{ icon: Eye, label: "AR Content", path: "/dashboard/admin?tab=content-ar" },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: Map,
|
|
||||||
label: "Geolocalización",
|
|
||||||
path: "/dashboard/admin?tab=geofences",
|
|
||||||
subItems: [
|
|
||||||
{ icon: Target, label: "Geofences", path: "/dashboard/admin?tab=geofences" },
|
|
||||||
{ icon: BarChart3, label: "Analytics", path: "/dashboard/admin?tab=analytics" },
|
|
||||||
{ icon: Zap, label: "Testing", path: "/dashboard/admin?tab=testing" },
|
|
||||||
{ icon: Shield, label: "Emergencia Geo", path: "/dashboard/admin?tab=emergency-geo" },
|
|
||||||
{ icon: Compass, label: "Navegación", path: "/dashboard/admin?tab=navigation" },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{ icon: AlertTriangle, label: "Emergencias", path: "/dashboard/admin?tab=emergency" },
|
|
||||||
{ icon: MessageSquare, label: "Soporte", path: "/dashboard/admin?tab=support" },
|
|
||||||
{ icon: Settings, label: "Configuración", path: "/dashboard/admin?tab=config" },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: Plus,
|
|
||||||
label: "Channel Manager",
|
|
||||||
path: "/dashboard/channel-manager",
|
|
||||||
subItems: [
|
|
||||||
{ icon: BarChart3, label: "Resumen", path: "/dashboard/channel-manager?tab=overview" },
|
|
||||||
{ icon: Zap, label: "Canales", path: "/dashboard/channel-manager?tab=channels" },
|
|
||||||
{ icon: Home, label: "Propiedades", path: "/dashboard/channel-manager?tab=listings" },
|
|
||||||
{ icon: BookOpen, label: "Reservas", path: "/dashboard/channel-manager?tab=reservations" },
|
|
||||||
{ icon: BarChart3, label: "Analytics", path: "/dashboard/channel-manager?tab=analytics" },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: Hotel,
|
|
||||||
label: "Hotel Management",
|
|
||||||
path: "/dashboard/hotel",
|
|
||||||
subItems: [
|
|
||||||
{ icon: Home, label: "Habitaciones", path: "/dashboard/hotel/rooms" },
|
|
||||||
{ icon: DoorOpen, label: "Check-in", path: "/dashboard/hotel/checkin" },
|
|
||||||
{ icon: BellRing, label: "Room Service", path: "/dashboard/hotel/room-service" },
|
|
||||||
{ icon: Key, label: "Acceso", path: "/dashboard/hotel/keyless" },
|
|
||||||
{ icon: Users, label: "Personal", path: "/dashboard/hotel/staff" },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: UtensilsCrossed,
|
|
||||||
label: "Restaurant POS",
|
|
||||||
path: "/dashboard/restaurant",
|
|
||||||
subItems: [
|
|
||||||
{ icon: CreditCard, label: "POS Terminal", path: "/dashboard/restaurant/pos" },
|
|
||||||
{ icon: Receipt, label: "Pedidos", path: "/dashboard/restaurant/orders" },
|
|
||||||
{ icon: ChefHat, label: "Cocina", path: "/dashboard/restaurant/kitchen" },
|
|
||||||
{ icon: Receipt, label: "Cuentas", path: "/dashboard/restaurant/bills" },
|
|
||||||
{ icon: Grid3x3, label: "Mesas", path: "/dashboard/restaurant/tables" },
|
|
||||||
{ icon: UtensilsCrossed, label: "Menú", path: "/dashboard/restaurant/menu" },
|
|
||||||
{ icon: Package, label: "Inventario", path: "/dashboard/restaurant/inventory" },
|
|
||||||
{ icon: Users, label: "Personal", path: "/dashboard/restaurant/staff" },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{ icon: Brain, label: "Personalization", path: "/dashboard/personalization" },
|
|
||||||
{ icon: Shield, label: "Security", path: "/dashboard/security" },
|
|
||||||
{ icon: Car, label: "Vehicle Management", path: "/dashboard/vehicle-management" },
|
|
||||||
{ icon: Leaf, label: "Sustainability", path: "/dashboard/sustainability" },
|
|
||||||
{ icon: Store, label: "Comercios", path: "/dashboard/establishments" },
|
|
||||||
{ icon: Wallet, label: "Wallet", path: "/dashboard/wallet" },
|
|
||||||
{ icon: MessageSquare, label: "Message", path: "/dashboard/messages", badge: "2" },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Listing",
|
|
||||||
items: [
|
|
||||||
{ icon: List, label: "My Listing", path: "/dashboard/my-listings" },
|
|
||||||
{ 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" },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Account",
|
|
||||||
items: [
|
|
||||||
{ icon: User, label: "Edit Profile", path: "/dashboard/profile" },
|
|
||||||
{ icon: CreditCard, label: "Wallet", path: "/dashboard/wallet" },
|
|
||||||
{ icon: Settings, label: "Setting", path: "/dashboard/settings" },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
import React, { createContext, useContext, useState, useEffect } from 'react';
|
import React, { createContext, useContext, useState, useEffect } from 'react';
|
||||||
import { translations } from '@/i18n/translations';
|
import { translations } from '@/i18n/translations';
|
||||||
|
|
||||||
type Language = 'en' | 'es' | 'fr' | 'zh' | 'de';
|
type Language = 'es' | 'en' | 'fr';
|
||||||
type TranslationKey = keyof typeof translations.en;
|
type TranslationKey = keyof typeof translations.es;
|
||||||
|
|
||||||
interface LanguageContextType {
|
interface LanguageContextType {
|
||||||
language: Language;
|
language: Language;
|
||||||
@@ -15,7 +15,7 @@ const LanguageContext = createContext<LanguageContextType | undefined>(undefined
|
|||||||
export const LanguageProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
export const LanguageProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
||||||
const [language, setLanguageState] = useState<Language>(() => {
|
const [language, setLanguageState] = useState<Language>(() => {
|
||||||
const saved = localStorage.getItem('karibeo-language');
|
const saved = localStorage.getItem('karibeo-language');
|
||||||
return (saved as Language) || 'en';
|
return (saved as Language) || 'es';
|
||||||
});
|
});
|
||||||
|
|
||||||
const setLanguage = (lang: Language) => {
|
const setLanguage = (lang: Language) => {
|
||||||
@@ -24,7 +24,7 @@ export const LanguageProvider: React.FC<{ children: React.ReactNode }> = ({ chil
|
|||||||
};
|
};
|
||||||
|
|
||||||
const t = (key: TranslationKey): string => {
|
const t = (key: TranslationKey): string => {
|
||||||
return translations[language][key] || translations.en[key] || key;
|
return translations[language][key] || translations.es[key] || key;
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -1,90 +1,4 @@
|
|||||||
export const translations = {
|
export const translations = {
|
||||||
en: {
|
|
||||||
// Navigation
|
|
||||||
home: 'Home',
|
|
||||||
explore: 'Explore',
|
|
||||||
about: 'About',
|
|
||||||
dashboard: 'Dashboard',
|
|
||||||
|
|
||||||
// Authentication
|
|
||||||
signIn: 'Sign In',
|
|
||||||
signUp: 'Sign Up',
|
|
||||||
signOut: 'Sign Out',
|
|
||||||
email: 'Email',
|
|
||||||
password: 'Password',
|
|
||||||
confirmPassword: 'Confirm Password',
|
|
||||||
fullName: 'Full Name',
|
|
||||||
rememberMe: 'Remember me',
|
|
||||||
forgotPassword: 'Forgot password',
|
|
||||||
|
|
||||||
// Welcome messages
|
|
||||||
welcomeBack: 'Welcome back! Please',
|
|
||||||
toContinue: 'to continue.',
|
|
||||||
welcomeSignUp: 'Welcome! Please',
|
|
||||||
unlockContent: 'Unlock a world of exclusive content, enjoy special offers, and be the first to dive into exciting news and updates by joining our community!',
|
|
||||||
|
|
||||||
// Social auth
|
|
||||||
signUpWithApple: 'Sign up with Apple',
|
|
||||||
signUpWithGoogle: 'Sign up with Google',
|
|
||||||
privacyNotice: 'We won\'t post anything without your permission and your personal details are kept private',
|
|
||||||
|
|
||||||
// Form labels
|
|
||||||
enterEmail: 'Enter Email',
|
|
||||||
enterValidEmail: 'Enter your valid email',
|
|
||||||
enterPassword: 'Enter Password',
|
|
||||||
|
|
||||||
// Hero section
|
|
||||||
heroBadge: 'WE ARE #1 ON THE MARKET',
|
|
||||||
heroTitle: 'We\'re Here To Help You Navigate While Traveling',
|
|
||||||
heroSubtitle: 'You\'ll get comprehensive results based on the provided location.',
|
|
||||||
searchPlaceholder: 'What are you looking for?',
|
|
||||||
locationPlaceholder: 'Location',
|
|
||||||
searchButton: 'Search places',
|
|
||||||
|
|
||||||
// Categories
|
|
||||||
apartments: 'Apartments',
|
|
||||||
restaurants: 'Restaurants',
|
|
||||||
events: 'Events/Arts',
|
|
||||||
shops: 'Shops',
|
|
||||||
museums: 'Museums',
|
|
||||||
gyms: 'Gymnasiums',
|
|
||||||
listings: 'listings',
|
|
||||||
|
|
||||||
// Dashboard
|
|
||||||
totalIncome: 'Total Income',
|
|
||||||
visitors: 'Visitors',
|
|
||||||
totalOrders: 'Total Orders',
|
|
||||||
recentBookings: 'Recent Bookings',
|
|
||||||
statistics: 'Statistics',
|
|
||||||
myListings: 'My Listings',
|
|
||||||
addListing: 'Add Listing',
|
|
||||||
wallet: 'Wallet',
|
|
||||||
profile: 'Profile',
|
|
||||||
settings: 'Settings',
|
|
||||||
|
|
||||||
// Common
|
|
||||||
loading: 'Loading...',
|
|
||||||
error: 'Error',
|
|
||||||
success: 'Success',
|
|
||||||
cancel: 'Cancel',
|
|
||||||
save: 'Save',
|
|
||||||
delete: 'Delete',
|
|
||||||
edit: 'Edit',
|
|
||||||
view: 'View',
|
|
||||||
|
|
||||||
// Process section
|
|
||||||
processTitle: 'Find Your Dream Place The Best Way',
|
|
||||||
processSubtitle: 'Discover exciting categories. Find what you\'re looking for.',
|
|
||||||
step1: 'Input your location to start looking for landmarks.',
|
|
||||||
step2: 'Make an appointment at the place you want to visit.',
|
|
||||||
step3: 'Visit the place and enjoy the experience.',
|
|
||||||
|
|
||||||
// Explore section
|
|
||||||
topRegions: 'Top Regions',
|
|
||||||
exploreCities: 'Explore Cities',
|
|
||||||
exploreMore: 'Explore more'
|
|
||||||
},
|
|
||||||
|
|
||||||
es: {
|
es: {
|
||||||
// Navigation
|
// Navigation
|
||||||
home: 'Inicio',
|
home: 'Inicio',
|
||||||
@@ -171,6 +85,92 @@ export const translations = {
|
|||||||
exploreMore: 'Explorar más'
|
exploreMore: 'Explorar más'
|
||||||
},
|
},
|
||||||
|
|
||||||
|
en: {
|
||||||
|
// Navigation
|
||||||
|
home: 'Home',
|
||||||
|
explore: 'Explore',
|
||||||
|
about: 'About',
|
||||||
|
dashboard: 'Dashboard',
|
||||||
|
|
||||||
|
// Authentication
|
||||||
|
signIn: 'Sign In',
|
||||||
|
signUp: 'Sign Up',
|
||||||
|
signOut: 'Sign Out',
|
||||||
|
email: 'Email',
|
||||||
|
password: 'Password',
|
||||||
|
confirmPassword: 'Confirm Password',
|
||||||
|
fullName: 'Full Name',
|
||||||
|
rememberMe: 'Remember me',
|
||||||
|
forgotPassword: 'Forgot password',
|
||||||
|
|
||||||
|
// Welcome messages
|
||||||
|
welcomeBack: 'Welcome back! Please',
|
||||||
|
toContinue: 'to continue.',
|
||||||
|
welcomeSignUp: 'Welcome! Please',
|
||||||
|
unlockContent: 'Unlock a world of exclusive content, enjoy special offers, and be the first to dive into exciting news and updates by joining our community!',
|
||||||
|
|
||||||
|
// Social auth
|
||||||
|
signUpWithApple: 'Sign up with Apple',
|
||||||
|
signUpWithGoogle: 'Sign up with Google',
|
||||||
|
privacyNotice: 'We won\'t post anything without your permission and your personal details are kept private',
|
||||||
|
|
||||||
|
// Form labels
|
||||||
|
enterEmail: 'Enter Email',
|
||||||
|
enterValidEmail: 'Enter your valid email',
|
||||||
|
enterPassword: 'Enter Password',
|
||||||
|
|
||||||
|
// Hero section
|
||||||
|
heroBadge: 'WE ARE #1 ON THE MARKET',
|
||||||
|
heroTitle: 'We\'re Here To Help You Navigate While Traveling',
|
||||||
|
heroSubtitle: 'You\'ll get comprehensive results based on the provided location.',
|
||||||
|
searchPlaceholder: 'What are you looking for?',
|
||||||
|
locationPlaceholder: 'Location',
|
||||||
|
searchButton: 'Search places',
|
||||||
|
|
||||||
|
// Categories
|
||||||
|
apartments: 'Apartments',
|
||||||
|
restaurants: 'Restaurants',
|
||||||
|
events: 'Events/Arts',
|
||||||
|
shops: 'Shops',
|
||||||
|
museums: 'Museums',
|
||||||
|
gyms: 'Gymnasiums',
|
||||||
|
listings: 'listings',
|
||||||
|
|
||||||
|
// Dashboard
|
||||||
|
totalIncome: 'Total Income',
|
||||||
|
visitors: 'Visitors',
|
||||||
|
totalOrders: 'Total Orders',
|
||||||
|
recentBookings: 'Recent Bookings',
|
||||||
|
statistics: 'Statistics',
|
||||||
|
myListings: 'My Listings',
|
||||||
|
addListing: 'Add Listing',
|
||||||
|
wallet: 'Wallet',
|
||||||
|
profile: 'Profile',
|
||||||
|
settings: 'Settings',
|
||||||
|
|
||||||
|
// Common
|
||||||
|
loading: 'Loading...',
|
||||||
|
error: 'Error',
|
||||||
|
success: 'Success',
|
||||||
|
cancel: 'Cancel',
|
||||||
|
save: 'Save',
|
||||||
|
delete: 'Delete',
|
||||||
|
edit: 'Edit',
|
||||||
|
view: 'View',
|
||||||
|
|
||||||
|
// Process section
|
||||||
|
processTitle: 'Find Your Dream Place The Best Way',
|
||||||
|
processSubtitle: 'Discover exciting categories. Find what you\'re looking for.',
|
||||||
|
step1: 'Input your location to start looking for landmarks.',
|
||||||
|
step2: 'Make an appointment at the place you want to visit.',
|
||||||
|
step3: 'Visit the place and enjoy the experience.',
|
||||||
|
|
||||||
|
// Explore section
|
||||||
|
topRegions: 'Top Regions',
|
||||||
|
exploreCities: 'Explore Cities',
|
||||||
|
exploreMore: 'Explore more'
|
||||||
|
},
|
||||||
|
|
||||||
fr: {
|
fr: {
|
||||||
// Navigation
|
// Navigation
|
||||||
home: 'Accueil',
|
home: 'Accueil',
|
||||||
@@ -255,177 +255,5 @@ export const translations = {
|
|||||||
topRegions: 'Principales Régions',
|
topRegions: 'Principales Régions',
|
||||||
exploreCities: 'Explorer les Villes',
|
exploreCities: 'Explorer les Villes',
|
||||||
exploreMore: 'Explorer plus'
|
exploreMore: 'Explorer plus'
|
||||||
},
|
|
||||||
|
|
||||||
zh: {
|
|
||||||
// Navigation
|
|
||||||
home: '首页',
|
|
||||||
explore: '探索',
|
|
||||||
about: '关于',
|
|
||||||
dashboard: '仪表板',
|
|
||||||
|
|
||||||
// Authentication
|
|
||||||
signIn: '登录',
|
|
||||||
signUp: '注册',
|
|
||||||
signOut: '退出',
|
|
||||||
email: '电子邮件',
|
|
||||||
password: '密码',
|
|
||||||
confirmPassword: '确认密码',
|
|
||||||
fullName: '全名',
|
|
||||||
rememberMe: '记住我',
|
|
||||||
forgotPassword: '忘记密码',
|
|
||||||
|
|
||||||
// Welcome messages
|
|
||||||
welcomeBack: '欢迎回来!请',
|
|
||||||
toContinue: '继续。',
|
|
||||||
welcomeSignUp: '欢迎!请',
|
|
||||||
unlockContent: '通过加入我们的社区,解锁专属内容世界,享受特别优惠,并率先了解激动人心的新闻和更新!',
|
|
||||||
|
|
||||||
// Social auth
|
|
||||||
signUpWithApple: '使用 Apple 注册',
|
|
||||||
signUpWithGoogle: '使用 Google 注册',
|
|
||||||
privacyNotice: '未经您的许可,我们不会发布任何内容,您的个人详细信息将保密',
|
|
||||||
|
|
||||||
// Form labels
|
|
||||||
enterEmail: '输入电子邮件',
|
|
||||||
enterValidEmail: '输入有效的电子邮件',
|
|
||||||
enterPassword: '输入密码',
|
|
||||||
|
|
||||||
// Hero section
|
|
||||||
heroBadge: '我们是市场第一',
|
|
||||||
heroTitle: '我们在这里帮助您在旅行时导航',
|
|
||||||
heroSubtitle: '根据提供的位置,您将获得全面的结果。',
|
|
||||||
searchPlaceholder: '您在寻找什么?',
|
|
||||||
locationPlaceholder: '位置',
|
|
||||||
searchButton: '搜索地点',
|
|
||||||
|
|
||||||
// Categories
|
|
||||||
apartments: '公寓',
|
|
||||||
restaurants: '餐厅',
|
|
||||||
events: '活动/艺术',
|
|
||||||
shops: '商店',
|
|
||||||
museums: '博物馆',
|
|
||||||
gyms: '健身房',
|
|
||||||
listings: '列表',
|
|
||||||
|
|
||||||
// Dashboard
|
|
||||||
totalIncome: '总收入',
|
|
||||||
visitors: '访客',
|
|
||||||
totalOrders: '总订单',
|
|
||||||
recentBookings: '最近预订',
|
|
||||||
statistics: '统计',
|
|
||||||
myListings: '我的列表',
|
|
||||||
addListing: '添加列表',
|
|
||||||
wallet: '钱包',
|
|
||||||
profile: '个人资料',
|
|
||||||
settings: '设置',
|
|
||||||
|
|
||||||
// Common
|
|
||||||
loading: '加载中...',
|
|
||||||
error: '错误',
|
|
||||||
success: '成功',
|
|
||||||
cancel: '取消',
|
|
||||||
save: '保存',
|
|
||||||
delete: '删除',
|
|
||||||
edit: '编辑',
|
|
||||||
view: '查看',
|
|
||||||
|
|
||||||
// Process section
|
|
||||||
processTitle: '以最佳方式找到您的梦想之地',
|
|
||||||
processSubtitle: '发现令人兴奋的类别。找到您想要的。',
|
|
||||||
step1: '输入您的位置以开始查找地标。',
|
|
||||||
step2: '在您想要访问的地方预约。',
|
|
||||||
step3: '参观地点并享受体验。',
|
|
||||||
|
|
||||||
// Explore section
|
|
||||||
topRegions: '热门地区',
|
|
||||||
exploreCities: '探索城市',
|
|
||||||
exploreMore: '探索更多'
|
|
||||||
},
|
|
||||||
|
|
||||||
de: {
|
|
||||||
// Navigation
|
|
||||||
home: 'Startseite',
|
|
||||||
explore: 'Erkunden',
|
|
||||||
about: 'Über uns',
|
|
||||||
dashboard: 'Dashboard',
|
|
||||||
|
|
||||||
// Authentication
|
|
||||||
signIn: 'Anmelden',
|
|
||||||
signUp: 'Registrieren',
|
|
||||||
signOut: 'Abmelden',
|
|
||||||
email: 'E-Mail',
|
|
||||||
password: 'Passwort',
|
|
||||||
confirmPassword: 'Passwort bestätigen',
|
|
||||||
fullName: 'Vollständiger Name',
|
|
||||||
rememberMe: 'Angemeldet bleiben',
|
|
||||||
forgotPassword: 'Passwort vergessen',
|
|
||||||
|
|
||||||
// Welcome messages
|
|
||||||
welcomeBack: 'Willkommen zurück! Bitte',
|
|
||||||
toContinue: 'um fortzufahren.',
|
|
||||||
welcomeSignUp: 'Willkommen! Bitte',
|
|
||||||
unlockContent: 'Entsperren Sie eine Welt exklusiver Inhalte, genießen Sie besondere Angebote und seien Sie der Erste, der spannende Neuigkeiten und Updates erhält, indem Sie unserer Community beitreten!',
|
|
||||||
|
|
||||||
// Social auth
|
|
||||||
signUpWithApple: 'Mit Apple registrieren',
|
|
||||||
signUpWithGoogle: 'Mit Google registrieren',
|
|
||||||
privacyNotice: 'Wir werden nichts ohne Ihre Erlaubnis veröffentlichen und Ihre persönlichen Daten werden privat gehalten',
|
|
||||||
|
|
||||||
// Form labels
|
|
||||||
enterEmail: 'E-Mail eingeben',
|
|
||||||
enterValidEmail: 'Gültige E-Mail eingeben',
|
|
||||||
enterPassword: 'Passwort eingeben',
|
|
||||||
|
|
||||||
// Hero section
|
|
||||||
heroBadge: 'WIR SIND #1 AUF DEM MARKT',
|
|
||||||
heroTitle: 'Wir sind hier, um Ihnen beim Navigieren auf Reisen zu helfen',
|
|
||||||
heroSubtitle: 'Sie erhalten umfassende Ergebnisse basierend auf dem angegebenen Standort.',
|
|
||||||
searchPlaceholder: 'Wonach suchen Sie?',
|
|
||||||
locationPlaceholder: 'Standort',
|
|
||||||
searchButton: 'Orte suchen',
|
|
||||||
|
|
||||||
// Categories
|
|
||||||
apartments: 'Apartments',
|
|
||||||
restaurants: 'Restaurants',
|
|
||||||
events: 'Veranstaltungen/Kunst',
|
|
||||||
shops: 'Geschäfte',
|
|
||||||
museums: 'Museen',
|
|
||||||
gyms: 'Fitnessstudios',
|
|
||||||
listings: 'Einträge',
|
|
||||||
|
|
||||||
// Dashboard
|
|
||||||
totalIncome: 'Gesamteinkommen',
|
|
||||||
visitors: 'Besucher',
|
|
||||||
totalOrders: 'Gesamtbestellungen',
|
|
||||||
recentBookings: 'Aktuelle Buchungen',
|
|
||||||
statistics: 'Statistiken',
|
|
||||||
myListings: 'Meine Einträge',
|
|
||||||
addListing: 'Eintrag hinzufügen',
|
|
||||||
wallet: 'Geldbörse',
|
|
||||||
profile: 'Profil',
|
|
||||||
settings: 'Einstellungen',
|
|
||||||
|
|
||||||
// Common
|
|
||||||
loading: 'Wird geladen...',
|
|
||||||
error: 'Fehler',
|
|
||||||
success: 'Erfolg',
|
|
||||||
cancel: 'Abbrechen',
|
|
||||||
save: 'Speichern',
|
|
||||||
delete: 'Löschen',
|
|
||||||
edit: 'Bearbeiten',
|
|
||||||
view: 'Ansehen',
|
|
||||||
|
|
||||||
// Process section
|
|
||||||
processTitle: 'Finden Sie Ihren Traumort auf die beste Weise',
|
|
||||||
processSubtitle: 'Entdecken Sie aufregende Kategorien. Finden Sie, was Sie suchen.',
|
|
||||||
step1: 'Geben Sie Ihren Standort ein, um nach Sehenswürdigkeiten zu suchen.',
|
|
||||||
step2: 'Vereinbaren Sie einen Termin an dem Ort, den Sie besuchen möchten.',
|
|
||||||
step3: 'Besuchen Sie den Ort und genießen Sie das Erlebnis.',
|
|
||||||
|
|
||||||
// Explore section
|
|
||||||
topRegions: 'Top-Regionen',
|
|
||||||
exploreCities: 'Städte erkunden',
|
|
||||||
exploreMore: 'Mehr erkunden'
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -1,178 +0,0 @@
|
|||||||
import { Link } from 'react-router-dom';
|
|
||||||
import { Card, CardContent } from '@/components/ui/card';
|
|
||||||
import { Store, CreditCard, Receipt, Package, Users, BarChart3, QrCode, ArrowRight } from 'lucide-react';
|
|
||||||
|
|
||||||
const EstablishmentsManagement = () => {
|
|
||||||
const stats = {
|
|
||||||
activeOrders: 24,
|
|
||||||
dailyRevenue: 8450,
|
|
||||||
inventory: 156,
|
|
||||||
staff: 18
|
|
||||||
};
|
|
||||||
|
|
||||||
const sections = [
|
|
||||||
{
|
|
||||||
title: 'POS Terminal',
|
|
||||||
description: 'Complete point of sale system',
|
|
||||||
icon: CreditCard,
|
|
||||||
path: '/dashboard/establishments/pos',
|
|
||||||
color: 'green'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Orders Management',
|
|
||||||
description: 'Manage all orders and transactions',
|
|
||||||
icon: Receipt,
|
|
||||||
path: '/dashboard/establishments/orders',
|
|
||||||
color: 'blue'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Inventory Control',
|
|
||||||
description: 'Track stock and product management',
|
|
||||||
icon: Package,
|
|
||||||
path: '/dashboard/establishments/inventory',
|
|
||||||
color: 'purple'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Staff Management',
|
|
||||||
description: 'Manage your team and schedules',
|
|
||||||
icon: Users,
|
|
||||||
path: '/dashboard/establishments/staff',
|
|
||||||
color: 'indigo'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Business List',
|
|
||||||
description: 'View and manage all establishments',
|
|
||||||
icon: Store,
|
|
||||||
path: '/dashboard/establishments/businesses',
|
|
||||||
color: 'orange'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Categories',
|
|
||||||
description: 'Manage business categories',
|
|
||||||
icon: QrCode,
|
|
||||||
path: '/dashboard/establishments/categories',
|
|
||||||
color: 'yellow'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Verification',
|
|
||||||
description: 'Review pending verifications',
|
|
||||||
icon: BarChart3,
|
|
||||||
path: '/dashboard/establishments/verification',
|
|
||||||
color: 'red'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Analytics',
|
|
||||||
description: 'Business insights and reports',
|
|
||||||
icon: BarChart3,
|
|
||||||
path: '/dashboard/establishments/analytics',
|
|
||||||
color: 'teal'
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="container mx-auto p-6 space-y-6">
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<div>
|
|
||||||
<h1 className="text-3xl font-bold flex items-center gap-2">
|
|
||||||
<Store className="h-8 w-8 text-primary" />
|
|
||||||
Establishments Management
|
|
||||||
</h1>
|
|
||||||
<p className="text-muted-foreground mt-1">
|
|
||||||
Complete business management system
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Stats Overview */}
|
|
||||||
<div className="grid gap-4 md:grid-cols-4">
|
|
||||||
<Card>
|
|
||||||
<CardContent className="p-6">
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<div>
|
|
||||||
<p className="text-sm text-muted-foreground mb-1">Active Orders</p>
|
|
||||||
<p className="text-2xl font-bold">{stats.activeOrders}</p>
|
|
||||||
<p className="text-xs text-muted-foreground">In progress now</p>
|
|
||||||
</div>
|
|
||||||
<div className="w-12 h-12 rounded-full bg-blue-100 flex items-center justify-center">
|
|
||||||
<Receipt className="w-6 h-6 text-blue-600" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
<Card>
|
|
||||||
<CardContent className="p-6">
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<div>
|
|
||||||
<p className="text-sm text-muted-foreground mb-1">Daily Revenue</p>
|
|
||||||
<p className="text-2xl font-bold">${stats.dailyRevenue.toLocaleString()}</p>
|
|
||||||
<p className="text-xs text-green-600">+15% vs yesterday</p>
|
|
||||||
</div>
|
|
||||||
<div className="w-12 h-12 rounded-full bg-green-100 flex items-center justify-center">
|
|
||||||
<CreditCard className="w-6 h-6 text-green-600" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
<Card>
|
|
||||||
<CardContent className="p-6">
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<div>
|
|
||||||
<p className="text-sm text-muted-foreground mb-1">Inventory Items</p>
|
|
||||||
<p className="text-2xl font-bold">{stats.inventory}</p>
|
|
||||||
<p className="text-xs text-muted-foreground">Products tracked</p>
|
|
||||||
</div>
|
|
||||||
<div className="w-12 h-12 rounded-full bg-purple-100 flex items-center justify-center">
|
|
||||||
<Package className="w-6 h-6 text-purple-600" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
<Card>
|
|
||||||
<CardContent className="p-6">
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<div>
|
|
||||||
<p className="text-sm text-muted-foreground mb-1">Staff Members</p>
|
|
||||||
<p className="text-2xl font-bold">{stats.staff}</p>
|
|
||||||
<p className="text-xs text-muted-foreground">Active employees</p>
|
|
||||||
</div>
|
|
||||||
<div className="w-12 h-12 rounded-full bg-indigo-100 flex items-center justify-center">
|
|
||||||
<Users className="w-6 h-6 text-indigo-600" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Sections Grid */}
|
|
||||||
<div>
|
|
||||||
<h2 className="text-xl font-semibold mb-4">System Modules</h2>
|
|
||||||
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
|
|
||||||
{sections.map((section) => {
|
|
||||||
const Icon = section.icon;
|
|
||||||
return (
|
|
||||||
<Link key={section.path} to={section.path}>
|
|
||||||
<Card className="hover:shadow-lg transition-shadow cursor-pointer h-full">
|
|
||||||
<CardContent className="p-6">
|
|
||||||
<div className="flex items-start justify-between mb-4">
|
|
||||||
<div className={`w-12 h-12 rounded-lg bg-${section.color}-100 flex items-center justify-center`}>
|
|
||||||
<Icon className={`w-6 h-6 text-${section.color}-600`} />
|
|
||||||
</div>
|
|
||||||
<ArrowRight className="w-5 h-5 text-muted-foreground" />
|
|
||||||
</div>
|
|
||||||
<h3 className="font-semibold mb-2">{section.title}</h3>
|
|
||||||
<p className="text-sm text-muted-foreground">{section.description}</p>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
</Link>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default EstablishmentsManagement;
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
|
||||||
import { Package } from 'lucide-react';
|
|
||||||
|
|
||||||
const EstablishmentInventory = () => {
|
|
||||||
return (
|
|
||||||
<div className="container mx-auto p-6">
|
|
||||||
<h1 className="text-3xl font-bold mb-6">Inventory Control</h1>
|
|
||||||
<Card>
|
|
||||||
<CardHeader>
|
|
||||||
<CardTitle className="flex items-center gap-2">
|
|
||||||
<Package className="h-5 w-5" />
|
|
||||||
Stock Management
|
|
||||||
</CardTitle>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
<p className="text-muted-foreground">Inventory connected to API</p>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default EstablishmentInventory;
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
|
||||||
import { Receipt } from 'lucide-react';
|
|
||||||
|
|
||||||
const EstablishmentOrders = () => {
|
|
||||||
return (
|
|
||||||
<div className="container mx-auto p-6">
|
|
||||||
<h1 className="text-3xl font-bold mb-6">Orders Management</h1>
|
|
||||||
<Card>
|
|
||||||
<CardHeader>
|
|
||||||
<CardTitle className="flex items-center gap-2">
|
|
||||||
<Receipt className="h-5 w-5" />
|
|
||||||
All Orders
|
|
||||||
</CardTitle>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
<p className="text-muted-foreground">Orders connected to API</p>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default EstablishmentOrders;
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
|
||||||
import { Users } from 'lucide-react';
|
|
||||||
|
|
||||||
const EstablishmentStaff = () => {
|
|
||||||
return (
|
|
||||||
<div className="container mx-auto p-6">
|
|
||||||
<h1 className="text-3xl font-bold mb-6">Staff Management</h1>
|
|
||||||
<Card>
|
|
||||||
<CardHeader>
|
|
||||||
<CardTitle className="flex items-center gap-2">
|
|
||||||
<Users className="h-5 w-5" />
|
|
||||||
Team Management
|
|
||||||
</CardTitle>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
<p className="text-muted-foreground">Staff management connected to API</p>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default EstablishmentStaff;
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
|
||||||
import { Button } from '@/components/ui/button';
|
|
||||||
import { CreditCard } from 'lucide-react';
|
|
||||||
|
|
||||||
const POSTerminal = () => {
|
|
||||||
return (
|
|
||||||
<div className="container mx-auto p-6">
|
|
||||||
<h1 className="text-3xl font-bold mb-6">POS Terminal</h1>
|
|
||||||
<Card>
|
|
||||||
<CardHeader>
|
|
||||||
<CardTitle className="flex items-center gap-2">
|
|
||||||
<CreditCard className="h-5 w-5" />
|
|
||||||
Point of Sale System
|
|
||||||
</CardTitle>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
<p className="text-muted-foreground mb-4">Complete POS terminal connected to API</p>
|
|
||||||
<Button>Process Payment</Button>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default POSTerminal;
|
|
||||||
Reference in New Issue
Block a user