Files
voice_guide/frontend/src/components/GuidePanel.tsx
T
fn-registry agent f803067cb1 chore: initial sync
2026-04-28 22:12:49 +02:00

91 lines
2.8 KiB
TypeScript

import { Paper, Text, Stack, Group, Badge, ScrollArea } from '@mantine/core'
import { IconMapPin, IconVolume } from '@tabler/icons-react'
import { ActionIcon } from '@mantine/core'
import type { GuideResponse } from '../lib/api'
interface GuidePanelProps {
response: GuideResponse | null
loading: boolean
onSpeak: (text: string) => void
speaking: boolean
onStopSpeaking: () => void
}
export function GuidePanel({ response, loading, onSpeak, speaking, onStopSpeaking }: GuidePanelProps) {
if (loading) {
return (
<Paper withBorder p="md" radius="md">
<Text size="sm" c="dimmed">Consultando tu entorno...</Text>
</Paper>
)
}
if (!response) {
return (
<Paper withBorder p="md" radius="md">
<Text size="sm" c="dimmed">
Pulsa el micrófono o el botón de explorar para recibir información sobre tu entorno.
</Text>
</Paper>
)
}
const { guide, location, pois } = response
return (
<Stack gap="sm">
{/* Location header */}
<Paper withBorder p="sm" radius="md">
<Group gap="xs" wrap="nowrap">
<IconMapPin size={16} style={{ flexShrink: 0 }} />
<Text size="xs" lineClamp={2}>
{location.street ? `${location.street}, ` : ''}
{location.neighbourhood ? `${location.neighbourhood}, ` : ''}
{location.city}
</Text>
</Group>
</Paper>
{/* Guide text */}
<Paper withBorder p="md" radius="md">
<Group justify="space-between" mb="xs">
<Text fw={600} size="sm">Guía</Text>
<ActionIcon
variant={speaking ? 'filled' : 'subtle'}
color="teal"
size="sm"
onClick={() => speaking ? onStopSpeaking() : onSpeak(guide)}
>
<IconVolume size={14} />
</ActionIcon>
</Group>
<Text size="sm" style={{ whiteSpace: 'pre-wrap' }}>{guide}</Text>
</Paper>
{/* POIs */}
{pois.length > 0 && (
<Paper withBorder p="sm" radius="md">
<Text fw={600} size="sm" mb="xs">Lugares cercanos</Text>
<ScrollArea.Autosize mah={200}>
<Stack gap={4}>
{pois.map(poi => (
<Group key={poi.id} gap="xs" wrap="nowrap">
<Text size="xs" fw={500} style={{ flex: 1 }} lineClamp={1}>
{poi.name}
</Text>
<Badge size="xs" variant="light">{poi.category}</Badge>
{(poi.score ?? 0) > 0 && (
<Badge size="xs" variant="dot" color="teal">
{poi.score?.toFixed(1)}
</Badge>
)}
</Group>
))}
</Stack>
</ScrollArea.Autosize>
</Paper>
)}
</Stack>
)
}