diff --git a/src/App.tsx b/src/App.tsx index f51a256..16993aa 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -52,6 +52,10 @@ import PersonalizationPage from "./pages/dashboard/config/PersonalizationPage"; // POLITUR pages import EmergencyDashboard from "./pages/dashboard/politur/EmergencyDashboard"; import PolReports from "./pages/dashboard/politur/Reports"; +// Guides pages +import GuideDashboard from "./pages/dashboard/guides/GuideDashboard"; +import ItineraryBuilder from "./pages/dashboard/guides/ItineraryBuilder"; +import ContentLibrary from "./pages/dashboard/guides/ContentLibrary"; // Commerce pages (for retail stores) import CommerceStore from "./pages/dashboard/commerce/Store"; import CommercePOS from "./pages/dashboard/commerce/POSTerminal"; @@ -590,6 +594,31 @@ const AppRouter = () => ( } /> + {/* Guides Routes */} + + + + + + } /> + + + + + + + } /> + + + + + + + } /> + {/* Catch-all route */} } /> diff --git a/src/components/DashboardLayout.tsx b/src/components/DashboardLayout.tsx index 4992d8e..8d79d96 100644 --- a/src/components/DashboardLayout.tsx +++ b/src/components/DashboardLayout.tsx @@ -58,7 +58,8 @@ import { Leaf, Store, Server, - ShieldAlert + ShieldAlert, + UserCircle } from 'lucide-react'; const DashboardLayout = ({ children }: { children: React.ReactNode }) => { @@ -186,6 +187,16 @@ const DashboardLayout = ({ children }: { children: React.ReactNode }) => { { icon: FileText, label: 'Reportes', path: '/dashboard/politur/reports' } ] }, + { + icon: UserCircle, + label: 'Guías Turísticos', + path: '/dashboard/guides', + subItems: [ + { icon: Home, label: 'Dashboard', path: '/dashboard/guides' }, + { icon: BookOpen, label: 'Crear Itinerario', path: '/dashboard/guides/itinerary' }, + { icon: BookOpen, label: 'Biblioteca', path: '/dashboard/guides/library' } + ] + }, { icon: Store, label: t('commerce'), diff --git a/src/pages/dashboard/guides/ContentLibrary.tsx b/src/pages/dashboard/guides/ContentLibrary.tsx new file mode 100644 index 0000000..524bc91 --- /dev/null +++ b/src/pages/dashboard/guides/ContentLibrary.tsx @@ -0,0 +1,271 @@ +import React, { useState } from 'react'; +import { BookOpen, Search, Plus, Play, Download, Edit, Trash2, Volume2 } from 'lucide-react'; +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; +import { Badge } from '@/components/ui/badge'; +import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; + +interface ContentItem { + id: string; + title: string; + type: 'historical' | 'cultural' | 'monument' | 'audio'; + category: string; + description: string; + audioUrl?: string; + duration?: string; +} + +const ContentLibrary = () => { + const [searchTerm, setSearchTerm] = useState(''); + const [contents] = useState([ + { + id: '1', + title: 'Historia de la Catedral Primada', + type: 'historical', + category: 'Arquitectura Colonial', + description: 'Primera catedral de América, construcción iniciada en 1514...', + audioUrl: '#', + duration: '3:45' + }, + { + id: '2', + title: 'Alcázar de Colón', + type: 'monument', + category: 'Monumentos', + description: 'Palacio virreinal construido entre 1510 y 1514...', + audioUrl: '#', + duration: '4:20' + }, + { + id: '3', + title: 'Cultura Taína', + type: 'cultural', + category: 'Cultura Precolombina', + description: 'Los taínos fueron los habitantes originales de La Española...', + audioUrl: '#', + duration: '5:15' + }, + { + id: '4', + title: 'Audio Guía: Zona Colonial', + type: 'audio', + category: 'Audio Guías', + description: 'Recorrido completo por la Zona Colonial de Santo Domingo', + audioUrl: '#', + duration: '12:30' + } + ]); + + const getTypeIcon = (type: string) => { + const icons = { + historical: '📚', + cultural: '🎭', + monument: '🏛️', + audio: '🎧' + }; + return icons[type] || '📄'; + }; + + const getTypeBadge = (type: string) => { + const variants = { + historical: 'default' as const, + cultural: 'secondary' as const, + monument: 'outline' as const, + audio: 'default' as const + }; + return {type}; + }; + + const filteredContents = contents.filter(content => + content.title.toLowerCase().includes(searchTerm.toLowerCase()) || + content.category.toLowerCase().includes(searchTerm.toLowerCase()) + ); + + return ( +
+
+
+

