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,380 @@
1
+ import React, { useState, useMemo } 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 { Button } from '../ui/button';
6
+ import { Checkbox } from '../ui/checkbox';
7
+ import { Label } from '../ui/label';
8
+ import { Utensils, Hotel, MapPin, Landmark, ShoppingBag, Coffee, X, Filter, type LucideIcon } from 'lucide-react';
9
+ import { cn } from '../ui/utils';
10
+
11
+ /**
12
+ * Exemplo avançado de mapa com marcadores personalizados e filtros por grupos
13
+ * Demonstra como criar marcadores com cores e ícones distintos que podem ser filtrados
14
+ * IMPORTANTE: Usa ícones Lucide com cores sólidas para contraste com fundos coloridos
15
+ */
16
+
17
+ // Helper para criar SVG string de ícones Lucide manualmente
18
+ function createLucideIconSvg(iconName: string): string {
19
+ // SVG paths para os ícones Lucide usados
20
+ const iconPaths: Record<string, string> = {
21
+ utensils: '<path d="M3 2v7c0 1.1.9 2 2 2h4a2 2 0 0 0 2-2V2"></path><path d="M7 2v20"></path><path d="M21 15V2v0a5 5 0 0 0-5 5v6c0 1.1.9 2 2 2h3Zm0 0v7"></path>',
22
+ hotel: '<path d="M18 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2Z"></path><path d="M8 6h8"></path><path d="M8 10h8"></path><path d="M8 14h8"></path><path d="M8 18h8"></path>',
23
+ landmark: '<path d="m3 21 18-18"></path><path d="M9 15V6h6v9"></path><path d="M12 3v3"></path><path d="M3 15h18"></path><path d="M6 21v-6"></path><path d="M18 21v-6"></path>',
24
+ shopping: '<path d="M6 2 3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4Z"></path><path d="M3 6h18"></path><path d="M16 10a4 4 0 0 1-8 0"></path>',
25
+ coffee: '<path d="M17 8h1a4 4 0 1 1 0 8h-1"></path><path d="M3 8h14v9a4 4 0 0 1-4 4H7a4 4 0 0 1-4-4Z"></path><line x1="6" x2="6" y1="2" y2="4"></line><line x1="10" x2="10" y1="2" y2="4"></line><line x1="14" x2="14" y1="2" y2="4"></line>',
26
+ };
27
+
28
+ const path = iconPaths[iconName] || '';
29
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">${path}</svg>`;
30
+ }
31
+
32
+ // Definir tipos de grupos com cores e ícones Lucide
33
+ export const markerGroups = {
34
+ restaurant: {
35
+ id: 'restaurant',
36
+ name: 'Restaurantes',
37
+ color: 'var(--destructive)', // red
38
+ iconColor: '#FFFFFF', // white para contraste
39
+ iconName: 'utensils',
40
+ icon: Utensils,
41
+ },
42
+ hotel: {
43
+ id: 'hotel',
44
+ name: 'Hotéis',
45
+ color: 'var(--info)', // blue
46
+ iconColor: '#FFFFFF',
47
+ iconName: 'hotel',
48
+ icon: Hotel,
49
+ },
50
+ landmark: {
51
+ id: 'landmark',
52
+ name: 'Pontos Turísticos',
53
+ color: 'var(--success)', // green
54
+ iconColor: '#FFFFFF',
55
+ iconName: 'landmark',
56
+ icon: Landmark,
57
+ },
58
+ shopping: {
59
+ id: 'shopping',
60
+ name: 'Compras',
61
+ color: 'var(--warning)', // orange
62
+ iconColor: '#FFFFFF',
63
+ iconName: 'shopping',
64
+ icon: ShoppingBag,
65
+ },
66
+ cafe: {
67
+ id: 'cafe',
68
+ name: 'Cafeterias',
69
+ color: 'var(--primary)', // purple
70
+ iconColor: '#FFFFFF',
71
+ iconName: 'coffee',
72
+ icon: Coffee,
73
+ },
74
+ } as const;
75
+
76
+ export type MarkerGroupId = keyof typeof markerGroups;
77
+
78
+ // Dados de exemplo com diferentes grupos
79
+ const sampleLocations = [
80
+ // Restaurantes
81
+ {
82
+ position: { lat: -23.5505, lng: -46.6333 },
83
+ title: 'Restaurante Italiano',
84
+ info: 'Melhor pizza da cidade',
85
+ group: 'restaurant' as MarkerGroupId,
86
+ },
87
+ {
88
+ position: { lat: -23.5485, lng: -46.6350 },
89
+ title: 'Sushi Bar',
90
+ info: 'Comida japonesa autêntica',
91
+ group: 'restaurant' as MarkerGroupId,
92
+ },
93
+ {
94
+ position: { lat: -23.5520, lng: -46.6310 },
95
+ title: 'Churrascaria Premium',
96
+ info: 'Rodízio tradicional brasileiro',
97
+ group: 'restaurant' as MarkerGroupId,
98
+ },
99
+ // Hotéis
100
+ {
101
+ position: { lat: -23.5475, lng: -46.6361 },
102
+ title: 'Hotel Luxo',
103
+ info: '5 estrelas - Vista panorâmica',
104
+ group: 'hotel' as MarkerGroupId,
105
+ },
106
+ {
107
+ position: { lat: -23.5530, lng: -46.6340 },
108
+ title: 'Hotel Boutique',
109
+ info: 'Design moderno e aconchegante',
110
+ group: 'hotel' as MarkerGroupId,
111
+ },
112
+ // Pontos Turísticos
113
+ {
114
+ position: { lat: -23.5613, lng: -46.6563 },
115
+ title: 'Parque Ibirapuera',
116
+ info: 'Maior parque urbano da cidade',
117
+ group: 'landmark' as MarkerGroupId,
118
+ },
119
+ {
120
+ position: { lat: -23.5558, lng: -46.6396 },
121
+ title: 'MASP',
122
+ info: 'Museu de Arte de São Paulo',
123
+ group: 'landmark' as MarkerGroupId,
124
+ },
125
+ {
126
+ position: { lat: -23.5489, lng: -46.6388 },
127
+ title: 'Avenida Paulista',
128
+ info: 'Centro financeiro e cultural',
129
+ group: 'landmark' as MarkerGroupId,
130
+ },
131
+ // Shopping
132
+ {
133
+ position: { lat: -23.5465, lng: -46.6400 },
134
+ title: 'Shopping Center',
135
+ info: 'Mais de 300 lojas',
136
+ group: 'shopping' as MarkerGroupId,
137
+ },
138
+ {
139
+ position: { lat: -23.5540, lng: -46.6380 },
140
+ title: 'Galeria de Arte',
141
+ info: 'Arte contemporânea e design',
142
+ group: 'shopping' as MarkerGroupId,
143
+ },
144
+ // Cafeterias
145
+ {
146
+ position: { lat: -23.5495, lng: -46.6345 },
147
+ title: 'Café Artesanal',
148
+ info: 'Café especial e brunch',
149
+ group: 'cafe' as MarkerGroupId,
150
+ },
151
+ {
152
+ position: { lat: -23.5510, lng: -46.6370 },
153
+ title: 'Coffee House',
154
+ info: 'Café gourmet e wi-fi',
155
+ group: 'cafe' as MarkerGroupId,
156
+ },
157
+ {
158
+ position: { lat: -23.5525, lng: -46.6355 },
159
+ title: 'Café Cultural',
160
+ info: 'Livros e café',
161
+ group: 'cafe' as MarkerGroupId,
162
+ },
163
+ ];
164
+
165
+ /**
166
+ * Componente de Mapa com Filtro de Grupos
167
+ * Permite filtrar marcadores por categorias com interface integrada no mapa
168
+ * COMPACTO: Usa checkboxes para economia de espaço
169
+ */
170
+ export function FilterableMapExample() {
171
+ const [activeFilters, setActiveFilters] = useState<Set<MarkerGroupId>>(
172
+ new Set(Object.keys(markerGroups) as MarkerGroupId[])
173
+ );
174
+ const [showFilters, setShowFilters] = useState(true);
175
+
176
+ // Filtrar locais baseado nos filtros ativos
177
+ const filteredLocations = useMemo(() => {
178
+ return sampleLocations.filter(location => activeFilters.has(location.group));
179
+ }, [activeFilters]);
180
+
181
+ // Toggle de filtro individual
182
+ const toggleFilter = (groupId: MarkerGroupId) => {
183
+ setActiveFilters(prev => {
184
+ const newFilters = new Set(prev);
185
+ if (newFilters.has(groupId)) {
186
+ newFilters.delete(groupId);
187
+ } else {
188
+ newFilters.add(groupId);
189
+ }
190
+ return newFilters;
191
+ });
192
+ };
193
+
194
+ // Selecionar/Desselecionar todos
195
+ const toggleAll = () => {
196
+ if (activeFilters.size === Object.keys(markerGroups).length) {
197
+ setActiveFilters(new Set());
198
+ } else {
199
+ setActiveFilters(new Set(Object.keys(markerGroups) as MarkerGroupId[]));
200
+ }
201
+ };
202
+
203
+ // Preparar marcadores com cores personalizadas e ícones
204
+ const markersWithCustomization = filteredLocations.map(location => ({
205
+ ...location,
206
+ customColor: markerGroups[location.group].color,
207
+ iconSvg: createLucideIconSvg(markerGroups[location.group].iconName),
208
+ iconColor: markerGroups[location.group].iconColor,
209
+ }));
210
+
211
+ // Contar marcadores por grupo
212
+ const getGroupCount = (groupId: MarkerGroupId) => {
213
+ return sampleLocations.filter(loc => loc.group === groupId).length;
214
+ };
215
+
216
+ return (
217
+ <div className="space-y-4">
218
+ <div className="flex items-center justify-between">
219
+ <div className="flex items-center gap-2">
220
+ <MapPin className="w-5 h-5 text-primary" />
221
+ <h3>Filtro por Categorias</h3>
222
+ </div>
223
+ <Badge variant="secondary">
224
+ {filteredLocations.length} de {sampleLocations.length} locais
225
+ </Badge>
226
+ </div>
227
+
228
+ <p className="text-muted-foreground">
229
+ Use os filtros compactos no canto superior esquerdo do mapa para visualizar marcadores por categoria
230
+ </p>
231
+
232
+ {/* Container do Mapa com Filtros Integrados */}
233
+ <div className="relative">
234
+ <Map
235
+ center={{ lat: -23.5505, lng: -46.6333 }}
236
+ zoom={14}
237
+ markers={markersWithCustomization}
238
+ height="500px"
239
+ zoomControl={true}
240
+ fullscreenControl={true}
241
+ />
242
+ {/* Painel de Filtros COMPACTO Integrado no Mapa */}
243
+ <div
244
+ className={cn(
245
+ "absolute top-4 left-4 z-10 transition-all duration-300",
246
+ showFilters ? "translate-x-0" : "-translate-x-full"
247
+ )}
248
+ >
249
+ <div
250
+ className="p-2.5 rounded-lg shadow-[var(--shadow-elevation-sm)] bg-card border border-border max-w-[220px]"
251
+ >
252
+ <div className="flex items-center justify-between mb-2 gap-2">
253
+ <div className="flex items-center gap-1.5">
254
+ <Filter className="w-3.5 h-3.5 text-foreground" />
255
+ <span className="text-sm text-foreground">Filtros</span>
256
+ </div>
257
+ <Button
258
+ size="sm"
259
+ variant="ghost"
260
+ onClick={() => setShowFilters(false)}
261
+ className="h-5 w-5 p-0"
262
+ >
263
+ <X className="w-3 h-3" />
264
+ </Button>
265
+ </div>
266
+
267
+ <div className="space-y-1.5">
268
+ {/* Checkbox Todos */}
269
+ <div className="flex items-center space-x-2 py-0.5">
270
+ <Checkbox
271
+ id="filter-all"
272
+ checked={activeFilters.size === Object.keys(markerGroups).length}
273
+ onCheckedChange={toggleAll}
274
+ />
275
+ <Label htmlFor="filter-all" className="flex items-center gap-1.5 cursor-pointer text-sm">
276
+ <MapPin className="w-3.5 h-3.5" />
277
+ Todos
278
+ <Badge variant="secondary" className="text-xs h-4 px-1">
279
+ {sampleLocations.length}
280
+ </Badge>
281
+ </Label>
282
+ </div>
283
+
284
+ {/* Checkboxes de Filtro por Grupo */}
285
+ {Object.entries(markerGroups).map(([id, group]) => {
286
+ const groupId = id as MarkerGroupId;
287
+ const Icon = group.icon;
288
+ const isActive = activeFilters.has(groupId);
289
+ const count = getGroupCount(groupId);
290
+
291
+ return (
292
+ <div key={id} className="flex items-center space-x-2 py-0.5">
293
+ <Checkbox
294
+ id={`filter-${id}`}
295
+ checked={isActive}
296
+ onCheckedChange={() => toggleFilter(groupId)}
297
+ />
298
+ <Label
299
+ htmlFor={`filter-${id}`}
300
+ className="flex items-center gap-1.5 cursor-pointer text-sm"
301
+ >
302
+ <Icon
303
+ className="w-3.5 h-3.5"
304
+ style={{ color: group.color }}
305
+ />
306
+ <span className={isActive ? "text-foreground" : "text-muted-foreground"}>
307
+ {group.name}
308
+ </span>
309
+ <Badge variant="outline" className="text-xs h-4 px-1">
310
+ {count}
311
+ </Badge>
312
+ </Label>
313
+ </div>
314
+ );
315
+ })}
316
+ </div>
317
+ </div>
318
+ </div>
319
+
320
+ {/* Botão para mostrar filtros quando estão escondidos */}
321
+ {!showFilters && (
322
+ <Button
323
+ size="sm"
324
+ onClick={() => setShowFilters(true)}
325
+ className="absolute top-4 left-4 z-10 h-8 bg-primary text-primary-foreground"
326
+ >
327
+ <Filter className="w-3.5 h-3.5 mr-1.5" />
328
+ Filtros
329
+ </Button>
330
+ )}
331
+ </div>
332
+
333
+ {/* Legenda Horizontal */}
334
+ <div className="flex flex-wrap gap-3 justify-center pt-2 border-t border-border">
335
+ {Object.entries(markerGroups).map(([id, group]) => {
336
+ const Icon = group.icon;
337
+ const count = getGroupCount(id as MarkerGroupId);
338
+ return (
339
+ <div
340
+ key={id}
341
+ className="flex items-center gap-2 px-3 py-1 rounded-lg bg-muted"
342
+ >
343
+ <div
344
+ className="w-3 h-3 rounded-full"
345
+ style={{ backgroundColor: group.color }}
346
+ />
347
+ <Icon className="w-4 h-4" style={{ color: group.color }} />
348
+ <span className="text-sm" style={{ color: 'var(--foreground)' }}>
349
+ {group.name}
350
+ </span>
351
+ <Badge variant="secondary" className="text-xs h-5">
352
+ {count}
353
+ </Badge>
354
+ </div>
355
+ );
356
+ })}
357
+ </div>
358
+
359
+ {/* Estatísticas */}
360
+ <div className="grid grid-cols-2 md:grid-cols-4 gap-3">
361
+ <div className="text-center p-3 rounded-lg bg-muted">
362
+ <p className="text-muted-foreground">Total de Locais</p>
363
+ <p className="text-2xl text-foreground">{sampleLocations.length}</p>
364
+ </div>
365
+ <div className="text-center p-3 rounded-lg bg-muted">
366
+ <p className="text-muted-foreground">Visíveis</p>
367
+ <p className="text-2xl text-primary">{filteredLocations.length}</p>
368
+ </div>
369
+ <div className="text-center p-3 rounded-lg bg-muted">
370
+ <p className="text-muted-foreground">Categorias</p>
371
+ <p className="text-2xl text-foreground">{Object.keys(markerGroups).length}</p>
372
+ </div>
373
+ <div className="text-center p-3 rounded-lg bg-muted">
374
+ <p className="text-muted-foreground">Filtros Ativos</p>
375
+ <p className="text-2xl text-chart-2">{activeFilters.size}</p>
376
+ </div>
377
+ </div>
378
+ </div>
379
+ );
380
+ }