Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,25 @@ query GetProductPickupLocations(
items {
id
name
description
contactEmail
contactPhone
workingHours
geoLocation
availabilityType
availableQuantity
availabilityNote
address {
id
line1
line2
city
countryName
countryCode
regionId
postalCode
phone
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GraphQL query missing regionName field for addresses

Low Severity

The new address sub-selection in the product pickup locations query fetches regionId but not regionName. The getAddressName utility destructures regionName to build the display string, so region names will be silently omitted from addresses shown in the product pickup locations modal list.

Fix in Cursor Fix in Web

}
}
}
2 changes: 1 addition & 1 deletion client-app/pages/product.vue
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@
<ProductPickupLocations
v-if="xPickupEnabled && pickupLocations?.length > 0"
:loading="pickupLocationsLoading"
:pickup-locations="pickupLocations"
:product-id="productId"
class="mt-5"
/>
</template>
Expand Down
152 changes: 106 additions & 46 deletions client-app/shared/catalog/components/product-pickup-locations.vue
Original file line number Diff line number Diff line change
@@ -1,74 +1,134 @@
<template>
<VcWidget :title="$t('shared.catalog.shipment_options.title')" class="product-pickup-locations">
<VcLoaderOverlay v-if="loading" />

<div class="product-pickup-locations__group">
<VcImage
src="in-store-pickup.svg"
:alt="$t('shared.catalog.shipment_options.in_store')"
class="product-pickup-locations__img"
/>

<div class="product-pickup-locations__content">
<div class="product-pickup-locations__header">
{{ $t("shared.catalog.shipment_options.in_store") }}
</div>
<VcWidget
:title="$t('shared.catalog.shipment_options.title')"
class="product-pickup-locations"
data-test-id="shipment-options-widget"
>
<template #default-container>
<div class="product-pickup-locations__container">
<VcLoaderOverlay v-if="loading || modalOpening" />

<div
v-for="pickupLocation in pickupLocations"
:key="pickupLocation.id"
class="product-pickup-locations__option"
>
<div class="product-pickup-locations__name">
{{ pickupLocation.name }}
</div>

<PickupAvailabilityInfo
:availability-type="pickupLocation.availabilityType"
:availability-note="pickupLocation.availabilityNote"
<div class="product-pickup-locations__group">
<VcImage
src="in-store-pickup.svg"
:alt="$t('shared.catalog.shipment_options.check_pickup_locations')"
class="product-pickup-locations__img"
/>

<button
type="button"
class="product-pickup-locations__link"
data-test-id="check-pickup-locations-button"
@click="openMapModal"
>
<span>{{ $t("shared.catalog.shipment_options.check_pickup_locations") }} </span>

<VcIcon class="product-pickup-locations__icon" name="arrow-right" color="primary" size="xs" />
</button>
</div>
</div>
</div>
</template>
</VcWidget>
</template>

<script setup lang="ts">
import type { ProductPickupLocation } from "@/core/api/graphql/types";
import PickupAvailabilityInfo from "@/shared/common/components/pickup-availability-info.vue";
import { computed, ref } from "vue";
import { useModuleSettings } from "@/core/composables/useModuleSettings";
import { BOPIS_MAP_API_KEY, MODULE_ID_SHIPPING } from "@/core/constants/modules";
import { useProductPickupLocations } from "@/shared/catalog/composables/useProductPickupLocations";
import { createProductFilterContext } from "@/shared/checkout/composables/usePickupFilterContext";
import { useModal } from "@/shared/modal";
import SelectAddressMapModal from "@/shared/checkout/components/select-address-map-modal.vue";

interface IProps {
pickupLocations?: ProductPickupLocation[];
loading: boolean;
productId: string;
}

const props = defineProps<IProps>();

const MODAL_FETCH_LIMIT = 50;

const { getSettingValue } = useModuleSettings(MODULE_ID_SHIPPING);
const apiKey = computed(() => getSettingValue(BOPIS_MAP_API_KEY));

const { openModal } = useModal();

const {
pickupLocations: modalPickupLocations,
fetchPickupLocations: fetchModalPickupLocations,
pickupLocationsLoading: modalLoading,
} = useProductPickupLocations();