Biblioteca de Contenidos

+

Gestiona tu contenido histórico y cultural

+
+ +
+ + {/* Stats */} +
+ + + + Total Contenidos + + + +
+ {contents.length} +
+
+
+ + + + + Audio Guías + + + +
+ {contents.filter(c => c.type === 'audio').length} +
+
+
+ + + + + Históricos + + + +
+ {contents.filter(c => c.type === 'historical').length} +
+
+
+ + + + + Monumentos + + + +
+ {contents.filter(c => c.type === 'monument').length} +
+
+
+
+ + + +
+
+
+ + setSearchTerm(e.target.value)} + /> +
+
+ +
+
+ + + + Todos + Histórico + Cultural + Monumentos + Audio + + + + {filteredContents.map((content) => ( +
+
+
+ {getTypeIcon(content.type)} +
+
+

{content.title}

+ {getTypeBadge(content.type)} +
+

{content.category}

+
+
+
+ {content.audioUrl && ( + + )} + + + +
+
+ +

{content.description}

+ + {content.duration && ( +
+ + Duración: {content.duration} +
+ )} +
+ ))} +
+ + {['historical', 'cultural', 'monument', 'audio'].map(type => ( + + {filteredContents + .filter(c => c.type === type) + .map((content) => ( +
+ {/* Same content structure as "all" tab */} +
+
+ {getTypeIcon(content.type)} +
+

{content.title}

+

{content.category}

+
+
+
+ {content.audioUrl && ( + + )} + +
+
+

{content.description}

+
+ ))} +
+ ))} +
+
+
+
+ ); +}; + +export default ContentLibrary; diff --git a/src/pages/dashboard/guides/GuideDashboard.tsx b/src/pages/dashboard/guides/GuideDashboard.tsx new file mode 100644 index 0000000..a6c3657 --- /dev/null +++ b/src/pages/dashboard/guides/GuideDashboard.tsx @@ -0,0 +1,278 @@ +import React, { useState } from 'react'; +import { User, Calendar, MapPin, DollarSign, Star, BookOpen, TrendingUp } from 'lucide-react'; +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; +import { Button } from '@/components/ui/button'; +import { Badge } from '@/components/ui/badge'; +import { useToast } from '@/hooks/use-toast'; + +interface Tour { + id: string; + touristName: string; + date: string; + time: string; + type: string; + status: 'pending' | 'confirmed' | 'completed' | 'cancelled'; + price: number; + location: string; +} + +const GuideDashboard = () => { + const { toast } = useToast(); + const [tours] = useState([ + { + id: '1', + touristName: 'John Smith', + date: '2024-01-20', + time: '09:00', + type: 'Zona Colonial Tour', + status: 'confirmed', + price: 75, + location: 'Santo Domingo' + }, + { + id: '2', + touristName: 'Maria Garcia', + date: '2024-01-20', + time: '14:00', + type: 'City Highlights', + status: 'pending', + price: 60, + location: 'Santo Domingo' + }, + { + id: '3', + touristName: 'Robert Johnson', + date: '2024-01-21', + time: '10:00', + type: 'Beach & Culture', + status: 'confirmed', + price: 90, + location: 'Boca Chica' + } + ]); + + const stats = { + upcomingTours: tours.filter(t => t.status === 'confirmed').length, + pendingRequests: tours.filter(t => t.status === 'pending').length, + monthEarnings: tours.filter(t => t.status === 'completed').reduce((acc, t) => acc + t.price, 0), + rating: 4.8, + totalReviews: 127 + }; + + const getStatusBadge = (status: string) => { + const variants = { + pending: 'secondary' as const, + confirmed: 'default' as const, + completed: 'outline' as const, + cancelled: 'destructive' as const + }; + return {status}; + }; + + return ( +
+ {/* Header */} +
+
+

Panel de Guía Turístico

+

Gestiona tus tours y disponibilidad

+
+ +
+ + {/* Stats Cards */} +
+ + + + Tours Próximos + + + +
+ {stats.upcomingTours} +
+
+
+ + + + + Solicitudes + + + +
+ {stats.pendingRequests} +
+
+
+ + + + + Ganancias del Mes + + + +
+ ${stats.monthEarnings} +
+
+
+ + + + + Calificación + + + +
+
+ {stats.rating} +
+ +
+
+
+ + + + + Reseñas + + + +
+ {stats.totalReviews} +
+
+
+
+ +
+ {/* Upcoming Tours */} + + + + + + Tours Próximos + + + + + +
+ {tours.map((tour) => ( +
+
+
+

{tour.type}

+

{tour.touristName}

+
+ {getStatusBadge(tour.status)} +
+ +
+
+ Fecha: +

{new Date(tour.date).toLocaleDateString('es-ES')}

+
+
+ Hora: +

{tour.time}

+
+
+ Precio: +

${tour.price}

+
+
+ +
+ + {tour.location} +
+ + {tour.status === 'pending' && ( +
+ + +
+ )} + + {tour.status === 'confirmed' && ( + + )} +
+ ))} +
+
+
+ + {/* Quick Actions */} + + + Acciones Rápidas + + +
+ + + + + + +
+
+
+
+
+ ); +}; + +export default GuideDashboard; diff --git a/src/pages/dashboard/guides/ItineraryBuilder.tsx b/src/pages/dashboard/guides/ItineraryBuilder.tsx new file mode 100644 index 0000000..32cd90f --- /dev/null +++ b/src/pages/dashboard/guides/ItineraryBuilder.tsx @@ -0,0 +1,263 @@ +import React, { useState } from 'react'; +import { Plus, MapPin, Clock, DollarSign, Save, Wand2, Image, Trash2 } from 'lucide-react'; +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; +import { Textarea } from '@/components/ui/textarea'; +import { useToast } from '@/hooks/use-toast'; + +interface ItineraryStop { + id: string; + name: string; + description: string; + duration: number; + order: number; + image?: string; +} + +const ItineraryBuilder = () => { + const { toast } = useToast(); + const [itineraryName, setItineraryName] = useState(''); + const [totalPrice, setTotalPrice] = useState(75); + const [stops, setStops] = useState([ + { + id: '1', + name: 'Catedral Primada de América', + description: 'Primera catedral construida en América (1514-1540)', + duration: 45, + order: 1 + }, + { + id: '2', + name: 'Alcázar de Colón', + description: 'Palacio virreinal construido en 1510', + duration: 60, + order: 2 + } + ]); + + const addStop = () => { + const newStop: ItineraryStop = { + id: Date.now().toString(), + name: '', + description: '', + duration: 30, + order: stops.length + 1 + }; + setStops([...stops, newStop]); + }; + + const removeStop = (id: string) => { + setStops(stops.filter(s => s.id !== id)); + }; + + const updateStop = (id: string, field: keyof ItineraryStop, value: any) => { + setStops(stops.map(s => + s.id === id ? { ...s, [field]: value } : s + )); + }; + + const generateWithAI = () => { + toast({ + title: "Generando con IA", + description: "Creando itinerario personalizado...", + }); + }; + + const saveItinerary = () => { + toast({ + title: "Itinerario Guardado", + description: "Tu itinerario ha sido guardado exitosamente", + }); + }; + + const totalDuration = stops.reduce((acc, stop) => acc + stop.duration, 0); + + return ( +
+
+
+

Constructor de Itinerarios

+

Crea tours personalizados para tus clientes

+
+
+ + +
+
+ +
+ {/* Itinerary Details */} + + + Detalles del Itinerario + + +
+ + setItineraryName(e.target.value)} + /> +
+ +
+
+ +
+ + setTotalPrice(Number(e.target.value))} + /> +
+
+
+ +
+ + +
+
+
+ +
+
+

Paradas del Tour

+ +
+ +
+ {stops.map((stop, index) => ( +
+
+
+
+ {index + 1} +
+

Parada {index + 1}

+
+ +
+ +
+ updateStop(stop.id, 'name', e.target.value)} + /> +
+ +
+