import { ArchiveBox, CurrencyDollar, Map, PencilSquare, Plus, Trash, TriangleDownMini, } from "@medusajs/icons" import { HttpTypes } from "@medusajs/types" import { Badge, Container, Divider, Heading, IconButton, StatusBadge, Text, toast, usePrompt, } from "@medusajs/ui" import { useMemo, useState } from "react" import { useTranslation } from "react-i18next" import { useNavigate } from "react-router-dom" import { ActionMenu } from "../../../../../components/common/action-menu" import { NoRecords } from "../../../../../components/common/empty-table-content" import { IconAvatar } from "../../../../../components/common/icon-avatar" import { LinkButton } from "../../../../../components/common/link-button" import { ListSummary } from "../../../../../components/common/list-summary" import { useDeleteFulfillmentServiceZone, useDeleteFulfillmentSet, } from "../../../../../hooks/api/fulfillment-sets" import { useDeleteShippingOption } from "../../../../../hooks/api/shipping-options" import { useCreateStockLocationFulfillmentSet, useDeleteStockLocation, } from "../../../../../hooks/api/stock-locations" import { getFormattedAddress } from "../../../../../lib/addresses" import { StaticCountry, countries as staticCountries, } from "../../../../../lib/data/countries" import { formatProvider } from "../../../../../lib/format-provider" import { isOptionEnabledInStore, isReturnOption, } from "../../../../../lib/shipping-options" import { FulfillmentSetType, ShippingOptionPriceType, } from "../../../common/constants" type LocationGeneralSectionProps = { location: HttpTypes.AdminStockLocation } export const LocationGeneralSection = ({ location, }: LocationGeneralSectionProps) => { return ( <>
{location.name} {getFormattedAddress({ address: location.address as HttpTypes.AdminOrderAddress | undefined, }).join(", ")}
f.type === FulfillmentSetType.Pickup )} /> f.type === FulfillmentSetType.Shipping )} /> ) } type ShippingOptionProps = { option: HttpTypes.AdminShippingOption fulfillmentSetId: string locationId: string } function ShippingOption({ option, fulfillmentSetId, locationId, }: ShippingOptionProps) { const prompt = usePrompt() const { t } = useTranslation() const isStoreOption = isOptionEnabledInStore(option) const { mutateAsync } = useDeleteShippingOption(option.id) const handleDelete = async () => { const res = await prompt({ title: t("general.areYouSure"), description: t("stockLocations.shippingOptions.delete.confirmation", { name: option.name, }), verificationInstruction: t("general.typeToConfirm"), verificationText: option.name, confirmText: t("actions.delete"), cancelText: t("actions.cancel"), }) if (!res) { return } await mutateAsync(undefined, { onSuccess: () => { toast.success( t("stockLocations.shippingOptions.delete.successToast", { name: option.name, }) ) }, onError: (e) => { toast.error(e.message) }, }) } return (
{option.name} - {option.shipping_profile.name} ( {formatProvider(option.provider_id)})
{isStoreOption ? t("general.store") : t("general.admin")} , label: t("stockLocations.shippingOptions.edit.action"), to: `/settings/locations/${locationId}/fulfillment-set/${fulfillmentSetId}/service-zone/${option.service_zone_id}/shipping-option/${option.id}/edit`, }, { label: t("stockLocations.shippingOptions.pricing.action"), icon: , disabled: option.price_type === ShippingOptionPriceType.Calculated, to: `/settings/locations/${locationId}/fulfillment-set/${fulfillmentSetId}/service-zone/${option.service_zone_id}/shipping-option/${option.id}/pricing`, }, ], }, { actions: [ { label: t("actions.delete"), icon: , onClick: handleDelete, }, ], }, ]} />
) } type ServiceZoneOptionsProps = { zone: HttpTypes.AdminServiceZone locationId: string fulfillmentSetId: string type: FulfillmentSetType } function ServiceZoneOptions({ zone, locationId, fulfillmentSetId, type, }: ServiceZoneOptionsProps) { const { t } = useTranslation() const shippingOptions = zone.shipping_options.filter( (o) => !isReturnOption(o) ) const returnOptions = zone.shipping_options.filter((o) => isReturnOption(o)) return (
{t(`stockLocations.shippingOptions.create.${type}.label`)} {t("stockLocations.shippingOptions.create.action")}
{!!shippingOptions.length && (
{shippingOptions.map((o) => ( ))}
)}
{t("stockLocations.shippingOptions.create.returns.label")} {t("stockLocations.shippingOptions.create.action")}
{!!returnOptions.length && (
{returnOptions.map((o) => ( ))}
)}
) } type ServiceZoneProps = { zone: HttpTypes.AdminServiceZone locationId: string fulfillmentSetId: string type: FulfillmentSetType } function ServiceZone({ zone, locationId, fulfillmentSetId, type, }: ServiceZoneProps) { const { t } = useTranslation() const prompt = usePrompt() const [open, setOpen] = useState(true) const { mutateAsync: deleteZone } = useDeleteFulfillmentServiceZone( fulfillmentSetId, zone.id ) const handleDelete = async () => { const res = await prompt({ title: t("general.areYouSure"), description: t("stockLocations.serviceZones.delete.confirmation", { name: zone.name, }), confirmText: t("actions.delete"), cancelText: t("actions.cancel"), }) if (!res) { return } await deleteZone(undefined, { onError: (e) => { toast.error(e.message) }, onSuccess: () => { toast.success( t("stockLocations.serviceZones.delete.successToast", { name: zone.name, }) ) }, }) } const countries = useMemo(() => { const countryGeoZones = zone.geo_zones.filter((g) => g.type === "country") const countries = countryGeoZones .map(({ country_code }) => staticCountries.find((c) => c.iso_2 === country_code) ) .filter((c) => !!c) as StaticCountry[] if ( process.env.NODE_ENV === "development" && countryGeoZones.length !== countries.length ) { console.warn( "Some countries are missing in the static countries list", countryGeoZones .filter((g) => !countries.find((c) => c.iso_2 === g.country_code)) .map((g) => g.country_code) ) } return countries.sort((c1, c2) => c1.name.localeCompare(c2.name)) }, [zone.geo_zones]) const [shippingOptionsCount, returnOptionsCount] = useMemo(() => { const options = zone.shipping_options const optionsCount = options.filter((o) => !isReturnOption(o)).length const returnOptionsCount = options.filter(isReturnOption).length return [optionsCount, returnOptionsCount] }, [zone.shipping_options]) return (
{zone.name}
c.display_name)} inline n={1} /> · {t(`stockLocations.shippingOptions.fields.count.${type}`, { count: shippingOptionsCount, })} · {t("stockLocations.shippingOptions.fields.count.returns", { count: returnOptionsCount, })}
setOpen((s) => !s)} variant="transparent" > , to: `/settings/locations/${locationId}/fulfillment-set/${fulfillmentSetId}/service-zone/${zone.id}/edit`, }, { label: t("stockLocations.serviceZones.manageAreas.action"), icon: , to: `/settings/locations/${locationId}/fulfillment-set/${fulfillmentSetId}/service-zone/${zone.id}/areas`, }, ], }, { actions: [ { label: t("actions.delete"), icon: , onClick: handleDelete, }, ], }, ]} />
{open && ( )}
) } type FulfillmentSetProps = { fulfillmentSet?: HttpTypes.AdminFulfillmentSet locationName: string locationId: string type: FulfillmentSetType } function FulfillmentSet(props: FulfillmentSetProps) { const { t } = useTranslation() const prompt = usePrompt() const { fulfillmentSet, locationName, locationId, type } = props const fulfillmentSetExists = !!fulfillmentSet const hasServiceZones = !!fulfillmentSet?.service_zones.length const { mutateAsync: createFulfillmentSet } = useCreateStockLocationFulfillmentSet(locationId) const { mutateAsync: deleteFulfillmentSet } = useDeleteFulfillmentSet( fulfillmentSet?.id! ) const handleCreate = async () => { await createFulfillmentSet( { name: `${locationName} ${ type === FulfillmentSetType.Pickup ? "pick up" : type }`, type, }, { onSuccess: () => { toast.success(t(`stockLocations.fulfillmentSets.enable.${type}`)) }, onError: (e) => { toast.error(e.message) }, } ) } const handleDelete = async () => { const res = await prompt({ title: t("general.areYouSure"), description: t(`stockLocations.fulfillmentSets.disable.confirmation`, { name: fulfillmentSet?.name, }), confirmText: t("actions.disable"), cancelText: t("actions.cancel"), }) if (!res) { return } await deleteFulfillmentSet(undefined, { onSuccess: () => { toast.success(t(`stockLocations.fulfillmentSets.disable.${type}`)) }, onError: (e) => { toast.error(e.message) }, }) } const groups = fulfillmentSet ? [ { actions: [ { icon: , label: t("stockLocations.serviceZones.create.action"), to: `/settings/locations/${locationId}/fulfillment-set/${fulfillmentSet.id}/service-zones/create`, }, ], }, { actions: [ { icon: , label: t("actions.disable"), onClick: handleDelete, }, ], }, ] : [ { actions: [ { icon: , label: t("actions.enable"), onClick: handleCreate, }, ], }, ] return (
{t(`stockLocations.fulfillmentSets.${type}.header`)}
{t( fulfillmentSetExists ? "statuses.enabled" : "statuses.disabled" )}
{fulfillmentSetExists && !hasServiceZones && (
)} {hasServiceZones && (
{fulfillmentSet?.service_zones.map((zone) => ( ))}
)}
) } const Actions = ({ location }: { location: HttpTypes.AdminStockLocation }) => { const navigate = useNavigate() const { t } = useTranslation() const { mutateAsync } = useDeleteStockLocation(location.id) const prompt = usePrompt() const handleDelete = async () => { const res = await prompt({ title: t("general.areYouSure"), description: t("stockLocations.delete.confirmation", { name: location.name, }), verificationText: location.name, verificationInstruction: t("general.typeToConfirm"), confirmText: t("actions.delete"), cancelText: t("actions.cancel"), }) if (!res) { return } await mutateAsync(undefined, { onSuccess: () => { toast.success( t("stockLocations.create.successToast", { name: location.name, }) ) navigate("/settings/locations", { replace: true }) }, onError: (e) => { toast.error(e.message) }, }) } return ( , label: t("actions.edit"), to: `edit`, }, { icon: , label: t("stockLocations.edit.viewInventory"), to: `/inventory?location_id=${location.id}`, }, ], }, { actions: [ { icon: , label: t("actions.delete"), onClick: handleDelete, }, ], }, ]} /> ) }