{"version":3,"file":"index-Bp3--DJo.js","sources":["../../ClientApp/src/Plugins/vuetify.ts","../../ClientApp/src/Helpers/ObjectHelpers.ts","../../ClientApp/src/components/ServiceAreas.vue","../../ClientApp/src/components/leaflet.curve.js","../../ClientApp/src/components/LeafletCurve.vue","../../ClientApp/src/components/TripMarkers.vue","../../ClientApp/src/components/UserPosition.vue","../../ClientApp/src/AppConfig.ts","../../ClientApp/src/EnumsDto/StatusDto.ts","../../ClientApp/src/components/GeolocateButton.vue","../../ClientApp/src/Helpers/GeocoderResultHelpers.ts","../../ClientApp/src/components/LocationInput.vue","../../ClientApp/src/Helpers/VueGmapHelpers.ts","../../ClientApp/src/Helpers/StringHelpers.ts","../../ClientApp/src/Services/IGeolocationService.ts","../../ClientApp/src/Services/IRequestService.ts","../../ClientApp/src/Filters/AddressFilters.ts","../../ClientApp/src/EnumsDto/RequestStatusDto.ts","../../ClientApp/src/Helpers/GeoJsonHelpers.ts","../../ClientApp/src/Helpers/NumberHelpers.ts","../../ClientApp/src/Services/RequestService.ts","../../ClientApp/src/Services/IServiceService.ts","../../ClientApp/src/Services/ServiceService.ts","../../ClientApp/src/Services/GeolocationService.ts","../../ClientApp/src/Plugins/i18n.ts","../../ClientApp/src/Services/LocalizationService.ts","../../ClientApp/src/Services/ILocalizationService.ts","../../ClientApp/src/Services/IConfigService.ts","../../ClientApp/src/Services/AppConfigService.ts","../../ClientApp/src/Plugins/inversify.ts","../../ClientApp/src/InjectionKeys.ts","../../ClientApp/src/Routes/BookingPage/usePickupDropoff.ts","../../ClientApp/src/Routes/BookingPage/PickupTab.vue","../../ClientApp/src/Routes/BookingPage/DropoffTab.vue","../../ClientApp/src/EnumsDto/TimeRuleDaysDto.ts","../../ClientApp/src/Validators/OperatingHoursValidators.ts","../../ClientApp/src/Helpers/OperatingHoursHelpers.ts","../../ClientApp/src/Models/RequestedPickupTsType.ts","../../ClientApp/src/components/TimePicker.vue","../../ClientApp/src/components/DatetimePicker.vue","../../ClientApp/src/components/NumberInput.vue","../../ClientApp/src/Filters/DateFilters.ts","../../ClientApp/src/Routes/BookingPage/TripDetails.vue","../../ClientApp/src/components/LocationText.vue","../../ClientApp/src/Routes/BookingPage/ContactInfo.vue","../../ClientApp/src/Plugins/gtm.ts","../../ClientApp/src/Helpers/SafariBugFixHelpers.ts","../../ClientApp/src/Routes/BookingPage/useBooking.ts","../../ClientApp/src/Routes/BookingPage/BookingPage.vue","../../ClientApp/src/Routes/RequestDetailsPage/Status.vue","../../ClientApp/src/components/VehiclePosition.vue","../../ClientApp/src/Routes/RequestDetailsPage/RequestDetails.vue","../../ClientApp/src/Routes/RequestDetailsPage/RequestDetailsPage.vue","../../ClientApp/src/Routes/index.ts","../../ClientApp/src/components/core/ErrorDialog.vue","../../ClientApp/src/components/RollerLoadingIcon.vue","../../ClientApp/src/Plugins/moment.ts","../../ClientApp/src/App.vue","../../ClientApp/src/main.ts"],"sourcesContent":["import { ThemeDefinition, createVuetify } from 'vuetify';\nimport 'vuetify/styles';\n\nconst customTheme: ThemeDefinition = {\n dark: false,\n colors: {\n primary: '#36a14b',\n error: '#a94442'\n }\n};\n\nexport default createVuetify({\n theme: {\n defaultTheme: 'customTheme',\n themes: {\n customTheme\n }\n }\n});\n","/**\n * Returns the property at the given property path.\n *\n * @param input Input object\n * @param path Property path (dot-separated)\n */\nexport function getByKey(input: Object, path: string) {\n if (!input || !path) return null;\n\n path = path.replace(/\\[(\\w+)\\]/g, '.$1'); // convert indexes to properties\n path = path.replace(/^\\./, ''); // strip a leading dot\n const a = path.split('.');\n for (let i = 0, n = a.length; i < n; ++i) {\n const k = a[i];\n if (k in input) {\n input = input[k];\n } else {\n return null;\n }\n }\n return input;\n}\n","\n\n\n\n\n","/*\n * Leaflet.curve v0.1.0 - a plugin for Leaflet mapping library. https://github.com/elfalem/Leaflet.curve\n * (c) elfalem 2015\n */\n/*\n * note that SVG (x, y) corresponds to (long, lat)\n */\n\nL.Curve = L.Path.extend({\n\toptions: {\n\t},\n\t\n\tinitialize: function(path, options){\n\t\tL.setOptions(this, options);\n\t\tthis._setPath(path);\n\t},\n\t\n\tgetPath: function(){\n\t\treturn this._coords;\n\t},\n\t\n\tsetPath: function(path){\n\t\tthis._setPath(path);\n\t\treturn this.redraw();\n\t},\n\t\n\tgetBounds: function() {\n\t\treturn this._bounds;\n\t},\n\n\t_setPath: function(path){\n\t\tthis._coords = path;\n\t\tthis._bounds = this._computeBounds();\n\t},\n\t\n\t_computeBounds: function(){\n\t\tvar bound = new L.LatLngBounds();\n\t\tvar lastPoint;\n\t\tvar lastCommand;\n\t\tfor(var i = 0; i < this._coords.length; i++){\n\t\t\tvar coord = this._coords[i];\n\t\t\tif(typeof coord == 'string' || coord instanceof String){\n\t\t\t\tlastCommand = coord;\n\t\t\t}else if(lastCommand == 'H'){\n\t\t\t\tbound.extend([lastPoint.lat,coord[0]]);\n\t\t\t\tlastPoint = new L.latLng(lastPoint.lat,coord[0]);\n\t\t\t}else if(lastCommand == 'V'){\n\t\t\t\tbound.extend([coord[0], lastPoint.lng]);\n\t\t\t\tlastPoint = new L.latLng(coord[0], lastPoint.lng);\n\t\t\t}else if(lastCommand == 'C'){\n\t\t\t\tvar controlPoint1 = new L.latLng(coord[0], coord[1]);\n\t\t\t\tcoord = this._coords[++i];\n\t\t\t\tvar controlPoint2 = new L.latLng(coord[0], coord[1]);\n\t\t\t\tcoord = this._coords[++i];\n\t\t\t\tvar endPoint = new L.latLng(coord[0], coord[1]);\n\n\t\t\t\tbound.extend(controlPoint1);\n\t\t\t\tbound.extend(controlPoint2);\n\t\t\t\tbound.extend(endPoint);\n\n\t\t\t\tendPoint.controlPoint1 = controlPoint1;\n\t\t\t\tendPoint.controlPoint2 = controlPoint2;\n\t\t\t\tlastPoint = endPoint;\n\t\t\t}else if(lastCommand == 'S'){\n\t\t\t\tvar controlPoint2 = new L.latLng(coord[0], coord[1]);\n\t\t\t\tcoord = this._coords[++i];\n\t\t\t\tvar endPoint = new L.latLng(coord[0], coord[1]);\n\n\t\t\t\tvar controlPoint1 = lastPoint;\n\t\t\t\tif(lastPoint.controlPoint2){\n\t\t\t\t\tvar diffLat = lastPoint.lat - lastPoint.controlPoint2.lat;\n\t\t\t\t\tvar diffLng = lastPoint.lng - lastPoint.controlPoint2.lng;\n\t\t\t\t\tcontrolPoint1 = new L.latLng(lastPoint.lat + diffLat, lastPoint.lng + diffLng);\n\t\t\t\t}\n\n\t\t\t\tbound.extend(controlPoint1);\n\t\t\t\tbound.extend(controlPoint2);\n\t\t\t\tbound.extend(endPoint);\n\n\t\t\t\tendPoint.controlPoint1 = controlPoint1;\n\t\t\t\tendPoint.controlPoint2 = controlPoint2;\n\t\t\t\tlastPoint = endPoint;\n\t\t\t}else if(lastCommand == 'Q'){\n\t\t\t\tvar controlPoint = new L.latLng(coord[0], coord[1]);\n\t\t\t\tcoord = this._coords[++i];\n\t\t\t\tvar endPoint = new L.latLng(coord[0], coord[1]);\n\n\t\t\t\tbound.extend(controlPoint);\n\t\t\t\tbound.extend(endPoint);\n\n\t\t\t\tendPoint.controlPoint = controlPoint;\n\t\t\t\tlastPoint = endPoint;\n\t\t\t}else if(lastCommand == 'T'){\n\t\t\t\tvar endPoint = new L.latLng(coord[0], coord[1]);\n\n\t\t\t\tvar controlPoint = lastPoint;\n\t\t\t\tif(lastPoint.controlPoint){\n\t\t\t\t\tvar diffLat = lastPoint.lat - lastPoint.controlPoint.lat;\n\t\t\t\t\tvar diffLng = lastPoint.lng - lastPoint.controlPoint.lng;\n\t\t\t\t\tcontrolPoint = new L.latLng(lastPoint.lat + diffLat, lastPoint.lng + diffLng);\n\t\t\t\t}\n\n\t\t\t\tbound.extend(controlPoint);\n\t\t\t\tbound.extend(endPoint);\n\n\t\t\t\tendPoint.controlPoint = controlPoint;\n\t\t\t\tlastPoint = endPoint;\n\t\t\t}else{\n\t\t\t\tbound.extend(coord);\n\t\t\t\tlastPoint = new L.latLng(coord[0], coord[1]);\n\t\t\t}\n\t\t}\n\t\treturn bound;\n\t},\n\t\n\t//TODO: use a centroid algorithm instead\n\tgetCenter: function () {\n\t\treturn this._bounds.getCenter();\n\t},\n\t\n\t_update: function(){\n\t\tif (!this._map) { return; }\n\t\t\n\t\tthis._updatePath();\n\t},\n\t\n\t_updatePath: function() {\n\t\tthis._renderer._updatecurve(this);\n\t},\n\n\t_project: function() {\n\t\tvar coord, lastCoord, curCommand, curPoint;\n\n\t\tthis._points = [];\n\t\t\n\t\tfor(var i = 0; i < this._coords.length; i++){\n\t\t\tcoord = this._coords[i];\n\t\t\tif(typeof coord == 'string' || coord instanceof String){\n\t\t\t\tthis._points.push(coord);\n\t\t\t\tcurCommand = coord;\n\t\t\t}else {\n\t\t\t\tswitch(coord.length){\n\t\t\t\t\tcase 2:\n\t\t\t\t\t\tcurPoint = this._map.latLngToLayerPoint(coord);\n\t\t\t\t\t\tlastCoord = coord;\n\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\tif(curCommand == 'H'){\n\t\t\t\t\t\t\tcurPoint = this._map.latLngToLayerPoint([lastCoord[0], coord[0]]);\n\t\t\t\t\t\t\tlastCoord = [lastCoord[0], coord[0]];\n\t\t\t\t\t\t}else{\n\t\t\t\t\t\t\tcurPoint = this._map.latLngToLayerPoint([coord[0], lastCoord[1]]);\n\t\t\t\t\t\t\tlastCoord = [coord[0], lastCoord[1]];\n\t\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tthis._points.push(curPoint);\n\t\t\t}\n\t\t}\n\t}\t\n});\n\nL.curve = function (path, options){\n\treturn new L.Curve(path, options);\n};\n\nL.SVG.include({\n\t_updatecurve: function(layer){\n\t\tthis._setPath(layer, this._curvePointsToPath(layer._points));\n \t},\n \t_curvePointsToPath: function(points){\n\t\tvar point, curCommand, str = '';\n\t\tfor(var i = 0; i < points.length; i++){\n\t\t\tpoint = points[i];\n\t\t\tif(typeof point == 'string' || point instanceof String){\n\t\t\t\tcurCommand = point;\n\t\t\t\tstr += curCommand;\n\t\t\t}else{\n\t\t\t\tswitch(curCommand){\n\t\t\t\t\tcase 'H':\n\t\t\t\t\t\tstr += point.x + ' ';\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 'V':\n\t\t\t\t\t\tstr += point.y + ' ';\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tstr += point.x + ',' + point.y + ' ';\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn str || 'M0 0';\n\t}\n});\n","\n\n\n","\n\n\n\n\n","\n\n\n","export default {\n apiRoutePrefix: '/api/v1.0',\n mapCenter: { lat: 65.57982867263381, lng: 16.89278813689722 },\n mapZoom: 5,\n timezone: 'Europe/Oslo',\n dateFormat: 'DD.MM.YYYY', // moment format\n timeFormat: 'HH:mm', // moment format\n luxonDateFormat: 'dd.MM.yyyy',\n luxonTimeFormat: 'HH:mm',\n luxonTimezone: 'Europe/Oslo',\n requestedFlexibility: 1200, // 20 minutes\n requestStatusUpdateInterval: 30000,\n vehicleLocationUpdateInterval: 5000,\n maxNumRiders: 6,\n vehicleLocationMinTime: 600, // 10 minutes\n showThankyouTimeout: 5000, // 5 seconds\n showPickupPointDelay: 2000, // 2 seconds\n maxDaysInTheFuture: 30 // Max number of days user can book for\n};\n","export enum StatusDto {\n Enabled = 1,\n Disabled = 2,\n Archived = 3\n}\n","\n\n\n\n\n","/**\n * Converts array of address components to human-readable string.\n *\n * @param value {google.maps.GeocoderAddressComponent[]} Array of address components\n * @returns {string} Formatted address\n */\nexport function formatAddress(value: google.maps.GeocoderAddressComponent[]): string {\n if (!value || value.length === 0) return null;\n\n // falback\n const administrativeAreaLevels = value.filter(c => c.types.filter(t => t.startsWith('administrative_area_level_')).length !== 0);\n administrativeAreaLevels.sort((a, b) => {\n const _a = a.types.filter(__a => __a.startsWith('administrative_area_level_'))[0];\n const _b = b.types.filter(__b => __b.startsWith('administrative_area_level_'))[0];\n\n // reversed\n if (_a < _b) return 1;\n if (_a > _b) return -1;\n return 0;\n });\n\n let city = administrativeAreaLevels[0];\n\n // use locality when available\n const locality = value.filter(c => c.types.indexOf('locality') !== -1)[0];\n if (locality) city = locality;\n\n // use postal town when available\n const postalTown = value.filter(c => c.types.indexOf('postal_town') !== -1)[0];\n if (postalTown) city = postalTown;\n\n // if transit station, we use just the station name\n const transit_station = value.filter(c => c.types.indexOf('transit_station') !== -1)[0];\n if (transit_station) {\n return `${transit_station.long_name}, ${city.long_name}`;\n }\n\n // build street address\n const route = value.filter(c => c.types.indexOf('route') !== -1)[0];\n const streetNumber = value.filter(c => c.types.indexOf('street_number') !== -1)[0];\n\n const streetAddress = streetNumber ? `${route.long_name} ${streetNumber.long_name}, ` : route ? `${route.long_name}, ` : '';\n\n return `${streetAddress}${city.long_name}`;\n}\n","\n\n\n","// ray-casting algorithm based on\n// http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html\nexport function inside(point: { lat: number; lng: number }, paths: Array<{ lat: number; lng: number }>) {\n const x = point.lat,\n y = point.lng;\n\n if (paths.length === 0) return false;\n\n let inside = false;\n for (let i = 0, j = paths.length - 1; i < paths.length; j = i++) {\n const xi = paths[i].lat,\n yi = paths[i].lng;\n const xj = paths[j].lat,\n yj = paths[j].lng;\n\n const intersect = yi > y != yj > y && x < ((xj - xi) * (y - yi)) / (yj - yi) + xi;\n if (intersect) inside = !inside;\n }\n\n return inside;\n}\n\n// ref: https://stackoverflow.com/questions/14505565/detect-if-a-set-of-points-in-an-array-that-are-the-vertices-of-a-complex-polygon\nexport function isClockwise(poly: google.maps.LatLngLiteral[]) {\n let area = 0;\n for (let i = 0; i < poly.length; i++) {\n const j = (i + 1) % poly.length;\n area += poly[i].lng * poly[j].lat;\n area -= poly[j].lng * poly[i].lat;\n }\n return area / 2 > 0;\n}\n","// Ref: https://stackoverflow.com/a/2970667\nexport function camelize(str: string): string {\n if (!str || typeof str !== 'string') return null;\n\n return str.replace(/(?:^\\w|[A-Z]|\\b\\w|\\s+)/g, (match, index) => {\n if (+match === 0) return ''; // or if (/\\s+/.test(match)) for white spaces\n return index == 0 ? match.toLowerCase() : match.toUpperCase();\n });\n}\n\nexport function linkify(str: string) {\n if (!str || typeof str !== 'string') return '';\n\n const urls = str.match(/((((ftp|https?):\\/\\/)|(w{3}\\.))[-\\w@:%_+.~#?,&\\/\\/=]+)/g);\n if (urls) {\n urls.forEach(function (url) {\n str = str.replace(url, '' + url + '');\n });\n }\n\n return str;\n}\n","export const IGeolocationServiceId = Symbol.for('IGeolocationService');\n\nexport interface IGeolocationService extends Geolocation {\n isAvailable: boolean;\n}\n","import { RequestEstimate } from '@/Models/RequestEstimate';\nimport { CreateRequestResponseDto } from '@/Dto/CreateRequestResponseDto';\nimport { Request } from '@/Models/Request';\nimport { VehicleLocationDto } from '@/Dto/VehicleLocationDto';\nimport { RequestParameters } from '@/Models/RequestParameters';\n\nexport const IRequestServiceId = Symbol.for('IRequestService');\n\nexport interface IRequestService {\n /**\n * Fetches an estimation for the next available trip.\n *\n * @param params Request parameters\n */\n getNextAvailableRequestEstimate(params: RequestParameters): Promise;\n\n /**\n * Fetches single or multiple estimations for a specific trip.\n *\n * @param params Request parameters\n */\n getRequestEstimates(params: RequestParameters): Promise;\n\n /**\n * Creates a Request.\n *\n * @param params Request parameters\n */\n createRequest(params: RequestParameters): Promise;\n\n /**\n * Fetches a specific Request.\n *\n * @param id Request ID\n */\n getRequest(companyId: string, id: string): Promise;\n\n /**\n * Cancels a specific Request.\n *\n * @param id Request ID\n */\n cancelRequest(companyId: string, id: string): Promise;\n\n /**\n * Fetches the vehicle location for a Request.\n *\n * @param id Request ID\n */\n getVehicleLocation(companyId: string, id: string): Promise;\n}\n","const ShortAddressFilter = (fullAddress: string) => fullAddress && fullAddress.split(',')[0];\n\nexport { ShortAddressFilter };\n","export enum RequestStatusDto {\n Processing = 10,\n ServiceDisruption = 15,\n NoDriversAvailable = 20,\n Accepted = 30,\n Arriving = 35,\n InProgress = 40,\n Cancelled = 50,\n Completed = 60\n}\n","import { Point, LineString } from 'geojson';\n\nexport function LineStringToLatLngLiteralArray(lineString: LineString): Array<{ lat: number; lng: number }> {\n return lineString\n ? lineString.coordinates.map(c => ({\n lat: Number(c[1]),\n lng: Number(c[0])\n }))\n : null;\n}\n\nexport function PointToLatLng(point: Point): { lat: number; lng: number } {\n if (!point) return null;\n const { coordinates } = point;\n const [lng, lat] = coordinates;\n return { lat, lng };\n}\n","import moment, { Moment } from 'moment';\n\nexport function NumberToMoment(value: number, timezone?: string): Moment {\n if (!value) return null;\n\n if (timezone) {\n return moment.unix(value).tz(timezone);\n } else {\n return moment.unix(value);\n }\n}\n","import { IRequestService } from '@/Services/IRequestService';\nimport { inject, injectable } from 'inversify';\nimport { RequestEstimateDto } from '@/Dto/RequestEstimateDto';\nimport { CreateRequestResponseDto } from '@/Dto/CreateRequestResponseDto';\nimport { RequestDto } from '@/Dto/RequestDto';\nimport { Request } from '@/Models/Request';\nimport { VehicleLocationDto } from '@/Dto/VehicleLocationDto';\nimport { AxiosStatic, AxiosError, AxiosResponse, isAxiosError } from 'axios';\nimport AppConfig from '@/AppConfig';\nimport { ShortAddressFilter } from '@/Filters/AddressFilters';\nimport { RequestStatusDto } from '@/EnumsDto/RequestStatusDto';\nimport { RequestEstimate } from '@/Models/RequestEstimate';\nimport { RequestParameters } from '@/Models/RequestParameters';\nimport { LineStringToLatLngLiteralArray, PointToLatLng } from '@/Helpers/GeoJsonHelpers';\nimport { NumberToMoment } from '@/Helpers/NumberHelpers';\nimport { LatLngLiteral } from 'leaflet';\nimport { EstimateServiceDto } from '@/Dto/EstimateServiceDto';\nimport { camelize } from '@/Helpers/StringHelpers';\n\nconst { requestedFlexibility } = AppConfig;\n\nfunction toRequestEstimate(requestEstimateDto: RequestEstimateDto): RequestEstimate {\n return {\n ...requestEstimateDto,\n requestedPickupTs: NumberToMoment(requestEstimateDto.requestedPickupTs, AppConfig.timezone),\n requestedPickupLocation: PointToLatLng(requestEstimateDto.requestedPickupLocation),\n requestedDropoffLocation: PointToLatLng(requestEstimateDto.requestedDropoffLocation),\n scheduledPickupTs: NumberToMoment(requestEstimateDto.scheduledPickupTs, AppConfig.timezone),\n scheduledPickupLocation: PointToLatLng(requestEstimateDto.scheduledPickupLocation),\n scheduledDropoffLocation: PointToLatLng(requestEstimateDto.scheduledDropoffLocation),\n pickupWalkingPolyline: LineStringToLatLngLiteralArray(requestEstimateDto.pickupWalkingPolyline),\n dropoffWalkingPolyline: LineStringToLatLngLiteralArray(requestEstimateDto.dropoffWalkingPolyline)\n };\n}\n\n// TODO: rethink how the DTO objects should look like, especially for error messages. Should the API return error message keys instead?\n@injectable()\nexport default class RequestService implements IRequestService {\n @inject('axios')\n protected http: AxiosStatic;\n\n getNextAvailableRequestEstimate(params: RequestParameters): Promise {\n return new Promise((resolve, reject) => {\n const { requestedPickupLocation, requestedDropoffLocation, service, numRiders } = params;\n const { lat: requestedPickupLatitude, lng: requestedPickupLongitude } = requestedPickupLocation || ({} as LatLngLiteral);\n const { lat: requestedDropoffLatitude, lng: requestedDropoffLongitude } = requestedDropoffLocation || ({} as LatLngLiteral);\n const { id: serviceId } = service || ({} as EstimateServiceDto);\n\n this.http\n .get(`${AppConfig.apiRoutePrefix}/estimates/nextAvailable`, {\n params: {\n requestedPickupLatitude,\n requestedPickupLongitude,\n requestedDropoffLatitude,\n requestedDropoffLongitude,\n serviceId,\n numRiders,\n requestedFlexibility\n }\n })\n .then(response => resolve(toRequestEstimate(response.data)))\n .catch(e => {\n if (isAxiosError(e)) {\n const { response } = e;\n const { status = 500, data } = response || {};\n\n let message: string;\n switch (status) {\n case 400:\n message = data ? `errorMessages.${camelize(data)}` : null;\n break;\n case 404:\n message = 'errorMessages.noServiceFound';\n break;\n default: {\n message = data || 'errorMessages.defaultErrorMessage';\n }\n }\n\n reject({ status, message });\n } else {\n reject(e);\n }\n });\n });\n }\n\n getRequestEstimates(params: RequestParameters): Promise {\n return new Promise((resolve, reject) => {\n const { requestedPickupLocation, requestedDropoffLocation, service, numRiders, requestedPickupTs } = params;\n const { lat: requestedPickupLatitude, lng: requestedPickupLongitude } = requestedPickupLocation || ({} as LatLngLiteral);\n const { lat: requestedDropoffLatitude, lng: requestedDropoffLongitude } = requestedDropoffLocation || ({} as LatLngLiteral);\n const { id: serviceId } = service || ({} as EstimateServiceDto);\n\n this.http\n .get(`${AppConfig.apiRoutePrefix}/estimates/request`, {\n params: {\n requestedPickupLatitude,\n requestedPickupLongitude,\n requestedDropoffLatitude,\n requestedDropoffLongitude,\n requestedPickupTs: requestedPickupTs.unix(),\n serviceId,\n numRiders,\n requestedFlexibility\n }\n })\n .then(response => {\n if (response.data instanceof Array) {\n resolve(response.data.map(x => toRequestEstimate(x)));\n } else {\n resolve(toRequestEstimate(response.data));\n }\n })\n .catch(e => {\n if (isAxiosError(e)) {\n const { response } = (e as AxiosError) || ({} as AxiosError);\n const { status, data = null } = response || ({} as AxiosResponse);\n\n let message: string;\n switch (status) {\n case 400: {\n const errorString = camelize(data);\n message = errorString ? `errorMessages.${errorString}` : null;\n break;\n }\n case 404:\n message = 'errorMessages.noServiceFound';\n break;\n default: {\n message = data || 'errorMessages.defaultErrorMessage';\n }\n }\n\n reject({\n status: status || 500,\n message\n });\n } else {\n reject(e);\n }\n });\n });\n }\n\n createRequest(params: RequestParameters): Promise {\n return new Promise((resolve, reject) => {\n const {\n requestedPickupAddress,\n requestedPickupLocation,\n requestedDropoffAddress,\n requestedDropoffLocation,\n service,\n numRiders,\n requestedPickupTs,\n firstName,\n phoneNumber,\n estimateId\n } = params;\n const { lat: requestedPickupLatitude, lng: requestedPickupLongitude } = requestedPickupLocation || ({} as LatLngLiteral);\n const { lat: requestedDropoffLatitude, lng: requestedDropoffLongitude } = requestedDropoffLocation || ({} as LatLngLiteral);\n const { id: serviceId } = service || ({} as EstimateServiceDto);\n\n this.http\n .post(`${AppConfig.apiRoutePrefix}/requests`, {\n requestedPickupAddress: ShortAddressFilter(requestedPickupAddress),\n requestedPickupLatitude,\n requestedPickupLongitude,\n requestedDropoffAddress: ShortAddressFilter(requestedDropoffAddress),\n requestedDropoffLatitude,\n requestedDropoffLongitude,\n requestedPickupTs: requestedPickupTs.unix(),\n numRiders,\n firstName,\n phoneNumber,\n serviceId,\n requestedFlexibility,\n estimateId\n })\n .then(response => {\n const getStatus = () => {\n let retries = 0,\n status = RequestStatusDto.Processing;\n\n this.getRequest(response.data.companyId, response.data.id).then(request => {\n status = request.status;\n retries++;\n\n if (retries <= 10 && status < RequestStatusDto.Accepted) {\n setTimeout(getStatus, 1000);\n } else {\n resolve(response.data);\n }\n });\n };\n\n getStatus();\n })\n .catch(e => {\n if (isAxiosError(e)) {\n const { response } = (e as AxiosError) || ({} as AxiosError);\n const { status, data } = response || ({} as AxiosResponse);\n\n reject({\n status: status || 500,\n message: data || 'errorMessages.defaultErrorMessage'\n });\n } else {\n reject(e);\n }\n });\n });\n }\n\n getRequest(companyId: string, id: string): Promise {\n return new Promise((resolve, reject) => {\n this.http\n .get(`${AppConfig.apiRoutePrefix}/requests/${companyId}/${id}`)\n .then(response => {\n resolve({\n ...response.data,\n companyId,\n requestedPickupTs: NumberToMoment(response.data.requestedPickupTs, AppConfig.timezone),\n requestedPickupLocation: PointToLatLng(response.data.requestedPickupLocation),\n requestedDropoffLocation: PointToLatLng(response.data.requestedDropoffLocation),\n scheduledPickupTs: NumberToMoment(response.data.scheduledPickupTs, AppConfig.timezone),\n scheduledPickupLocation: PointToLatLng(response.data.scheduledPickupLocation),\n scheduledDropoffLocation: PointToLatLng(response.data.scheduledDropoffLocation),\n pickupWalkingPolyline: LineStringToLatLngLiteralArray(response.data.pickupWalkingPolyline),\n dropoffWalkingPolyline: LineStringToLatLngLiteralArray(response.data.dropoffWalkingPolyline),\n pickupEta: NumberToMoment(response.data.pickupEta, AppConfig.timezone),\n dropoffEta: NumberToMoment(response.data.dropoffEta, AppConfig.timezone),\n scheduledDropoffTs: NumberToMoment(response.data.scheduledDropoffTs, AppConfig.timezone)\n });\n })\n .catch(e => {\n if (isAxiosError(e)) {\n const { response } = e || {};\n const { status, data } = response || {};\n\n let message: string;\n switch (status) {\n case 404: {\n switch (data) {\n case 'ExpiredError':\n message = 'errorMessages.requestExpired';\n break;\n case 'CancelledError':\n message = 'messages.requestCancelled';\n break;\n case 'CompletedError':\n message = 'errorMessages.requestCompleted';\n break;\n case 'NotFoundError':\n default:\n message = 'errorMessages.requestNotFound';\n }\n break;\n }\n default: {\n message = data || 'errorMessages.defaultErrorMessage';\n }\n }\n reject({\n status: status || 500,\n message\n });\n } else {\n reject(e);\n }\n });\n });\n }\n\n cancelRequest(companyId: string, id: string): Promise {\n return new Promise((resolve, reject) => {\n this.http\n .delete(`${AppConfig.apiRoutePrefix}/requests/${companyId}/${id}`)\n .then(() => resolve())\n .catch(e => {\n if (isAxiosError(e)) {\n const { response } = (e as AxiosError) || ({} as AxiosError);\n const { status, data } = response || ({} as AxiosResponse);\n\n let message: string;\n switch (status) {\n case 404:\n message = 'errorMessages.requestNotFound';\n break;\n default: {\n message = data || 'errorMessages.defaultErrorMessage';\n }\n }\n\n reject({\n status: status || 500,\n message\n });\n } else {\n reject(e);\n }\n });\n });\n }\n\n getVehicleLocation(companyId: string, id: string): Promise {\n return new Promise((resolve, reject) => {\n this.http\n .get(`${AppConfig.apiRoutePrefix}/requests/${companyId}/${id}/vehicleLocation`)\n .then(response => resolve(response.data))\n .catch(e => {\n if (isAxiosError(e)) {\n const { response } = (e as AxiosError) || ({} as AxiosError);\n const { status } = response || ({} as AxiosResponse);\n\n reject({\n status: status || 500,\n message: 'errorMessages.defaultErrorMessage'\n });\n } else {\n reject(e);\n }\n });\n });\n }\n}\n","import { EstimateServiceDto } from '@/Dto/EstimateServiceDto';\nimport { ServiceDto } from '@/Dto/ServiceDto';\nimport { StatusDto } from '@/EnumsDto/StatusDto';\nimport { RequestParameters } from '@/Models/RequestParameters';\n\nexport const IServiceServiceId = Symbol.for('IServiceService');\n\nexport interface IServiceService {\n /**\n * Fetches a service estimate.\n *\n * @param params Request parameters\n */\n getServiceEstimate(params: RequestParameters): Promise;\n\n /**\n * Fetches a list of services.\n *\n * @param status Status of services to fetch\n */\n getServices(status?: StatusDto, active?: boolean): Promise;\n}\n","import { IServiceService } from '@/Services/IServiceService';\nimport { inject, injectable } from 'inversify';\nimport { EstimateServiceDto } from '@/Dto/EstimateServiceDto';\nimport { AxiosStatic, AxiosError, AxiosResponse, isAxiosError } from 'axios';\nimport AppConfig from '@/AppConfig';\nimport { ServiceDto } from '@/Dto/ServiceDto';\nimport { StatusDto } from '@/EnumsDto/StatusDto';\nimport { RequestParameters } from '@/Models/RequestParameters';\nimport { LatLngLiteral } from 'leaflet';\n\n@injectable()\nexport default class ServiceService implements IServiceService {\n @inject('axios')\n protected axios: AxiosStatic;\n\n getServiceEstimate(params: RequestParameters): Promise {\n return new Promise((resolve, reject) => {\n const { requestedPickupLocation, requestedDropoffLocation } = params;\n const { lat: requestedPickupLatitude, lng: requestedPickupLongitude } = requestedPickupLocation || ({} as LatLngLiteral);\n const { lat: requestedDropoffLatitude, lng: requestedDropoffLongitude } = requestedDropoffLocation || ({} as LatLngLiteral);\n\n this.axios\n .get(`${AppConfig.apiRoutePrefix}/estimates/services`, {\n params: {\n requestedPickupLatitude,\n requestedPickupLongitude,\n requestedDropoffLatitude,\n requestedDropoffLongitude\n }\n })\n .then(response => {\n // TODO: out of service area errors should be returned as Bad Request response\n if (!response.data.length) {\n return reject('errorMessages.noServiceFound');\n }\n\n // return the first item\n resolve(response.data[0]);\n })\n .catch(e => {\n if (isAxiosError(e)) {\n const { response } = (e as AxiosError) || ({} as AxiosError);\n const { status, data } = response || ({} as AxiosResponse);\n\n let message: string;\n switch (status) {\n case 400:\n message = 'errorMessages.outOfServiceArea';\n break;\n default: {\n message = data || 'errorMessages.defaultErrorMessage';\n }\n }\n\n reject({\n status: status || 500,\n message\n });\n } else {\n reject(e);\n }\n });\n });\n }\n\n getServices(status?: StatusDto, active?: boolean): Promise {\n return new Promise((resolve, reject) => {\n this.axios\n .get(`${AppConfig.apiRoutePrefix}/services`, {\n params: {\n status,\n active\n }\n })\n .then(response => {\n resolve(response.data);\n })\n .catch(e => {\n if (isAxiosError(e)) {\n const { response } = (e as AxiosError) || ({} as AxiosError);\n const { status, data } = response || ({} as AxiosResponse);\n\n reject({\n status: status || 500,\n message: data || 'errorMessages.defaultErrorMessage'\n });\n } else {\n reject(e);\n }\n });\n });\n }\n}\n","import { IGeolocationService } from '@/Services/IGeolocationService';\nimport { injectable } from 'inversify';\n\n@injectable()\nexport default class GeolocationService implements IGeolocationService {\n private currentPosition: GeolocationPosition;\n\n isAvailable = false;\n\n constructor() {\n if ('geolocation' in navigator) {\n this.getCurrentPosition(position => {\n this.isAvailable = true;\n this.currentPosition = position;\n });\n }\n }\n\n clearWatch(watchId: number): void {\n this.currentPosition = null;\n navigator.geolocation.clearWatch(watchId);\n }\n\n getCurrentPosition(successCallback: PositionCallback, errorCallback?: PositionErrorCallback, options?: PositionOptions): void {\n if (this.currentPosition) {\n // if we have current position from watchPosition, return that\n successCallback(this.currentPosition);\n return;\n }\n\n // otherwise query geolocation\n return navigator.geolocation.getCurrentPosition(\n position => {\n // we need this for FF which does not seem to detect the variable change\n this.isAvailable = true;\n successCallback(position);\n },\n errorCallback,\n options\n );\n }\n\n watchPosition(successCallback: PositionCallback, errorCallback?: PositionErrorCallback, options?: PositionOptions): number {\n return navigator.geolocation.watchPosition(\n position => {\n // same reason as getCurrentPosition\n this.isAvailable = true;\n this.currentPosition = position;\n successCallback(position);\n },\n err => {\n this.currentPosition = null;\n errorCallback(err);\n },\n options\n );\n }\n}\n","import { createI18n, type I18n } from 'vue-i18n';\nimport { nextTick } from 'vue';\nimport type { RouteLocationNormalized, NavigationGuardNext, RouteLocationRaw } from 'vue-router';\nimport { isEmpty, merge } from 'lodash-es';\nimport { useContainer } from '@/Plugins/inversify';\nimport { type AxiosStatic } from 'axios';\nimport { en, no } from 'vuetify/locale';\n\nconst defaultMessages: any = {\n en: {\n $vuetify: en\n },\n no: {\n $vuetify: no\n }\n};\n\nfunction setupI18n(options = { locale: 'no' }) {\n const i18n = createI18n({\n fallbackLocale: 'en',\n legacy: false,\n globalInjection: true,\n ...options\n });\n setI18nLanguage(i18n, options.locale);\n return i18n;\n}\n\nconst i18n = setupI18n();\nexport default i18n;\n\nexport function setI18nLanguage(i18n: I18n<{}, {}, {}, string, false>, locale: string) {\n i18n.global.locale.value = locale;\n document.querySelector('html')?.setAttribute('lang', locale);\n}\n\nexport async function loadLocaleMessages(i18n: I18n<{}, {}, {}, string, false>, locale: string) {\n const container = useContainer();\n const axios = container.get('axios');\n\n try {\n // load locale messages with dynamic import\n const res = await axios.get(`/jsl10n/HentMeg.Web.Resources.HentMegResources?camel=true&json=true&lang=${locale}`);\n\n // set locale and locale message\n i18n.global.setLocaleMessage(locale, merge({}, defaultMessages[locale], res.data.hentMeg.web.resources.hentMegResources));\n } catch (e) {\n console.error(e);\n }\n\n return nextTick();\n}\n\nexport async function routeMiddleware(to: RouteLocationNormalized, _from: RouteLocationNormalized, next: NavigationGuardNext) {\n const paramLocale = (Array.isArray(to.params.locale) ? to.params.locale[0] : to.params.locale) || 'no';\n\n // load locale messages\n if (!i18n.global.availableLocales.includes(paramLocale) || isEmpty(i18n.global.getLocaleMessage(paramLocale))) {\n await loadLocaleMessages(i18n, paramLocale);\n }\n\n // set i18n language\n setI18nLanguage(i18n, paramLocale);\n\n return next();\n}\n\nexport function i18nRoute(to: any): RouteLocationRaw {\n return merge(to, { params: { locale: i18n.global.locale.value } });\n}\n","import { ILocalizationService } from '@/Services/ILocalizationService';\nimport { inject, injectable } from 'inversify';\nimport { AxiosStatic } from 'axios';\nimport i18n from '@/Plugins/i18n';\nimport moment from 'moment';\n\nconst loadedLanguages = [i18n.global.locale];\n\nfunction setI18nLanguage(lang: string): void {\n i18n.global.locale = lang;\n document.querySelector('html').setAttribute('lang', lang);\n moment.locale(lang);\n}\n\n@injectable()\nexport default class LocalizationService implements ILocalizationService {\n @inject('axios')\n protected axios: AxiosStatic;\n\n loadLanguage(lang: string): void {\n if (i18n.global.locale === lang || loadedLanguages.indexOf(lang) > -1) {\n return setI18nLanguage(lang);\n } else {\n this.axios\n .get(`/jsl10n/HentMeg.Web.Resources.HentMegResources?camel=true&json=true&lang=${lang}`)\n .then(res => {\n const langMsg = 'data.hentMeg.web.resources.hentMegResources'\n .split('.')\n .reduce((acc, curr) => (acc && acc[curr] ? acc[curr] : null), res);\n i18n.global.setLocaleMessage(lang, langMsg);\n loadedLanguages.push(lang);\n setI18nLanguage(lang);\n })\n .catch(err => {\n throw err;\n });\n }\n }\n}\n","export const ILocalizationServiceId = Symbol.for('ILocalizationService');\n\nexport interface ILocalizationService {\n /**\n * Sets app language.\n *\n * @param params Locale name\n */\n loadLanguage(params: string): void;\n}\n","import type { AppConfigDto } from '@/Dto/AppConfigDto';\n\nexport const IConfigServiceId = Symbol.for('IConfigService');\n\nexport interface IConfigService {\n getConfig(): Promise;\n}\n","import type { AppConfigDto } from '@/Dto/AppConfigDto';\nimport { IConfigService } from './IConfigService';\nimport { inject, injectable } from 'inversify';\nimport { AxiosStatic } from 'axios';\nimport AppConfig from '@/AppConfig';\n\n@injectable()\nexport default class AppConfigService implements IConfigService {\n @inject('axios')\n protected axios: AxiosStatic;\n\n getConfig() {\n return new Promise((resolve, reject) => {\n this.axios\n .get(`${AppConfig.apiRoutePrefix}/config`)\n .then(response => resolve(response.data))\n .catch(reject);\n });\n }\n}\n","import 'reflect-metadata';\nimport { Container } from 'inversify';\nimport axios, { AxiosStatic } from 'axios';\nimport { IRequestService, IRequestServiceId } from '@/Services/IRequestService';\nimport RequestService from '@/Services/RequestService';\nimport { IServiceService, IServiceServiceId } from '@/Services/IServiceService';\nimport ServiceService from '@/Services/ServiceService';\nimport { IGeolocationService, IGeolocationServiceId } from '@/Services/IGeolocationService';\nimport GeolocationService from '@/Services/GeolocationService';\nimport LocalizationService from '@/Services/LocalizationService';\nimport { ILocalizationService, ILocalizationServiceId } from '@/Services/ILocalizationService';\nimport { InjectionKey, Plugin, inject } from 'vue';\nimport { IConfigService, IConfigServiceId } from '@/Services/IConfigService';\nimport AppConfigService from '@/Services/AppConfigService';\n\nconst __INVERSIFY_SYMBOL__ = Symbol.for('app:inversify-container');\nconst injectionKey: InjectionKey = __INVERSIFY_SYMBOL__;\n\nexport function createContainer(): Plugin {\n const container = new Container();\n\n container.bind('axios').toConstantValue(axios);\n container.bind(IConfigServiceId).to(AppConfigService);\n container.bind(IRequestServiceId).to(RequestService);\n container.bind(IServiceServiceId).to(ServiceService);\n container.bind(IGeolocationServiceId).to(GeolocationService).inSingletonScope();\n container.bind(ILocalizationServiceId).to(LocalizationService).inSingletonScope();\n\n return {\n install(app) {\n app.provide(injectionKey, container);\n }\n };\n}\n\nexport function useContainer() {\n const _container = inject(injectionKey);\n if (!_container) throw new Error('You must call createContainer() first');\n\n return _container;\n}\n","import { InjectionKey, Ref } from 'vue';\nimport { AppConfigDto } from './Dto/AppConfigDto';\nimport { LMap } from '@vue-leaflet/vue-leaflet';\nimport { LatLngBounds, LatLngLiteral } from 'leaflet';\n\ninterface LoadingOverlayProvide {\n isVisible: Ref;\n showOverlay: (msg?: string) => void;\n hideOverlay: () => void;\n}\n\ninterface BookingPageProvide {\n map: Ref>;\n center: Ref;\n zoom: Ref;\n bounds: Ref;\n}\n\nexport const AppConfigProvideKey: InjectionKey> = Symbol.for('app:appConfig');\nexport const LoadingOverlayProvideKey: InjectionKey = Symbol.for('app:loadingOverlay');\nexport const BookingPageProvideKey: InjectionKey = Symbol.for('app:bookingPage');\n","import { PickupDropoffProps } from '@/Models/PickupDropoff';\nimport { IGeolocationService, IGeolocationServiceId } from '@/Services/IGeolocationService';\nimport { MapLocation, SetMapViewArgs } from '@/Models/MapLocation';\nimport { LatLngLiteral, LatLng } from 'leaflet';\nimport { ref, watch, inject } from 'vue';\nimport { useContainer } from '@/Plugins/inversify';\nimport { BookingPageProvideKey } from '@/InjectionKeys';\nimport LocationInput from '@/components/LocationInput.vue';\n\nexport interface Emits {\n (e: 'map:setView', value: SetMapViewArgs): void;\n (e: 'update:user-position', value: GeolocationPosition): void;\n (e: 'update:is-loading', value: boolean): void;\n}\n\nexport function usePickupDropoff(props: PickupDropoffProps, emit: Emits) {\n const container = useContainer();\n const geolocation = container.get(IGeolocationServiceId);\n const { map, center, zoom, bounds } = inject(BookingPageProvideKey);\n\n const locationInputRef = ref>();\n\n const mapLocation = ref(null);\n let skipSetCenter = false;\n let userMove = false;\n let timeout = null;\n\n const isValid = (isInServiceArea: boolean) => {\n if (!mapLocation.value) return false;\n\n const { address, location, place_id } = mapLocation.value;\n return !!address && !!location && !!place_id && isInServiceArea;\n };\n\n function addEventListeners() {\n if (!map.value?.leafletObject) return;\n\n // set up map event handlers\n map.value.leafletObject.on('movestart', onMapMoveStart);\n }\n\n function removeEventListeners() {\n if (!map.value?.leafletObject) return;\n\n // remove map event handlers\n map.value.leafletObject.off('movestart', onMapMoveStart);\n }\n\n function onInput() {\n setLoading(false);\n }\n\n function onInputUserSelected(value: MapLocation) {\n mapLocation.value = value;\n if (!value) return;\n\n // set skip address lookup flag\n skipSetCenter = true;\n mapZoomTo(value.location);\n }\n\n function setLocationInputCenter(value: LatLng | LatLngLiteral) {\n if (skipSetCenter) {\n skipSetCenter = false;\n setLoading(false);\n return;\n }\n\n if (locationInputRef.value) locationInputRef.value.setCoordinates(value);\n }\n\n function onMapMoveStart() {\n clearTimeout(timeout);\n\n if (!props.watchCenter) return;\n userMove = true;\n setLoading(true);\n }\n\n function onMapBoundsChanged() {\n clearTimeout(timeout);\n if (!userMove || !props.watchCenter) return;\n\n // set loading state to true to prevent the brief awkward moment while timeout is counting down\n if (locationInputRef.value) locationInputRef.value.setLoading(true);\n\n timeout = setTimeout(() => {\n userMove = false;\n setLocationInputCenter(center.value);\n\n // reset loading state\n if (locationInputRef.value) locationInputRef.value.setLoading(false);\n }, 800);\n }\n\n function onGeolocateInput(value: GeolocationPosition) {\n const { latitude: lat, longitude: lng } = value.coords;\n setLocationInputCenter({ lat, lng });\n afterOnGeolocateInput(value);\n }\n\n function setLoading(value: boolean) {\n onSetLoading(value);\n }\n\n async function mapZoomTo(location: LatLngLiteral, noMoveStart = true) {\n await onMapZoomTo(location, noMoveStart);\n }\n\n function afterOnGeolocateInput(value: GeolocationPosition) {\n const { latitude: lat, longitude: lng } = value.coords;\n emit('update:user-position', value);\n emit('map:setView', { center: { lat, lng }, zoom: 17, options: { animate: true } });\n }\n\n function onSetLoading(value: boolean) {\n emit('update:is-loading', value);\n }\n\n function onMapZoomTo(location: LatLngLiteral, noMoveStart = true) {\n return new Promise(resolve =>\n map.value.leafletObject.once('moveend zoomend', () => resolve()).setView(location, 17, { animate: true, noMoveStart })\n );\n }\n\n watch(\n () => map.value?.leafletObject,\n value => {\n if (value) {\n addEventListeners();\n } else {\n removeEventListeners();\n }\n },\n {\n immediate: true\n }\n );\n\n watch(bounds, onMapBoundsChanged, { deep: true });\n\n return {\n locationInputRef,\n isValid,\n mapLocation,\n onInput,\n onInputUserSelected,\n onGeolocateInput,\n mapZoomTo,\n setLocationInputCenter,\n geolocation,\n map,\n center,\n zoom,\n bounds\n };\n}\n","\n\n\n","\n\n\n","export enum TimeRuleDaysDto {\n Mondays = 1,\n Tuesdays = 2,\n Wednesdays = 3,\n Thursdays = 4,\n Fridays = 5,\n Saturdays = 6,\n Sundays = 7,\n Weekdays = 8,\n Weekends = 9,\n Everyday = 10\n}\n\nexport const AbstractDayTimeRuleToDays = {\n 8: [1, 2, 3, 4, 5],\n 9: [6, 7],\n 10: [1, 2, 3, 4, 5, 6, 7]\n};\n","import { Moment, unitOfTime } from 'moment';\nimport { AbstractDayTimeRuleToDays, TimeRuleDaysDto } from '@/EnumsDto/TimeRuleDaysDto';\nimport { ServiceTimeRulesDto } from '@/Dto/ServiceTimeRulesDto';\n\n/**\n * Tests the given value against an array of service time rules to determine its validity.\n *\n * @param value Moment instance to test\n * @param timeRules Array of ServiceTimeRulesDto\n * @param granularity Granularity level to apply when testing\n */\nexport function isWithinOperatingHours(value: Moment, timeRules: ServiceTimeRulesDto[], granularity?: unitOfTime.StartOf) {\n // check for invalid inputs\n if (!value || !timeRules) {\n return false;\n }\n\n // if no timeRules, return true\n if (timeRules.length === 0) {\n return true;\n }\n\n // we check against timeRules\n const day = value.isoWeekday();\n const midnight = value.clone().startOf('day');\n\n for (let i = 0; i < timeRules.length; i++) {\n const timeRule = timeRules[i];\n\n if (!timeRule) continue;\n\n // if we have an exact day match, or the day is included in AbstractDayTimeRuleToDays (i.e. Weekdays)\n if (\n timeRule.days === day ||\n (timeRule.days > TimeRuleDaysDto.Sundays && AbstractDayTimeRuleToDays[timeRule.days].indexOf(day) > -1)\n ) {\n // for days, we can stop here\n if (!granularity) {\n return true;\n }\n\n // else validate time with the given granularity\n const startTime = midnight.clone().add(timeRule.startTs, 's');\n const endTime = midnight.clone().add(timeRule.endTs, 's');\n\n if (value.isSameOrAfter(startTime, granularity) && value.isBefore(endTime, granularity)) {\n return true;\n }\n }\n }\n\n return false;\n}\n\n// for vee-validate\nexport default {\n validate(value: Moment, timeRules: ServiceTimeRulesDto[]) {\n return isWithinOperatingHours(value, timeRules, 'm');\n }\n};\n","import { Moment } from 'moment';\nimport IsWithinOperatingHours from '@/Validators/OperatingHoursValidators';\nimport { AbstractDayTimeRuleToDays, TimeRuleDaysDto } from '@/EnumsDto/TimeRuleDaysDto';\nimport { ServiceTimeRulesDto } from '@/Dto/ServiceTimeRulesDto';\n\n/**\n * Calculates the next valid time based on provided time rules.\n *\n * @param baseTime Moment value to use as base\n * @param timeRules Collection of time rules\n */\nexport function timeRulesToClosestMomentInFuture(baseTime: Moment, timeRules: ServiceTimeRulesDto[]): Moment {\n if (!timeRules || timeRules.length === 0) {\n return null;\n }\n\n const moments = [];\n\n // push all possible future days in [] of moments\n timeRules.forEach((timeRule: ServiceTimeRulesDto) => {\n const timeToCheckMidnight = baseTime.clone().startOf('day');\n\n // again not dealing with 1 to 1\n if (timeRule.days > 7) {\n // iterate and add possible days\n AbstractDayTimeRuleToDays[timeRule.days].forEach((dayOfWeek: TimeRuleDaysDto) => {\n const futureMoment = timeToCheckMidnight.clone().isoWeekday(dayOfWeek).add(timeRule.startTs, 'seconds');\n // if the created Moment is in the past, add a week\n if (futureMoment.isBefore(baseTime)) {\n futureMoment.add(1, 'week');\n }\n moments.push(futureMoment);\n });\n } else {\n const futureMoment = timeToCheckMidnight.isoWeekday(timeRule.days).add(timeRule.startTs, 'seconds');\n // if the created Moment is in the past, add a week\n if (futureMoment.isBefore(baseTime)) {\n futureMoment.add(1, 'week');\n }\n\n moments.push(futureMoment);\n }\n });\n\n // return the closest day/moment\n return moments.length === 0 ? null : moments.reduce((prev: Moment, curr: Moment) => (curr.isBefore(prev) ? curr : prev));\n}\n\n/**\n * Fetches the most relevant time rule.\n * Individual days have higher priority than generic collections. i.e. Monday > Weekdays\n *\n * @param timeToCheck Base Moment instance\n * @param timeRules Collection of time rules\n */\nexport function getMatchingTimeRule(timeToCheck: Moment, timeRules: ServiceTimeRulesDto[]): ServiceTimeRulesDto {\n if (!timeToCheck || !timeRules || timeRules.length === 0) return null;\n\n const valueDay = timeToCheck.isoWeekday();\n const midnight = timeToCheck.clone().startOf('day');\n const valueTime = timeToCheck.diff(midnight, 'seconds');\n\n const matchingRules = timeRules.filter(timeRule =>\n timeRule.days > 7\n ? AbstractDayTimeRuleToDays[timeRule.days].indexOf(valueDay) > -1 && valueTime >= timeRule.startTs && valueTime < timeRule.endTs\n : valueDay === timeRule.days && valueTime >= timeRule.startTs && valueTime < timeRule.endTs\n );\n\n return matchingRules.length === 0 ? null : matchingRules.reduce((prev, curr) => (curr.days < prev.days ? curr : prev));\n}\n\n/**\n * Validates the Moment instance against the given time rules,\n * and adjusts it if necessary.\n *\n * @param timeToCheck Moment value to validate and adjust\n * @param timeRules Collection of time rules\n */\nexport function adjustTimeWithinOperatingHours(timeToCheck: Moment, timeRules: ServiceTimeRulesDto[]): Moment {\n // if data is valid, return as-is\n if (!timeRules || timeRules.length === 0 || IsWithinOperatingHours.validate(timeToCheck, timeRules)) {\n return timeToCheck;\n }\n\n // timeToCheck values for timeRules\n const midnight = timeToCheck.clone().startOf('day');\n const valueTime = timeToCheck.diff(midnight, 'seconds');\n\n // check if any timerules match our day, filter array to the rules that are applicable\n const matchingRule = getMatchingTimeRule(timeToCheck, timeRules);\n\n if (matchingRule) {\n // the day fits fine, offer closest time to chosen one\n if (valueTime < matchingRule.startTs && valueTime < matchingRule.endTs) {\n // too early\n const secondsToAdd = matchingRule.startTs - valueTime;\n\n return timeToCheck.clone().add(secondsToAdd, 'seconds');\n } else {\n // too late, let's find a date in the future\n return timeRulesToClosestMomentInFuture(timeToCheck, timeRules);\n }\n } else {\n // no matching days, offer a day in the future\n return timeRulesToClosestMomentInFuture(timeToCheck, timeRules);\n }\n}\n","export enum RequestedPickupTsType {\n Now = 0,\n Specific\n}\n","\n\n\n\n\n","\n\n\n\n\n","\n\n\n\n\n","import moment from 'moment';\nimport { TimeRuleDaysDto } from '@/EnumsDto/TimeRuleDaysDto';\nimport i18n from '@/Plugins/i18n';\n\nconst MomentFilter = (value: number) => moment.unix(value);\nconst DateFilter = (value: moment.Moment) => value && value.format('DD.MM.YYYY');\nconst TimeFilter = (value: moment.Moment) => value && value.format('HH:mm');\nconst HumanizeFilter = (value: number, unit: moment.unitOfTime.DurationConstructor) => value && moment.duration(value, unit).humanize();\nconst DayFilter = (value: TimeRuleDaysDto) => i18n.global.t(`days.${TimeRuleDaysDto[value].toLowerCase()}`);\n\nexport { MomentFilter, DateFilter, TimeFilter, HumanizeFilter, DayFilter };\n","