const modalAddresses = computed(() =>
modalPickupLocations.value.map((location) => ({
...location,
...location.address,
id: location.id,
description: location.description,
})),
);

function fetchLocations(keyword?: string) {
return fetchModalPickupLocations({
productId: props.productId,
first: MODAL_FETCH_LIMIT,
keyword: keyword || undefined,
});
}

defineProps<IProps>();
const filterContext = createProductFilterContext({
loading: modalLoading,
});

const modalOpening = ref(false);

async function openMapModal() {
modalOpening.value = true;
filterContext.clearFilter();

try {
await fetchLocations();
} finally {
modalOpening.value = false;
}

openModal({
component: SelectAddressMapModal,
props: {
addresses: modalAddresses,
apiKey: apiKey.value,
selectable: false,
filterContext,

onFilterChange: () => {
void fetchLocations(filterContext.filterKeyword.value);
},
},
});
}
</script>

<style lang="scss">
.product-pickup-locations {
&__group {
@apply flex flex-row gap-x-3 items-start border rounded p-3;
}

&__content {
@apply min-w-0;
&__container {
@apply relative py-4 px-5;
}

&__header {
@apply font-bold text-lg;

word-break: break-word;
&__group {
@apply flex flex-row gap-x-3 items-center border border-neutral-400 rounded p-2.5 min-h-[74px];
}

&__option {
@apply my-3;
&__img {
@apply size-12 shrink-0 rounded;
}

&__name {
@apply font-bold text-sm;
&__link {
@apply inline-flex items-center gap-1 text-sm text-accent cursor-pointer whitespace-nowrap;

word-break: break-word;
&:hover {
@apply text-accent-700;
}
}
}
</style>
24 changes: 17 additions & 7 deletions client-app/shared/checkout/components/pickup-location-card.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<VcDialog v-if="location" dividers class="pickup-location-card" size="xs">
<VcDialog v-if="location" dividers class="pickup-location-card" size="xs" data-test-id="pickup-location-card-dialog">
<VcDialogHeader @close="$emit('close')">
<template #main>
<div class="pickup-location-card__header">
Expand All @@ -11,7 +11,7 @@
<VcDialogContent>
<template #container>
<div class="pickup-location-card__content">
<div class="pickup-location-card__name">
<div class="pickup-location-card__name" data-test-id="pickup-location-card-name">
{{ location.name }}
</div>

Expand All @@ -21,7 +21,7 @@
:availability-note="location.availabilityNote"
/>

<dl class="pickup-location-card__info">
<dl class="pickup-location-card__info" data-test-id="pickup-location-card-info">
<dt>{{ $t("shared.checkout.select_bopis_modal.location_label") }}</dt>

<dd>{{ getAddressName(location) }}</dd>
Expand Down Expand Up @@ -60,14 +60,21 @@
</template>
</VcDialogContent>

<VcDialogFooter>
<VcDialogFooter v-if="selectable">
<template #container>
<div class="pickup-location-card__actions">
<VcButton variant="outline" color="secondary" truncate size="sm" @click="$emit('close')">
<VcButton
variant="outline"
color="secondary"
truncate
size="sm"
data-test-id="pickup-location-card-cancel"
@click="$emit('close')"
>
{{ $t("common.buttons.cancel") }}
</VcButton>

<VcButton truncate size="sm" @click="onSelect">
<VcButton truncate size="sm" data-test-id="pickup-location-card-select" @click="onSelect">
{{ $t("shared.checkout.select_bopis_modal.pick_up_here") }}
</VcButton>
</div>
Expand All @@ -83,6 +90,7 @@ import PickupAvailabilityInfo from "@/shared/common/components/pickup-availabili

interface IProps {
location?: PickupLocationType;
selectable?: boolean;
}

interface IEmits {
Expand All @@ -91,7 +99,9 @@ interface IEmits {
}

const emit = defineEmits<IEmits>();
const props = defineProps<IProps>();
const props = withDefaults(defineProps<IProps>(), {
selectable: true,
});

function onSelect() {
if (props.location?.id) {
Expand Down
Loading