feat(frontend): map feature in vue 3 (#3376)

* feat(frontend): Map feature update

* feat(frontend): Fix linting
This commit is contained in:
MateuszMichalowski 2024-10-18 14:59:22 +02:00 committed by GitHub
parent ba91f8d6fe
commit 60d91927a5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 696 additions and 88 deletions

View File

@ -22,10 +22,12 @@
"@babel/node": "^7.13.13",
"@babel/preset-env": "^7.13.12",
"@morev/vue-transitions": "^3.0.2",
"@types/leaflet": "^1.9.12",
"@vee-validate/i18n": "^4.13.2",
"@vee-validate/rules": "^4.13.2",
"@vee-validate/yup": "^4.13.2",
"@vitejs/plugin-vue": "3.2.0",
"@vue-leaflet/vue-leaflet": "^0.10.1",
"@vue/apollo-composable": "^4.0.2",
"@vue/apollo-option": "^4.0.0",
"@vue/compat": "^3.4.31",
@ -45,6 +47,8 @@
"graphql-tag": "^2.12.6",
"identity-obj-proxy": "^3.0.0",
"jwt-decode": "^3.1.2",
"leaflet": "^1.9.4",
"leaflet-geosearch": "^4.0.0",
"portal-vue": "^3.0.0",
"qrcanvas-vue": "3",
"regenerator-runtime": "^0.13.7",

View File

@ -0,0 +1,46 @@
<template>
<div class="coordinates-display">
<div class="p-2">
<BButton class="me-1" size="sm" @click="emit('centerMap', 'USER')"><IBiPinMapFill /></BButton>
<span>
{{
$t('settings.GMS.map.userCoords', {
lat: userPosition.lat.toFixed(6),
lng: userPosition.lng.toFixed(6),
})
}}
</span>
</div>
<div class="p-2">
<BButton class="me-1" size="sm" @click="emit('centerMap', 'COMMUNITY')">
<IBiPinMap />
</BButton>
<span>
{{
$t('settings.GMS.map.communityCoords', {
lat: communityPosition.lat.toFixed(6),
lng: communityPosition.lng.toFixed(6),
})
}}
</span>
</div>
</div>
</template>
<script setup>
import { BButton } from 'bootstrap-vue-next'
const emit = defineEmits(['centerMap'])
const props = defineProps({
userPosition: Object,
communityPosition: Object,
})
</script>
<style scoped>
.coordinates-display {
margin-top: 10px;
font-weight: bold;
}
</style>

View File

@ -1,6 +0,0 @@
<template>
<BButton>{{ $t('settings.GMS.location.button') }}</BButton>
</template>
<script setup>
import { BButton } from 'bootstrap-vue-next'
</script>

View File

@ -0,0 +1,91 @@
<template>
<BButton @click="isModalOpen = !isModalOpen">{{ $t('settings.GMS.location.button') }}</BButton>
<BModal :model-value="isModalOpen" fullscreen @update:modelValue="isModalOpen = !isModalOpen">
<template #title>
<h3>{{ $t('settings.GMS.map.headline') }}</h3>
</template>
<template #default>
<BContainer class="bg-white appBoxShadow gradido-border-radius p-4 mt--3">
<user-location-map
v-if="isModalOpen"
:user-marker-coords="userLocation"
:community-marker-coords="communityLocation"
@update:userPosition="updateUserLocation"
/>
</BContainer>
</template>
<template #footer>
<BButton variant="gradido" @click="saveUserLocation">
{{ $t('settings.GMS.location.saveLocation') }}
</BButton>
</template>
</BModal>
</template>
<script setup>
import { ref, computed } from 'vue'
import { useI18n } from 'vue-i18n'
import { useMutation, useQuery } from '@vue/apollo-composable'
import 'leaflet/dist/leaflet.css'
import { updateUserInfos } from '@/graphql/mutations'
import { useAppToast } from '@/composables/useToast'
import UserLocationMap from '@/components/UserSettings/UserLocationMap'
import { BButton, BModal } from 'bootstrap-vue-next'
import { userLocationQuery } from '@/graphql/queries'
import CONFIG from '@/config'
const { t } = useI18n()
const { mutate: updateUserInfo } = useMutation(updateUserInfos)
const { onResult, onError } = useQuery(userLocationQuery, {}, { fetchPolicy: 'network-only' })
const { toastSuccess, toastError } = useAppToast()
const capturedLocation = ref(null)
const isModalOpen = ref(false)
const userLocation = ref({ lat: 0, lng: 0 })
const communityLocation = ref({ lat: 0, lng: 0 })
const emit = defineEmits(['close'])
onResult(({ data }) => {
communityLocation.value.lng = data.userLocation.longitude
communityLocation.value.lat = data.userLocation.latitude
userLocation.value.lng = data?.userLocation?.longitude ?? communityLocation.value.lng
userLocation.value.lat = data?.userLocation?.latitude ?? communityLocation.value.lat
})
onError((err) => {
userLocation.value = defaultLocation.value
communityLocation.value = defaultLocation.value
toastError(err.message)
})
const defaultLocation = computed(() => {
const defaultCommunityCoords = CONFIG.COMMUNITY_LOCATION.split(',')
return {
lat: parseFloat(defaultCommunityCoords[0]),
lng: parseFloat(defaultCommunityCoords[1]),
}
})
const saveUserLocation = async () => {
try {
const loc = { longitude: capturedLocation.value.lng, latitude: capturedLocation.value.lat }
await updateUserInfo({
gmsLocation: {
longitude: capturedLocation.value.lng,
latitude: capturedLocation.value.lat,
},
})
toastSuccess(t('settings.GMS.location.updateSuccess'))
isModalOpen.value = false
} catch (error) {
toastError(error.message)
}
}
const updateUserLocation = (currentUserLocation) => {
capturedLocation.value = currentUserLocation
}
</script>

View File

@ -0,0 +1,218 @@
<template>
<div>
<coordinates-display
:community-position="communityPosition"
:user-position="userPosition"
@centerMap="handleMapCenter"
/>
<div ref="mapContainer" class="map-container" />
</div>
</template>
<script setup>
import { ref, onMounted, onUnmounted, watch, nextTick } from 'vue'
import L from 'leaflet'
import { GeoSearchControl, OpenStreetMapProvider } from 'leaflet-geosearch'
import 'leaflet-geosearch/dist/geosearch.css'
import CoordinatesDisplay from '@/components/UserSettings/CoordinatesDisplay.vue'
import { useI18n } from 'vue-i18n'
const mapContainer = ref(null)
const map = ref(null)
const userMarker = ref(null)
const communityMarker = ref(null)
const searchQuery = ref('')
const userPosition = ref({ lat: 0, lng: 0 })
const communityPosition = ref({ lat: 0, lng: 0 })
const defaultZoom = 13
const emit = defineEmits(['update:userPosition'])
const props = defineProps({
userMarkerCoords: Object,
communityMarkerCoords: Object,
})
const { t } = useI18n()
onMounted(async () => {
if (props.userMarkerCoords) {
userPosition.value = props.userMarkerCoords
}
if (props.communityMarkerCoords) {
communityPosition.value = props.communityMarkerCoords
}
await nextTick()
initMap()
window.addEventListener('resize', handleResize)
})
onUnmounted(() => {
if (map.value) {
map.value.remove()
}
window.removeEventListener('resize', handleResize)
})
function initMap() {
if (mapContainer.value && !map.value) {
map.value = L.map(mapContainer.value, {
center: [userPosition.value.lat, userPosition.value.lng],
zoom: defaultZoom,
zoomControl: false,
})
L.control.zoom({ position: 'topleft' }).addTo(map.value)
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors',
maxZoom: 19,
}).addTo(map.value)
// User marker (movable)
userMarker.value = L.marker([userPosition.value.lat, userPosition.value.lng], {
draggable: true,
icon: L.icon({
iconUrl:
'https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-2x-blue.png',
shadowUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.7/images/marker-shadow.png',
iconSize: [25, 41],
iconAnchor: [12, 41],
popupAnchor: [1, -34],
shadowSize: [41, 41],
}),
}).addTo(map.value)
userMarker.value.bindPopup(t('settings.GMS.map.userLocationLabel')).openPopup()
// Community marker (fixed)
communityMarker.value = L.marker([communityPosition.value.lat, communityPosition.value.lng], {
draggable: false,
icon: L.icon({
iconUrl:
'https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-2x-red.png',
shadowUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.7/images/marker-shadow.png',
iconSize: [25, 41],
iconAnchor: [12, 41],
popupAnchor: [1, -34],
shadowSize: [41, 41],
}),
}).addTo(map.value)
communityMarker.value.bindPopup(t('settings.GMS.map.communityLocationLabel'))
map.value.on('click', onMapClick)
userMarker.value.on('dragend', onMarkerDragEnd)
// GeoSearch control
const provider = new OpenStreetMapProvider()
const searchControl = new GeoSearchControl({
provider: provider,
style: 'button',
showMarker: false,
showPopup: false,
autoClose: true,
retainZoomLevel: false,
animateZoom: true,
keepResult: false,
searchLabel: t('settings.GMS.map.search'),
})
map.value.addControl(searchControl)
map.value.on('geosearch/showlocation', (result) => {
const { x, y, label } = result.location
updateUserPosition({ lat: y, lng: x })
})
// Center map on user position
centerMapOnUser()
}
}
function handleResize() {
if (map.value) {
map.value.invalidateSize()
centerMapOnUser()
}
}
function onMapClick(e) {
updateUserPosition(e.latlng)
}
function onMarkerDragEnd() {
if (userMarker.value) {
updateUserPosition(userMarker.value.getLatLng())
}
}
function updateUserPosition(latlng) {
userPosition.value = { lat: latlng.lat, lng: latlng.lng }
if (userMarker.value) {
userMarker.value.setLatLng(latlng)
userMarker.value.openPopup()
}
centerMapOnUser()
emit('update:userPosition', userPosition.value)
}
function centerMapOnUser() {
if (map.value && userPosition.value) {
map.value.setView([userPosition.value.lat, userPosition.value.lng], map.value.getZoom(), {
animate: true,
pan: {
duration: 0.5,
},
})
}
}
function centerMapOnCommunity() {
if (map.value && communityPosition.value) {
map.value.setView(
[communityPosition.value.lat, communityPosition.value.lng],
map.value.getZoom(),
{
animate: true,
pan: {
duration: 0.5,
},
},
)
}
}
function handleMapCenter(centerMode) {
if (centerMode === 'USER') centerMapOnUser()
else centerMapOnCommunity()
}
watch(userPosition, (newPosition) => {
emit('update:userPosition', newPosition)
})
</script>
<style scoped>
.map-container {
height: 400px;
width: 100%;
}
.leaflet-control-custom a {
background-color: #fff;
width: 30px;
height: 30px;
line-height: 30px;
text-align: center;
text-decoration: none;
color: black;
}
.leaflet-control-custom a:hover {
background-color: #f4f4f4;
}
:deep(.leaflet-control-zoom > a) {
color: #555 !important;
}
</style>

