xertica-ui 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. package/App.tsx +182 -0
  2. package/README.md +330 -0
  3. package/assets/xertica-logo.svg +38 -0
  4. package/assets/xertica-x-logo.svg +21 -0
  5. package/bin/cli.ts +193 -0
  6. package/components/AssistenteXertica.tsx +2003 -0
  7. package/components/AudioPlayer.tsx +203 -0
  8. package/components/CodeBlock.tsx +242 -0
  9. package/components/DocumentEditor.tsx +504 -0
  10. package/components/ForgotPasswordPage.tsx +170 -0
  11. package/components/FormattedDocument.tsx +87 -0
  12. package/components/HomeContent.tsx +123 -0
  13. package/components/HomePage.tsx +70 -0
  14. package/components/LanguageSelector.tsx +54 -0
  15. package/components/LoginPage.tsx +199 -0
  16. package/components/MarkdownMessage.tsx +62 -0
  17. package/components/ModernChatInput.tsx +502 -0
  18. package/components/PodcastPlayer.tsx +409 -0
  19. package/components/ResetPasswordPage.tsx +234 -0
  20. package/components/Sidebar.tsx +489 -0
  21. package/components/TemplateContent.tsx +629 -0
  22. package/components/TemplatePage.tsx +70 -0
  23. package/components/ThemeToggle.tsx +65 -0
  24. package/components/VerifyEmailPage.tsx +187 -0
  25. package/components/XerticaLogo.tsx +69 -0
  26. package/components/XerticaOrbe.tsx +1339 -0
  27. package/components/XerticaXLogo.tsx +53 -0
  28. package/components/examples/DrawingMapExample.tsx +530 -0
  29. package/components/examples/FilterableMapExample.tsx +380 -0
  30. package/components/examples/LocationPickerExample.tsx +330 -0
  31. package/components/examples/MapExamples.tsx +280 -0
  32. package/components/examples/MapShowcase.tsx +446 -0
  33. package/components/examples/RouteMapExamples.tsx +329 -0
  34. package/components/examples/SimpleFilterableMap.tsx +192 -0
  35. package/components/examples/index.ts +52 -0
  36. package/components/figma/ImageWithFallback.tsx +27 -0
  37. package/components/index.ts +44 -0
  38. package/components/media/AudioPlayer.tsx +278 -0
  39. package/components/media/FloatingMediaWrapper.tsx +166 -0
  40. package/components/media/VideoPlayer.tsx +285 -0
  41. package/components/ui/accordion.tsx +66 -0
  42. package/components/ui/alert-dialog.tsx +159 -0
  43. package/components/ui/alert.tsx +91 -0
  44. package/components/ui/aspect-ratio.tsx +11 -0
  45. package/components/ui/avatar.tsx +65 -0
  46. package/components/ui/badge.tsx +55 -0
  47. package/components/ui/breadcrumb.tsx +109 -0
  48. package/components/ui/button.tsx +78 -0
  49. package/components/ui/calendar.tsx +235 -0
  50. package/components/ui/card.tsx +92 -0
  51. package/components/ui/carousel.tsx +241 -0
  52. package/components/ui/chart.tsx +353 -0
  53. package/components/ui/checkbox.tsx +32 -0
  54. package/components/ui/collapsible.tsx +33 -0
  55. package/components/ui/command.tsx +177 -0
  56. package/components/ui/context-menu.tsx +252 -0
  57. package/components/ui/dialog.tsx +138 -0
  58. package/components/ui/drawer.tsx +134 -0
  59. package/components/ui/dropdown-menu.tsx +257 -0
  60. package/components/ui/empty.tsx +90 -0
  61. package/components/ui/file-upload.tsx +152 -0
  62. package/components/ui/form.tsx +195 -0
  63. package/components/ui/google-maps-loader.tsx +379 -0
  64. package/components/ui/hover-card.tsx +44 -0
  65. package/components/ui/index.ts +242 -0
  66. package/components/ui/input-otp.tsx +77 -0
  67. package/components/ui/input.tsx +38 -0
  68. package/components/ui/label.tsx +24 -0
  69. package/components/ui/map-config.ts +12 -0
  70. package/components/ui/map-layers.tsx +129 -0
  71. package/components/ui/map.exports.ts +31 -0
  72. package/components/ui/map.tsx +412 -0
  73. package/components/ui/menubar.tsx +276 -0
  74. package/components/ui/navigation-menu.tsx +162 -0
  75. package/components/ui/notification-badge.tsx +61 -0
  76. package/components/ui/page-header.tsx +229 -0
  77. package/components/ui/pagination.tsx +127 -0
  78. package/components/ui/popover.tsx +48 -0
  79. package/components/ui/progress.tsx +31 -0
  80. package/components/ui/radio-group.tsx +56 -0
  81. package/components/ui/rating.tsx +102 -0
  82. package/components/ui/resizable.tsx +405 -0
  83. package/components/ui/route-map.tsx +246 -0
  84. package/components/ui/scroll-area.tsx +58 -0
  85. package/components/ui/search.tsx +70 -0
  86. package/components/ui/select.tsx +176 -0
  87. package/components/ui/separator.tsx +28 -0
  88. package/components/ui/sheet.tsx +138 -0
  89. package/components/ui/sidebar.tsx +726 -0
  90. package/components/ui/simple-map.tsx +92 -0
  91. package/components/ui/skeleton.tsx +13 -0
  92. package/components/ui/slider.tsx +58 -0
  93. package/components/ui/sonner.tsx +77 -0
  94. package/components/ui/stats-card.tsx +84 -0
  95. package/components/ui/stepper.tsx +126 -0
  96. package/components/ui/switch.tsx +34 -0
  97. package/components/ui/table.tsx +116 -0
  98. package/components/ui/tabs.tsx +66 -0
  99. package/components/ui/textarea.tsx +26 -0
  100. package/components/ui/timeline.tsx +140 -0
  101. package/components/ui/toggle-group.tsx +71 -0
  102. package/components/ui/toggle.tsx +46 -0
  103. package/components/ui/tooltip.tsx +61 -0
  104. package/components/ui/tree-view.tsx +123 -0
  105. package/components/ui/use-mobile.ts +24 -0
  106. package/components/ui/utils.ts +6 -0
  107. package/components/ui/xertica-assistant.tsx +1420 -0
  108. package/contexts/ApiKeyContext.tsx +123 -0
  109. package/contexts/AssistenteContext.tsx +118 -0
  110. package/contexts/BrandColorsContext.tsx +551 -0
  111. package/contexts/LanguageContext.tsx +36 -0
  112. package/contexts/ThemeContext.tsx +85 -0
  113. package/dist/cli.js +20922 -0
  114. package/eslint.config.js +41 -0
  115. package/guidelines/Guidelines.md +61 -0
  116. package/hooks/useTheme.ts +4 -0
  117. package/imports/Podcast.tsx +389 -0
  118. package/imports/XerticaAi.tsx +46 -0
  119. package/imports/XerticaX.tsx +20 -0
  120. package/imports/svg-aueiaqngck.ts +11 -0
  121. package/imports/svg-v9krss1ozd.ts +16 -0
  122. package/imports/svg-vhrdofe3qe.ts +5 -0
  123. package/index.css +4448 -0
  124. package/index.html +14 -0
  125. package/main.tsx +10 -0
  126. package/package.json +119 -0
  127. package/postcss.config.js +6 -0
  128. package/routes.tsx +33 -0
  129. package/styles/globals.css +15 -0
  130. package/styles/xertica/app-overrides/chat.css +61 -0
  131. package/styles/xertica/app-overrides/scrollbar.css +33 -0
  132. package/styles/xertica/base.css +70 -0
  133. package/styles/xertica/integrations/google-maps.css +76 -0
  134. package/styles/xertica/integrations/sonner.css +73 -0
  135. package/styles/xertica/theme-map.css +88 -0
  136. package/styles/xertica/tokens.css +190 -0
  137. package/tsconfig.json +31 -0
  138. package/tsconfig.node.json +10 -0
  139. package/utils/gemini.ts +140 -0
  140. package/vite-env.d.ts +12 -0
  141. package/vite.config.ts +36 -0
