From 6156253a9df2755bc29a3c43bc74c2ebaae7e754 Mon Sep 17 00:00:00 2001 From: Louis Date: Sun, 22 Apr 2018 14:36:45 +0200 Subject: [PATCH 1/2] require node 8 to use async/await instead of promises --- package.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/package.json b/package.json index 38de2d9..33bc18a 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,9 @@ "start": "nodemon ./bin/www", "web": "node ./bin/www" }, + "engines": { + "node": ">=8.0" + }, "dependencies": { "airtable": "^0.4.5", "autoprefixer": "^6.7.7", From 663d7d824513189bc1a4fa870d0d2746aaaddf59 Mon Sep 17 00:00:00 2001 From: Louis Date: Sun, 22 Apr 2018 16:59:38 +0200 Subject: [PATCH 2/2] fix indentation: some files use tabs, other spaces. All should be set to two spaces #8 --- app.js | 38 ++-- helpers/airtable.js | 8 +- helpers/bedsCounter.js | 50 +++--- helpers/dateFormatter.js | 76 ++++---- mangrove_retreat.textClipping | Bin 231 -> 0 bytes middleware/auth.js | 70 ++++---- middleware/charge.js | 58 +++---- middleware/faq.js | 60 +++---- middleware/participants.js | 316 +++++++++++++++++----------------- middleware/price.js | 212 +++++++++++------------ middleware/retreat.js | 150 ++++++++-------- middleware/weather.js | 30 ++-- public/javascripts/main.js | 308 ++++++++++++++++----------------- public/javascripts/stripe.js | 52 +++--- 14 files changed, 714 insertions(+), 714 deletions(-) delete mode 100644 mangrove_retreat.textClipping diff --git a/app.js b/app.js index 5afdb46..35bebc5 100644 --- a/app.js +++ b/app.js @@ -25,7 +25,7 @@ var app = express() // session cookies app.use(cookieSession({ - name: 'mangrove-retreat-session', + name: 'mangrove-retreat-session', keys: _.filter([process.env.SECRET_KEY, 'M@ngR0ve']), maxAge: 30 * 24 * 3600 * 1000 // 30 days })) @@ -54,17 +54,17 @@ app.use(bodyParser.urlencoded({ extended: false })) app.use(cookieParser()) app.use('/stylesheets', sassMiddleware({ - src: __dirname + '/public/sass', - dest: __dirname + '/public/stylesheets', - debug: true, - outputStyle: 'expanded' + src: __dirname + '/public/sass', + dest: __dirname + '/public/stylesheets', + debug: true, + outputStyle: 'expanded' })) app.use('/stylesheets', postcssMiddleware({ - src: function(req) { - return path.join(__dirname, 'public', 'stylesheets', req.path) - }, - plugins: [autoprefixer()] + src: function(req) { + return path.join(__dirname, 'public', 'stylesheets', req.path) + }, + plugins: [autoprefixer()] })) app.use(express.static(path.join(__dirname, 'public'))) @@ -73,25 +73,25 @@ app.use('/', index) // catch 404 and forward to error handler app.use(function(req, res, next) { - var err = new Error('Not Found') - err.status = 404 - next(err) + var err = new Error('Not Found') + err.status = 404 + next(err) }) // error handler app.use(function(err, req, res, next) { - // set locals, only providing error in development - const isDev = req.app.get('env') === 'development' + // set locals, only providing error in development + const isDev = req.app.get('env') === 'development' - // render the error page - res.status(err.status || 500) - res.render('error', {error: isDev ? err : null}) + // render the error page + res.status(err.status || 500) + res.render('error', {error: isDev ? err : null}) }) // proper logging of UnhandledPromiseRejection process.on('unhandledRejection', function(reason, p) { - console.log("Possibly Unhandled Rejection at: Promise ", - p, " reason: ", reason, reason ? reason.message : null) + console.log("Possibly Unhandled Rejection at: Promise ", + p, " reason: ", reason, reason ? reason.message : null) }) module.exports = app diff --git a/helpers/airtable.js b/helpers/airtable.js index 11ae27c..b141e5d 100644 --- a/helpers/airtable.js +++ b/helpers/airtable.js @@ -2,8 +2,8 @@ var Airtable = require('airtable') var base = new Airtable({apiKey: process.env.AIRTABLE_API_KEY}).base(process.env.AIRTABLE_BASE_KEY) module.exports = { - retreat: base('Retreats'), - participants: base('Retreat Participants'), - faq: base('Retreat FAQ'), - members: base('Members') + retreat: base('Retreats'), + participants: base('Retreat Participants'), + faq: base('Retreat FAQ'), + members: base('Members') } diff --git a/helpers/bedsCounter.js b/helpers/bedsCounter.js index effed26..2b5c658 100644 --- a/helpers/bedsCounter.js +++ b/helpers/bedsCounter.js @@ -1,35 +1,35 @@ function haveDaysInCommon(days1, days2) { - for (var i = 0; i < days1.length; i++) { - var day1 = days1[i] - for (var j = 0; j < days2.length; j++) { - var day2 = days2[j] - if (day1.id === day2.id) { - return true - } - } - } - return false + for (var i = 0; i < days1.length; i++) { + var day1 = days1[i] + for (var j = 0; j < days2.length; j++) { + var day2 = days2[j] + if (day1.id === day2.id) { + return true + } + } + } + return false } function addBedsCountPerWeek(retreat, participants) { - for (var i = 0; i < retreat.weeks.length; i++) { - var bedsAvailable = retreat.house.beds - var week = retreat.weeks[i] + for (var i = 0; i < retreat.weeks.length; i++) { + var bedsAvailable = retreat.house.beds + var week = retreat.weeks[i] - // Remove a bed each time a participant is found in the week - for (var j = 0; j < participants.length; j++) { - var participant = participants[j] - if (haveDaysInCommon(week.days, participant.days)) { - bedsAvailable -= 1 - } - } + // Remove a bed each time a participant is found in the week + for (var j = 0; j < participants.length; j++) { + var participant = participants[j] + if (haveDaysInCommon(week.days, participant.days)) { + bedsAvailable -= 1 + } + } - // Add beds count to week - retreat.weeks[i].beds = bedsAvailable - } - return retreat + // Add beds count to week + retreat.weeks[i].beds = bedsAvailable + } + return retreat } module.exports = { - addBedsCountPerWeek: addBedsCountPerWeek + addBedsCountPerWeek: addBedsCountPerWeek } diff --git a/helpers/dateFormatter.js b/helpers/dateFormatter.js index 179e876..f034654 100644 --- a/helpers/dateFormatter.js +++ b/helpers/dateFormatter.js @@ -1,51 +1,51 @@ function formatDay(date) { - const weekDays = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'] - const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Nov', 'Dec'] - return { - id: months[date.getMonth()] + date.getDate(), - name: weekDays[date.getDay()], - number: date.getDate(), - month: months[date.getMonth()], - date: date - } + const weekDays = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'] + const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Nov', 'Dec'] + return { + id: months[date.getMonth()] + date.getDate(), + name: weekDays[date.getDay()], + number: date.getDate(), + month: months[date.getMonth()], + date: date + } } function formatWeeks(beginDate, endDate) { - var days = [] - var day = new Date(beginDate) - - while(day.getTime() < new Date(endDate).getTime()) { - days.push(formatDay(day)) - day = new Date(day.getTime() + 24 * 60 * 60 * 1000) - } - days.push(formatDay(new Date(endDate))) - - var weeks = [] - for (var i = 0; i < days.length; i++) { - if (i % 7 == 0) { - weeks.push({days: []}) - } - var weekIndex = parseInt(i / 7) - weeks[weekIndex].days.push(days[i]) - } - - return weeks + var days = [] + var day = new Date(beginDate) + + while(day.getTime() < new Date(endDate).getTime()) { + days.push(formatDay(day)) + day = new Date(day.getTime() + 24 * 60 * 60 * 1000) + } + days.push(formatDay(new Date(endDate))) + + var weeks = [] + for (var i = 0; i < days.length; i++) { + if (i % 7 == 0) { + weeks.push({days: []}) + } + var weekIndex = parseInt(i / 7) + weeks[weekIndex].days.push(days[i]) + } + + return weeks } function formatDays(beginDate, endDate) { - var days = [] - var day = new Date(beginDate) + var days = [] + var day = new Date(beginDate) - while(day.getTime() < new Date(endDate).getTime()) { - days.push(formatDay(day)) - day = new Date(day.getTime() + 24 * 60 * 60 * 1000) - } - days.push(formatDay(new Date(endDate))) + while(day.getTime() < new Date(endDate).getTime()) { + days.push(formatDay(day)) + day = new Date(day.getTime() + 24 * 60 * 60 * 1000) + } + days.push(formatDay(new Date(endDate))) - return days + return days } module.exports = { - formatDays: formatDays, - formatWeeks: formatWeeks + formatDays: formatDays, + formatWeeks: formatWeeks } diff --git a/mangrove_retreat.textClipping b/mangrove_retreat.textClipping deleted file mode 100644 index b3b5b90023e4a878cfe733c58f6cdda04d0c28ba..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 231 zcmYc)$jK}&F)+Bu$P^J8;;HMBSdw^|nT3^&og-dAB{@G=FR`E?CsnVcC^03oBr`uR zF-JEyF 1) { - description += 's' - } - description += ' for ' + retreat.get('Name') - return description - } - } - - function getPricesForCustomStay() { - const timeDifference = lastNight.getTime() - firstNight.getTime() - const numberOfDays = Math.ceil(timeDifference / (1000 * 3600 * 24)) + 1 - const pricePerNight = retreat.get('Price Per Night') - - return { - price: pricePerNight * numberOfDays, - discount: 0, - canBook: false, - description: '' - } - } - }) + return new Promise(function (resolve, reject) { + firstNight = new Date(params.firstNight) + lastNight = new Date(params.lastNight) + const retreatId = params.retreatId + if (typeof firstNight === 'undefined' || typeof lastNight === 'undefined' + || retreatId === 'undefined') { + reject('Params are not correct. You should send retreatId, firstNight and lastNight') + return + } + + + getRetreat(retreatId).then(function () { + if (isFullWeekStay()) { + resolve(getPricesForFullWeekStay()) + } else { + resolve(getPricesForCustomStay()) + } + }) + + function getRetreat(retreatId) { + return new Promise(function (resolve, reject) { + airtable.retreat.find(retreatId, function(err, record) { + if (err) { reject(err); return } + retreat = record + resolve() + }) + }) + } + + function isFullWeekStay() { + const weeks = dateFormatter.formatWeeks(retreat.get('First Night'), retreat.get('Last Night')) + var firstNightMatches = false + var isFullWeek = false + + for (var i = 0; i < weeks.length; i++) { + var week = weeks[i] + if (week.days[0].date.isSameDateAs(firstNight)) { + firstNightMatches = true + } + + if (firstNightMatches && week.days[week.days.length - 1].date.isSameDateAs(lastNight)) { + isFullWeek = true + } + } + + return isFullWeek + } + + function getPricesForFullWeekStay() { + const weeksCount = getWeeksCount() + + return { + price: retreat.get('Price Per Week') * weeksCount, + discount: retreat.get('Week Discount') * weeksCount, + canBook: true, + description: getDescription(weeksCount) + } + + function getWeeksCount() { + const weeks = dateFormatter.formatWeeks(retreat.get('First Night'), retreat.get('Last Night')) + var firstNightWeekIndex = -1 + var lastNightWeekIndex = -1 + + for (var i = 0; i < weeks.length; i++) { + var week = weeks[i] + if (week.days[0].date.isSameDateAs(firstNight)) { + firstNightWeekIndex = i + } + + if (week.days[week.days.length - 1].date.isSameDateAs(lastNight)) { + lastNightWeekIndex = i + } + } + + return (1 + lastNightWeekIndex - firstNightWeekIndex) + } + + function getDescription(weeksCount) { + var description = 'Booking ' + weeksCount + ' week' + if (weeksCount > 1) { + description += 's' + } + description += ' for ' + retreat.get('Name') + return description + } + } + + function getPricesForCustomStay() { + const timeDifference = lastNight.getTime() - firstNight.getTime() + const numberOfDays = Math.ceil(timeDifference / (1000 * 3600 * 24)) + 1 + const pricePerNight = retreat.get('Price Per Night') + + return { + price: pricePerNight * numberOfDays, + discount: 0, + canBook: false, + description: '' + } + } + }) } module.exports = { - compute: compute + compute: compute } diff --git a/middleware/retreat.js b/middleware/retreat.js index 3cd9316..30bcaee 100644 --- a/middleware/retreat.js +++ b/middleware/retreat.js @@ -5,14 +5,14 @@ var dateFormatter = require('../helpers/dateFormatter.js') var weather = require('./weather.js') function getRetreat(slug) { - return new Promise(function (resolve, reject) { - airtable.retreat.select({ - maxRecords: 1, - filterByFormula: `{Slug}='${slug}'`, - }).firstPage(function(err, records) { - if (err) return reject(err) - // TODO: properly handle when no ongoing retreat in Airtable - if (!records.length) return reject("found no ongoing retreats on Airtable") + return new Promise(function (resolve, reject) { + airtable.retreat.select({ + maxRecords: 1, + filterByFormula: `{Slug}='${slug}'`, + }).firstPage(function(err, records) { + if (err) return reject(err) + // TODO: properly handle when no ongoing retreat in Airtable + if (!records.length) return reject("found no ongoing retreats on Airtable") resolve(records[0]) }) }) @@ -43,37 +43,37 @@ function getRetreat(slug) { } function getOrganizer(retreat) { - return new Promise(function (resolve, reject) { - const organizerId = retreat.get('Organizer')[0] - if (typeof organizerId !== 'undefined') { - airtable.retreat.find(organizerId, function(err, organizer) { - if (err) { reject(err); return } - resolve(formatOrganizer(organizer)) - }) - } - }) + return new Promise(function (resolve, reject) { + const organizerId = retreat.get('Organizer')[0] + if (typeof organizerId !== 'undefined') { + airtable.retreat.find(organizerId, function(err, organizer) { + if (err) { reject(err); return } + resolve(formatOrganizer(organizer)) + }) + } + }) - function formatOrganizer(organizer) { - return { - id: organizer.id, - name: organizer.get('Name'), - username: organizer.get('Slack Handle') - } - } + function formatOrganizer(organizer) { + return { + id: organizer.id, + name: organizer.get('Name'), + username: organizer.get('Slack Handle') + } + } } function getCurrent() { return new Promise(function (resolve, reject) { - airtable.retreat.select({ + airtable.retreat.select({ // TODO: this fetches the 10 most recent retreats which is not technically // guaranteed to include the one closest to today, but looks good enough - maxRecords: 10, + maxRecords: 10, sort: [{field: 'First Night', direction: 'desc'}], - }).firstPage(function(err, records) { - if (err) return reject(err) + }).firstPage(function(err, records) { + if (err) return reject(err) if (!records.length) return reject(`no retreats found on Airtable`) - // pick retreat closest to today + // pick retreat closest to today const now = moment() const closest = _.first(_.orderBy(records, function(r) { return Math.abs(now.diff(r.get('First Night'))) @@ -85,62 +85,62 @@ function getCurrent() { function formatRetreat(retreat) { - return { - id: retreat.id, + return { + id: retreat.id, slug: retreat.get('Slug'), - weeks: dateFormatter.formatWeeks(retreat.get('First Night'), retreat.get('Last Night')), - name: retreat.get('Name'), - description: retreat.get('Description'), - channel: retreat.get('Channel'), - house : formatHouse(retreat), - price: formatPrice(retreat), - totalPrice: retreat.get('Total Price'), + weeks: dateFormatter.formatWeeks(retreat.get('First Night'), retreat.get('Last Night')), + name: retreat.get('Name'), + description: retreat.get('Description'), + channel: retreat.get('Channel'), + house : formatHouse(retreat), + price: formatPrice(retreat), + totalPrice: retreat.get('Total Price'), generated: retreat.get('Generated'), - location: formatLocation(retreat), - } + location: formatLocation(retreat), + } - function formatHouse(retreat) { - return { - url: retreat.get('House Url'), - beds: retreat.get('Beds'), - pictures: formatPictures(retreat.get('Pictures')), - rentPrice: retreat.get('House Rent Price') - } + function formatHouse(retreat) { + return { + url: retreat.get('House Url'), + beds: retreat.get('Beds'), + pictures: formatPictures(retreat.get('Pictures')), + rentPrice: retreat.get('House Rent Price') + } - function formatPictures(pictures) { - var formattedPictures = [] + function formatPictures(pictures) { + var formattedPictures = [] - if (typeof pictures !== 'undefined') { - for (var i = 0; i < pictures.length; i++) { - var picture = pictures[i] - formattedPictures.push(picture.url) - } - } + if (typeof pictures !== 'undefined') { + for (var i = 0; i < pictures.length; i++) { + var picture = pictures[i] + formattedPictures.push(picture.url) + } + } - return formattedPictures - } - } + return formattedPictures + } + } - function formatPrice(retreat) { - return { - perWeek: retreat.get('Price Per Week'), - perNight: retreat.get('Price Per Night'), - weekDiscount: retreat.get('Week Discount') - } - } + function formatPrice(retreat) { + return { + perWeek: retreat.get('Price Per Week'), + perNight: retreat.get('Price Per Night'), + weekDiscount: retreat.get('Week Discount') + } + } - function formatLocation(retreat) { - return { - latitude: retreat.get('Latitude'), - longitude: retreat.get('Longitude'), - fullAddress: retreat.get('Address'), - city: retreat.get('City'), - country: retreat.get('Country') - } - } + function formatLocation(retreat) { + return { + latitude: retreat.get('Latitude'), + longitude: retreat.get('Longitude'), + fullAddress: retreat.get('Address'), + city: retreat.get('City'), + country: retreat.get('Country') + } + } } module.exports = { - get: getRetreat, + get: getRetreat, getCurrent, } diff --git a/middleware/weather.js b/middleware/weather.js index 704e65b..acd5f1c 100644 --- a/middleware/weather.js +++ b/middleware/weather.js @@ -2,23 +2,23 @@ var request = require('request') var moment = require('moment-timezone'); function getTemperatureAndLocalTime(latitude, longitude) { - return new Promise(function (resolve, reject) { + return new Promise(function (resolve, reject) { if (!latitude || !longitude) return reject(`missing latitude and/or longitude`) - const url = 'https://api.darksky.net/forecast/0fe81fed15cafca7534a3b3d7e921229/' - + latitude + ',' + longitude + '?exclude=minutely,hourly,daily,alerts,flags&units=si' - request.get(url, function (error, response, body) { - if (!error && response.statusCode == 200) { - resolve({ - temperature: JSON.parse(body).currently.temperature.toFixed(1), - localTime: moment().tz(JSON.parse(body).timezone).format('HH:mm') - }) - } else { - reject(error || `status ${response.statusCode} ${body}`) - } - }) - }) + const url = 'https://api.darksky.net/forecast/0fe81fed15cafca7534a3b3d7e921229/' + + latitude + ',' + longitude + '?exclude=minutely,hourly,daily,alerts,flags&units=si' + request.get(url, function (error, response, body) { + if (!error && response.statusCode == 200) { + resolve({ + temperature: JSON.parse(body).currently.temperature.toFixed(1), + localTime: moment().tz(JSON.parse(body).timezone).format('HH:mm') + }) + } else { + reject(error || `status ${response.statusCode} ${body}`) + } + }) + }) } module.exports = { - getTemperatureAndLocalTime: getTemperatureAndLocalTime + getTemperatureAndLocalTime: getTemperatureAndLocalTime } diff --git a/public/javascripts/main.js b/public/javascripts/main.js index 6b4c863..e908f1f 100644 --- a/public/javascripts/main.js +++ b/public/javascripts/main.js @@ -9,192 +9,192 @@ $('body').on('click', function() { var organiserUsername = $('#retreat_organizer_username').val() var stripeParams = { - key: config.stripePublishableKey, - amount: 2000, - description: 'Retreat' + key: config.stripePublishableKey, + amount: 2000, + description: 'Retreat' } $('.booking-board-row').on('click', function(){ - $(this).toggleClass('is-active') + $(this).toggleClass('is-active') }) $('.booking-board-footer.step-1 .booking-board-btn').on('click', function(){ - $(this).unbind('click') - - var selectedDaysIds = $(this).data('days').split(' ').slice(0, -1) - computePrice($(this), selectedDaysIds) - var firstDaySelected = false - var lastDaySelected = false - $(this).addClass('is-active') - $(this).find('span').html('Pay $200') - $('.booking-board-footer').removeClass('step-1').addClass('step-2') - for (var i = 0, len = selectedDaysIds.length; i < len; i++) { - $('#' + selectedDaysIds[i]).addClass('is-active') - } - - var divs = $('.booking-board-days-selector .day-item.is-active') - var index = 0; - - var delay = setInterval( function(){ - if ( index <= divs.length ){ - $( divs[ index ] ).addClass( 'is-visible' ); - index += 1; - }else{ - clearInterval( delay ); - } - }, i * 2); - - $('.booking-board-days-selector .day-item').on('click', function() { - if (lastDaySelected) { - $('.booking-board-days-selector .day-item').removeClass('is-active') - } - if($('.booking-board-days-selector .day-item').hasClass('is-active')) { - var divs = [] - - if($('.booking-board-days-selector .day-item.is-active').attr('id') == $(this).attr('id')) return - - selectedDaysIds = []; - selectedDaysIds.push($(this).attr('id')) - selectedDaysIds.push($('.booking-board-days-selector .day-item.is-active').attr('id')) - - if(isBefore($('.booking-board-days-selector .day-item.is-active'), $(this))) { - divs = $(this).prevUntil($('.booking-board-days-selector .day-item.is-active')).addClass('is-active is-visible') - } else { - divs = $(this).nextUntil($('.booking-board-days-selector .day-item.is-active')).addClass('is-active is-visible') - } - for (var i = 0, len = divs.length; i < len; i++) { - selectedDaysIds.push(divs[i].getAttribute('id')) - } - $(this).addClass('is-active is-visible') - - lastDaySelected = true - firstDaySelected = false - computePrice($('.booking-board-btn.is-active'), selectedDaysIds) - } else { - $(this).addClass('is-active is-visible') - firstDaySelected = true - lastDaySelected = false - } - - }) - - $('.booking-board-days-selector').on('mouseenter', function() { - if(!firstDaySelected) { - $('.booking-board-days-selector .day-item').removeClass('is-active') - } - }).mouseleave(function(){ - if(!firstDaySelected) { - lastDaySelected = false - - $('.booking-board-days-selector .day-item').removeClass('is-active') - for (var i = 0, len = selectedDaysIds.length; i < len; i++) { - $('#' + selectedDaysIds[i]).addClass('is-active is-visible') - } - - } - }) - - $(this).on('click', function() { - if($(this).hasClass('can-book')) { - openStripeCheckout(stripeParams, function(token) { - selectedDays = datify(selectedDaysIds) - postStripeTokenAndInfos(token, config.retreatId, selectedDays[0], selectedDays[selectedDays.length - 1]) - }) - } else { - // 👦 need le slack id du retreat organiser, dynamiquement (ci dessous @ben en brut: U1NK4E3QE) - window.open('slack://user?team=T0QJH8NJK&id=U1NK4E3QE') - } - }) + $(this).unbind('click') + + var selectedDaysIds = $(this).data('days').split(' ').slice(0, -1) + computePrice($(this), selectedDaysIds) + var firstDaySelected = false + var lastDaySelected = false + $(this).addClass('is-active') + $(this).find('span').html('Pay $200') + $('.booking-board-footer').removeClass('step-1').addClass('step-2') + for (var i = 0, len = selectedDaysIds.length; i < len; i++) { + $('#' + selectedDaysIds[i]).addClass('is-active') + } + + var divs = $('.booking-board-days-selector .day-item.is-active') + var index = 0; + + var delay = setInterval( function(){ + if ( index <= divs.length ){ + $( divs[ index ] ).addClass( 'is-visible' ); + index += 1; + }else{ + clearInterval( delay ); + } + }, i * 2); + + $('.booking-board-days-selector .day-item').on('click', function() { + if (lastDaySelected) { + $('.booking-board-days-selector .day-item').removeClass('is-active') + } + if($('.booking-board-days-selector .day-item').hasClass('is-active')) { + var divs = [] + + if($('.booking-board-days-selector .day-item.is-active').attr('id') == $(this).attr('id')) return + + selectedDaysIds = []; + selectedDaysIds.push($(this).attr('id')) + selectedDaysIds.push($('.booking-board-days-selector .day-item.is-active').attr('id')) + + if(isBefore($('.booking-board-days-selector .day-item.is-active'), $(this))) { + divs = $(this).prevUntil($('.booking-board-days-selector .day-item.is-active')).addClass('is-active is-visible') + } else { + divs = $(this).nextUntil($('.booking-board-days-selector .day-item.is-active')).addClass('is-active is-visible') + } + for (var i = 0, len = divs.length; i < len; i++) { + selectedDaysIds.push(divs[i].getAttribute('id')) + } + $(this).addClass('is-active is-visible') + + lastDaySelected = true + firstDaySelected = false + computePrice($('.booking-board-btn.is-active'), selectedDaysIds) + } else { + $(this).addClass('is-active is-visible') + firstDaySelected = true + lastDaySelected = false + } + + }) + + $('.booking-board-days-selector').on('mouseenter', function() { + if(!firstDaySelected) { + $('.booking-board-days-selector .day-item').removeClass('is-active') + } + }).mouseleave(function(){ + if(!firstDaySelected) { + lastDaySelected = false + + $('.booking-board-days-selector .day-item').removeClass('is-active') + for (var i = 0, len = selectedDaysIds.length; i < len; i++) { + $('#' + selectedDaysIds[i]).addClass('is-active is-visible') + } + + } + }) + + $(this).on('click', function() { + if($(this).hasClass('can-book')) { + openStripeCheckout(stripeParams, function(token) { + selectedDays = datify(selectedDaysIds) + postStripeTokenAndInfos(token, config.retreatId, selectedDays[0], selectedDays[selectedDays.length - 1]) + }) + } else { + // 👦 need le slack id du retreat organiser, dynamiquement (ci dessous @ben en brut: U1NK4E3QE) + window.open('slack://user?team=T0QJH8NJK&id=U1NK4E3QE') + } + }) }) function computePrice(that, selectedDays) { - that.addClass('is-loading') - $('.booking-board-footer-title').addClass('is-loading') + that.addClass('is-loading') + $('.booking-board-footer-title').addClass('is-loading') - selectedDays = datify(selectedDays) + selectedDays = datify(selectedDays) - $.post('/' + config.retreatSlug + '/computeprice', - { + $.post('/' + config.retreatSlug + '/computeprice', + { retreatId: config.retreatId, - firstNight: selectedDays[0], - lastNight: selectedDays[selectedDays.length - 1] - }, function(data){ - that.removeClass('is-loading') - $('.booking-board-footer-title').removeClass('is-loading') - - stripeParams.amount = data.price - stripeParams.description = data.description - var text - if(data.canBook) { - text = 'Pay ' + data.price + "€" - that.addClass('can-book') - that.removeClass('redirect-to-slack') - } else { - text = "DM " + organiserUsername - that.removeClass('can-book') - that.addClass('redirect-to-slack') - - } - that.find('span').text(text) - $('.booking-board-footer-title .data-item-header').html(data.price + '€') - $('.booking-board-footer-title .data-item-footer').html(selectedDays.length + ' nights') - }) + firstNight: selectedDays[0], + lastNight: selectedDays[selectedDays.length - 1] + }, function(data){ + that.removeClass('is-loading') + $('.booking-board-footer-title').removeClass('is-loading') + + stripeParams.amount = data.price + stripeParams.description = data.description + var text + if(data.canBook) { + text = 'Pay ' + data.price + "€" + that.addClass('can-book') + that.removeClass('redirect-to-slack') + } else { + text = "DM " + organiserUsername + that.removeClass('can-book') + that.addClass('redirect-to-slack') + + } + that.find('span').text(text) + $('.booking-board-footer-title .data-item-header').html(data.price + '€') + $('.booking-board-footer-title .data-item-footer').html(selectedDays.length + ' nights') + }) } function datify(array) { - array = array.map(function(item, i) { - return $('#' + item).data('date') - }) + array = array.map(function(item, i) { + return $('#' + item).data('date') + }) - array = array.sort(function(a,b) { - return new Date(a).getTime() - new Date(b).getTime() - }); + array = array.sort(function(a,b) { + return new Date(a).getTime() - new Date(b).getTime() + }); - return array + return array } function isBefore(el1, el2) { - return el1.nextAll().filter(el2).length !== 0; + return el1.nextAll().filter(el2).length !== 0; } var touch = 'ontouchstart' in document.documentElement - || navigator.maxTouchPoints > 0 - || navigator.msMaxTouchPoints > 0; + || navigator.maxTouchPoints > 0 + || navigator.msMaxTouchPoints > 0; if (touch) { // remove all :hover stylesheets - try { // prevent exception on browsers not supporting DOM styleSheets properly - for (var si in document.styleSheets) { - var styleSheet = document.styleSheets[si]; - if (!styleSheet.rules) continue; - - for (var ri = styleSheet.rules.length - 1; ri >= 0; ri--) { - if (!styleSheet.rules[ri].selectorText) continue; - - if (styleSheet.rules[ri].selectorText.match(':hover')) { - styleSheet.deleteRule(ri); - } - } - } - } catch (ex) {} + try { // prevent exception on browsers not supporting DOM styleSheets properly + for (var si in document.styleSheets) { + var styleSheet = document.styleSheets[si]; + if (!styleSheet.rules) continue; + + for (var ri = styleSheet.rules.length - 1; ri >= 0; ri--) { + if (!styleSheet.rules[ri].selectorText) continue; + + if (styleSheet.rules[ri].selectorText.match(':hover')) { + styleSheet.deleteRule(ri); + } + } + } + } catch (ex) {} } var ink, d, x, y; $(".ripplelink").click(function(e){ - if($(this).find(".ink").length === 0){ - $(this).prepend(""); - } + if($(this).find(".ink").length === 0){ + $(this).prepend(""); + } - ink = $(this).find(".ink"); - ink.removeClass("animate"); + ink = $(this).find(".ink"); + ink.removeClass("animate"); - if(!ink.height() && !ink.width()){ - d = Math.max($(this).outerWidth(), $(this).outerHeight()); - ink.css({height: d, width: d}); - } + if(!ink.height() && !ink.width()){ + d = Math.max($(this).outerWidth(), $(this).outerHeight()); + ink.css({height: d, width: d}); + } - x = e.pageX - $(this).offset().left - ink.width()/2; - y = e.pageY - $(this).offset().top - ink.height()/2; + x = e.pageX - $(this).offset().left - ink.width()/2; + y = e.pageY - $(this).offset().top - ink.height()/2; - ink.css({top: y+'px', left: x+'px'}).addClass("animate"); + ink.css({top: y+'px', left: x+'px'}).addClass("animate"); }) diff --git a/public/javascripts/stripe.js b/public/javascripts/stripe.js index 6e1cb61..6ca2a21 100644 --- a/public/javascripts/stripe.js +++ b/public/javascripts/stripe.js @@ -1,32 +1,32 @@ function openStripeCheckout(stripeParams, tokenCallback) { - var handler = StripeCheckout.configure({ - key: stripeParams.key, - image: '/images/mangrove_logo.png', - locale: 'auto', - token: tokenCallback - }) + var handler = StripeCheckout.configure({ + key: stripeParams.key, + image: '/images/mangrove_logo.png', + locale: 'auto', + token: tokenCallback + }) - // Open Checkout with further options: - handler.open({ - name: 'Mangrove', - description: stripeParams.description, - currency: 'eur', - amount: stripeParams.amount * 100 - }) + // Open Checkout with further options: + handler.open({ + name: 'Mangrove', + description: stripeParams.description, + currency: 'eur', + amount: stripeParams.amount * 100 + }) } function postStripeTokenAndInfos(token, retreatId, firstNight, lastNight) { - $.post('/' + config.retreatSlug + '/charge', { - tokenId: token.id, - email: token.email, - retreatId: retreatId, - firstNight: firstNight, - lastNight: lastNight - }, function(data) { - if (data.success) { - window.location = '/booked' - } else { - window.location = '/booking_error?error=' + data.error - } - }) + $.post('/' + config.retreatSlug + '/charge', { + tokenId: token.id, + email: token.email, + retreatId: retreatId, + firstNight: firstNight, + lastNight: lastNight + }, function(data) { + if (data.success) { + window.location = '/booked' + } else { + window.location = '/booking_error?error=' + data.error + } + }) }