From 027323919e93091d0305fc2470f304085d29e63c Mon Sep 17 00:00:00 2001 From: Edin Dazdarevic Date: Sat, 8 Apr 2017 02:11:54 +0200 Subject: [PATCH] Sorting & showing date --- backend/build/server.js | 1519 +++++++++++++++++++++++++++++++++++- backend/package.json | 1 + backend/server.js | 23 +- backend/yarn.lock | 4 + web/components/Listings.js | 56 +- web/components/Main.js | 6 +- web/lib/api.js | 5 +- web/lib/handlers.js | 12 +- 8 files changed, 1559 insertions(+), 67 deletions(-) diff --git a/backend/build/server.js b/backend/build/server.js index e0cf123..eb66a42 100644 --- a/backend/build/server.js +++ b/backend/build/server.js @@ -56,18 +56,24 @@ var _bodyParser2 = _interopRequireDefault(_bodyParser); + var _distance_in_words_to_now = __webpack_require__(3); + + var _distance_in_words_to_now2 = _interopRequireDefault(_distance_in_words_to_now); + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } - var MongoClient = __webpack_require__(3).MongoClient; - var ObjectID = __webpack_require__(3).ObjectID; + var hr = __webpack_require__(17); + + var MongoClient = __webpack_require__(20).MongoClient; + var ObjectID = __webpack_require__(20).ObjectID; var url = 'mongodb://localhost:27017/kivi'; - __webpack_require__(4); + __webpack_require__(21); var router = _express2.default.Router({ mergeParams: true }); @@ -124,7 +130,7 @@ router.get('/search/listings', function () { var _ref2 = _asyncToGenerator(regeneratorRuntime.mark(function _callee2(req, res, next) { - var bounds, minPrice, maxPrice, minSize, maxSize, rooms, adType, category, sort, page, pins, properties, query, _bounds$split$map, _bounds$split$map2, lat1, lng1, lat2, lng2, box, price, and, allRooms, or, size, allCategories, _or, cnt, all, isPins; + var bounds, minPrice, maxPrice, minSize, maxSize, rooms, adType, category, sort, page, pins, properties, query, _bounds$split$map, _bounds$split$map2, lat1, lng1, lat2, lng2, box, price, and, allRooms, or, size, allCategories, _or, cnt, getSort, all, isPins; return regeneratorRuntime.wrap(function _callee2$(_context2) { while (1) { @@ -177,6 +183,8 @@ price["$lte"] = parseFloat(maxPrice); } + price["$ne"] = "Po dogovoru"; + query = Object.assign(query, { price: price }); @@ -247,33 +255,46 @@ res.header('X-Total-Count', cnt); + getSort = function getSort() { + if (sort === 'price-min') { + return [['price', 'asc']]; + } else if (sort === 'price-max') { + return [['price', 'desc']]; + } else if (sort === 'newest') { + return [['_id', 'desc']]; + } else if (sort === 'relevance') { + // TODO: figure out what the relevance is + return []; + } + }; + all = properties.find(query, { //"sort": [['field1','asc'], ['field2','desc']] - "sort": [['price', 'asc']] + "sort": getSort() }); isPins = pins === "true"; if (isPins) { - _context2.next = 35; + _context2.next = 36; break; } - _context2.next = 32; + _context2.next = 33; return all.skip(20 * page).limit(20).toArray(); - case 32: + case 33: all = _context2.sent; - _context2.next = 38; + _context2.next = 39; break; - case 35: - _context2.next = 37; + case 36: + _context2.next = 38; return all.toArray(); - case 37: + case 38: all = _context2.sent; - case 38: + case 39: if (all.length > 0) { res.header('X-Last-Record-Id', [].concat(_toConsumableArray(all)).pop()._id); @@ -302,28 +323,28 @@ price: price, rooms: rooms, size: size, - time: time + time: (0, _distance_in_words_to_now2.default)(new Date(time), { locale: hr }) }; })); } res.end(); - _context2.next = 47; + _context2.next = 48; break; - case 43: - _context2.prev = 43; + case 44: + _context2.prev = 44; _context2.t0 = _context2['catch'](0); console.log('error:', _context2.t0); next(_context2.t0); - case 47: + case 48: case 'end': return _context2.stop(); } } - }, _callee2, undefined, [[0, 43]]); + }, _callee2, undefined, [[0, 44]]); })); return function (_x4, _x5, _x6) { @@ -367,12 +388,1470 @@ /***/ }, /* 3 */ +/***/ function(module, exports, __webpack_require__) { + + var distanceInWords = __webpack_require__(4) + + /** + * @category Common Helpers + * @summary Return the distance between the given date and now in words. + * + * @description + * Return the distance between the given date and now in words. + * + * | Distance to now | Result | + * |-------------------------------------------------------------------|---------------------| + * | 0 ... 30 secs | less than a minute | + * | 30 secs ... 1 min 30 secs | 1 minute | + * | 1 min 30 secs ... 44 mins 30 secs | [2..44] minutes | + * | 44 mins ... 30 secs ... 89 mins 30 secs | about 1 hour | + * | 89 mins 30 secs ... 23 hrs 59 mins 30 secs | about [2..24] hours | + * | 23 hrs 59 mins 30 secs ... 41 hrs 59 mins 30 secs | 1 day | + * | 41 hrs 59 mins 30 secs ... 29 days 23 hrs 59 mins 30 secs | [2..30] days | + * | 29 days 23 hrs 59 mins 30 secs ... 44 days 23 hrs 59 mins 30 secs | about 1 month | + * | 44 days 23 hrs 59 mins 30 secs ... 59 days 23 hrs 59 mins 30 secs | about 2 months | + * | 59 days 23 hrs 59 mins 30 secs ... 1 yr | [2..12] months | + * | 1 yr ... 1 yr 3 months | about 1 year | + * | 1 yr 3 months ... 1 yr 9 month s | over 1 year | + * | 1 yr 9 months ... 2 yrs | almost 2 years | + * | N yrs ... N yrs 3 months | about N years | + * | N yrs 3 months ... N yrs 9 months | over N years | + * | N yrs 9 months ... N+1 yrs | almost N+1 years | + * + * With `options.includeSeconds == true`: + * | Distance to now | Result | + * |---------------------|----------------------| + * | 0 secs ... 5 secs | less than 5 seconds | + * | 5 secs ... 10 secs | less than 10 seconds | + * | 10 secs ... 20 secs | less than 20 seconds | + * | 20 secs ... 40 secs | half a minute | + * | 40 secs ... 60 secs | less than a minute | + * | 60 secs ... 90 secs | 1 minute | + * + * @param {Date|String|Number} date - the given date + * @param {Object} [options] - the object with options + * @param {Boolean} [options.includeSeconds=false] - distances less than a minute are more detailed + * @param {Boolean} [options.addSuffix=false] - result specifies if the second date is earlier or later than the first + * @param {Object} [options.locale=enLocale] - the locale object + * @returns {String} the distance in words + * + * @example + * // If today is 1 January 2015, what is the distance to 2 July 2014? + * var result = distanceInWordsToNow( + * new Date(2014, 6, 2) + * ) + * //=> '6 months' + * + * @example + * // If now is 1 January 2015 00:00:00, + * // what is the distance to 1 January 2015 00:00:15, including seconds? + * var result = distanceInWordsToNow( + * new Date(2015, 0, 1, 0, 0, 15), + * {includeSeconds: true} + * ) + * //=> 'less than 20 seconds' + * + * @example + * // If today is 1 January 2015, + * // what is the distance to 1 January 2016, with a suffix? + * var result = distanceInWordsToNow( + * new Date(2016, 0, 1), + * {addSuffix: true} + * ) + * //=> 'in about 1 year' + * + * @example + * // If today is 1 January 2015, + * // what is the distance to 1 August 2016 in Esperanto? + * var eoLocale = require('date-fns/locale/eo') + * var result = distanceInWordsToNow( + * new Date(2016, 7, 1), + * {locale: eoLocale} + * ) + * //=> 'pli ol 1 jaro' + */ + function distanceInWordsToNow (dirtyDate, dirtyOptions) { + return distanceInWords(Date.now(), dirtyDate, dirtyOptions) + } + + module.exports = distanceInWordsToNow + + +/***/ }, +/* 4 */ +/***/ function(module, exports, __webpack_require__) { + + var compareDesc = __webpack_require__(5) + var parse = __webpack_require__(6) + var differenceInSeconds = __webpack_require__(8) + var differenceInMonths = __webpack_require__(10) + var enLocale = __webpack_require__(13) + + var MINUTES_IN_DAY = 1440 + var MINUTES_IN_ALMOST_TWO_DAYS = 2520 + var MINUTES_IN_MONTH = 43200 + var MINUTES_IN_TWO_MONTHS = 86400 + + /** + * @category Common Helpers + * @summary Return the distance between the given dates in words. + * + * @description + * Return the distance between the given dates in words. + * + * | Distance between dates | Result | + * |-------------------------------------------------------------------|---------------------| + * | 0 ... 30 secs | less than a minute | + * | 30 secs ... 1 min 30 secs | 1 minute | + * | 1 min 30 secs ... 44 mins 30 secs | [2..44] minutes | + * | 44 mins ... 30 secs ... 89 mins 30 secs | about 1 hour | + * | 89 mins 30 secs ... 23 hrs 59 mins 30 secs | about [2..24] hours | + * | 23 hrs 59 mins 30 secs ... 41 hrs 59 mins 30 secs | 1 day | + * | 41 hrs 59 mins 30 secs ... 29 days 23 hrs 59 mins 30 secs | [2..30] days | + * | 29 days 23 hrs 59 mins 30 secs ... 44 days 23 hrs 59 mins 30 secs | about 1 month | + * | 44 days 23 hrs 59 mins 30 secs ... 59 days 23 hrs 59 mins 30 secs | about 2 months | + * | 59 days 23 hrs 59 mins 30 secs ... 1 yr | [2..12] months | + * | 1 yr ... 1 yr 3 months | about 1 year | + * | 1 yr 3 months ... 1 yr 9 month s | over 1 year | + * | 1 yr 9 months ... 2 yrs | almost 2 years | + * | N yrs ... N yrs 3 months | about N years | + * | N yrs 3 months ... N yrs 9 months | over N years | + * | N yrs 9 months ... N+1 yrs | almost N+1 years | + * + * With `options.includeSeconds == true`: + * | Distance between dates | Result | + * |------------------------|----------------------| + * | 0 secs ... 5 secs | less than 5 seconds | + * | 5 secs ... 10 secs | less than 10 seconds | + * | 10 secs ... 20 secs | less than 20 seconds | + * | 20 secs ... 40 secs | half a minute | + * | 40 secs ... 60 secs | less than a minute | + * | 60 secs ... 90 secs | 1 minute | + * + * @param {Date|String|Number} dateToCompare - the date to compare with + * @param {Date|String|Number} date - the other date + * @param {Object} [options] - the object with options + * @param {Boolean} [options.includeSeconds=false] - distances less than a minute are more detailed + * @param {Boolean} [options.addSuffix=false] - result indicates if the second date is earlier or later than the first + * @param {Object} [options.locale=enLocale] - the locale object + * @returns {String} the distance in words + * + * @example + * // What is the distance between 2 July 2014 and 1 January 2015? + * var result = distanceInWords( + * new Date(2014, 6, 2), + * new Date(2015, 0, 1) + * ) + * //=> '6 months' + * + * @example + * // What is the distance between 1 January 2015 00:00:15 + * // and 1 January 2015 00:00:00, including seconds? + * var result = distanceInWords( + * new Date(2015, 0, 1, 0, 0, 15), + * new Date(2015, 0, 1, 0, 0, 0), + * {includeSeconds: true} + * ) + * //=> 'less than 20 seconds' + * + * @example + * // What is the distance from 1 January 2016 + * // to 1 January 2015, with a suffix? + * var result = distanceInWords( + * new Date(2016, 0, 1), + * new Date(2015, 0, 1), + * {addSuffix: true} + * ) + * //=> 'about 1 year ago' + * + * @example + * // What is the distance between 1 August 2016 and 1 January 2015 in Esperanto? + * var eoLocale = require('date-fns/locale/eo') + * var result = distanceInWords( + * new Date(2016, 7, 1), + * new Date(2015, 0, 1), + * {locale: eoLocale} + * ) + * //=> 'pli ol 1 jaro' + */ + function distanceInWords (dirtyDateToCompare, dirtyDate, dirtyOptions) { + var options = dirtyOptions || {} + + var comparison = compareDesc(dirtyDateToCompare, dirtyDate) + + var locale = options.locale + var localize = enLocale.distanceInWords.localize + if (locale && locale.distanceInWords && locale.distanceInWords.localize) { + localize = locale.distanceInWords.localize + } + + var localizeOptions = { + addSuffix: Boolean(options.addSuffix), + comparison: comparison + } + + var dateLeft, dateRight + if (comparison > 0) { + dateLeft = parse(dirtyDateToCompare) + dateRight = parse(dirtyDate) + } else { + dateLeft = parse(dirtyDate) + dateRight = parse(dirtyDateToCompare) + } + + var seconds = differenceInSeconds(dateRight, dateLeft) + var offset = dateRight.getTimezoneOffset() - dateLeft.getTimezoneOffset() + var minutes = Math.round(seconds / 60) - offset + var months + + // 0 up to 2 mins + if (minutes < 2) { + if (options.includeSeconds) { + if (seconds < 5) { + return localize('lessThanXSeconds', 5, localizeOptions) + } else if (seconds < 10) { + return localize('lessThanXSeconds', 10, localizeOptions) + } else if (seconds < 20) { + return localize('lessThanXSeconds', 20, localizeOptions) + } else if (seconds < 40) { + return localize('halfAMinute', null, localizeOptions) + } else if (seconds < 60) { + return localize('lessThanXMinutes', 1, localizeOptions) + } else { + return localize('xMinutes', 1, localizeOptions) + } + } else { + if (minutes === 0) { + return localize('lessThanXMinutes', 1, localizeOptions) + } else { + return localize('xMinutes', minutes, localizeOptions) + } + } + + // 2 mins up to 0.75 hrs + } else if (minutes < 45) { + return localize('xMinutes', minutes, localizeOptions) + + // 0.75 hrs up to 1.5 hrs + } else if (minutes < 90) { + return localize('aboutXHours', 1, localizeOptions) + + // 1.5 hrs up to 24 hrs + } else if (minutes < MINUTES_IN_DAY) { + var hours = Math.round(minutes / 60) + return localize('aboutXHours', hours, localizeOptions) + + // 1 day up to 1.75 days + } else if (minutes < MINUTES_IN_ALMOST_TWO_DAYS) { + return localize('xDays', 1, localizeOptions) + + // 1.75 days up to 30 days + } else if (minutes < MINUTES_IN_MONTH) { + var days = Math.round(minutes / MINUTES_IN_DAY) + return localize('xDays', days, localizeOptions) + + // 1 month up to 2 months + } else if (minutes < MINUTES_IN_TWO_MONTHS) { + months = Math.round(minutes / MINUTES_IN_MONTH) + return localize('aboutXMonths', months, localizeOptions) + } + + months = differenceInMonths(dateRight, dateLeft) + + // 2 months up to 12 months + if (months < 12) { + var nearestMonth = Math.round(minutes / MINUTES_IN_MONTH) + return localize('xMonths', nearestMonth, localizeOptions) + + // 1 year up to max Date + } else { + var monthsSinceStartOfYear = months % 12 + var years = Math.floor(months / 12) + + // N years up to 1 years 3 months + if (monthsSinceStartOfYear < 3) { + return localize('aboutXYears', years, localizeOptions) + + // N years 3 months up to N years 9 months + } else if (monthsSinceStartOfYear < 9) { + return localize('overXYears', years, localizeOptions) + + // N years 9 months up to N year 12 months + } else { + return localize('almostXYears', years + 1, localizeOptions) + } + } + } + + module.exports = distanceInWords + + +/***/ }, +/* 5 */ +/***/ function(module, exports, __webpack_require__) { + + var parse = __webpack_require__(6) + + /** + * @category Common Helpers + * @summary Compare the two dates reverse chronologically and return -1, 0 or 1. + * + * @description + * Compare the two dates and return -1 if the first date is after the second, + * 1 if the first date is before the second or 0 if dates are equal. + * + * @param {Date|String|Number} dateLeft - the first date to compare + * @param {Date|String|Number} dateRight - the second date to compare + * @returns {Number} the result of the comparison + * + * @example + * // Compare 11 February 1987 and 10 July 1989 reverse chronologically: + * var result = compareDesc( + * new Date(1987, 1, 11), + * new Date(1989, 6, 10) + * ) + * //=> 1 + * + * @example + * // Sort the array of dates in reverse chronological order: + * var result = [ + * new Date(1995, 6, 2), + * new Date(1987, 1, 11), + * new Date(1989, 6, 10) + * ].sort(compareDesc) + * //=> [ + * // Sun Jul 02 1995 00:00:00, + * // Mon Jul 10 1989 00:00:00, + * // Wed Feb 11 1987 00:00:00 + * // ] + */ + function compareDesc (dirtyDateLeft, dirtyDateRight) { + var dateLeft = parse(dirtyDateLeft) + var timeLeft = dateLeft.getTime() + var dateRight = parse(dirtyDateRight) + var timeRight = dateRight.getTime() + + if (timeLeft > timeRight) { + return -1 + } else if (timeLeft < timeRight) { + return 1 + } else { + return 0 + } + } + + module.exports = compareDesc + + +/***/ }, +/* 6 */ +/***/ function(module, exports, __webpack_require__) { + + var isDate = __webpack_require__(7) + + var MILLISECONDS_IN_HOUR = 3600000 + var MILLISECONDS_IN_MINUTE = 60000 + var DEFAULT_ADDITIONAL_DIGITS = 2 + + var parseTokenDateTimeDelimeter = /[T ]/ + var parseTokenPlainTime = /:/ + + // year tokens + var parseTokenYY = /^(\d{2})$/ + var parseTokensYYY = [ + /^([+-]\d{2})$/, // 0 additional digits + /^([+-]\d{3})$/, // 1 additional digit + /^([+-]\d{4})$/ // 2 additional digits + ] + + var parseTokenYYYY = /^(\d{4})/ + var parseTokensYYYYY = [ + /^([+-]\d{4})/, // 0 additional digits + /^([+-]\d{5})/, // 1 additional digit + /^([+-]\d{6})/ // 2 additional digits + ] + + // date tokens + var parseTokenMM = /^-(\d{2})$/ + var parseTokenDDD = /^-?(\d{3})$/ + var parseTokenMMDD = /^-?(\d{2})-?(\d{2})$/ + var parseTokenWww = /^-?W(\d{2})$/ + var parseTokenWwwD = /^-?W(\d{2})-?(\d{1})$/ + + // time tokens + var parseTokenHH = /^(\d{2}([.,]\d*)?)$/ + var parseTokenHHMM = /^(\d{2}):?(\d{2}([.,]\d*)?)$/ + var parseTokenHHMMSS = /^(\d{2}):?(\d{2}):?(\d{2}([.,]\d*)?)$/ + + // timezone tokens + var parseTokenTimezone = /([Z+-].*)$/ + var parseTokenTimezoneZ = /^(Z)$/ + var parseTokenTimezoneHH = /^([+-])(\d{2})$/ + var parseTokenTimezoneHHMM = /^([+-])(\d{2}):?(\d{2})$/ + + /** + * @category Common Helpers + * @summary Convert the given argument to an instance of Date. + * + * @description + * Convert the given argument to an instance of Date. + * + * If the argument is an instance of Date, the function returns its clone. + * + * If the argument is a number, it is treated as a timestamp. + * + * If an argument is a string, the function tries to parse it. + * Function accepts complete ISO 8601 formats as well as partial implementations. + * ISO 8601: http://en.wikipedia.org/wiki/ISO_8601 + * + * If all above fails, the function passes the given argument to Date constructor. + * + * @param {Date|String|Number} argument - the value to convert + * @param {Object} [options] - the object with options + * @param {0 | 1 | 2} [options.additionalDigits=2] - the additional number of digits in the extended year format + * @returns {Date} the parsed date in the local time zone + * + * @example + * // Convert string '2014-02-11T11:30:30' to date: + * var result = parse('2014-02-11T11:30:30') + * //=> Tue Feb 11 2014 11:30:30 + * + * @example + * // Parse string '+02014101', + * // if the additional number of digits in the extended year format is 1: + * var result = parse('+02014101', {additionalDigits: 1}) + * //=> Fri Apr 11 2014 00:00:00 + */ + function parse (argument, dirtyOptions) { + if (isDate(argument)) { + // Prevent the date to lose the milliseconds when passed to new Date() in IE10 + return new Date(argument.getTime()) + } else if (typeof argument !== 'string') { + return new Date(argument) + } + + var options = dirtyOptions || {} + var additionalDigits = options.additionalDigits + if (additionalDigits == null) { + additionalDigits = DEFAULT_ADDITIONAL_DIGITS + } else { + additionalDigits = Number(additionalDigits) + } + + var dateStrings = splitDateString(argument) + + var parseYearResult = parseYear(dateStrings.date, additionalDigits) + var year = parseYearResult.year + var restDateString = parseYearResult.restDateString + + var date = parseDate(restDateString, year) + + if (date) { + var timestamp = date.getTime() + var time = 0 + var offset + + if (dateStrings.time) { + time = parseTime(dateStrings.time) + } + + if (dateStrings.timezone) { + offset = parseTimezone(dateStrings.timezone) + } else { + // get offset accurate to hour in timezones that change offset + offset = new Date(timestamp + time).getTimezoneOffset() + offset = new Date(timestamp + time + offset * MILLISECONDS_IN_MINUTE).getTimezoneOffset() + } + + return new Date(timestamp + time + offset * MILLISECONDS_IN_MINUTE) + } else { + return new Date(argument) + } + } + + function splitDateString (dateString) { + var dateStrings = {} + var array = dateString.split(parseTokenDateTimeDelimeter) + var timeString + + if (parseTokenPlainTime.test(array[0])) { + dateStrings.date = null + timeString = array[0] + } else { + dateStrings.date = array[0] + timeString = array[1] + } + + if (timeString) { + var token = parseTokenTimezone.exec(timeString) + if (token) { + dateStrings.time = timeString.replace(token[1], '') + dateStrings.timezone = token[1] + } else { + dateStrings.time = timeString + } + } + + return dateStrings + } + + function parseYear (dateString, additionalDigits) { + var parseTokenYYY = parseTokensYYY[additionalDigits] + var parseTokenYYYYY = parseTokensYYYYY[additionalDigits] + + var token + + // YYYY or ±YYYYY + token = parseTokenYYYY.exec(dateString) || parseTokenYYYYY.exec(dateString) + if (token) { + var yearString = token[1] + return { + year: parseInt(yearString, 10), + restDateString: dateString.slice(yearString.length) + } + } + + // YY or ±YYY + token = parseTokenYY.exec(dateString) || parseTokenYYY.exec(dateString) + if (token) { + var centuryString = token[1] + return { + year: parseInt(centuryString, 10) * 100, + restDateString: dateString.slice(centuryString.length) + } + } + + // Invalid ISO-formatted year + return { + year: null + } + } + + function parseDate (dateString, year) { + // Invalid ISO-formatted year + if (year === null) { + return null + } + + var token + var date + var month + var week + + // YYYY + if (dateString.length === 0) { + date = new Date(0) + date.setUTCFullYear(year) + return date + } + + // YYYY-MM + token = parseTokenMM.exec(dateString) + if (token) { + date = new Date(0) + month = parseInt(token[1], 10) - 1 + date.setUTCFullYear(year, month) + return date + } + + // YYYY-DDD or YYYYDDD + token = parseTokenDDD.exec(dateString) + if (token) { + date = new Date(0) + var dayOfYear = parseInt(token[1], 10) + date.setUTCFullYear(year, 0, dayOfYear) + return date + } + + // YYYY-MM-DD or YYYYMMDD + token = parseTokenMMDD.exec(dateString) + if (token) { + date = new Date(0) + month = parseInt(token[1], 10) - 1 + var day = parseInt(token[2], 10) + date.setUTCFullYear(year, month, day) + return date + } + + // YYYY-Www or YYYYWww + token = parseTokenWww.exec(dateString) + if (token) { + week = parseInt(token[1], 10) - 1 + return dayOfISOYear(year, week) + } + + // YYYY-Www-D or YYYYWwwD + token = parseTokenWwwD.exec(dateString) + if (token) { + week = parseInt(token[1], 10) - 1 + var dayOfWeek = parseInt(token[2], 10) - 1 + return dayOfISOYear(year, week, dayOfWeek) + } + + // Invalid ISO-formatted date + return null + } + + function parseTime (timeString) { + var token + var hours + var minutes + + // hh + token = parseTokenHH.exec(timeString) + if (token) { + hours = parseFloat(token[1].replace(',', '.')) + return (hours % 24) * MILLISECONDS_IN_HOUR + } + + // hh:mm or hhmm + token = parseTokenHHMM.exec(timeString) + if (token) { + hours = parseInt(token[1], 10) + minutes = parseFloat(token[2].replace(',', '.')) + return (hours % 24) * MILLISECONDS_IN_HOUR + + minutes * MILLISECONDS_IN_MINUTE + } + + // hh:mm:ss or hhmmss + token = parseTokenHHMMSS.exec(timeString) + if (token) { + hours = parseInt(token[1], 10) + minutes = parseInt(token[2], 10) + var seconds = parseFloat(token[3].replace(',', '.')) + return (hours % 24) * MILLISECONDS_IN_HOUR + + minutes * MILLISECONDS_IN_MINUTE + + seconds * 1000 + } + + // Invalid ISO-formatted time + return null + } + + function parseTimezone (timezoneString) { + var token + var absoluteOffset + + // Z + token = parseTokenTimezoneZ.exec(timezoneString) + if (token) { + return 0 + } + + // ±hh + token = parseTokenTimezoneHH.exec(timezoneString) + if (token) { + absoluteOffset = parseInt(token[2], 10) * 60 + return (token[1] === '+') ? -absoluteOffset : absoluteOffset + } + + // ±hh:mm or ±hhmm + token = parseTokenTimezoneHHMM.exec(timezoneString) + if (token) { + absoluteOffset = parseInt(token[2], 10) * 60 + parseInt(token[3], 10) + return (token[1] === '+') ? -absoluteOffset : absoluteOffset + } + + return 0 + } + + function dayOfISOYear (isoYear, week, day) { + week = week || 0 + day = day || 0 + var date = new Date(0) + date.setUTCFullYear(isoYear, 0, 4) + var fourthOfJanuaryDay = date.getUTCDay() || 7 + var diff = week * 7 + day + 1 - fourthOfJanuaryDay + date.setUTCDate(date.getUTCDate() + diff) + return date + } + + module.exports = parse + + +/***/ }, +/* 7 */ +/***/ function(module, exports) { + + /** + * @category Common Helpers + * @summary Is the given argument an instance of Date? + * + * @description + * Is the given argument an instance of Date? + * + * @param {*} argument - the argument to check + * @returns {Boolean} the given argument is an instance of Date + * + * @example + * // Is 'mayonnaise' a Date? + * var result = isDate('mayonnaise') + * //=> false + */ + function isDate (argument) { + return argument instanceof Date + } + + module.exports = isDate + + +/***/ }, +/* 8 */ +/***/ function(module, exports, __webpack_require__) { + + var differenceInMilliseconds = __webpack_require__(9) + + /** + * @category Second Helpers + * @summary Get the number of seconds between the given dates. + * + * @description + * Get the number of seconds between the given dates. + * + * @param {Date|String|Number} dateLeft - the later date + * @param {Date|String|Number} dateRight - the earlier date + * @returns {Number} the number of seconds + * + * @example + * // How many seconds are between + * // 2 July 2014 12:30:07.999 and 2 July 2014 12:30:20.000? + * var result = differenceInSeconds( + * new Date(2014, 6, 2, 12, 30, 20, 0), + * new Date(2014, 6, 2, 12, 30, 7, 999) + * ) + * //=> 12 + */ + function differenceInSeconds (dirtyDateLeft, dirtyDateRight) { + var diff = differenceInMilliseconds(dirtyDateLeft, dirtyDateRight) / 1000 + return diff > 0 ? Math.floor(diff) : Math.ceil(diff) + } + + module.exports = differenceInSeconds + + +/***/ }, +/* 9 */ +/***/ function(module, exports, __webpack_require__) { + + var parse = __webpack_require__(6) + + /** + * @category Millisecond Helpers + * @summary Get the number of milliseconds between the given dates. + * + * @description + * Get the number of milliseconds between the given dates. + * + * @param {Date|String|Number} dateLeft - the later date + * @param {Date|String|Number} dateRight - the earlier date + * @returns {Number} the number of milliseconds + * + * @example + * // How many milliseconds are between + * // 2 July 2014 12:30:20.600 and 2 July 2014 12:30:21.700? + * var result = differenceInMilliseconds( + * new Date(2014, 6, 2, 12, 30, 21, 700), + * new Date(2014, 6, 2, 12, 30, 20, 600) + * ) + * //=> 1100 + */ + function differenceInMilliseconds (dirtyDateLeft, dirtyDateRight) { + var dateLeft = parse(dirtyDateLeft) + var dateRight = parse(dirtyDateRight) + return dateLeft.getTime() - dateRight.getTime() + } + + module.exports = differenceInMilliseconds + + +/***/ }, +/* 10 */ +/***/ function(module, exports, __webpack_require__) { + + var parse = __webpack_require__(6) + var differenceInCalendarMonths = __webpack_require__(11) + var compareAsc = __webpack_require__(12) + + /** + * @category Month Helpers + * @summary Get the number of full months between the given dates. + * + * @description + * Get the number of full months between the given dates. + * + * @param {Date|String|Number} dateLeft - the later date + * @param {Date|String|Number} dateRight - the earlier date + * @returns {Number} the number of full months + * + * @example + * // How many full months are between 31 January 2014 and 1 September 2014? + * var result = differenceInMonths( + * new Date(2014, 8, 1), + * new Date(2014, 0, 31) + * ) + * //=> 7 + */ + function differenceInMonths (dirtyDateLeft, dirtyDateRight) { + var dateLeft = parse(dirtyDateLeft) + var dateRight = parse(dirtyDateRight) + + var sign = compareAsc(dateLeft, dateRight) + var difference = Math.abs(differenceInCalendarMonths(dateLeft, dateRight)) + dateLeft.setMonth(dateLeft.getMonth() - sign * difference) + + // Math.abs(diff in full months - diff in calendar months) === 1 if last calendar month is not full + // If so, result must be decreased by 1 in absolute value + var isLastMonthNotFull = compareAsc(dateLeft, dateRight) === -sign + return sign * (difference - isLastMonthNotFull) + } + + module.exports = differenceInMonths + + +/***/ }, +/* 11 */ +/***/ function(module, exports, __webpack_require__) { + + var parse = __webpack_require__(6) + + /** + * @category Month Helpers + * @summary Get the number of calendar months between the given dates. + * + * @description + * Get the number of calendar months between the given dates. + * + * @param {Date|String|Number} dateLeft - the later date + * @param {Date|String|Number} dateRight - the earlier date + * @returns {Number} the number of calendar months + * + * @example + * // How many calendar months are between 31 January 2014 and 1 September 2014? + * var result = differenceInCalendarMonths( + * new Date(2014, 8, 1), + * new Date(2014, 0, 31) + * ) + * //=> 8 + */ + function differenceInCalendarMonths (dirtyDateLeft, dirtyDateRight) { + var dateLeft = parse(dirtyDateLeft) + var dateRight = parse(dirtyDateRight) + + var yearDiff = dateLeft.getFullYear() - dateRight.getFullYear() + var monthDiff = dateLeft.getMonth() - dateRight.getMonth() + + return yearDiff * 12 + monthDiff + } + + module.exports = differenceInCalendarMonths + + +/***/ }, +/* 12 */ +/***/ function(module, exports, __webpack_require__) { + + var parse = __webpack_require__(6) + + /** + * @category Common Helpers + * @summary Compare the two dates and return -1, 0 or 1. + * + * @description + * Compare the two dates and return 1 if the first date is after the second, + * -1 if the first date is before the second or 0 if dates are equal. + * + * @param {Date|String|Number} dateLeft - the first date to compare + * @param {Date|String|Number} dateRight - the second date to compare + * @returns {Number} the result of the comparison + * + * @example + * // Compare 11 February 1987 and 10 July 1989: + * var result = compareAsc( + * new Date(1987, 1, 11), + * new Date(1989, 6, 10) + * ) + * //=> -1 + * + * @example + * // Sort the array of dates: + * var result = [ + * new Date(1995, 6, 2), + * new Date(1987, 1, 11), + * new Date(1989, 6, 10) + * ].sort(compareAsc) + * //=> [ + * // Wed Feb 11 1987 00:00:00, + * // Mon Jul 10 1989 00:00:00, + * // Sun Jul 02 1995 00:00:00 + * // ] + */ + function compareAsc (dirtyDateLeft, dirtyDateRight) { + var dateLeft = parse(dirtyDateLeft) + var timeLeft = dateLeft.getTime() + var dateRight = parse(dirtyDateRight) + var timeRight = dateRight.getTime() + + if (timeLeft < timeRight) { + return -1 + } else if (timeLeft > timeRight) { + return 1 + } else { + return 0 + } + } + + module.exports = compareAsc + + +/***/ }, +/* 13 */ +/***/ function(module, exports, __webpack_require__) { + + var buildDistanceInWordsLocale = __webpack_require__(14) + var buildFormatLocale = __webpack_require__(15) + + /** + * @category Locales + * @summary English locale. + */ + module.exports = { + distanceInWords: buildDistanceInWordsLocale(), + format: buildFormatLocale() + } + + +/***/ }, +/* 14 */ +/***/ function(module, exports) { + + function buildDistanceInWordsLocale () { + var distanceInWordsLocale = { + lessThanXSeconds: { + one: 'less than a second', + other: 'less than {{count}} seconds' + }, + + xSeconds: { + one: '1 second', + other: '{{count}} seconds' + }, + + halfAMinute: 'half a minute', + + lessThanXMinutes: { + one: 'less than a minute', + other: 'less than {{count}} minutes' + }, + + xMinutes: { + one: '1 minute', + other: '{{count}} minutes' + }, + + aboutXHours: { + one: 'about 1 hour', + other: 'about {{count}} hours' + }, + + xHours: { + one: '1 hour', + other: '{{count}} hours' + }, + + xDays: { + one: '1 day', + other: '{{count}} days' + }, + + aboutXMonths: { + one: 'about 1 month', + other: 'about {{count}} months' + }, + + xMonths: { + one: '1 month', + other: '{{count}} months' + }, + + aboutXYears: { + one: 'about 1 year', + other: 'about {{count}} years' + }, + + xYears: { + one: '1 year', + other: '{{count}} years' + }, + + overXYears: { + one: 'over 1 year', + other: 'over {{count}} years' + }, + + almostXYears: { + one: 'almost 1 year', + other: 'almost {{count}} years' + } + } + + function localize (token, count, options) { + options = options || {} + + var result + if (typeof distanceInWordsLocale[token] === 'string') { + result = distanceInWordsLocale[token] + } else if (count === 1) { + result = distanceInWordsLocale[token].one + } else { + result = distanceInWordsLocale[token].other.replace('{{count}}', count) + } + + if (options.addSuffix) { + if (options.comparison > 0) { + return 'in ' + result + } else { + return result + ' ago' + } + } + + return result + } + + return { + localize: localize + } + } + + module.exports = buildDistanceInWordsLocale + + +/***/ }, +/* 15 */ +/***/ function(module, exports, __webpack_require__) { + + var buildFormattingTokensRegExp = __webpack_require__(16) + + function buildFormatLocale () { + // Note: in English, the names of days of the week and months are capitalized. + // If you are making a new locale based on this one, check if the same is true for the language you're working on. + // Generally, formatted dates should look like they are in the middle of a sentence, + // e.g. in Spanish language the weekdays and months should be in the lowercase. + var months3char = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] + var monthsFull = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'] + var weekdays2char = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'] + var weekdays3char = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'] + var weekdaysFull = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'] + var meridiemUppercase = ['AM', 'PM'] + var meridiemLowercase = ['am', 'pm'] + var meridiemFull = ['a.m.', 'p.m.'] + + var formatters = { + // Month: Jan, Feb, ..., Dec + 'MMM': function (date) { + return months3char[date.getMonth()] + }, + + // Month: January, February, ..., December + 'MMMM': function (date) { + return monthsFull[date.getMonth()] + }, + + // Day of week: Su, Mo, ..., Sa + 'dd': function (date) { + return weekdays2char[date.getDay()] + }, + + // Day of week: Sun, Mon, ..., Sat + 'ddd': function (date) { + return weekdays3char[date.getDay()] + }, + + // Day of week: Sunday, Monday, ..., Saturday + 'dddd': function (date) { + return weekdaysFull[date.getDay()] + }, + + // AM, PM + 'A': function (date) { + return (date.getHours() / 12) >= 1 ? meridiemUppercase[1] : meridiemUppercase[0] + }, + + // am, pm + 'a': function (date) { + return (date.getHours() / 12) >= 1 ? meridiemLowercase[1] : meridiemLowercase[0] + }, + + // a.m., p.m. + 'aa': function (date) { + return (date.getHours() / 12) >= 1 ? meridiemFull[1] : meridiemFull[0] + } + } + + // Generate ordinal version of formatters: M -> Mo, D -> Do, etc. + var ordinalFormatters = ['M', 'D', 'DDD', 'd', 'Q', 'W'] + ordinalFormatters.forEach(function (formatterToken) { + formatters[formatterToken + 'o'] = function (date, formatters) { + return ordinal(formatters[formatterToken](date)) + } + }) + + return { + formatters: formatters, + formattingTokensRegExp: buildFormattingTokensRegExp(formatters) + } + } + + function ordinal (number) { + var rem100 = number % 100 + if (rem100 > 20 || rem100 < 10) { + switch (rem100 % 10) { + case 1: + return number + 'st' + case 2: + return number + 'nd' + case 3: + return number + 'rd' + } + } + return number + 'th' + } + + module.exports = buildFormatLocale + + +/***/ }, +/* 16 */ +/***/ function(module, exports) { + + var commonFormatterKeys = [ + 'M', 'MM', 'Q', 'D', 'DD', 'DDD', 'DDDD', 'd', + 'E', 'W', 'WW', 'YY', 'YYYY', 'GG', 'GGGG', + 'H', 'HH', 'h', 'hh', 'm', 'mm', + 's', 'ss', 'S', 'SS', 'SSS', + 'Z', 'ZZ', 'X', 'x' + ] + + function buildFormattingTokensRegExp (formatters) { + var formatterKeys = [] + for (var key in formatters) { + if (formatters.hasOwnProperty(key)) { + formatterKeys.push(key) + } + } + + var formattingTokens = commonFormatterKeys + .concat(formatterKeys) + .sort() + .reverse() + var formattingTokensRegExp = new RegExp( + '(\\[[^\\[]*\\])|(\\\\)?' + '(' + formattingTokens.join('|') + '|.)', 'g' + ) + + return formattingTokensRegExp + } + + module.exports = buildFormattingTokensRegExp + + +/***/ }, +/* 17 */ +/***/ function(module, exports, __webpack_require__) { + + var buildDistanceInWordsLocale = __webpack_require__(18) + var buildFormatLocale = __webpack_require__(19) + + /** + * @category Locales + * @summary Croatian locale. + * @author Matija Marohnić [@silvenon]{@link https://github.com/silvenon} + */ + module.exports = { + distanceInWords: buildDistanceInWordsLocale(), + format: buildFormatLocale() + } + + +/***/ }, +/* 18 */ +/***/ function(module, exports) { + + function buildDistanceInWordsLocale () { + var distanceInWordsLocale = { + lessThanXSeconds: { + one: { + standalone: 'manje od 1 sekunde', + withPrepositionAgo: 'manje od 1 sekunde', + withPrepositionIn: 'manje od 1 sekundu' + }, + dual: 'manje od {{count}} sekunde', + other: 'manje od {{count}} sekundi' + }, + + xSeconds: { + one: { + standalone: '1 sekunda', + withPrepositionAgo: '1 sekunde', + withPrepositionIn: '1 sekundu' + }, + dual: '{{count}} sekunde', + other: '{{count}} sekundi' + }, + + halfAMinute: 'pola minute', + + lessThanXMinutes: { + one: { + standalone: 'manje od 1 minute', + withPrepositionAgo: 'manje od 1 minute', + withPrepositionIn: 'manje od 1 minutu' + }, + dual: 'manje od {{count}} minute', + other: 'manje od {{count}} minuta' + }, + + xMinutes: { + one: { + standalone: '1 minuta', + withPrepositionAgo: '1 minute', + withPrepositionIn: '1 minutu' + }, + dual: '{{count}} minute', + other: '{{count}} minuta' + }, + + aboutXHours: { + one: { + standalone: 'oko 1 sat', + withPrepositionAgo: 'oko 1 sat', + withPrepositionIn: 'oko 1 sat' + }, + dual: 'oko {{count}} sata', + other: 'oko {{count}} sati' + }, + + xHours: { + one: { + standalone: '1 sat', + withPrepositionAgo: '1 sat', + withPrepositionIn: '1 sat' + }, + dual: '{{count}} sata', + other: '{{count}} sati' + }, + + xDays: { + one: { + standalone: '1 dan', + withPrepositionAgo: '1 dan', + withPrepositionIn: '1 dan' + }, + dual: '{{count}} dana', + other: '{{count}} dana' + }, + + aboutXMonths: { + one: { + standalone: 'oko 1 mjesec', + withPrepositionAgo: 'oko 1 mjesec', + withPrepositionIn: 'oko 1 mjesec' + }, + dual: 'oko {{count}} mjeseca', + other: 'oko {{count}} mjeseci' + }, + + xMonths: { + one: { + standalone: '1 mjesec', + withPrepositionAgo: '1 mjesec', + withPrepositionIn: '1 mjesec' + }, + dual: '{{count}} mjeseca', + other: '{{count}} mjeseci' + }, + + aboutXYears: { + one: { + standalone: 'oko 1 godinu', + withPrepositionAgo: 'oko 1 godinu', + withPrepositionIn: 'oko 1 godinu' + }, + dual: 'oko {{count}} godine', + other: 'oko {{count}} godina' + }, + + xYears: { + one: { + standalone: '1 godina', + withPrepositionAgo: '1 godine', + withPrepositionIn: '1 godinu' + }, + dual: '{{count}} godine', + other: '{{count}} godina' + }, + + overXYears: { + one: { + standalone: 'preko 1 godinu', + withPrepositionAgo: 'preko 1 godinu', + withPrepositionIn: 'preko 1 godinu' + }, + dual: 'preko {{count}} godine', + other: 'preko {{count}} godina' + }, + + almostXYears: { + one: { + standalone: 'gotovo 1 godinu', + withPrepositionAgo: 'gotovo 1 godinu', + withPrepositionIn: 'gotovo 1 godinu' + }, + dual: 'gotovo {{count}} godine', + other: 'gotovo {{count}} godina' + } + } + + function localize (token, count, options) { + options = options || {} + + var result + + if (typeof distanceInWordsLocale[token] === 'string') { + result = distanceInWordsLocale[token] + } else if (count === 1) { + if (options.addSuffix) { + if (options.comparison > 0) { + result = distanceInWordsLocale[token].one.withPrepositionIn + } else { + result = distanceInWordsLocale[token].one.withPrepositionAgo + } + } else { + result = distanceInWordsLocale[token].one.standalone + } + } else if ( + count % 10 > 1 && count % 10 < 5 && // if last digit is between 2 and 4 + String(count).substr(-2, 1) !== '1' // unless the 2nd to last digit is "1" + ) { + result = distanceInWordsLocale[token].dual.replace('{{count}}', count) + } else { + result = distanceInWordsLocale[token].other.replace('{{count}}', count) + } + + if (options.addSuffix) { + if (options.comparison > 0) { + return 'za ' + result + } else { + return 'prije ' + result + } + } + + return result + } + + return { + localize: localize + } + } + + module.exports = buildDistanceInWordsLocale + + +/***/ }, +/* 19 */ +/***/ function(module, exports, __webpack_require__) { + + var buildFormattingTokensRegExp = __webpack_require__(16) + + function buildFormatLocale () { + var months3char = ['sij', 'velj', 'ožu', 'tra', 'svi', 'lip', 'srp', 'kol', 'ruj', 'lis', 'stu', 'pro'] + var monthsFull = ['siječanj', 'veljača', 'ožujak', 'travanj', 'svibanj', 'lipanj', 'srpanj', 'kolovoz', 'rujan', 'listopad', 'studeni', 'prosinac'] + var monthsGenitive = ['siječnja', 'veljače', 'ožujka', 'travnja', 'svibnja', 'lipnja', 'srpnja', 'kolovoza', 'rujna', 'listopada', 'studenog', 'prosinca'] + var weekdays2char = ['ne', 'po', 'ut', 'sr', 'če', 'pe', 'su'] + var weekdays3char = ['ned', 'pon', 'uto', 'sri', 'čet', 'pet', 'sub'] + var weekdaysFull = ['nedjelja', 'ponedjeljak', 'utorak', 'srijeda', 'četvrtak', 'petak', 'subota'] + var meridiemUppercase = ['ujutro', 'popodne'] + var meridiemLowercase = ['ujutro', 'popodne'] + var meridiemFull = ['ujutro', 'popodne'] + + var formatters = { + // Month: Jan, Feb, ..., Dec + 'MMM': function (date) { + return months3char[date.getMonth()] + }, + + // Month: January, February, ..., December + 'MMMM': function (date) { + return monthsFull[date.getMonth()] + }, + + // Day of week: Su, Mo, ..., Sa + 'dd': function (date) { + return weekdays2char[date.getDay()] + }, + + // Day of week: Sun, Mon, ..., Sat + 'ddd': function (date) { + return weekdays3char[date.getDay()] + }, + + // Day of week: Sunday, Monday, ..., Saturday + 'dddd': function (date) { + return weekdaysFull[date.getDay()] + }, + + // AM, PM + 'A': function (date) { + return (date.getHours() / 12) >= 1 ? meridiemUppercase[1] : meridiemUppercase[0] + }, + + // am, pm + 'a': function (date) { + return (date.getHours() / 12) >= 1 ? meridiemLowercase[1] : meridiemLowercase[0] + }, + + // a.m., p.m. + 'aa': function (date) { + return (date.getHours() / 12) >= 1 ? meridiemFull[1] : meridiemFull[0] + } + } + + // Generate ordinal version of formatters: M -> Mo, D -> Do, etc. + var ordinalFormatters = ['M', 'D', 'DDD', 'd', 'Q', 'W'] + ordinalFormatters.forEach(function (formatterToken) { + formatters[formatterToken + 'o'] = function (date, formatters) { + return ordinal(formatters[formatterToken](date)) + } + }) + + // Generate formatters like 'D MMMM', where the month is in the genitive case + var monthsGenitiveFormatters = ['D', 'Do', 'DD'] + monthsGenitiveFormatters.forEach(function (formatterToken) { + formatters[formatterToken + ' MMM'] = function (date, commonFormatters) { + var formatter = formatters[formatterToken] || commonFormatters[formatterToken] + return formatter(date, commonFormatters) + ' ' + monthsGenitive[date.getMonth()] + } + }) + + return { + formatters: formatters, + formattingTokensRegExp: buildFormattingTokensRegExp(formatters) + } + } + + function ordinal (number) { + return number + '.' + } + + module.exports = buildFormatLocale + + +/***/ }, +/* 20 */ /***/ function(module, exports) { module.exports = require("mongodb"); /***/ }, -/* 4 */ +/* 21 */ /***/ function(module, exports) { module.exports = require("babel-polyfill"); diff --git a/backend/package.json b/backend/package.json index 419f3c5..2bd5285 100644 --- a/backend/package.json +++ b/backend/package.json @@ -17,6 +17,7 @@ "babel-preset-es2017": "^6.22.0", "body-parser": "^1.17.1", "cookie-parser": "^1.4.3", + "date-fns": "^1.28.2", "express": "^4.15.2", "isomorphic-fetch": "^2.2.1", "mongodb": "^2.2.25" diff --git a/backend/server.js b/backend/server.js index 44a8385..e070446 100644 --- a/backend/server.js +++ b/backend/server.js @@ -1,5 +1,8 @@ import express from 'express' import bodyParser from 'body-parser'; +import distanceInWordsToNow from 'date-fns/distance_in_words_to_now'; +var hr = require('date-fns/locale/hr'); + var MongoClient = require('mongodb').MongoClient; var ObjectID = require('mongodb').ObjectID; @@ -140,9 +143,22 @@ router.get('/search/listings', async (req, res, next) => { res.header('X-Total-Count', cnt); + const getSort = () => { + if (sort === 'price-min') { + return [['price', 'asc']]; + } else if (sort === 'price-max') { + return [['price', 'desc']]; + } else if (sort === 'newest') { + return [['_id', 'desc']]; + } else if (sort === 'relevance') { + // TODO: figure out what the relevance is + return []; + } + }; + let all = properties.find(query, { //"sort": [['field1','asc'], ['field2','desc']] - "sort": [['price','asc']] + "sort": getSort() }); const isPins = pins === "true"; @@ -179,7 +195,10 @@ router.get('/search/listings', async (req, res, next) => { price, rooms, size, - time + time: distanceInWordsToNow( + new Date(time), + {locale: hr} + ) }))); } diff --git a/backend/yarn.lock b/backend/yarn.lock index 5826d15..4d839d6 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -562,6 +562,10 @@ core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" +date-fns: + version "1.28.2" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.28.2.tgz#19e4192d68875c0bf7c9537e3f296a8ec64853ef" + debug@^2.1.1, debug@^2.2.0, debug@2.6.3: version "2.6.3" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.3.tgz#0f7eb8c30965ec08c72accfa0130c8b79984141d" diff --git a/web/components/Listings.js b/web/components/Listings.js index f210c9a..0e6f8d9 100644 --- a/web/components/Listings.js +++ b/web/components/Listings.js @@ -11,29 +11,17 @@ export default class Listings extends React.Component { const node = e.target; const offset = node.scrollHeight - node.scrollTop - node.clientHeight; - //console.log('-----------------'); - //console.log('node.scrollHeight', node.scrollHeight); - //console.log('node.parentNode.scrollTop', node.scrollTop); - //console.log('node.parentNode.clientHeight', node.clientHeight); - if (this.props && this.props.loadingMore) { - - console.log('still loading!'); return; } - console.log('scrolling', node.scrollTop, node.scrollHeight, offset); if (offset < 50) { - - console.log('load more'); - //this.removeScrollListener(); this.props.dispatch({type: 'LOAD_MORE_LISTINGS'}); } } } onListingClick(id) { - loadListing(id).then(l => l.text()).then(l => { console.log('listing loaded', l); this.props.dispatch({type: 'VIEW_LISTING_DETAILS', action: { @@ -41,14 +29,6 @@ export default class Listings extends React.Component { listing: JSON.parse(l) }}); }); - - - //this.props.dispatch({ - //type: 'VIEW_LISTING_DETAILS', - //action: { - //id - //} - //}); } onMouseEnter (id) { @@ -60,17 +40,6 @@ export default class Listings extends React.Component { }); } - componentDidUpdate (prevProps) { - console.log('RECEIVING PROPS: ', prevProps, 'new are', this.props); - - //if (this.props.loadingMore != null) { - //if (!this.props.loadingMore && prevProps.loadingMore) { - //this.attachScrollListener(); - //console.log('ATTACHING AGAIN', this); - //} - //} - } - renderListings () { const {listings = (new Map())} = this.props; @@ -110,7 +79,7 @@ export default class Listings extends React.Component { {l.location} -
Prije 2 sata
+
Prije {l.time}
@@ -138,23 +107,30 @@ export default class Listings extends React.Component { listings.parentNode.removeEventListener('scroll', this.handleScroll); } - //componentDidUpdate() { - //console.log('componentDidUpdate'); - ////this.attachScrollListener(); - //} + onSortChange (e) { + this.props.dispatch({type: 'SORT_CHANGE', action: { + sort: e.target.value + }}); + } render () { - const {listings = (new Map()), totalCount} = this.props; + const {listings = (new Map()), totalCount, sort} = this.props; return (
- + + + +
diff --git a/web/components/Main.js b/web/components/Main.js index 188e41e..261e1e8 100644 --- a/web/components/Main.js +++ b/web/components/Main.js @@ -14,6 +14,7 @@ class Main extends React.Component { listings: (new Map()), imageIndex: 0, page: 0, + sort: 'relevance', filters: { rooms: {}, category: {} @@ -91,7 +92,6 @@ class Main extends React.Component { map.addListener('idle', () => { console.log('idle'); this.dispatch({type: 'MAP_IDLE'}); - //this.refreshListings(); }); } @@ -289,7 +289,8 @@ class Main extends React.Component { minPrice, maxPrice, category, - page: this.state.page + page: this.state.page, + sort: this.state.sort }); @@ -419,6 +420,7 @@ class Main extends React.Component { } else { children.push(); children.push( { const allRooms = Object .keys(rooms) @@ -31,7 +32,7 @@ export const loadProperties = ({ // TODO: handle errors //return fetch(process.env.API_URL + '/api/search', { - let url = `http://localhost:3001/api/search/listings?bounds=${bounds}&minPrice=${minPrice}&maxPrice=${maxPrice}&rooms=${allRooms}&minSize=${minSize}&maxSize=${maxSize}&category=${allCategories}&page=${page}&pins=${pins}` + let url = `http://localhost:3001/api/search/listings?bounds=${bounds}&minPrice=${minPrice}&maxPrice=${maxPrice}&rooms=${allRooms}&minSize=${minSize}&maxSize=${maxSize}&category=${allCategories}&page=${page}&pins=${pins}&sort=${sort}` return fetch(url, { //credentials: 'include' diff --git a/web/lib/handlers.js b/web/lib/handlers.js index e9dd6a1..2b6f5a4 100644 --- a/web/lib/handlers.js +++ b/web/lib/handlers.js @@ -246,6 +246,15 @@ const mapIdle = ({type, action}, component) => { }) } +const sortChange = ({type, action}, component) => { + component.setState({ + sort: action.sort, + page: 0 + }, () => { + component.refreshListings(); + }); +} + const handlers = { SET_MIN_PRICE: setMinPrice, SET_MAX_PRICE: setMaxPrice, @@ -265,7 +274,8 @@ const handlers = { BACK_TO_RESULTS: backToResults, LOAD_MORE_LISTINGS: loadMoreListings, MAP_IDLE: mapIdle, - PINS_LOADED: pinsLoaded + PINS_LOADED: pinsLoaded, + SORT_CHANGE: sortChange }; export const handleMessage = ({ type, action }, component) => {