All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 1m4s
- Agregar botón "Crear Webhook de Debug" en WebhookReceiverSection - Detectar si ya existe un webhook apuntando al receptor de debug - Permitir eliminar el webhook de debug - Incluir todos los eventos disponibles al crear el webhook - También incluye mejoras previas de manejo de media y mensajes
119 lines
3.3 KiB
Vue
119 lines
3.3 KiB
Vue
<template>
|
|
<div
|
|
class="rounded-lg overflow-hidden cursor-pointer max-w-[280px]"
|
|
@click="openInMaps"
|
|
>
|
|
<!-- Map preview image -->
|
|
<div class="relative">
|
|
<img
|
|
:src="mapImageUrl"
|
|
:alt="location.name || 'Ubicación'"
|
|
class="w-full h-[150px] object-cover"
|
|
@error="mapError = true"
|
|
/>
|
|
|
|
<!-- Fallback si la imagen falla -->
|
|
<div
|
|
v-if="mapError"
|
|
class="w-full h-[150px] flex items-center justify-center bg-[var(--wa-bg-light)]"
|
|
>
|
|
<UIcon name="i-lucide-map" class="w-12 h-12 text-[var(--wa-text-muted)]" />
|
|
</div>
|
|
|
|
<!-- Pin overlay -->
|
|
<div class="absolute inset-0 flex items-center justify-center pointer-events-none">
|
|
<div class="w-8 h-8 -mt-4">
|
|
<UIcon name="i-lucide-map-pin" class="w-8 h-8 text-red-500 drop-shadow-lg" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Location info -->
|
|
<div
|
|
class="p-3"
|
|
:class="fromMe ? 'bg-white/10' : 'bg-[var(--wa-bg-light)]'"
|
|
>
|
|
<p
|
|
v-if="location.name"
|
|
class="font-medium truncate"
|
|
:class="fromMe ? 'text-white' : 'text-[var(--wa-text)]'"
|
|
>
|
|
{{ location.name }}
|
|
</p>
|
|
<p
|
|
v-if="location.address"
|
|
class="text-sm mt-0.5 line-clamp-2"
|
|
:class="fromMe ? 'text-white/70' : 'text-[var(--wa-text-muted)]'"
|
|
>
|
|
{{ location.address }}
|
|
</p>
|
|
<p
|
|
v-if="!location.name && !location.address"
|
|
class="text-sm"
|
|
:class="fromMe ? 'text-white/70' : 'text-[var(--wa-text-muted)]'"
|
|
>
|
|
{{ formatCoordinates(location.latitude, location.longitude) }}
|
|
</p>
|
|
|
|
<!-- Open in maps hint -->
|
|
<div
|
|
class="flex items-center gap-1 mt-2 text-xs"
|
|
:class="fromMe ? 'text-white/50' : 'text-[var(--wa-text-muted)]'"
|
|
>
|
|
<UIcon name="i-lucide-external-link" class="w-3 h-3" />
|
|
<span>Abrir en Google Maps</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import type { LocationInfo } from '~/types/message'
|
|
|
|
interface Props {
|
|
location: LocationInfo
|
|
fromMe?: boolean
|
|
}
|
|
|
|
const props = withDefaults(defineProps<Props>(), {
|
|
fromMe: false
|
|
})
|
|
|
|
const mapError = ref(false)
|
|
|
|
// URL de imagen estática de mapa (usando OpenStreetMap tiles)
|
|
const mapImageUrl = computed(() => {
|
|
const { latitude, longitude } = props.location
|
|
const zoom = 15
|
|
|
|
// Usar Google Static Maps API (requiere API key en producción)
|
|
// Por ahora usamos un servicio gratuito alternativo
|
|
return `https://static-maps.yandex.ru/1.x/?ll=${longitude},${latitude}&z=${zoom}&l=map&size=300,150&pt=${longitude},${latitude},pm2rdm`
|
|
})
|
|
|
|
const mapsUrl = computed(() => {
|
|
const { latitude, longitude, name } = props.location
|
|
const query = name ? encodeURIComponent(name) : `${latitude},${longitude}`
|
|
return `https://www.google.com/maps/search/?api=1&query=${query}`
|
|
})
|
|
|
|
const openInMaps = () => {
|
|
window.open(mapsUrl.value, '_blank')
|
|
}
|
|
|
|
const formatCoordinates = (lat: number, lng: number): string => {
|
|
const latDir = lat >= 0 ? 'N' : 'S'
|
|
const lngDir = lng >= 0 ? 'E' : 'W'
|
|
return `${Math.abs(lat).toFixed(4)}° ${latDir}, ${Math.abs(lng).toFixed(4)}° ${lngDir}`
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
.line-clamp-2 {
|
|
display: -webkit-box;
|
|
-webkit-line-clamp: 2;
|
|
-webkit-box-orient: vertical;
|
|
overflow: hidden;
|
|
}
|
|
</style>
|