View File

@ -50,6 +50,7 @@ const community = {
COMMUNITY_DESCRIPTION:
process.env.COMMUNITY_DESCRIPTION ?? 'Die lokale Entwicklungsumgebung von Gradido.',
COMMUNITY_SUPPORT_MAIL: process.env.COMMUNITY_SUPPORT_MAIL ?? 'support@supportmail.com',
COMMUNITY_LOCATION: process.env.COMMUNITY_LOCATION ?? '49.280377, 9.690151',
}
const meta = {

View File

@ -28,6 +28,15 @@ export const authenticateGmsUserSearch = gql`
}
`
export const userLocationQuery = gql`
query {
userLocation {
userLocation
communityLocation
}
}
`
export const authenticateHumhubAutoLogin = gql`
query {
authenticateHumhubAutoLogin

View File

@ -312,10 +312,20 @@
"disabled": "Daten werden nicht nach GMS exportiert",
"enabled": "Daten werden nach GMS exportiert",
"location": {
"button": "Klick mich!",
"label": "Positionsbestimmung",
"button": "Klick mich!"
"saveLocation": "Standort speichern",
"updateSuccess": "Standort erfolgreich gespeichert"
},
"location-format": "Position auf Karte anzeigen:",
"map": {
"communityCoords": "Ihr Gemeinschafts-Standort: Breitengrad {lat}, Längengrad {lng}",
"communityLocationLabel": "Ihr Gemeinschafts-Standort",
"headline": "Geografische Standorterfassung des Benutzers",
"userCoords": "Ihr Standort: Breitengrad {lat}, Längengrad {lng}",
"userLocationLabel": "Ihr Standort",
"search": "Nach einem Standort suchen"
},
"naming-format": "Namen anzeigen:",
"publish-location": {
"exact": "Genaue Position",

View File

@ -312,10 +312,20 @@
"disabled": "Data not exported to GMS",
"enabled": "Data exported to GMS",
"location": {
"label": "pinpoint location",
"button": "click me!"
"button": "Click me!",
"label": "Pinpoint location",
"saveLocation": "Save Location",
"updateSuccess": "Location successfully saved"
},
"location-format": "Show position on map:",
"map": {
"communityCoords": "Your Community Location: Lat {lat}, Lng {lng}",
"communityLocationLabel": "Your Community-Location",
"headline": "Geographic Location-Capturing of the User",
"userCoords": "Your Location: Lat {lat}, Lng {lng}",
"userLocationLabel": "Your Location",
"search": "Search for a location"
},
"naming-format": "Show Name:",
"publish-location": {
"exact": "exact position",

View File

@ -268,6 +268,23 @@
"warningText": "Aún estas?"
},
"settings": {
"GMS": {
"location": {
"button": "¡Haz clic aquí!",
"label": "Ubicación exacta",
"saveLocation": "Guardar ubicación",
"updateSuccess": "Ubicación guardada exitosamente"
},
"map": {
"communityCoords": "Ubicación de tu comunidad: Lat {lat}, Lng {lng}",
"communityLocationLabel": "Ubicación de tu comunidad",
"headline": "Captura de ubicación geográfica del usuario",
"userCoords": "Tu ubicación: Lat {lat}, Lng {lng}",
"userLocationLabel": "Tu ubicación",
"search": "Buscar una ubicación",
"success": "Ubicación guardada exitosamente"
}
},
"language": {
"changeLanguage": "Cambiar idioma",
"de": "Deutsch",

View File

@ -276,6 +276,23 @@
"warningText": "Êtes-vous toujours connecté?"
},
"settings": {
"GMS": {
"location": {
"button": "Cliquez ici !",
"label": "Emplacement précis",
"saveLocation": "Enregistrer l'emplacement",
"updateSuccess": "Emplacement enregistré avec succès"
},
"map": {
"communityCoords": "Emplacement de votre communauté : Lat {lat}, Long {lng}",
"communityLocationLabel": "Emplacement de votre communauté",
"headline": "Capture de la localisation géographique de l'utilisateur",
"userCoords": "Votre emplacement : Lat {lat}, Long {lng}",
"userLocationLabel": "Votre emplacement",
"search": "Rechercher un emplacement",
"success": "Emplacement enregistré avec succès"
}
},
"hideAmountGDD": "Votre montant GDD est caché.",
"hideAmountGDT": "Votre montant GDT est caché.",
"language": {

View File

@ -268,6 +268,23 @@
"warningText": "Ben je er nog?"
},
"settings": {
"GMS": {
"location": {
"button": "Klik hier!",
"label": "Exacte locatie",
"saveLocation": "Locatie opslaan",
"updateSuccess": "Locatie succesvol opgeslagen"
},
"map": {
"communityCoords": "Locatie van je gemeenschap: Lat {lat}, Lng {lng}",
"communityLocationLabel": "Locatie van je gemeenschap",
"headline": "Geografische locatiebepaling van de gebruiker",
"userCoords": "Jouw locatie: Lat {lat}, Lng {lng}",
"userLocationLabel": "Jouw locatie",
"search": "Zoek een locatie",
"success": "Locatie succesvol opgeslagen"
}
},
"language": {
"changeLanguage": "Taal veranderen",
"de": "Deutsch",

View File

@ -245,6 +245,23 @@
"warningText": "Hala orada mısın?"
},
"settings": {
"GMS": {
"location": {
"button": "Buraya tıklayın!",
"label": "Tam konum",
"saveLocation": "Konumu Kaydet",
"updateSuccess": "Konum başarıyla kaydedildi"
},
"map": {
"communityCoords": "Topluluk Konumunuz: Enlem {lat}, Boylam {lng}",
"communityLocationLabel": "Topluluk Konumunuz",
"headline": "Kullanıcının Coğrafi Konum Tespiti",
"userCoords": "Konumunuz: Enlem {lat}, Boylam {lng}",
"userLocationLabel": "Konumunuz",
"search": "Konum ara",
"success": "Konum başarıyla kaydedildi"
}
},
"language": {
"changeLanguage": "Dili değiştir",
"de": "Deutsch",

View File

@ -81,100 +81,102 @@
</BCol>
</BRow>
</BTab>
<div v-if="isCommunityService">
<BTab class="community-service-tabs" :title="$t('settings.community')">
<div class="h2">{{ $t('settings.allow-community-services') }}</div>
<div v-if="isHumhub" class="mt-3">
<BRow>
<BTab
v-if="isCommunityService"
class="community-service-tabs"
:title="$t('settings.community')"
>
<div class="h2">{{ $t('settings.allow-community-services') }}</div>
<div v-if="isHumhub" class="mt-3">
<BRow>
<BCol cols="12" md="6" lg="6">
<div class="h3">{{ $t('Humhub.title') }}</div>
</BCol>
<BCol cols="12" md="6" lg="6" class="text-end">
<user-settings-switch
:initial-value="$store.state.humhubAllowed"
:attr-name="'humhubAllowed'"
:disabled="isHumhubActivated"
:enabled-text="$t('settings.humhub.enabled')"
:disabled-text="$t('settings.humhub.disabled')"
:not-allowed-text="$t('settings.humhub.delete-disabled')"
@value-changed="humhubStateSwitch"
/>
</BCol>
</BRow>
<div class="h4">{{ $t('Humhub.desc') }}</div>
<BRow v-if="humhubAllowed" class="mb-4 humhub-publish-name-row">
<BCol cols="12" md="6" lg="6">
{{ $t('settings.humhub.naming-format') }}
</BCol>
<BCol cols="12" md="6" lg="6">
<user-naming-format
:initial-value="$store.state.humhubPublishName"
:attr-name="'humhubPublishName'"
:success-message="$t('settings.humhub.publish-name.updated')"
/>
</BCol>
</BRow>
</div>
<div v-if="isGMS" class="mt-3">
<BRow>
<BCol cols="12" md="6" lg="6">
<div class="h3">{{ $t('GMS.title') }}</div>
</BCol>
<BCol cols="12" md="6" lg="6" class="text-end">
<user-settings-switch
:initial-value="$store.state.gmsAllowed"
:attr-name="'gmsAllowed'"
:enabled-text="$t('settings.GMS.enabled')"
:disabled-text="$t('settings.GMS.disabled')"
@value-changed="gmsStateSwitch"
/>
</BCol>
</BRow>
<div class="h4 mt-3">{{ $t('GMS.desc') }}</div>
<div v-if="gmsAllowed">
<BRow class="mb-4">
<BCol cols="12" md="6" lg="6">
<div class="h3">{{ $t('Humhub.title') }}</div>
</BCol>
<BCol cols="12" md="6" lg="6" class="text-end">
<user-settings-switch
:initial-value="$store.state.humhubAllowed"
:attr-name="'humhubAllowed'"
:disabled="isHumhubActivated"
:enabled-text="$t('settings.humhub.enabled')"
:disabled-text="$t('settings.humhub.disabled')"
:not-allowed-text="$t('settings.humhub.delete-disabled')"
@value-changed="humhubStateSwitch"
/>
</BCol>
</BRow>
<div class="h4">{{ $t('Humhub.desc') }}</div>
<BRow v-if="humhubAllowed" class="mb-4 humhub-publish-name-row">
<BCol cols="12" md="6" lg="6">
{{ $t('settings.humhub.naming-format') }}
{{ $t('settings.GMS.naming-format') }}
</BCol>
<BCol cols="12" md="6" lg="6">
<user-naming-format
:initial-value="$store.state.humhubPublishName"
:attr-name="'humhubPublishName'"
:success-message="$t('settings.humhub.publish-name.updated')"
:initial-value="$store.state.gmsPublishName"
:attr-name="'gmsPublishName'"
:success-message="$t('settings.GMS.publish-name.updated')"
/>
</BCol>
</BRow>
</div>
<div v-if="isGMS" class="mt-3">
<BRow>
<BRow class="mb-4">
<BCol cols="12" md="6" lg="6">
<div class="h3">{{ $t('GMS.title') }}</div>
{{ $t('settings.GMS.location-format') }}
</BCol>
<BCol cols="12" md="6" lg="6" class="text-end">
<user-settings-switch
:initial-value="$store.state.gmsAllowed"
:attr-name="'gmsAllowed'"
:enabled-text="$t('settings.GMS.enabled')"
:disabled-text="$t('settings.GMS.disabled')"
@value-changed="gmsStateSwitch"
/>
<BCol cols="12" md="6" lg="6">
<user-g-m-s-location-format />
</BCol>
</BRow>
<div class="h4 mt-3">{{ $t('GMS.desc') }}</div>
<div v-if="gmsAllowed">
<BRow class="mb-4">
<BCol cols="12" md="6" lg="6">
{{ $t('settings.GMS.naming-format') }}
</BCol>
<BCol cols="12" md="6" lg="6">
<user-naming-format
:initial-value="$store.state.gmsPublishName"
:attr-name="'gmsPublishName'"
:success-message="$t('settings.GMS.publish-name.updated')"
/>
</BCol>
</BRow>
<BRow class="mb-4">
<BCol cols="12" md="6" lg="6">
{{ $t('settings.GMS.location-format') }}
</BCol>
<BCol cols="12" md="6" lg="6">
<user-g-m-s-location-format />
</BCol>
</BRow>
<BRow class="mb-5">
<BCol cols="12" md="6" lg="6">
{{ $t('settings.GMS.location.label') }}
</BCol>
<BCol cols="12" md="6" lg="6">
<user-g-m-s-location />
</BCol>
</BRow>
</div>
</div>
<div v-else>
<BRow>
<BRow class="mb-5">
<BCol cols="12" md="6" lg="6">
<div class="h3 text-muted">{{ $t('GMS.title') }}</div>
{{ $t('settings.GMS.location.label') }}
</BCol>
<BCol cols="12" md="6" lg="6" class="text-end">
<user-settings-switch :disabled="true" />
<BCol cols="12" md="6" lg="6">
<user-gms-location-capturing />
</BCol>
</BRow>
<div class="h4 mt-3 text-muted">{{ $t('GMS.desc') }}</div>
</div>
</BTab>
</div>
</div>
<div v-else>
<BRow>
<BCol cols="12" md="6" lg="6">
<div class="h3 text-muted">{{ $t('GMS.title') }}</div>
</BCol>
<BCol cols="12" md="6" lg="6" class="text-end">
<user-settings-switch :disabled="true" />
</BCol>
</BRow>
<div class="h4 mt-3 text-muted">{{ $t('GMS.desc') }}</div>
</div>
</BTab>
</BTabs>
<!-- TODO<BRow>
@ -200,7 +202,7 @@ import UserPassword from '@/components/UserSettings/UserPassword'
import UserSettingsSwitch from '../components/UserSettings/UserSettingsSwitch.vue'
import UserNamingFormat from '@/components/UserSettings/UserNamingFormat'
import UserGMSLocationFormat from '@/components/UserSettings/UserGMSLocationFormat'
import UserGMSLocation from '@/components/UserSettings/UserGMSLocation'
import UserGmsLocationCapturing from '@/components/UserSettings/UserGmsLocationCapturing'
import UserNewsletter from '@/components/UserSettings/UserNewsletter.vue'
import { BTabs, BTab, BRow, BCol, BFormInput, BFormGroup, BForm, BButton } from 'bootstrap-vue-next'
@ -305,4 +307,12 @@ const humhubStateSwitch = (eventData) => {
:deep(.form-label) {
padding-bottom: 0;
}
:deep(.nav-link) {
color: #383838 !important;
}
:deep(.nav-link.active) {
color: #525f7f !important;
}
</style>

View File

@ -1278,6 +1278,11 @@
"@floating-ui/utils" "^0.2.7"
vue-demi ">=0.13.0"
"@googlemaps/js-api-loader@^1.16.6":
version "1.16.8"
resolved "https://registry.yarnpkg.com/@googlemaps/js-api-loader/-/js-api-loader-1.16.8.tgz#1595a2af80ca07e551fc961d921a2437d1cb3643"
integrity sha512-CROqqwfKotdO6EBjZO/gQGVTbeDps5V7Mt9+8+5Q+jTg5CRMi3Ii/L9PmV3USROrt2uWxtGzJHORmByxyo9pSQ==
"@graphql-typed-document-node/core@^3.1.1":
version "3.2.0"
resolved "https://registry.yarnpkg.com/@graphql-typed-document-node/core/-/core-3.2.0.tgz#5f3d96ec6b2354ad6d8a28bf216a1d97b5426861"
@ -1641,6 +1646,11 @@
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4"
integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==
"@types/geojson@*":
version "7946.0.14"
resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.14.tgz#319b63ad6df705ee2a65a73ef042c8271e696613"
integrity sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg==
"@types/json-schema@^7.0.5":
version "7.0.15"
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841"
@ -1651,6 +1661,13 @@
resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==
"@types/leaflet@^1.9.12":
version "1.9.12"
resolved "https://registry.yarnpkg.com/@types/leaflet/-/leaflet-1.9.12.tgz#a6626a0b3fba36fd34723d6e95b22e8024781ad6"
integrity sha512-BK7XS+NyRI291HIo0HCfE18Lp8oA30H1gpi1tf0mF3TgiCEzanQjOqNZ4x126SXzzi2oNSZhZ5axJp1k0iM6jg==
dependencies:
"@types/geojson" "*"
"@types/node@>=6":
version "22.1.0"
resolved "https://registry.yarnpkg.com/@types/node/-/node-22.1.0.tgz#6d6adc648b5e03f0e83c78dc788c2b037d0ad94b"
@ -1813,6 +1830,13 @@
loupe "^3.1.1"
tinyrainbow "^1.2.0"
"@vue-leaflet/vue-leaflet@^0.10.1":
version "0.10.1"
resolved "https://registry.yarnpkg.com/@vue-leaflet/vue-leaflet/-/vue-leaflet-0.10.1.tgz#17330515629d500ac113009a49f8cf98d140e8cc"
integrity sha512-RNEDk8TbnwrJl8ujdbKgZRFygLCxd0aBcWLQ05q/pGv4+d0jamE3KXQgQBqGAteE1mbQsk3xoNcqqUgaIGfWVg==
dependencies:
vue "^3.2.25"
"@vue/apollo-composable@^4.0.2":
version "4.0.2"
resolved "https://registry.yarnpkg.com/@vue/apollo-composable/-/apollo-composable-4.0.2.tgz#ea3c001d25b3bf659aad5ae7a849fed3928aaa35"
@ -1860,6 +1884,17 @@
estree-walker "^2.0.2"
source-map-js "^1.2.0"
"@vue/compiler-core@3.5.12":
version "3.5.12"
resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.5.12.tgz#bd70b7dabd12b0b6f31bc53418ba3da77994c437"
integrity sha512-ISyBTRMmMYagUxhcpyEH0hpXRd/KqDU4ymofPgl2XAkY9ZhQ+h0ovEZJIiPop13UmR/54oA2cgMDjgroRelaEw==
dependencies:
"@babel/parser" "^7.25.3"
"@vue/shared" "3.5.12"
entities "^4.5.0"
estree-walker "^2.0.2"
source-map-js "^1.2.0"
"@vue/compiler-dom@3.4.31":
version "3.4.31"
resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.4.31.tgz#30961ca847f5d6ad18ffa26236c219f61b195f6b"
@ -1876,6 +1911,14 @@
"@vue/compiler-core" "3.4.36"
"@vue/shared" "3.4.36"
"@vue/compiler-dom@3.5.12":
version "3.5.12"
resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.5.12.tgz#456d631d11102535b7ee6fd954cf2c93158d0354"
integrity sha512-9G6PbJ03uwxLHKQ3P42cMTi85lDRvGLB2rSGOiQqtXELat6uI4n8cNz9yjfVHRPIu+MsK6TE418Giruvgptckg==
dependencies:
"@vue/compiler-core" "3.5.12"
"@vue/shared" "3.5.12"
"@vue/compiler-sfc@3.4.31":
version "3.4.31"
resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.4.31.tgz#cc6bfccda17df8268cc5440842277f61623c591f"
@ -1891,6 +1934,21 @@
postcss "^8.4.38"
source-map-js "^1.2.0"
"@vue/compiler-sfc@3.5.12":
version "3.5.12"
resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.5.12.tgz#6688120d905fcf22f7e44d3cb90f8dabc4dd3cc8"
integrity sha512-2k973OGo2JuAa5+ZlekuQJtitI5CgLMOwgl94BzMCsKZCX/xiqzJYzapl4opFogKHqwJk34vfsaKpfEhd1k5nw==
dependencies:
"@babel/parser" "^7.25.3"
"@vue/compiler-core" "3.5.12"
"@vue/compiler-dom" "3.5.12"
"@vue/compiler-ssr" "3.5.12"
"@vue/shared" "3.5.12"
estree-walker "^2.0.2"
magic-string "^0.30.11"
postcss "^8.4.47"
source-map-js "^1.2.0"
"@vue/compiler-sfc@^3.4.35":
version "3.4.36"
resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.4.36.tgz#887809183a273dc0ef8337d5e84ef6a781727ccc"
@ -1922,6 +1980,14 @@
"@vue/compiler-dom" "3.4.36"
"@vue/shared" "3.4.36"
"@vue/compiler-ssr@3.5.12":
version "3.5.12"
resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.5.12.tgz#5f1a3fbd5c44b79a6dbe88729f7801d9c9218bde"
integrity sha512-eLwc7v6bfGBSM7wZOGPmRavSWzNFF6+PdRhE+VFJhNCgHiF8AM7ccoqcv5kBXA2eWUfigD7byekvf/JsOfKvPA==
dependencies:
"@vue/compiler-dom" "3.5.12"
"@vue/shared" "3.5.12"
"@vue/devtools-api@^6.0.0-beta.11", "@vue/devtools-api@^6.5.0", "@vue/devtools-api@^6.6.1", "@vue/devtools-api@^6.6.3":
version "6.6.3"
resolved "https://registry.yarnpkg.com/@vue/devtools-api/-/devtools-api-6.6.3.tgz#b23a588154cba8986bba82b6e1d0248bde3fd1a0"
@ -1943,6 +2009,13 @@
dependencies:
"@vue/shared" "3.4.31"
"@vue/reactivity@3.5.12":
version "3.5.12"
resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.5.12.tgz#a2815d91842ed7b9e7e7936c851923caf6b6e603"
integrity sha512-UzaN3Da7xnJXdz4Okb/BGbAaomRHc3RdoWqTzlvd9+WBR5m3J39J1fGcHes7U3za0ruYn/iYy/a1euhMEHvTAg==
dependencies:
"@vue/shared" "3.5.12"
"@vue/runtime-core@3.4.31":
version "3.4.31"
resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.4.31.tgz#ad3a41ad76385c0429e3e4dbefb81918494e10cf"
@ -1951,6 +2024,14 @@
"@vue/reactivity" "3.4.31"
"@vue/shared" "3.4.31"
"@vue/runtime-core@3.5.12":
version "3.5.12"
resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.5.12.tgz#849207f203d0fd82971f19574d30dbe7134c78c7"
integrity sha512-hrMUYV6tpocr3TL3Ad8DqxOdpDe4zuQY4HPY3X/VRh+L2myQO8MFXPAMarIOSGNu0bFAjh1yBkMPXZBqCk62Uw==
dependencies:
"@vue/reactivity" "3.5.12"
"@vue/shared" "3.5.12"
"@vue/runtime-dom@3.4.31":
version "3.4.31"
resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.4.31.tgz#bae7ad844f944af33699c73581bc36125bab96ce"
@ -1961,6 +2042,16 @@
"@vue/shared" "3.4.31"
csstype "^3.1.3"
"@vue/runtime-dom@3.5.12":
version "3.5.12"
resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.5.12.tgz#6d4de3df49a90a460b311b1100baa5e2d0d1c8c9"
integrity sha512-q8VFxR9A2MRfBr6/55Q3umyoN7ya836FzRXajPB6/Vvuv0zOPL+qltd9rIMzG/DbRLAIlREmnLsplEF/kotXKA==
dependencies:
"@vue/reactivity" "3.5.12"
"@vue/runtime-core" "3.5.12"
"@vue/shared" "3.5.12"
csstype "^3.1.3"
"@vue/server-renderer@3.4.31":
version "3.4.31"
resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.4.31.tgz#bbe990f793c36d62d05bdbbaf142511d53e159fd"
@ -1969,6 +2060,14 @@
"@vue/compiler-ssr" "3.4.31"
"@vue/shared" "3.4.31"
"@vue/server-renderer@3.5.12":
version "3.5.12"
resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.5.12.tgz#79c6bc3860e4e4ef80d85653c5d03fd94b26574e"
integrity sha512-I3QoeDDeEPZm8yR28JtY+rk880Oqmj43hreIBVTicisFTx/Dl7JpG72g/X7YF8hnQD3IFhkky5i2bPonwrTVPg==
dependencies:
"@vue/compiler-ssr" "3.5.12"
"@vue/shared" "3.5.12"
"@vue/shared@3.4.31":
version "3.4.31"
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.4.31.tgz#af9981f57def2c3f080c14bf219314fc0dc808a0"
@ -1979,6 +2078,11 @@
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.4.36.tgz#7551f41684966acb6a307152b49a8308e7f69203"
integrity sha512-fdPLStwl1sDfYuUftBaUVn2pIrVFDASYerZSrlBvVBfylObPA1gtcWJHy5Ox8jLEJ524zBibss488Q3SZtU1uA==
"@vue/shared@3.5.12":
version "3.5.12"
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.5.12.tgz#f9e45b7f63f2c3f40d84237b1194b7f67de192e3"
integrity sha512-L2RPSAwUFbgZH20etwrXyVyCBu9OxRSi8T/38QsvnkJyvq2LufW2lDCOzm7t/U9C1mkhJGWYfCuFBCmIuNivrg==
"@vue/test-utils@^2.4.5":
version "2.4.6"
resolved "https://registry.yarnpkg.com/@vue/test-utils/-/test-utils-2.4.6.tgz#7d534e70c4319d2a587d6a3b45a39e9695ade03c"
@ -4962,6 +5066,19 @@ kolorist@^1.8.0:
resolved "https://registry.yarnpkg.com/kolorist/-/kolorist-1.8.0.tgz#edddbbbc7894bc13302cdf740af6374d4a04743c"
integrity sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==
leaflet-geosearch@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/leaflet-geosearch/-/leaflet-geosearch-4.0.0.tgz#d7488830004515452368d333f7a49d06d59ea81b"
integrity sha512-a92VNY9gxyv3oyEDqIWoCNoBllajWRYejztzOSNmpLRtzpA6JtGgy/wwl9tsB8+6Eek1fe+L6+W0MDEOaidbXA==
optionalDependencies:
"@googlemaps/js-api-loader" "^1.16.6"
leaflet "^1.6.0"
leaflet@^1.6.0, leaflet@^1.9.4:
version "1.9.4"
resolved "https://registry.yarnpkg.com/leaflet/-/leaflet-1.9.4.tgz#23fae724e282fa25745aff82ca4d394748db7d8d"
integrity sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==
levn@^0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade"
@ -5700,6 +5817,11 @@ picocolors@^1.0.0, picocolors@^1.0.1:
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.1.tgz#a8ad579b571952f0e5d25892de5445bcfe25aaa1"
integrity sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==
picocolors@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.0.tgz#5358b76a78cde483ba5cef6a9dc9671440b27d59"
integrity sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==
picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.3.1:
version "2.3.1"
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
@ -5823,6 +5945,15 @@ postcss@^8.4.43:
picocolors "^1.0.1"
source-map-js "^1.2.0"
postcss@^8.4.47:
version "8.4.47"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.47.tgz#5bf6c9a010f3e724c503bf03ef7947dcb0fea365"
integrity sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==
dependencies:
nanoid "^3.3.7"
picocolors "^1.1.0"
source-map-js "^1.2.1"
prelude-ls@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
@ -6333,6 +6464,11 @@ source-map-js@^1.0.1, source-map-js@^1.0.2, source-map-js@^1.2.0:
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.0.tgz#16b809c162517b5b8c3e7dcd315a2a5c2612b2af"
integrity sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==
source-map-js@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46"
integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==
source-map-support@^0.5.16, source-map-support@~0.5.20:
version "0.5.21"
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f"
@ -7276,6 +7412,17 @@ vue@3.4.31:
"@vue/server-renderer" "3.4.31"
"@vue/shared" "3.4.31"
vue@^3.2.25:
version "3.5.12"
resolved "https://registry.yarnpkg.com/vue/-/vue-3.5.12.tgz#e08421c601b3617ea2c9ef0413afcc747130b36c"
integrity sha512-CLVZtXtn2ItBIi/zHZ0Sg1Xkb7+PU32bJJ8Bmy7ts3jxXTcbfsEfBivFYYWz1Hur+lalqGAh65Coin0r+HRUfg==
dependencies:
"@vue/compiler-dom" "3.5.12"
"@vue/compiler-sfc" "3.5.12"
"@vue/runtime-dom" "3.5.12"
"@vue/server-renderer" "3.5.12"
"@vue/shared" "3.5.12"
vuex-persistedstate@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/vuex-persistedstate/-/vuex-persistedstate-4.1.0.tgz#127165f85f5b4534fb3170a5d3a8be9811bd2a53"