@@ -0,0 +1,329 @@
1
+ import React, { useState } from 'react';
2
+ import { RouteMap } from '../ui/route-map';
3
+ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '../ui/card';
4
+ import { Badge } from '../ui/badge';
5
+ import { Button } from '../ui/button';
6
+ import { Tabs, TabsContent, TabsList, TabsTrigger } from '../ui/tabs';
7
+ import { Navigation, Car, PersonStanding, Bike, Clock, MapPin } from 'lucide-react';
8
+
9
+ /**
10
+ * RouteMapExamples - Exemplos práticos de mapas com rotas
11
+ *
12
+ * Demonstra diferentes casos de uso para o componente RouteMap:
13
+ * - Rotas de entrega
14
+ * - Rotas turísticas
15
+ * - Diferentes modos de transporte
16
+ * - Rotas com múltiplos waypoints
17
+ */
18
+
19
+ // 1. Rota de Entrega Simples
20
+ export function DeliveryRouteMap() {
21
+ const [routeInfo, setRouteInfo] = useState<{ distance: string; duration: string } | null>(null);
22
+
23
+ const origin = { lat: -23.5505, lng: -46.6333 }; // Avenida Paulista
24
+ const destination = { lat: -23.5613, lng: -46.6563 }; // Parque Ibirapuera
25
+
26
+ return (
27
+ <Card>
28
+ <CardHeader>
29
+ <div className="flex items-center gap-2">
30
+ <Car className="w-5 h-5 text-primary" />
31
+ <CardTitle>Rota de Entrega</CardTitle>
32
+ </div>
33
+ <CardDescription>
34
+ Calcule o tempo e distância para sua entrega
35
+ </CardDescription>
36
+ </CardHeader>
37
+ <CardContent className="space-y-4">
38
+ <RouteMap
39
+ origin={origin}
40
+ destination={destination}
41
+ travelMode="DRIVING"
42
+ height="400px"
43
+ onRouteCalculated={(distance, duration) => {
44
+ setRouteInfo({ distance, duration });
45
+ }}
46
+ />
47
+
48
+ {routeInfo && (
49
+ <div className="grid grid-cols-2 gap-3">
50
+ <div className="p-3 bg-muted rounded-lg">
51
+ <div className="flex items-center gap-2 mb-1">
52
+ <Navigation className="w-4 h-4 text-muted-foreground" />
53
+ <span className="text-xs text-muted-foreground">Distância</span>
54
+ </div>
55
+ <p className="text-lg font-semibold">{routeInfo.distance}</p>
56
+ </div>
57
+ <div className="p-3 bg-muted rounded-lg">
58
+ <div className="flex items-center gap-2 mb-1">
59
+ <Clock className="w-4 h-4 text-muted-foreground" />
60
+ <span className="text-xs text-muted-foreground">Tempo</span>
61
+ </div>
62
+ <p className="text-lg font-semibold">{routeInfo.duration}</p>
63
+ </div>
64
+ </div>
65
+ )}
66
+ </CardContent>
67
+ </Card>
68
+ );
69
+ }
70
+
71
+ // 2. Rota Turística com Múltiplos Pontos
72
+ export function TouristRouteMap() {
73
+ const [routeInfo, setRouteInfo] = useState<{ distance: string; duration: string } | null>(null);
74
+
75
+ const route = {
76
+ origin: { lat: -23.5505, lng: -46.6333 }, // Av. Paulista
77
+ destination: { lat: -23.5475, lng: -46.6361 }, // MASP
78
+ waypoints: [
79
+ { lat: -23.5558, lng: -46.6396 }, // Parque Trianon
80
+ { lat: -23.5506, lng: -46.6378 }, // Casa das Rosas
81
+ ]
82
+ };
83
+
84
+ return (
85
+ <Card>
86
+ <CardHeader>
87
+ <CardTitle>Rota Turística - Avenida Paulista</CardTitle>
88
+ <CardDescription>
89
+ Roteiro a pé pelos principais pontos turísticos
90
+ </CardDescription>
91
+ </CardHeader>
92
+ <CardContent className="space-y-4">
93
+ <RouteMap
94
+ origin={route.origin}
95
+ destination={route.destination}
96
+ waypoints={route.waypoints}
97
+ travelMode="WALKING"
98
+ height="450px"
99
+ onRouteCalculated={(distance, duration) => {
100
+ setRouteInfo({ distance, duration });
101
+ }}
102
+ />
103
+
104
+ <div className="space-y-2">
105
+ <h4 className="text-sm font-medium">Pontos da Rota:</h4>
106
+ <div className="space-y-1.5">
107
+ <div className="flex items-center gap-2 text-sm">
108
+ <Badge variant="outline" className="w-6 h-6 flex items-center justify-center p-0">A</Badge>
109
+ <span>Início - Avenida Paulista</span>
110
+ </div>
111
+ <div className="flex items-center gap-2 text-sm">
112
+ <Badge variant="outline" className="w-6 h-6 flex items-center justify-center p-0">C</Badge>
113
+ <span>Parque Trianon</span>
114
+ </div>
115
+ <div className="flex items-center gap-2 text-sm">
116
+ <Badge variant="outline" className="w-6 h-6 flex items-center justify-center p-0">D</Badge>
117
+ <span>Casa das Rosas</span>
118
+ </div>
119
+ <div className="flex items-center gap-2 text-sm">
120
+ <Badge variant="outline" className="w-6 h-6 flex items-center justify-center p-0">B</Badge>
121
+ <span>Fim - MASP</span>
122
+ </div>
123
+ </div>
124
+ </div>
125
+
126
+ {routeInfo && (
127
+ <div className="p-4 bg-green-50 dark:bg-green-950 rounded-lg border border-green-200 dark:border-green-800">
128
+ <div className="flex items-center justify-between">
129
+ <div className="flex items-center gap-2">
130
+ <PersonStanding className="w-5 h-5 text-green-600 dark:text-green-400" />
131
+ <span className="text-sm text-green-900 dark:text-green-100">
132
+ <strong>Caminhada:</strong> {routeInfo.distance} em {routeInfo.duration}
133
+ </span>
134
+ </div>
135
+ </div>
136
+ </div>
137
+ )}
138
+ </CardContent>
139
+ </Card>
140
+ );
141
+ }
142
+
143
+ // 3. Comparação de Modos de Transporte
144
+ export function TransportModeComparison() {
145
+ const [selectedMode, setSelectedMode] = useState<'DRIVING' | 'WALKING' | 'BICYCLING'>('DRIVING');
146
+ const [routeInfo, setRouteInfo] = useState<{ distance: string; duration: string } | null>(null);
147
+
148
+ const origin = { lat: -23.5505, lng: -46.6333 };
149
+ const destination = { lat: -23.5613, lng: -46.6563 };
150
+
151
+ const modes = [
152
+ { value: 'DRIVING' as const, label: 'Carro', icon: Car, color: 'blue' },
153
+ { value: 'WALKING' as const, label: 'A Pé', icon: PersonStanding, color: 'green' },
154
+ { value: 'BICYCLING' as const, label: 'Bicicleta', icon: Bike, color: 'orange' },
155
+ ];
156
+
157
+ return (
158
+ <Card>
159
+ <CardHeader>
160
+ <CardTitle>Comparação de Transportes</CardTitle>
161
+ <CardDescription>
162
+ Veja o tempo e distância para cada modo de transporte
163
+ </CardDescription>
164
+ </CardHeader>
165
+ <CardContent className="space-y-4">
166
+ <div className="flex gap-2">
167
+ {modes.map((mode) => {
168
+ const Icon = mode.icon;
169
+ return (
170
+ <Button
171
+ key={mode.value}
172
+ variant={selectedMode === mode.value ? 'default' : 'outline'}
173
+ size="sm"
174
+ onClick={() => setSelectedMode(mode.value)}
175
+ className="flex-1"
176
+ >
177
+ <Icon className="w-4 h-4 mr-2" />
178
+ {mode.label}
179
+ </Button>
180
+ );
181
+ })}
182
+ </div>
183
+
184
+ <RouteMap
185
+ origin={origin}
186
+ destination={destination}
187
+ travelMode={selectedMode}
188
+ height="400px"
189
+ onRouteCalculated={(distance, duration) => {
190
+ setRouteInfo({ distance, duration });
191
+ }}
192
+ />
193
+
194
+ {routeInfo && (
195
+ <div className="grid grid-cols-2 gap-3">
196
+ <div className="p-4 bg-muted rounded-lg text-center">
197
+ <p className="text-2xl font-bold">{routeInfo.distance}</p>
198
+ <p className="text-xs text-muted-foreground mt-1">Distância total</p>
199
+ </div>
200
+ <div className="p-4 bg-muted rounded-lg text-center">
201
+ <p className="text-2xl font-bold">{routeInfo.duration}</p>
202
+ <p className="text-xs text-muted-foreground mt-1">Tempo estimado</p>
203
+ </div>
204
+ </div>
205
+ )}
206
+ </CardContent>
207
+ </Card>
208
+ );
209
+ }
210
+
211
+ // 4. Rota de Entrega com Múltiplas Paradas
212
+ export function MultiStopDeliveryRoute() {
213
+ const [routeInfo, setRouteInfo] = useState<{ distance: string; duration: string } | null>(null);
214
+
215
+ const deliveryRoute = {
216
+ origin: { lat: -23.5505, lng: -46.6333 }, // Base
217
+ destination: { lat: -23.5505, lng: -46.6333 }, // Retorno à base
218
+ waypoints: [
219
+ { lat: -23.5558, lng: -46.6500 }, // Cliente 1
220
+ { lat: -23.5475, lng: -46.6361 }, // Cliente 2
221
+ { lat: -23.5613, lng: -46.6563 }, // Cliente 3
222
+ ]
223
+ };
224
+
225
+ return (
226
+ <Card>
227
+ <CardHeader>
228
+ <div className="flex items-center justify-between">
229
+ <div>
230
+ <CardTitle>Rota de Entrega - Múltiplas Paradas</CardTitle>
231
+ <CardDescription>
232
+ Otimize sua rota de entregas com paradas intermediárias
233
+ </CardDescription>
234
+ </div>
235
+ <Badge variant="secondary">
236
+ {deliveryRoute.waypoints.length} paradas
237
+ </Badge>
238
+ </div>
239
+ </CardHeader>
240
+ <CardContent className="space-y-4">
241
+ <RouteMap
242
+ origin={deliveryRoute.origin}
243
+ destination={deliveryRoute.destination}
244
+ waypoints={deliveryRoute.waypoints}
245
+ travelMode="DRIVING"
246
+ height="450px"
247
+ onRouteCalculated={(distance, duration) => {
248
+ setRouteInfo({ distance, duration });
249
+ }}
250
+ />
251
+
252
+ <div className="space-y-3">
253
+ <div className="flex items-center justify-between p-3 bg-blue-50 dark:bg-blue-950 rounded-lg border border-blue-200 dark:border-blue-800">
254
+ <div className="flex items-center gap-2">
255
+ <MapPin className="w-4 h-4 text-blue-600 dark:text-blue-400" />
256
+ <span className="text-sm text-blue-900 dark:text-blue-100">
257
+ <strong>Base Central</strong> → 3 entregas → Retorno
258
+ </span>
259
+ </div>
260
+ </div>
261
+
262
+ {routeInfo && (
263
+ <div className="grid grid-cols-2 gap-3">
264
+ <div className="p-3 bg-muted rounded-lg">
265
+ <p className="text-sm text-muted-foreground mb-1">Distância Total</p>
266
+ <p className="text-xl font-semibold">{routeInfo.distance}</p>
267
+ </div>
268
+ <div className="p-3 bg-muted rounded-lg">
269
+ <p className="text-sm text-muted-foreground mb-1">Tempo Total</p>
270
+ <p className="text-xl font-semibold">{routeInfo.duration}</p>
271
+ </div>
272
+ </div>
273
+ )}
274
+ </div>
275
+ </CardContent>
276
+ </Card>
277
+ );
278
+ }
279
+
280
+ // 5. Showcase Completo com Tabs
281
+ export function RouteMapShowcase() {
282
+ return (
283
+ <div className="space-y-6 max-w-6xl mx-auto">
284
+ <div className="space-y-2">
285
+ <h2 className="text-2xl">Route Maps</h2>
286
+ <p className="text-muted-foreground">
287
+ Exemplos de mapas com rotas calculadas automaticamente usando Google Maps Directions API
288
+ </p>
289
+ </div>
290
+
291
+ <Tabs defaultValue="delivery" className="space-y-4">
292
+ <TabsList className="grid w-full grid-cols-4">
293
+ <TabsTrigger value="delivery">
294
+ <Car className="w-4 h-4 mr-2" />
295
+ Entrega
296
+ </TabsTrigger>
297
+ <TabsTrigger value="tourist">
298
+ <PersonStanding className="w-4 h-4 mr-2" />
299
+ Turismo
300
+ </TabsTrigger>
301
+ <TabsTrigger value="transport">
302
+ <Bike className="w-4 h-4 mr-2" />
303
+ Comparação
304
+ </TabsTrigger>
305
+ <TabsTrigger value="multistop">
306
+ <MapPin className="w-4 h-4 mr-2" />
307
+ Multi-paradas
308
+ </TabsTrigger>
309
+ </TabsList>
310
+
311
+ <TabsContent value="delivery">
312
+ <DeliveryRouteMap />
313
+ </TabsContent>
314
+
315
+ <TabsContent value="tourist">
316
+ <TouristRouteMap />
317
+ </TabsContent>
318
+
319
+ <TabsContent value="transport">
320
+ <TransportModeComparison />
321
+ </TabsContent>
322
+
323
+ <TabsContent value="multistop">
324
+ <MultiStopDeliveryRoute />
325
+ </TabsContent>
326
+ </Tabs>
327
+ </div>
328
+ );
329
+ }
@@ -0,0 +1,192 @@
1
+ import React, { useState } from 'react';
2
+ import { Map } from '../ui/map';
3
+ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '../ui/card';
4
+ import { Badge } from '../ui/badge';
5
+ import { Checkbox } from '../ui/checkbox';
6
+ import { Label } from '../ui/label';
7
+
8
+ /**
9
+ * Exemplo simples de mapa com marcadores de EMOJI e filtro
10
+ * Demonstra uso de emojis com fundo branco para contraste
11
+ * IMPORTANTE: Marcadores com emojis usam fundo branco (#FFFFFF)
12
+ */
13
+
14
+ // Dados de exemplo com emojis e fundo branco
15
+ const locations = [
16
+ {
17
+ position: { lat: -23.5505, lng: -46.6333 },
18
+ label: '🍕',
19
+ title: 'Pizzaria do Bairro',
20
+ info: 'Melhor pizza artesanal',
21
+ color: '#FFFFFF', // Fundo branco para emojis
22
+ iconColor: '#000000', // Texto preto para emojis (melhor contraste)
23
+ category: 'food',
24
+ },
25
+ {
26
+ position: { lat: -23.5485, lng: -46.6350 },
27
+ label: '🍣',
28
+ title: 'Sushi Premium',
29
+ info: 'Culinária japonesa autêntica',
30
+ color: '#FFFFFF',
31
+ iconColor: '#000000',
32
+ category: 'food',
33
+ },
34
+ {
35
+ position: { lat: -23.5475, lng: -46.6361 },
36
+ label: '🏨',
37
+ title: 'Hotel Luxo',
38
+ info: '5 estrelas com spa',
39
+ color: '#FFFFFF',
40
+ iconColor: '#000000',
41
+ category: 'hotel',
42
+ },
43
+ {
44
+ position: { lat: -23.5530, lng: -46.6340 },
45
+ label: '🏩',
46
+ title: 'Hotel Boutique',
47
+ info: 'Design contemporâneo',
48
+ color: '#FFFFFF',
49
+ iconColor: '#000000',
50
+ category: 'hotel',
51
+ },
52
+ {
53
+ position: { lat: -23.5613, lng: -46.6563 },
54
+ label: '🌳',
55
+ title: 'Parque Ibirapuera',
56
+ info: 'Maior parque da cidade',
57
+ color: '#FFFFFF',
58
+ iconColor: '#000000',
59
+ category: 'attraction',
60
+ },
61
+ {
62
+ position: { lat: -23.5558, lng: -46.6396 },
63
+ label: '🎨',
64
+ title: 'MASP',
65
+ info: 'Museu de Arte',
66
+ color: '#FFFFFF',
67
+ iconColor: '#000000',
68
+ category: 'attraction',
69
+ },
70
+ {
71
+ position: { lat: -23.5465, lng: -46.6400 },
72
+ label: '🛍️',
73
+ title: 'Shopping Center',
74
+ info: 'Mais de 300 lojas',
75
+ color: '#FFFFFF',
76
+ iconColor: '#000000',
77
+ category: 'shopping',
78
+ },
79
+ {
80
+ position: { lat: -23.5495, lng: -46.6345 },
81
+ label: '☕',
82
+ title: 'Café Artesanal',
83
+ info: 'Café especial e brunch',
84
+ color: '#FFFFFF',
85
+ iconColor: '#000000',
86
+ category: 'cafe',
87
+ },
88
+ ];
89
+
90
+ export function SimpleFilterableMap() {
91
+ const [filters, setFilters] = useState({
92
+ food: true,
93
+ hotel: true,
94
+ attraction: true,
95
+ shopping: true,
96
+ cafe: true,
97
+ });
98
+
99
+ // Filtrar marcadores
100
+ const filteredMarkers = locations
101
+ .filter(loc => filters[loc.category as keyof typeof filters])
102
+ .map(loc => ({
103
+ position: loc.position,
104
+ label: loc.label,
105
+ title: loc.title,
106
+ info: loc.info,
107
+ customColor: loc.color, // Sempre branco para emojis
108
+ iconColor: loc.iconColor, // Texto preto para emojis
109
+ }));
110
+
111
+ const toggleFilter = (category: keyof typeof filters) => {
112
+ setFilters(prev => ({ ...prev, [category]: !prev[category] }));
113
+ };
114
+
115
+ const filterConfig = [
116
+ { id: 'food', label: 'Restaurantes', emoji: '🍕', color: 'var(--destructive)', count: locations.filter(l => l.category === 'food').length },
117
+ { id: 'hotel', label: 'Hotéis', emoji: '🏨', color: 'var(--info)', count: locations.filter(l => l.category === 'hotel').length },
118
+ { id: 'attraction', label: 'Atrações', emoji: '🌳', color: 'var(--success)', count: locations.filter(l => l.category === 'attraction').length },
119
+ { id: 'shopping', label: 'Compras', emoji: '🛍️', color: 'var(--warning)', count: locations.filter(l => l.category === 'shopping').length },
120
+ { id: 'cafe', label: 'Cafés', emoji: '☕', color: 'var(--primary)', count: locations.filter(l => l.category === 'cafe').length },
121
+ ];
122
+
123
+ return (
124
+ <Card>
125
+ <CardHeader>
126
+ <div className="flex items-center justify-between">
127
+ <CardTitle>Mapa com Emojis</CardTitle>
128
+ <Badge variant="secondary">
129
+ {filteredMarkers.length} de {locations.length} locais
130
+ </Badge>
131
+ </div>
132
+ <CardDescription>
133
+ Marcadores com emojis em fundo branco - Selecione as categorias para visualizar
134
+ </CardDescription>
135
+ </CardHeader>
136
+ <CardContent className="space-y-4">
137
+ {/* Filtros Compactos com Checkboxes */}
138
+ <div className="flex flex-wrap gap-4 p-3 rounded-lg border bg-muted">
139
+ {filterConfig.map(filter => (
140
+ <div key={filter.id} className="flex items-center space-x-2">
141
+ <Checkbox
142
+ id={filter.id}
143
+ checked={filters[filter.id as keyof typeof filters]}
144
+ onCheckedChange={() => toggleFilter(filter.id as keyof typeof filters)}
145
+ />
146
+ <Label htmlFor={filter.id} className="flex items-center gap-2 cursor-pointer text-sm">
147
+ <span className="text-base">{filter.emoji}</span>
148
+ <span>{filter.label}</span>
149
+ <Badge variant="outline" className="text-xs h-4 px-1">
150
+ {filter.count}
151
+ </Badge>
152
+ </Label>
153
+ </div>
154
+ ))}
155
+ </div>
156
+
157
+ {/* Mapa */}
158
+ <Map
159
+ center={{ lat: -23.5505, lng: -46.6333 }}
160
+ zoom={14}
161
+ markers={filteredMarkers}
162
+ height="450px"
163
+ zoomControl={true}
164
+ fullscreenControl={true}
165
+ />
166
+
167
+ {/* Legenda */}
168
+ <div className="flex flex-wrap gap-3 justify-center pt-3 border-t border-border">
169
+ {filterConfig.map(filter => (
170
+ <div
171
+ key={filter.id}
172
+ className="flex items-center gap-2 px-3 py-1 rounded-lg"
173
+ style={{
174
+ backgroundColor: filters[filter.id as keyof typeof filters]
175
+ ? `color-mix(in srgb, ${filter.color}, transparent 85%)`
176
+ : 'var(--muted)',
177
+ opacity: filters[filter.id as keyof typeof filters] ? 1 : 0.5,
178
+ }}
179
+ >
180
+ <div
181
+ className="w-3 h-3 rounded-full"
182
+ style={{ backgroundColor: filter.color }}
183
+ />
184
+ <span className="text-base">{filter.emoji}</span>
185
+ <span className="text-sm">{filter.label}</span>
186
+ </div>
187
+ ))}
188
+ </div>
189
+ </CardContent>
190
+ </Card>
191
+ );
192
+ }
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Map Examples - Index
3
+ *
4
+ * Exportação centralizada de todos os exemplos de Map
5
+ */
6
+
7
+ // Exemplos básicos
8
+ export {
9
+ StoreDeliveryMap,
10
+ FavoritePlacesMap,
11
+ CompanyOfficesMap,
12
+ ServiceZoneMap,
13
+ CompactMapsGrid
14
+ } from './MapExamples';
15
+
16
+ // Exemplos de rotas
17
+ export {
18
+ DeliveryRouteMap,
19
+ TouristRouteMap,
20
+ TransportModeComparison,
21
+ MultiStopDeliveryRoute,
22
+ RouteMapShowcase
23
+ } from './RouteMapExamples';
24
+
25
+ // Exemplos avançados
26
+ export {
27
+ LocationPickerExample,
28
+ DeliveryAddressSelector
29
+ } from './LocationPickerExample';
30
+
31
+ // Exemplo com filtros
32
+ export {
33
+ FilterableMapExample,
34
+ markerGroups,
35
+ type MarkerGroupId
36
+ } from './FilterableMapExample';
37
+
38
+ // Exemplo simples com filtros
39
+ export { SimpleFilterableMap } from './SimpleFilterableMap';
40
+
41
+ // Showcase completo
42
+ export { MapShowcase } from './MapShowcase';
43
+
44
+ /**
45
+ * Uso:
46
+ *
47
+ * import { StoreDeliveryMap, LocationPickerExample } from '@/components/examples';
48
+ *
49
+ * ou
50
+ *
51
+ * import { StoreDeliveryMap } from '@/components/examples/MapExamples';
52
+ */
@@ -0,0 +1,27 @@
1
+ import React, { useState } from 'react'
2
+
3
+ const ERROR_IMG_SRC =
4
+ 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iODgiIGhlaWdodD0iODgiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgc3Ryb2tlPSIjMDAwIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBvcGFjaXR5PSIuMyIgZmlsbD0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIzLjciPjxyZWN0IHg9IjE2IiB5PSIxNiIgd2lkdGg9IjU2IiBoZWlnaHQ9IjU2IiByeD0iNiIvPjxwYXRoIGQ9Im0xNiA1OCAxNi0xOCAzMiAzMiIvPjxjaXJjbGUgY3g9IjUzIiBjeT0iMzUiIHI9IjciLz48L3N2Zz4KCg=='
5
+
6
+ export function ImageWithFallback(props: React.ImgHTMLAttributes<HTMLImageElement>) {
7
+ const [didError, setDidError] = useState(false)
8
+
9
+ const handleError = () => {
10
+ setDidError(true)
11
+ }
12
+
13
+ const { src, alt, style, className, ...rest } = props
14
+
15
+ return didError ? (
16
+ <div
17
+ className={`inline-block bg-gray-100 text-center align-middle ${className ?? ''}`}
18
+ style={style}
19
+ >
20
+ <div className="flex items-center justify-center w-full h-full">
21
+ <img src={ERROR_IMG_SRC} alt="Error loading image" {...rest} data-original-url={src} />
22
+ </div>
23
+ </div>
24
+ ) : (
25
+ <img src={src} alt={alt} className={className} style={style} {...rest} onError={handleError} />
26
+ )
27
+ }
@@ -0,0 +1,44 @@
1
+ // ============================================================================
2
+ // Xertica Assistant Exports
3
+ // ============================================================================
4
+
5
+ export { XerticaAssistant } from './ui/xertica-assistant';
6
+ export type {
7
+ XerticaAssistantProps,
8
+ Message,
9
+ Conversation,
10
+ Suggestion,
11
+ SearchResult,
12
+ SearchSource,
13
+ SearchCommand,
14
+ MessageType,
15
+ AttachmentType,
16
+ SearchResultType,
17
+ AssistantMode,
18
+ AssistantTab
19
+ } from './ui/xertica-assistant';
20
+
21
+ // ============================================================================
22
+ // Page Components
23
+ // ============================================================================
24
+
25
+ export { TemplatePage } from './TemplatePage';
26
+
27
+ // UI Components - All available via /components/ui/index.ts
28
+ export * from './ui';
29
+
30
+ // Utility Components (used by XerticaAssistant)
31
+ export { CodeBlock } from './CodeBlock';
32
+ export { MarkdownMessage } from './MarkdownMessage';
33
+ export { ModernChatInput } from './ModernChatInput';
34
+ export { FormattedDocument } from './FormattedDocument';
35
+
36
+ // Branding
37
+ export { XerticaLogo } from './XerticaLogo';
38
+ export { XerticaXLogo } from './XerticaXLogo';
39
+ export { XerticaOrbe } from './XerticaOrbe';
40
+
41
+ // Layout
42
+ export { Sidebar } from './Sidebar';
43
+ export { ThemeToggle } from './ThemeToggle';
44
+ export { LanguageSelector } from './LanguageSelector';