166 lines
4.3 KiB
Vue
166 lines
4.3 KiB
Vue
<template>
|
||
<div v-if="coords" class="delivery">
|
||
<div class="delivery__sidebar">
|
||
<DeliveryInfo v-if="isPickupPointSelected" />
|
||
|
||
<div v-else class="delivery__search">
|
||
<UInput
|
||
v-model="searchTerm"
|
||
size="xl"
|
||
class="w-full"
|
||
placeholder="Выберите пункт выдачи"
|
||
:ui="{ trailing: 'pe-1' }"
|
||
>
|
||
<template v-if="searchTerm?.length" #trailing>
|
||
<UButton
|
||
color="neutral"
|
||
variant="link"
|
||
size="sm"
|
||
icon="i-lucide-circle-x"
|
||
aria-label="Clear input"
|
||
@click="searchTerm = ''"
|
||
/>
|
||
</template>
|
||
</UInput>
|
||
|
||
<div class="delivery__items">
|
||
<div
|
||
v-for="pickupPoint in filteredPoints"
|
||
:key="pickupPoint.id"
|
||
class="pickup-point-card"
|
||
@click="checkoutPickupPoint = pickupPoint"
|
||
>
|
||
<div class="pickup-point-card__content">
|
||
<h3>Yandex</h3>
|
||
<p>{{ `${pickupPoint?.address?.street} ${pickupPoint?.address?.house}` }}</p>
|
||
<h3>с day month бесплатно</h3>
|
||
</div>
|
||
<Icon class="pickup-point-card__action" name="lucide:chevron-right" />
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<PvzMap
|
||
v-model="checkoutPickupPoint"
|
||
:pickup-points="filteredPoints"
|
||
@update:model-value="setCheckoutPickupPoint(checkoutPickupPoint)"
|
||
/>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import type { PickupPoint, YandexPvzResponse } from '~/server/shared/types/yandex_pvz'
|
||
import { useGeolocation } from '@vueuse/core'
|
||
import { computed, onMounted, ref } from 'vue'
|
||
import DeliveryInfo from '~/components/DeliveryInfo.vue'
|
||
import PvzMap from '~/components/PvzMap.vue'
|
||
import { useCheckout } from '~/composables/useCheckout'
|
||
|
||
const { setCheckoutPickupPoint, isPickupPointSelected, checkoutPickupPoint } = useCheckout()
|
||
const { coords } = useGeolocation()
|
||
const yandexPvz = ref<YandexPvzResponse>()
|
||
const searchTerm = 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('/api/yandex_location', {
|
||
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
|
||
})
|
||
|
||
// TODO сделать отдельный компонент UISearch
|
||
const filteredPoints = computed<PickupPoint[]>(() => {
|
||
if (!searchTerm.value || searchTerm.value === '') {
|
||
return yandexPvz.value?.points || []
|
||
}
|
||
|
||
return yandexPvz.value?.points?.filter(point =>
|
||
point?.address?.full_address?.toLowerCase().includes(searchTerm.value.toLowerCase()),
|
||
) || []
|
||
})
|
||
|
||
definePageMeta({
|
||
layout: 'checkout',
|
||
})
|
||
</script>
|
||
|
||
<style lang="scss">
|
||
@use '~/assets/scss/utils' as *;
|
||
|
||
.delivery {
|
||
display: flex;
|
||
flex-direction: row;
|
||
|
||
&__sidebar {
|
||
flex-shrink: 0;
|
||
max-height: calc(100vh - 40px);
|
||
overflow-y: auto;
|
||
width: 410px;
|
||
padding-inline: 24px;
|
||
}
|
||
|
||
&__items {
|
||
height: calc(100dvh - 54px - 40px - 24px);
|
||
overflow-y: auto;
|
||
flex-shrink: 0;
|
||
}
|
||
}
|
||
|
||
.pickup-point-card {
|
||
position: relative;
|
||
width: 100%;
|
||
padding: 20px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
gap: 16px;
|
||
cursor: pointer;
|
||
|
||
&__content {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 4px;
|
||
}
|
||
|
||
&__action {
|
||
flex-shrink: 0;
|
||
width: 16px;
|
||
height: 16px;
|
||
color: #999;
|
||
}
|
||
}
|
||
</style>
|