@open-tender/utils
Version:
A library of utils for use with Open Tender applications that utilize our cloud-based Order API.
335 lines (334 loc) • 13.3 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.makeMapStyles = exports.renameLocation = exports.LOCATIONS_MESSAGES = exports.makeDisplayedRevenueCenters = exports.makeDeliveryMesssaging = exports.makeWalkinMessaging = exports.makePickupMesssaging = exports.makeDeliveryRevenueCenters = exports.makeWalkinRevenueCenters = exports.makePickupRevenueCenters = exports.checkServiceType = exports.calcMinDistance = exports.sortRevenueCenters = exports.addDistance = exports.inZone = exports.getDistance = void 0;
const constants_1 = require("./constants");
const RADIUS_MILES = 3959; // radius of the earth in miles
const RADIUS_KM = 6371; // radius of the earth in kilometers
// https://stackoverflow.com/questions/18883601/function-to-calculate-distance-between-two-coordinates
const getDistance = (pointA, pointB, inMiles = true) => {
const { lat: lat1, lng: lng1 } = pointA;
const { lat: lat2, lng: lng2 } = pointB;
const R = inMiles ? RADIUS_MILES : RADIUS_KM;
const dLat = deg2rad(lat2 - lat1); // see deg2rad below
const dLng = deg2rad(lng2 - lng1);
const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(deg2rad(lat1)) *
Math.cos(deg2rad(lat2)) *
Math.sin(dLng / 2) *
Math.sin(dLng / 2);
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
const d = R * c;
return d;
};
exports.getDistance = getDistance;
const deg2rad = (deg) => {
return deg * (Math.PI / 180);
};
// ray-casting algorithm based on
// http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
const pointInPolygon = (point, vs) => {
const x = point[0], y = point[1];
let inside = false;
for (let i = 0, j = vs.length - 1; i < vs.length; j = i++) {
const xi = vs[i][0], yi = vs[i][1];
const xj = vs[j][0], yj = vs[j][1];
const intersect = yi > y != yj > y && x < ((xj - xi) * (y - yi)) / (yj - yi) + xi;
if (intersect)
inside = !inside;
}
return inside;
};
const inZone = (latLng, polygon) => {
const point = [latLng.lat, latLng.lng];
return pointInPolygon(point, polygon);
};
exports.inZone = inZone;
const addDistance = (revenueCenters, latLng) => {
if (!latLng)
return revenueCenters;
const withDistance = revenueCenters.map(i => {
const iLatLng = i.address.lat
? { lat: i.address.lat, lng: i.address.lng }
: null;
i.distance = iLatLng ? (0, exports.getDistance)(latLng, iLatLng) : 1000;
const { coordinates, priority } = i.delivery_zone;
i.inZone = coordinates ? (0, exports.inZone)(latLng, coordinates) : false;
i.priority = priority !== null && priority !== void 0 ? priority : 0;
return i;
});
return withDistance;
};
exports.addDistance = addDistance;
const sortRevenueCenters = (revenueCenters, isDelivery = false) => {
if (!isDelivery) {
return [...revenueCenters].sort((a, b) => { var _a, _b; return ((_a = a.distance) !== null && _a !== void 0 ? _a : 0) - ((_b = b.distance) !== null && _b !== void 0 ? _b : 0); });
}
const inZoneWithPriority = revenueCenters
.filter(i => i.inZone && i.priority)
.sort((a, b) => { var _a, _b; return ((_a = a.priority) !== null && _a !== void 0 ? _a : 0) - ((_b = b.priority) !== null && _b !== void 0 ? _b : 0); });
const inZoneWithoutPriority = revenueCenters
.filter(i => i.inZone && !i.priority)
.sort((a, b) => { var _a, _b; return ((_a = a.distance) !== null && _a !== void 0 ? _a : 0) - ((_b = b.distance) !== null && _b !== void 0 ? _b : 0); });
const outOfZone = revenueCenters
.filter(i => !i.inZone)
.sort((a, b) => { var _a, _b; return ((_a = a.distance) !== null && _a !== void 0 ? _a : 0) - ((_b = b.distance) !== null && _b !== void 0 ? _b : 0); });
return [...inZoneWithPriority, ...inZoneWithoutPriority, ...outOfZone];
};
exports.sortRevenueCenters = sortRevenueCenters;
const calcMinDistance = (revenueCenters, maxDistance = constants_1.MAX_DISTANCE) => {
const withDistance = revenueCenters
.filter(i => i.distance !== null && i.distance !== undefined)
.map(i => { var _a; return (_a = i.distance) !== null && _a !== void 0 ? _a : 0; });
return withDistance ? Math.min(...withDistance) : maxDistance;
};
exports.calcMinDistance = calcMinDistance;
const checkServiceType = (revenueCenter, serviceType) => {
const { service_types } = revenueCenter;
if (!service_types)
return false;
return service_types.includes(serviceType);
};
exports.checkServiceType = checkServiceType;
const makePickupRevenueCenters = (revenueCenters, maxDistance = constants_1.MAX_DISTANCE) => {
const hasPickup = revenueCenters
.filter(i => (0, exports.checkServiceType)(i, 'PICKUP'))
.filter(i => !i.distance || i.distance < maxDistance);
return (0, exports.sortRevenueCenters)(hasPickup);
};
exports.makePickupRevenueCenters = makePickupRevenueCenters;
const makeWalkinRevenueCenters = (revenueCenters, maxDistance = constants_1.MAX_DISTANCE) => {
const hasWalkin = revenueCenters
.filter(i => (0, exports.checkServiceType)(i, 'WALKIN'))
.filter(i => !i.distance || i.distance < maxDistance);
return (0, exports.sortRevenueCenters)(hasWalkin);
};
exports.makeWalkinRevenueCenters = makeWalkinRevenueCenters;
const makeDeliveryRevenueCenters = (revenueCenters) => {
const hasDelivery = revenueCenters.filter(i => (0, exports.checkServiceType)(i, 'DELIVERY'));
const sorted = (0, exports.sortRevenueCenters)(hasDelivery, true);
return sorted.filter(i => i.inZone);
};
exports.makeDeliveryRevenueCenters = makeDeliveryRevenueCenters;
const makePickupMesssaging = (address, latLng, count, minDistance, maxDistance = constants_1.MAX_DISTANCE, messages = exports.LOCATIONS_MESSAGES) => {
if (address) {
if (minDistance >= maxDistance) {
return messages.PICKUP.addressFar;
}
else {
return {
title: `${count} ${messages.PICKUP.address.title}`,
msg: messages.PICKUP.address.msg
};
}
}
else if (latLng) {
if (minDistance >= maxDistance) {
return messages.PICKUP.geoFar;
}
else {
return {
title: `${count} ${messages.PICKUP.geo.title}`,
msg: messages.PICKUP.geo.msg
};
}
}
else {
return messages.PICKUP.default;
}
};
exports.makePickupMesssaging = makePickupMesssaging;
const makeWalkinMessaging = (address, latLng, count, minDistance, maxDistance = constants_1.MAX_DISTANCE, messages = exports.LOCATIONS_MESSAGES) => {
const { title, msg } = (0, exports.makePickupMesssaging)(address, latLng, count, minDistance, maxDistance, messages);
return {
title: title.replace('pickup', 'dine-in'),
msg: msg.replace('pickup', 'dine-in')
};
};
exports.makeWalkinMessaging = makeWalkinMessaging;
const makeDeliveryMesssaging = (address, count, messages = exports.LOCATIONS_MESSAGES) => {
if (!address) {
return messages.DELIVERY.default;
}
else if (!address.street) {
return messages.DELIVERY.noStreet;
}
else {
if (count) {
const locationMsg = count > 1 ? 'locations deliver' : 'location delivers';
return {
title: messages.DELIVERY.hasDelivery.title,
msg: `${count} ${locationMsg} to your address.`,
error: null
};
}
else {
return messages.DELIVERY.noDelivery;
}
}
};
exports.makeDeliveryMesssaging = makeDeliveryMesssaging;
const makeDisplayedRevenueCenters = (revenueCenters, serviceType, address, latLng, maxDistance, isGroupOrder) => {
const filtered = isGroupOrder
? revenueCenters.filter(i => i.group_ordering)
: revenueCenters;
if (serviceType === 'DELIVERY') {
const displayed = (0, exports.makeDeliveryRevenueCenters)(filtered);
const count = displayed.length;
const { title, msg, error } = (0, exports.makeDeliveryMesssaging)(address, count);
return { title, msg, error, displayed };
}
else if (serviceType === 'WALKIN') {
const displayed = (0, exports.makeWalkinRevenueCenters)(filtered, maxDistance);
const minDistance = (0, exports.calcMinDistance)(displayed);
const count = displayed.length;
const { title, msg } = (0, exports.makeWalkinMessaging)(address, latLng, count, minDistance, maxDistance);
const error = null;
return { title, msg, error, displayed };
}
else {
const displayed = (0, exports.makePickupRevenueCenters)(filtered, maxDistance);
const minDistance = (0, exports.calcMinDistance)(displayed);
const count = displayed.length;
const { title, msg } = (0, exports.makePickupMesssaging)(address, latLng, count, minDistance, maxDistance);
const error = null;
return { title, msg, error, displayed };
}
};
exports.makeDisplayedRevenueCenters = makeDisplayedRevenueCenters;
exports.LOCATIONS_MESSAGES = {
PICKUP: {
default: {
title: 'Please choose a location',
msg: 'Or enter a zip code to find the location nearest you.'
},
address: {
title: 'locations near you',
msg: 'Please choose a location below.'
},
addressFar: {
title: "Looks like we don't have any locations that offer pickup in your area",
msg: 'Sorry about that. Please enter a different address or head back and choose a different order type.'
},
geo: {
title: 'locations in your area',
msg: 'Please enter an address or zip code for a more accurate result.'
},
geoFar: {
title: "Looks like we don't have any locations that offer pickup in your area",
msg: 'Please enter an address or zip code if you live in a different area.'
}
},
DELIVERY: {
default: {
title: "Let's find the nearest location",
msg: 'Please enter your address.',
error: null
},
noStreet: {
title: 'Please enter a street address',
msg: '',
error: 'A full address with street number is required for delivery orders.'
},
hasDelivery: {
title: 'Delivery is available!',
msg: 'Please choose a location below.',
error: null
},
noDelivery: {
title: "Delivery isn't available in your area at this time",
msg: "We're really sorry about that. Please enter a different address or head back and start a pickup order.",
error: null
}
}
};
const renameLocation = (str, names) => {
const [singular, plural] = names;
return str
.replace('1 locations', '1 location')
.replace('locations', plural)
.replace('location', singular)
.replace(' a a', ' an a')
.replace(' a e', ' an e')
.replace(' a i', ' an i')
.replace(' a o', ' an o')
.replace(' a u', ' an u');
};
exports.renameLocation = renameLocation;
const makeMapStyles = ({ labelColor, roadColor, featureColor, waterColor, backgroundColor }) => [
{
featureType: 'transit',
elementType: 'all',
stylers: [{ visibility: 'off' }]
},
{
featureType: 'administrative',
elementType: 'labels.text.fill',
stylers: [{ visibility: 'off' }]
},
{
featureType: 'administrative',
elementType: 'labels.text.stroke',
stylers: [{ visibility: 'off' }]
},
{
featureType: 'road',
elementType: 'labels.icon',
stylers: [{ visibility: 'off' }]
},
{
featureType: 'road',
elementType: 'geometry',
stylers: [{ visibility: 'simplified' }, { color: roadColor }]
},
{
featureType: 'road',
elementType: 'labels.text.fill',
stylers: [{ color: labelColor }]
},
{
featureType: 'road',
elementType: 'labels.text.stroke',
stylers: [{ visibility: 'off' }]
},
{
featureType: 'road.highway',
elementType: 'geometry',
stylers: [{ color: featureColor }]
},
{
featureType: 'road.highway',
elementType: 'labels',
stylers: [{ visibility: 'off' }]
},
{
featureType: 'water',
elementType: 'geometry',
stylers: [{ color: waterColor }]
},
{
featureType: 'water',
elementType: 'labels',
stylers: [{ visibility: 'off' }]
},
{
featureType: 'poi',
elementType: 'geometry',
stylers: [{ color: backgroundColor }]
},
{
featureType: 'poi',
elementType: 'labels',
stylers: [{ visibility: 'off' }]
},
{
featureType: 'landscape',
elementType: 'geometry',
stylers: [{ color: backgroundColor }]
},
{
featureType: 'landscape',
elementType: 'labels',
stylers: [{ visibility: 'off' }]
}
];
exports.makeMapStyles = makeMapStyles;