forked from potsda.mn/mobilizon
feat(addresses): Allow to enter manual addresses
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
parent
50ab531156
commit
85d643d0ec
|
@ -10,32 +10,24 @@
|
||||||
>
|
>
|
||||||
<template #label>
|
<template #label>
|
||||||
{{ actualLabel }}
|
{{ actualLabel }}
|
||||||
<span v-if="gettingLocation">{{ t("Getting location") }}</span>
|
|
||||||
</template>
|
</template>
|
||||||
<p class="control" v-if="canShowLocateMeButton">
|
|
||||||
<o-loading
|
|
||||||
:full-page="false"
|
|
||||||
v-model:active="gettingLocation"
|
|
||||||
:can-cancel="false"
|
|
||||||
:container="mapMarker?.$el"
|
|
||||||
/>
|
|
||||||
<o-button
|
<o-button
|
||||||
|
v-if="canShowLocateMeButton"
|
||||||
ref="mapMarker"
|
ref="mapMarker"
|
||||||
icon-right="map-marker"
|
icon-right="map-marker"
|
||||||
@click="locateMe"
|
@click="locateMe"
|
||||||
:title="t('Use my location')"
|
:title="t('Use my location')"
|
||||||
/>
|
/>
|
||||||
</p>
|
|
||||||
<o-autocomplete
|
<o-autocomplete
|
||||||
:data="addressData"
|
:data="addressData"
|
||||||
v-model="queryText"
|
v-model="queryTextWithDefault"
|
||||||
:placeholder="placeholderWithDefault"
|
:placeholder="placeholderWithDefault"
|
||||||
:customFormatter="(elem: IAddress) => addressFullName(elem)"
|
:customFormatter="(elem: IAddress) => addressFullName(elem)"
|
||||||
:debounceTyping="debounceDelay"
|
:debounceTyping="debounceDelay"
|
||||||
@typing="asyncData"
|
@typing="asyncData"
|
||||||
:icon="canShowLocateMeButton ? null : 'map-marker'"
|
:icon="canShowLocateMeButton ? null : 'map-marker'"
|
||||||
expanded
|
expanded
|
||||||
@select="updateSelected"
|
@select="setSelected"
|
||||||
:id="id"
|
:id="id"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
dir="auto"
|
dir="auto"
|
||||||
|
@ -49,35 +41,39 @@
|
||||||
<small>{{ addressToPoiInfos(option).alternativeName }}</small>
|
<small>{{ addressToPoiInfos(option).alternativeName }}</small>
|
||||||
</template>
|
</template>
|
||||||
<template #empty>
|
<template #empty>
|
||||||
<span v-if="isFetching">{{ t("Searching…") }}</span>
|
<template v-if="isFetching">{{ t("Searching…") }}</template>
|
||||||
<div v-else-if="queryText.length >= 3" class="enabled">
|
<template v-else-if="queryTextWithDefault.length >= 3">
|
||||||
<span>{{
|
<p>
|
||||||
t('No results for "{queryText}"', { queryText })
|
{{
|
||||||
}}</span>
|
t('No results for "{queryText}"', {
|
||||||
<span>{{
|
queryText: queryTextWithDefault,
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{{
|
||||||
t(
|
t(
|
||||||
"You can try another search term or drag and drop the marker on the map",
|
"You can try another search term or add the address details manually below."
|
||||||
{
|
|
||||||
queryText,
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
}}</span>
|
}}
|
||||||
<!-- <p class="control" @click="openNewAddressModal">-->
|
</p>
|
||||||
<!-- <button type="button" class="button is-primary">{{ t('Add') }}</button>-->
|
</template>
|
||||||
<!-- </p>-->
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
</o-autocomplete>
|
</o-autocomplete>
|
||||||
<o-button
|
<o-button
|
||||||
:disabled="!queryText"
|
:disabled="!queryTextWithDefault"
|
||||||
@click="resetAddress"
|
@click="resetAddress"
|
||||||
class="reset-area"
|
class="reset-area"
|
||||||
icon-left="close"
|
icon-left="close"
|
||||||
:title="t('Clear address field')"
|
:title="t('Clear address field')"
|
||||||
/>
|
/>
|
||||||
</o-field>
|
</o-field>
|
||||||
|
<p v-if="gettingLocation" class="flex gap-2">
|
||||||
|
<Loading class="animate-spin" />
|
||||||
|
{{ t("Getting location") }}
|
||||||
|
</p>
|
||||||
<div
|
<div
|
||||||
class="mt-2 p-2 rounded-lg shadow-md dark:bg-violet-3"
|
class="mt-2 p-2 rounded-lg shadow-md bg-white dark:bg-violet-3"
|
||||||
v-if="!hideSelected && (selected?.originId || selected?.url)"
|
v-if="!hideSelected && (selected?.originId || selected?.url)"
|
||||||
>
|
>
|
||||||
<div class="">
|
<div class="">
|
||||||
|
@ -90,16 +86,80 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="map" v-if="!hideMap && selected && selected.geom">
|
<o-collapse
|
||||||
|
v-model:open="detailsAddress"
|
||||||
|
:aria-id="`${id}-address-details`"
|
||||||
|
class="my-3"
|
||||||
|
>
|
||||||
|
<template #trigger>
|
||||||
|
<o-button
|
||||||
|
variant="primary"
|
||||||
|
outlined
|
||||||
|
:aria-controls="`${id}-address-details`"
|
||||||
|
:icon-right="detailsAddress ? 'chevron-up' : 'chevron-down'"
|
||||||
|
>
|
||||||
|
{{ t("Details") }}
|
||||||
|
</o-button>
|
||||||
|
</template>
|
||||||
|
<form @submit.prevent="saveManualAddress">
|
||||||
|
<header>
|
||||||
|
<h2>{{ t("Manually enter address") }}</h2>
|
||||||
|
</header>
|
||||||
|
<section>
|
||||||
|
<o-field :label="t('Name')" labelFor="addressNameInput">
|
||||||
|
<o-input
|
||||||
|
aria-required="true"
|
||||||
|
required
|
||||||
|
v-model="selected.description"
|
||||||
|
id="addressNameInput"
|
||||||
|
/>
|
||||||
|
</o-field>
|
||||||
|
|
||||||
|
<o-field :label="t('Street')" labelFor="streetInput">
|
||||||
|
<o-input v-model="selected.street" id="streetInput" />
|
||||||
|
</o-field>
|
||||||
|
|
||||||
|
<o-field grouped>
|
||||||
|
<o-field :label="t('Postal Code')" labelFor="postalCodeInput">
|
||||||
|
<o-input v-model="selected.postalCode" id="postalCodeInput" />
|
||||||
|
</o-field>
|
||||||
|
|
||||||
|
<o-field :label="t('Locality')" labelFor="localityInput">
|
||||||
|
<o-input v-model="selected.locality" id="localityInput" />
|
||||||
|
</o-field>
|
||||||
|
</o-field>
|
||||||
|
|
||||||
|
<o-field grouped>
|
||||||
|
<o-field :label="t('Region')" labelFor="regionInput">
|
||||||
|
<o-input v-model="selected.region" id="regionInput" />
|
||||||
|
</o-field>
|
||||||
|
|
||||||
|
<o-field :label="t('Country')" labelFor="countryInput">
|
||||||
|
<o-input v-model="selected.country" id="countryInput" />
|
||||||
|
</o-field>
|
||||||
|
</o-field>
|
||||||
|
</section>
|
||||||
|
<footer class="mt-3 flex gap-2 items-center">
|
||||||
|
<o-button native-type="submit">
|
||||||
|
{{ t("Save") }}
|
||||||
|
</o-button>
|
||||||
|
<o-button outlined type="button" @click="resetAddress">
|
||||||
|
{{ t("Clear") }}
|
||||||
|
</o-button>
|
||||||
|
<p>
|
||||||
|
{{
|
||||||
|
t(
|
||||||
|
"You can drag and drop the marker below to the desired location"
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</p>
|
||||||
|
</footer>
|
||||||
|
</form>
|
||||||
|
</o-collapse>
|
||||||
|
<div class="map" v-if="!hideMap">
|
||||||
<map-leaflet
|
<map-leaflet
|
||||||
:coords="selected.geom"
|
:coords="selected.geom ?? defaultCoords"
|
||||||
:marker="{
|
:marker="mapMarkerValue"
|
||||||
text: [
|
|
||||||
addressToPoiInfos(selected).name,
|
|
||||||
addressToPoiInfos(selected).alternativeName,
|
|
||||||
],
|
|
||||||
icon: addressToPoiInfos(selected).poiIcon.icon,
|
|
||||||
}"
|
|
||||||
:updateDraggableMarkerCallback="reverseGeoCode"
|
:updateDraggableMarkerCallback="reverseGeoCode"
|
||||||
:options="{ zoom: mapDefaultZoom }"
|
:options="{ zoom: mapDefaultZoom }"
|
||||||
:readOnly="false"
|
:readOnly="false"
|
||||||
|
@ -114,15 +174,25 @@ import {
|
||||||
IAddress,
|
IAddress,
|
||||||
addressFullName,
|
addressFullName,
|
||||||
addressToPoiInfos,
|
addressToPoiInfos,
|
||||||
|
resetAddress as resetAddressAction,
|
||||||
} from "../../types/address.model";
|
} from "../../types/address.model";
|
||||||
import AddressInfo from "../../components/Address/AddressInfo.vue";
|
import AddressInfo from "../../components/Address/AddressInfo.vue";
|
||||||
import { computed, ref, watch, defineAsyncComponent } from "vue";
|
import {
|
||||||
|
computed,
|
||||||
|
ref,
|
||||||
|
watch,
|
||||||
|
defineAsyncComponent,
|
||||||
|
onMounted,
|
||||||
|
reactive,
|
||||||
|
onBeforeMount,
|
||||||
|
} from "vue";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
import { useGeocodingAutocomplete } from "@/composition/apollo/config";
|
import { useGeocodingAutocomplete } from "@/composition/apollo/config";
|
||||||
import { ADDRESS } from "@/graphql/address";
|
import { ADDRESS } from "@/graphql/address";
|
||||||
import { useReverseGeocode } from "@/composition/apollo/address";
|
import { useReverseGeocode } from "@/composition/apollo/address";
|
||||||
import { useLazyQuery } from "@vue/apollo-composable";
|
import { useLazyQuery } from "@vue/apollo-composable";
|
||||||
import { AddressSearchType } from "@/types/enums";
|
import { AddressSearchType } from "@/types/enums";
|
||||||
|
import Loading from "vue-material-design-icons/Loading.vue";
|
||||||
const MapLeaflet = defineAsyncComponent(
|
const MapLeaflet = defineAsyncComponent(
|
||||||
() => import("@/components/LeafletMap.vue")
|
() => import("@/components/LeafletMap.vue")
|
||||||
);
|
);
|
||||||
|
@ -139,29 +209,38 @@ const props = withDefaults(
|
||||||
hideSelected?: boolean;
|
hideSelected?: boolean;
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
resultType?: AddressSearchType;
|
resultType?: AddressSearchType;
|
||||||
|
defaultCoords?: string;
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
|
defaultCoords: "0;0",
|
||||||
labelClass: "",
|
labelClass: "",
|
||||||
defaultText: "",
|
|
||||||
disabled: false,
|
disabled: false,
|
||||||
hideMap: false,
|
hideMap: false,
|
||||||
hideSelected: false,
|
hideSelected: false,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// const addressModalActive = ref(false);
|
const componentId = ref(0);
|
||||||
|
|
||||||
const componentId = 0;
|
|
||||||
|
|
||||||
const emit = defineEmits(["update:modelValue"]);
|
const emit = defineEmits(["update:modelValue"]);
|
||||||
|
|
||||||
const gettingLocationError = ref<string | null>(null);
|
const gettingLocationError = ref<string | null>(null);
|
||||||
const gettingLocation = ref(false);
|
const gettingLocation = ref(false);
|
||||||
const mapDefaultZoom = ref(15);
|
const mapDefaultZoom = computed(() => {
|
||||||
|
if (selected.description) {
|
||||||
|
return 15;
|
||||||
|
}
|
||||||
|
return 5;
|
||||||
|
});
|
||||||
|
|
||||||
const addressData = ref<IAddress[]>([]);
|
const addressData = ref<IAddress[]>([]);
|
||||||
|
|
||||||
const selected = ref<IAddress | null>(null);
|
const defaultAddress = new Address();
|
||||||
|
defaultAddress.geom = undefined;
|
||||||
|
defaultAddress.id = undefined;
|
||||||
|
const selected = reactive<IAddress>(defaultAddress);
|
||||||
|
|
||||||
|
const detailsAddress = ref(false);
|
||||||
|
|
||||||
const isFetching = ref(false);
|
const isFetching = ref(false);
|
||||||
|
|
||||||
|
@ -171,40 +250,45 @@ const placeholderWithDefault = computed(
|
||||||
() => props.placeholder ?? t("e.g. 10 Rue Jangot")
|
() => props.placeholder ?? t("e.g. 10 Rue Jangot")
|
||||||
);
|
);
|
||||||
|
|
||||||
// created(): void {
|
onBeforeMount(() => {
|
||||||
// componentId += 1;
|
componentId.value += 1;
|
||||||
// }
|
});
|
||||||
|
|
||||||
const id = computed((): string => {
|
const id = computed((): string => {
|
||||||
return `full-address-autocomplete-${componentId}`;
|
return `full-address-autocomplete-${componentId.value}`;
|
||||||
});
|
});
|
||||||
|
|
||||||
const modelValue = computed(() => props.modelValue);
|
const modelValue = computed(() => props.modelValue);
|
||||||
|
|
||||||
watch(modelValue, () => {
|
watch(modelValue, () => {
|
||||||
if (!modelValue.value) return;
|
console.debug("modelValue changed");
|
||||||
selected.value = modelValue.value;
|
setSelected(modelValue.value);
|
||||||
});
|
});
|
||||||
|
|
||||||
const updateSelected = (option: IAddress): void => {
|
onMounted(() => {
|
||||||
if (option == null) return;
|
setSelected(modelValue.value);
|
||||||
selected.value = option;
|
});
|
||||||
emit("update:modelValue", selected.value);
|
|
||||||
|
const setSelected = (newValue: IAddress | null) => {
|
||||||
|
if (!newValue) return;
|
||||||
|
console.debug("setting selected to model value");
|
||||||
|
Object.assign(selected, newValue);
|
||||||
};
|
};
|
||||||
|
|
||||||
// const resetPopup = (): void => {
|
const saveManualAddress = (): void => {
|
||||||
// selected.value = new Address();
|
console.debug("saving address");
|
||||||
// };
|
selected.id = undefined;
|
||||||
|
selected.originId = undefined;
|
||||||
// const openNewAddressModal = (): void => {
|
selected.url = undefined;
|
||||||
// resetPopup();
|
emit("update:modelValue", selected);
|
||||||
// addressModalActive.value = true;
|
detailsAddress.value = false;
|
||||||
// };
|
};
|
||||||
|
|
||||||
const checkCurrentPosition = (e: LatLng): boolean => {
|
const checkCurrentPosition = (e: LatLng): boolean => {
|
||||||
if (!selected.value?.geom) return false;
|
console.debug("checkCurrentPosition");
|
||||||
const lat = parseFloat(selected.value?.geom.split(";")[1]);
|
if (!selected?.geom || !e) return false;
|
||||||
const lon = parseFloat(selected.value?.geom.split(";")[0]);
|
const lat = parseFloat(selected?.geom.split(";")[1]);
|
||||||
|
const lon = parseFloat(selected?.geom.split(";")[0]);
|
||||||
|
|
||||||
return e.lat === lat && e.lng === lon;
|
return e.lat === lat && e.lng === lon;
|
||||||
};
|
};
|
||||||
|
@ -238,12 +322,11 @@ onAddressSearchResult((result) => {
|
||||||
isFetching.value = false;
|
isFetching.value = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
const searchQuery = ref("");
|
|
||||||
|
|
||||||
const asyncData = async (query: string): Promise<void> => {
|
const asyncData = async (query: string): Promise<void> => {
|
||||||
|
console.debug("Finding addresses");
|
||||||
if (!query.length) {
|
if (!query.length) {
|
||||||
addressData.value = [];
|
addressData.value = [];
|
||||||
selected.value = new Address();
|
Object.assign(selected, defaultAddress);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -254,33 +337,39 @@ const asyncData = async (query: string): Promise<void> => {
|
||||||
|
|
||||||
isFetching.value = true;
|
isFetching.value = true;
|
||||||
|
|
||||||
searchQuery.value = query;
|
|
||||||
|
|
||||||
searchAddress(undefined, {
|
searchAddress(undefined, {
|
||||||
query: searchQuery.value,
|
query,
|
||||||
locale: locale,
|
locale: locale,
|
||||||
type: props.resultType,
|
type: props.resultType,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const queryText = computed({
|
const selectedAddressText = computed(() => {
|
||||||
|
if (!selected) return undefined;
|
||||||
|
return addressFullName(selected);
|
||||||
|
});
|
||||||
|
|
||||||
|
const queryText = ref();
|
||||||
|
|
||||||
|
const queryTextWithDefault = computed({
|
||||||
get() {
|
get() {
|
||||||
|
console.log("queryTextWithDefault 1", queryText.value);
|
||||||
|
console.log("queryTextWithDefault 2", selectedAddressText.value);
|
||||||
|
console.log("queryTextWithDefault 3", props.defaultText);
|
||||||
return (
|
return (
|
||||||
(selected.value ? addressFullName(selected.value) : props.defaultText) ??
|
queryText.value ?? selectedAddressText.value ?? props.defaultText ?? ""
|
||||||
""
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
set(text) {
|
set(newValue: string) {
|
||||||
if (text === "" && selected.value?.id) {
|
queryText.value = newValue;
|
||||||
console.debug("doing reset");
|
|
||||||
resetAddress();
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const resetAddress = (): void => {
|
const resetAddress = (): void => {
|
||||||
|
console.debug("resetting address");
|
||||||
emit("update:modelValue", null);
|
emit("update:modelValue", null);
|
||||||
selected.value = new Address();
|
resetAddressAction(selected);
|
||||||
|
queryTextWithDefault.value = "";
|
||||||
};
|
};
|
||||||
|
|
||||||
const locateMe = async (): Promise<void> => {
|
const locateMe = async (): Promise<void> => {
|
||||||
|
@ -288,7 +377,7 @@ const locateMe = async (): Promise<void> => {
|
||||||
gettingLocationError.value = null;
|
gettingLocationError.value = null;
|
||||||
try {
|
try {
|
||||||
const location = await getLocation();
|
const location = await getLocation();
|
||||||
mapDefaultZoom.value = 12;
|
// mapDefaultZoom.value = 12;
|
||||||
reverseGeoCode(
|
reverseGeoCode(
|
||||||
new LatLng(location.coords.latitude, location.coords.longitude),
|
new LatLng(location.coords.latitude, location.coords.longitude),
|
||||||
12
|
12
|
||||||
|
@ -308,15 +397,26 @@ onReverseGeocodeResult((result) => {
|
||||||
addressData.value = data.reverseGeocode;
|
addressData.value = data.reverseGeocode;
|
||||||
|
|
||||||
if (addressData.value.length > 0) {
|
if (addressData.value.length > 0) {
|
||||||
const defaultAddress = addressData.value[0];
|
const foundAddress = addressData.value[0];
|
||||||
selected.value = defaultAddress;
|
Object.assign(selected, foundAddress);
|
||||||
emit("update:modelValue", selected.value);
|
console.debug("reverse geocode succeded, setting new address");
|
||||||
|
queryTextWithDefault.value = addressFullName(foundAddress);
|
||||||
|
emit("update:modelValue", selected);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const reverseGeoCode = (e: LatLng, zoom: number) => {
|
const reverseGeoCode = (e: LatLng, zoom: number) => {
|
||||||
|
console.debug("reverse geocode");
|
||||||
|
|
||||||
|
// If the details is opened, just update coords, don't reverse geocode
|
||||||
|
if (e && detailsAddress.value) {
|
||||||
|
selected.geom = `${e.lng};${e.lat}`;
|
||||||
|
console.debug("no reverse geocode, just setting new coords");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// If the position has been updated through autocomplete selection, no need to geocode it!
|
// If the position has been updated through autocomplete selection, no need to geocode it!
|
||||||
if (checkCurrentPosition(e)) return;
|
if (!e || checkCurrentPosition(e)) return;
|
||||||
|
|
||||||
loadReverseGeocode(undefined, {
|
loadReverseGeocode(undefined, {
|
||||||
latitude: e.lat,
|
latitude: e.lat,
|
||||||
|
@ -358,6 +458,17 @@ const getLocation = async (): Promise<GeolocationPosition> => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const mapMarkerValue = computed(() => {
|
||||||
|
if (!selected.description) return undefined;
|
||||||
|
return {
|
||||||
|
text: [
|
||||||
|
addressToPoiInfos(selected).name,
|
||||||
|
addressToPoiInfos(selected).alternativeName,
|
||||||
|
],
|
||||||
|
icon: addressToPoiInfos(selected).poiIcon.icon,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
const fieldErrors = computed(() => {
|
const fieldErrors = computed(() => {
|
||||||
return gettingLocationError.value;
|
return gettingLocationError.value;
|
||||||
});
|
});
|
||||||
|
|
|
@ -21,10 +21,15 @@
|
||||||
<l-marker
|
<l-marker
|
||||||
:lat-lng="[lat, lon]"
|
:lat-lng="[lat, lon]"
|
||||||
@add="openPopup"
|
@add="openPopup"
|
||||||
@update:latLng="updateDraggableMarkerPosition"
|
@update:latLng="updateDraggableMarkerPositionDebounced"
|
||||||
:draggable="!readOnly"
|
:draggable="!readOnly"
|
||||||
>
|
>
|
||||||
<l-icon>
|
<l-icon
|
||||||
|
:icon-size="[48, 48]"
|
||||||
|
:shadow-size="[30, 30]"
|
||||||
|
:icon-anchor="[24, 48]"
|
||||||
|
:popup-anchor="[-24, -40]"
|
||||||
|
>
|
||||||
<MapMarker :size="48" class="text-mbz-purple" />
|
<MapMarker :size="48" class="text-mbz-purple" />
|
||||||
</l-icon>
|
</l-icon>
|
||||||
<l-popup v-if="popupMultiLine" :options="{ offset: new Point(22, 8) }">
|
<l-popup v-if="popupMultiLine" :options="{ offset: new Point(22, 8) }">
|
||||||
|
@ -63,6 +68,7 @@ import { useI18n } from "vue-i18n";
|
||||||
import Locatecontrol from "leaflet.locatecontrol";
|
import Locatecontrol from "leaflet.locatecontrol";
|
||||||
import CrosshairsGps from "vue-material-design-icons/CrosshairsGps.vue";
|
import CrosshairsGps from "vue-material-design-icons/CrosshairsGps.vue";
|
||||||
import MapMarker from "vue-material-design-icons/MapMarker.vue";
|
import MapMarker from "vue-material-design-icons/MapMarker.vue";
|
||||||
|
import { useDebounceFn } from "@vueuse/core";
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
|
@ -159,22 +165,25 @@ const mergedOptions = computed((): Record<string, unknown> => {
|
||||||
});
|
});
|
||||||
|
|
||||||
const lat = computed((): number => {
|
const lat = computed((): number => {
|
||||||
return Number.parseFloat(props.coords?.split(";")[1]);
|
return Number.parseFloat(props.coords?.split(";")[1] || "0");
|
||||||
});
|
});
|
||||||
|
|
||||||
const lon = computed((): number => {
|
const lon = computed((): number => {
|
||||||
return Number.parseFloat(props.coords.split(";")[0]);
|
return Number.parseFloat(props.coords?.split(";")[0] || "0");
|
||||||
});
|
});
|
||||||
|
|
||||||
const popupMultiLine = computed((): Array<string | undefined> => {
|
const popupMultiLine = computed((): Array<string> | undefined => {
|
||||||
if (Array.isArray(props.marker?.text)) {
|
if (Array.isArray(props.marker?.text)) {
|
||||||
return props.marker?.text as string[];
|
return props.marker?.text as string[];
|
||||||
}
|
}
|
||||||
|
if (props.marker?.text) {
|
||||||
return [props.marker?.text];
|
return [props.marker?.text];
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
});
|
});
|
||||||
|
|
||||||
const clickMap = (event: LeafletMouseEvent): void => {
|
const clickMap = (event: LeafletMouseEvent): void => {
|
||||||
updateDraggableMarkerPosition(event.latlng);
|
updateDraggableMarkerPositionDebounced(event.latlng);
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateDraggableMarkerPosition = (e: LatLng): void => {
|
const updateDraggableMarkerPosition = (e: LatLng): void => {
|
||||||
|
@ -183,6 +192,10 @@ const updateDraggableMarkerPosition = (e: LatLng): void => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const updateDraggableMarkerPositionDebounced = useDebounceFn((e: LatLng) => {
|
||||||
|
updateDraggableMarkerPosition(e);
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
const updateZoom = (newZoom: number): void => {
|
const updateZoom = (newZoom: number): void => {
|
||||||
zoom.value = newZoom;
|
zoom.value = newZoom;
|
||||||
};
|
};
|
||||||
|
@ -211,4 +224,9 @@ div.map-container {
|
||||||
</style>
|
</style>
|
||||||
<style>
|
<style>
|
||||||
@import "leaflet.locatecontrol/dist/L.Control.Locate.css";
|
@import "leaflet.locatecontrol/dist/L.Control.Locate.css";
|
||||||
|
|
||||||
|
.leaflet-div-icon {
|
||||||
|
background: unset;
|
||||||
|
border: unset;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -54,6 +54,50 @@ export const LIST_GROUPS = gql`
|
||||||
${ACTOR_FRAGMENT}
|
${ACTOR_FRAGMENT}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const GROUP_VERY_BASIC_FIELDS_FRAGMENTS = gql`
|
||||||
|
fragment GroupVeryBasicFields on Group {
|
||||||
|
...ActorFragment
|
||||||
|
suspended
|
||||||
|
visibility
|
||||||
|
openness
|
||||||
|
manuallyApprovesFollowers
|
||||||
|
physicalAddress {
|
||||||
|
description
|
||||||
|
street
|
||||||
|
locality
|
||||||
|
postalCode
|
||||||
|
region
|
||||||
|
country
|
||||||
|
geom
|
||||||
|
type
|
||||||
|
id
|
||||||
|
originId
|
||||||
|
url
|
||||||
|
}
|
||||||
|
avatar {
|
||||||
|
id
|
||||||
|
url
|
||||||
|
name
|
||||||
|
metadata {
|
||||||
|
width
|
||||||
|
height
|
||||||
|
blurhash
|
||||||
|
}
|
||||||
|
}
|
||||||
|
banner {
|
||||||
|
id
|
||||||
|
url
|
||||||
|
name
|
||||||
|
metadata {
|
||||||
|
width
|
||||||
|
height
|
||||||
|
blurhash
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
${ACTOR_FRAGMENT}
|
||||||
|
`;
|
||||||
|
|
||||||
export const GROUP_BASIC_FIELDS_FRAGMENTS = gql`
|
export const GROUP_BASIC_FIELDS_FRAGMENTS = gql`
|
||||||
fragment GroupBasicFields on Group {
|
fragment GroupBasicFields on Group {
|
||||||
...ActorFragment
|
...ActorFragment
|
||||||
|
@ -296,17 +340,10 @@ export const UPDATE_GROUP = gql`
|
||||||
physicalAddress: $physicalAddress
|
physicalAddress: $physicalAddress
|
||||||
manuallyApprovesFollowers: $manuallyApprovesFollowers
|
manuallyApprovesFollowers: $manuallyApprovesFollowers
|
||||||
) {
|
) {
|
||||||
...ActorFragment
|
...GroupVeryBasicFields
|
||||||
visibility
|
|
||||||
openness
|
|
||||||
manuallyApprovesFollowers
|
|
||||||
banner {
|
|
||||||
id
|
|
||||||
url
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
${GROUP_VERY_BASIC_FIELDS_FRAGMENTS}
|
||||||
${ACTOR_FRAGMENT}
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const DELETE_GROUP = gql`
|
export const DELETE_GROUP = gql`
|
||||||
|
|
|
@ -93,7 +93,10 @@ export function addressToPoiInfos(address: IAddress): IPoiInfo {
|
||||||
switch (addressType) {
|
switch (addressType) {
|
||||||
case "house":
|
case "house":
|
||||||
name = address.description;
|
name = address.description;
|
||||||
alternativeName = [address.postalCode, address.locality, address.country]
|
alternativeName = (
|
||||||
|
address.description !== address.street ? [address.street] : []
|
||||||
|
)
|
||||||
|
.concat([address.postalCode, address.locality, address.country])
|
||||||
.filter((zone) => zone)
|
.filter((zone) => zone)
|
||||||
.join(", ");
|
.join(", ");
|
||||||
poiIcon = poiIcons.defaultAddress;
|
poiIcon = poiIcons.defaultAddress;
|
||||||
|
@ -123,8 +126,11 @@ export function addressToPoiInfos(address: IAddress): IPoiInfo {
|
||||||
alternativeName = "";
|
alternativeName = "";
|
||||||
if (address.street && address.street.trim()) {
|
if (address.street && address.street.trim()) {
|
||||||
alternativeName = `${address.street}`;
|
alternativeName = `${address.street}`;
|
||||||
|
if (address.postalCode) {
|
||||||
|
alternativeName += `, ${address.postalCode}`;
|
||||||
|
}
|
||||||
if (address.locality) {
|
if (address.locality) {
|
||||||
alternativeName += ` (${address.locality})`;
|
alternativeName += `, ${address.locality}`;
|
||||||
}
|
}
|
||||||
} else if (address.locality && address.locality.trim()) {
|
} else if (address.locality && address.locality.trim()) {
|
||||||
alternativeName = `${address.locality}, ${address.region}, ${address.country}`;
|
alternativeName = `${address.locality}, ${address.region}, ${address.country}`;
|
||||||
|
@ -158,3 +164,19 @@ export function addressFullName(address: IAddress): string {
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function resetAddress(address: IAddress): void {
|
||||||
|
address.id = undefined;
|
||||||
|
address.description = "";
|
||||||
|
address.street = "";
|
||||||
|
address.locality = "";
|
||||||
|
address.postalCode = "";
|
||||||
|
address.region = "";
|
||||||
|
address.country = "";
|
||||||
|
address.type = "";
|
||||||
|
address.geom = undefined;
|
||||||
|
address.url = undefined;
|
||||||
|
address.originId = undefined;
|
||||||
|
address.timezone = undefined;
|
||||||
|
address.pictureInfo = undefined;
|
||||||
|
}
|
||||||
|
|
|
@ -612,59 +612,6 @@ const FullAddressAutoComplete = defineAsyncComponent(
|
||||||
() => import("@/components/Event/FullAddressAutoComplete.vue")
|
() => import("@/components/Event/FullAddressAutoComplete.vue")
|
||||||
);
|
);
|
||||||
|
|
||||||
// apollo: {
|
|
||||||
// config: CONFIG_EDIT_EVENT,
|
|
||||||
// event: {
|
|
||||||
// query: FETCH_EVENT,
|
|
||||||
// variables() {
|
|
||||||
// return {
|
|
||||||
// uuid: this.eventId,
|
|
||||||
// };
|
|
||||||
// },
|
|
||||||
// update(data) {
|
|
||||||
// let event = data.event;
|
|
||||||
// if (this.isDuplicate) {
|
|
||||||
// event = { ...event, organizerActor: this.currentActor };
|
|
||||||
// }
|
|
||||||
// return new EventModel(event);
|
|
||||||
// },
|
|
||||||
// skip() {
|
|
||||||
// return !this.eventId;
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// person: {
|
|
||||||
// query: PERSON_STATUS_GROUP,
|
|
||||||
// fetchPolicy: "cache-and-network",
|
|
||||||
// variables() {
|
|
||||||
// return {
|
|
||||||
// id: this.currentActor.id,
|
|
||||||
// group: usernameWithDomain(this.event?.attributedTo),
|
|
||||||
// };
|
|
||||||
// },
|
|
||||||
// skip() {
|
|
||||||
// return (
|
|
||||||
// !this.event?.attributedTo ||
|
|
||||||
// !this.event?.attributedTo?.preferredUsername
|
|
||||||
// );
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// group: {
|
|
||||||
// query: FETCH_GROUP_PUBLIC,
|
|
||||||
// fetchPolicy: "cache-and-network",
|
|
||||||
// variables() {
|
|
||||||
// return {
|
|
||||||
// name: this.event?.attributedTo?.preferredUsername,
|
|
||||||
// };
|
|
||||||
// },
|
|
||||||
// skip() {
|
|
||||||
// return (
|
|
||||||
// !this.event?.attributedTo ||
|
|
||||||
// !this.event?.attributedTo?.preferredUsername
|
|
||||||
// );
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
|
|
||||||
const { t } = useI18n({ useScope: "global" });
|
const { t } = useI18n({ useScope: "global" });
|
||||||
|
|
||||||
useHead({
|
useHead({
|
||||||
|
@ -689,7 +636,6 @@ const unmodifiedEvent = ref<IEditableEvent>(new EventModel());
|
||||||
|
|
||||||
const pictureFile = ref<File | null>(null);
|
const pictureFile = ref<File | null>(null);
|
||||||
|
|
||||||
// const canPromote = ref(true);
|
|
||||||
const limitedPlaces = ref(false);
|
const limitedPlaces = ref(false);
|
||||||
const showFixedNavbar = ref(true);
|
const showFixedNavbar = ref(true);
|
||||||
|
|
||||||
|
@ -1051,6 +997,7 @@ const buildVariables = async () => {
|
||||||
res.picture = { mediaId: event.value?.picture.id };
|
res.picture = { mediaId: event.value?.picture.id };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
console.debug("builded variables", res);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -369,7 +369,10 @@ const currentAddress = computed({
|
||||||
},
|
},
|
||||||
set(address: IAddress) {
|
set(address: IAddress) {
|
||||||
if (editableGroup.value) {
|
if (editableGroup.value) {
|
||||||
editableGroup.value.physicalAddress = address;
|
editableGroup.value = {
|
||||||
|
...editableGroup.value,
|
||||||
|
physicalAddress: address,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue