paxton-front/pages/checkout/delivery.vue
alsaze 2e01f58e67
Some checks failed
Deploy / build (push) Has been cancelled
refactoring
2025-11-21 12:48:00 +03:00

195 lines
5.7 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div v-if="coords" class="delivery">
<!-- Desktop -->
<div v-if="!isMobile" class="delivery__sidebar">
<DeliveryInfo v-if="isPickupPointSelected" />
<DeliverySearch
v-else
v-model:checkout-pickup-point="checkoutPickupPoint"
v-model:search-term="searchTerm"
:filtered-points="filteredPoints"
@update:checkout-pickup-point="updatePoint()"
/>
</div>
<!-- Mobile -->
<UDrawer
v-if="isMobile"
v-model:open="open"
fixed
:ui="{
content: 'fixed bg-default ring ring-default flex focus:outline-none overflow-hidden',
container: 'w-full flex flex-col gap-4 p-4 overflow-hidden',
body: 'flex-1 overflow-y-auto',
}"
>
<template #content>
<div class="px-4 pb-4">
<DeliveryInfo v-if="isPickupPointSelected" />
</div>
</template>
</UDrawer>
<UModal
v-model:open="openDeliverySearch"
fullscreen
:dismissible="false"
:ui="{
body: 'overflow-hidden',
overlay: 'bg-black/50',
header: 'hidden',
}"
>
<template #body>
<DeliverySearch
v-if="activeTab === IPvzMapTabs.LIST"
v-model:checkout-pickup-point="checkoutPickupPoint"
v-model:search-term="searchTerm"
:filtered-points="filteredPoints"
@update:checkout-pickup-point="updatePoint()"
/>
<UDrawer
v-if="isMobile"
v-model:open="open"
fixed
:ui="{
content: 'fixed bg-default ring ring-default flex focus:outline-none overflow-hidden',
container: 'w-full flex flex-col gap-4 p-4 overflow-hidden',
body: 'flex-1 overflow-y-auto',
}"
>
<template #content>
<div class="px-4 pb-4">
<DeliveryInfo v-if="isPickupPointSelected" />
</div>
</template>
</UDrawer>
</template>
<template #footer>
<div class="d-flex flex-row w-full justify-between">
<MapControlFitting v-model:fitting="fitting" />
<MapControlTabs v-model:active-tab="activeTab" />
</div>
</template>
</UModal>
<!-- Desktop Mobile -->
<PvzMap
v-model:checkout-pickup-point="checkoutPickupPoint"
v-model:active-tab="activeTab"
v-model:fitting="fitting"
:pickup-points="filteredPoints"
@update:checkout-pickup-point="updatePoint()"
/>
</div>
</template>
<script setup lang="ts">
import type { YandexLocationDetectResponse } from '#shared/yandex_location_detect'
import type { YandexPvzPoint, YandexPvzResponse } from '#shared/yandex_pvz'
import { IPvzMapFittingTabs, IPvzMapTabs } from '#shared/types'
import { useGeolocation, useMediaQuery } from '@vueuse/core'
import { computed, onMounted, ref } from 'vue'
import DeliveryInfo from '~/components/DeliveryInfo.vue'
import DeliverySearch from '~/components/DeliverySearch.vue'
import PvzMap from '~/components/PvzMap.vue'
import { useCheckout } from '~/composables/useCheckout'
const { setCheckoutPickupPoint, isPickupPointSelected, checkoutPickupPoint } = useCheckout()
const { coords } = useGeolocation()
const open = ref(false)
const isMobile = useMediaQuery('(max-width: 1280px)')
const yandexPvz = ref<YandexPvzResponse>()
const searchTerm = ref('')
const activeTab = ref()
const fitting = ref(IPvzMapFittingTabs.ALL)
const openDeliverySearch = ref()
const waitForCoords = () =>
new Promise<void>((resolve) => {
const stop = watch(coords, (newCoords) => {
if (newCoords.latitude && newCoords.longitude) {
stop()
resolve()
}
})
})
onMounted(async () => {
await waitForCoords()
// обратное геокодирование (т.е. получаение города из координат)
const response = await fetch(
`https://nominatim.openstreetmap.org/reverse?lat=${coords.value.latitude}&lon=${coords.value.longitude}&format=json&accept-language=ru`,
)
const openstreetmap = await response.json()
// получение geo_id из названию города
const { data: yandexLocation } = await useFetch<YandexLocationDetectResponse>('/api/yandex_location_detect', {
method: 'POST',
body: {
location: openstreetmap?.address?.city,
},
})
// получения пунктов выдачи города из geo_id
const { data: yandexPvzApi } = await useFetch<YandexPvzResponse>('/api/yandex_pvz', {
method: 'POST',
body: {
geo_id: yandexLocation?.value?.variants[0]?.geo_id,
},
})
yandexPvz.value = yandexPvzApi.value
})
const filteredPoints = computed<YandexPvzPoint[]>(() => {
const points = yandexPvz.value?.points || []
const term = searchTerm.value?.toLowerCase() || ''
return points.filter((point) => {
const matchesSearch = !term || point.address.full_address.toLowerCase().includes(term)
const isAllowed = point.pickup_services.is_fitting_allowed
const matchesFitting
= fitting.value === IPvzMapFittingTabs.ALL
|| (fitting.value === IPvzMapFittingTabs.ALLOW && isAllowed)
|| (fitting.value === IPvzMapFittingTabs.FORBID && !isAllowed)
return matchesSearch && matchesFitting
})
})
function updatePoint() {
setCheckoutPickupPoint(checkoutPickupPoint.value)
open.value = true
}
watch(() => activeTab.value, () => {
openDeliverySearch.value = activeTab.value === IPvzMapTabs.LIST
})
definePageMeta({
layout: 'checkout',
})
</script>
<style lang="scss">
.delivery {
display: flex;
flex-direction: row;
&__sidebar {
flex-shrink: 0;
max-height: calc(100vh - 40px);
overflow-y: auto;
width: 410px;
padding-inline: 24px;
}
}
</style>