diff --git a/assets/javascripts/discourse/controllers/admin-wizard.js.es6 b/assets/javascripts/discourse/controllers/admin-wizard.js.es6 index fd1bc147..af36539a 100644 --- a/assets/javascripts/discourse/controllers/admin-wizard.js.es6 +++ b/assets/javascripts/discourse/controllers/admin-wizard.js.es6 @@ -1,4 +1,5 @@ import { default as computed } from 'ember-addons/ember-computed-decorators'; +import showModal from 'discourse/lib/show-modal'; export default Ember.Controller.extend({ @computed('model.id', 'model.name') @@ -6,6 +7,12 @@ export default Ember.Controller.extend({ return window.location.origin + '/w/' + Ember.String.dasherize(wizardId); }, + @computed('model.after_time_scheduled') + nextSessionScheduledLabel(scheduled) { + return scheduled ? moment(scheduled).format('MMMM Do, HH:mm') : + I18n.t('admin.wizard.after_time_time_label'); + }, + actions: { save() { this.setProperties({ @@ -21,7 +28,6 @@ export default Ember.Controller.extend({ this.send("refreshWizard"); } }).catch((result) => { - console.log(result) this.set('saving', false); this.set('error', I18n.t(`admin.wizard.error.${result.error}`)); Ember.run.later(() => this.set('error', null), 10000); @@ -29,9 +35,21 @@ export default Ember.Controller.extend({ }, remove() { - this.get('model').remove().then(() => { + const wizard = this.get('model'); + wizard.remove().then(() => { this.send("refreshAllWizards"); }); - } + }, + + setNextSessionScheduled() { + let controller = showModal('next-session-scheduled', { + model: { + dateTime: this.get('model.after_time_scheduled'), + update: (dateTime) => this.set('model.after_time_scheduled', dateTime) + } + }); + + controller.setup(); + }, } }); diff --git a/assets/javascripts/discourse/controllers/next-session-scheduled.js.es6 b/assets/javascripts/discourse/controllers/next-session-scheduled.js.es6 new file mode 100644 index 00000000..7edec46d --- /dev/null +++ b/assets/javascripts/discourse/controllers/next-session-scheduled.js.es6 @@ -0,0 +1,56 @@ +import { default as computed } from 'ember-addons/ember-computed-decorators'; + +export default Ember.Controller.extend({ + title: 'admin.wizard.after_time_modal.title', + + setup() { + const dateTime = this.get('model.dateTime'); + const ROUNDING = 30 * 60 * 1000; + const nextInterval = moment(Math.ceil((+moment()) / ROUNDING) * ROUNDING); + const mDateTime = dateTime ? moment(dateTime) : nextInterval; + const mDateTimeLocal = mDateTime.local(); + const date = mDateTimeLocal.format('YYYY-MM-DD'); + const time = mDateTimeLocal.format('HH:mm'); + + this.setProperties({ date, time }); + + Ember.run.scheduleOnce('afterRender', this, () => { + const $timePicker = $("#time-picker"); + $timePicker.timepicker({ timeFormat: 'H:i' }); + $timePicker.timepicker('setTime', time); + $timePicker.change(() => this.set('time', $timePicker.val())); + }); + }, + + @computed('date', 'time') + dateTime: function(date, time) { + return moment(date + 'T' + time).format(); + }, + + @computed('dateTime') + submitDisabled(dateTime) { + return moment().isAfter(dateTime); + }, + + resetProperties() { + this.setProperties({ + date: null, + time: null + }); + }, + + actions: { + clear() { + this.resetProperties(); + this.get('model.update')(null); + }, + + submit() { + const dateTime = this.get('dateTime'); + const formatted = moment(dateTime).utc().toISOString(); + this.get('model.update')(formatted); + this.resetProperties(); + this.send("closeModal"); + } + } +}); diff --git a/assets/javascripts/discourse/helpers/custom-wizard.js.es6 b/assets/javascripts/discourse/helpers/custom-wizard.js.es6 index 7bc4100e..c761d9a0 100644 --- a/assets/javascripts/discourse/helpers/custom-wizard.js.es6 +++ b/assets/javascripts/discourse/helpers/custom-wizard.js.es6 @@ -1,6 +1,5 @@ import { registerUnbound } from 'discourse-common/lib/helpers'; registerUnbound('dasherize', function(string) { - console.log(string) return Ember.String.dasherize(string); }); diff --git a/assets/javascripts/discourse/initializers/custom-wizard-redirect.js.es6 b/assets/javascripts/discourse/initializers/custom-wizard-redirect.js.es6 new file mode 100644 index 00000000..7f6effae --- /dev/null +++ b/assets/javascripts/discourse/initializers/custom-wizard-redirect.js.es6 @@ -0,0 +1,15 @@ +export default { + name: "custom-wizard-redirect", + after: "message-bus", + + initialize: function (container) { + const messageBus = container.lookup('message-bus:main'); + + if (!messageBus) { return; } + + messageBus.subscribe("/redirect_to_wizard", function (wizardId) { + const wizardUrl = window.location.origin + '/w/' + wizardId; + window.location.href = wizardUrl; + }); + } +}; diff --git a/assets/javascripts/discourse/models/custom-wizard.js.es6 b/assets/javascripts/discourse/models/custom-wizard.js.es6 index f0561df2..6d9c0656 100644 --- a/assets/javascripts/discourse/models/custom-wizard.js.es6 +++ b/assets/javascripts/discourse/models/custom-wizard.js.es6 @@ -1,5 +1,16 @@ import { ajax } from 'discourse/lib/ajax'; +const wizardProperties = [ + 'name', + 'background', + 'save_submissions', + 'multiple_submissions', + 'after_signup', + 'after_time', + 'after_time_scheduled', + 'required' +]; + const CustomWizard = Discourse.Model.extend({ save() { return new Ember.RSVP.Promise((resolve, reject) => { @@ -8,14 +19,22 @@ const CustomWizard = Discourse.Model.extend({ let wizard = { id: id.underscore() }; + wizardProperties.forEach((p) => { + const value = this.get(p); + if (value) wizard[p] = value; + }); + + if (wizard['after_time'] && wizard['after_time_scheduled']) { + return reject({ error: 'after_time_need_time' }); + }; + const steps = this.get('steps'); if (steps.length > 0) { const stepsResult = this.buildSteps(steps); - console.log(stepsResult) if (stepsResult.error) { - reject({ error: stepsResult.error }) + reject({ error: stepsResult.error }); } else { - wizard['steps'] = stepsResult; + wizard['steps'] = stepsResult.steps; } } @@ -23,25 +42,12 @@ const CustomWizard = Discourse.Model.extend({ return reject({ error: 'steps_required' }); } - const name = this.get('name'); - if (name) wizard['name'] = name; - - const background = this.get('background'); - if (background) wizard['background'] = background; - - const save_submissions = this.get('save_submissions'); - if (save_submissions) wizard['save_submissions'] = save_submissions; - - const multiple_submissions = this.get('multiple_submissions'); - if (multiple_submissions) wizard['multiple_submissions'] = multiple_submissions; - ajax("/admin/wizards/custom/save", { type: 'PUT', data: { wizard: JSON.stringify(wizard) } }).then((result) => { - console.log(result) if (result.error) { reject(result); } else { @@ -86,10 +92,10 @@ const CustomWizard = Discourse.Model.extend({ if (f.type === 'dropdown') { const choices = f.choices; - //if ((!choices || choices.length < 1) && !f.choices_key && !f.choices_categories) { - //error = 'field.need_choices'; - //return; - //} + if ((!choices || choices.length < 1) && !f.choices_key && !f.choices_categories) { + error = 'field.need_choices'; + return; + } } delete f.isNew; @@ -166,10 +172,10 @@ CustomWizard.reopenClass({ if (w) { props['id'] = w.id; props['existingId'] = true; - props['name'] = w.name; - props['background'] = w.background; - props['save_submissions'] = w.save_submissions; - props['multiple_submissions'] = w.multiple_submissions; + + wizardProperties.forEach((p) => { + props[p] = w[p]; + }); if (w.steps && w.steps.length) { w.steps.forEach((s) => { @@ -226,6 +232,9 @@ CustomWizard.reopenClass({ props['background'] = ''; props['save_submissions'] = true; props['multiple_submissions'] = false; + props['after_signup'] = false; + props['after_time'] = false; + props['required'] = false; props['steps'] = Ember.A(); }; diff --git a/assets/javascripts/discourse/routes/admin-wizard-submissions.js.es6 b/assets/javascripts/discourse/routes/admin-wizard-submissions.js.es6 index 3650bc1c..59ee7fe0 100644 --- a/assets/javascripts/discourse/routes/admin-wizard-submissions.js.es6 +++ b/assets/javascripts/discourse/routes/admin-wizard-submissions.js.es6 @@ -9,7 +9,7 @@ export default Discourse.Route.extend({ }, setupController(controller, model) { - let fields = ['user_id', 'completed']; + let fields = ['user']; model.wizard.steps.forEach((s) => { if (s.fields) { diff --git a/assets/javascripts/discourse/templates/admin-wizard.hbs b/assets/javascripts/discourse/templates/admin-wizard.hbs index 555a32a7..1bbb6107 100644 --- a/assets/javascripts/discourse/templates/admin-wizard.hbs +++ b/assets/javascripts/discourse/templates/admin-wizard.hbs @@ -41,7 +41,7 @@ -
+

{{i18n 'admin.wizard.multiple_submissions'}}

@@ -51,6 +51,37 @@
+
+
+

{{i18n 'admin.wizard.required'}}

+
+
+ {{input type='checkbox' checked=model.required}} + {{i18n 'admin.wizard.required_label'}} +
+
+ +
+
+

{{i18n 'admin.wizard.after_signup'}}

+
+
+ {{input type='checkbox' checked=model.after_signup}} + {{i18n 'admin.wizard.after_signup_label'}} +
+
+ +
+
+

{{i18n 'admin.wizard.after_time'}}

+
+
+ {{input type='checkbox' checked=model.after_time}} + {{i18n 'admin.wizard.after_time_label'}} + {{d-button action='setNextSessionScheduled' translatedLabel=nextSessionScheduledLabel icon='calendar-o'}} +
+
+

{{i18n 'admin.wizard.url'}}

diff --git a/assets/javascripts/discourse/templates/modal/next-session-scheduled.hbs b/assets/javascripts/discourse/templates/modal/next-session-scheduled.hbs new file mode 100644 index 00000000..36c08a3f --- /dev/null +++ b/assets/javascripts/discourse/templates/modal/next-session-scheduled.hbs @@ -0,0 +1,24 @@ +{{#d-modal-body class="next-session-time-modal" title=title}} +
+ +
+
+{{/d-modal-body}} + + diff --git a/assets/javascripts/wizard/controllers/custom-step.js.es6 b/assets/javascripts/wizard/controllers/custom-step.js.es6 index 9dc4f3ce..e273ef7b 100644 --- a/assets/javascripts/wizard/controllers/custom-step.js.es6 +++ b/assets/javascripts/wizard/controllers/custom-step.js.es6 @@ -19,14 +19,6 @@ export default StepController.extend({ showMessage(message) { this.set('stepMessage', message); - }, - - finished(result) { - let url = "/"; - if (result.topic_id) { - url += `t/${result.topic_id}`; - } - window.location.href = getUrl(url); } } }); diff --git a/assets/javascripts/wizard/initializers/custom.js.es6 b/assets/javascripts/wizard/initializers/custom.js.es6 index 10340365..37a67e02 100644 --- a/assets/javascripts/wizard/initializers/custom.js.es6 +++ b/assets/javascripts/wizard/initializers/custom.js.es6 @@ -32,6 +32,12 @@ export default { }); WizardStep.reopen({ + showQuitButton: function() { + const index = this.get('step.index'); + const required = this.get('wizard.required'); + return index === 0 && !required; + }.property('step.index', 'wizard.required'), + bannerImage: function() { const src = this.get('step.banner'); if (!src) return; @@ -53,7 +59,7 @@ export default { this.get('step').save() .then(response => { if (this.get('finalStep')) { - this.sendAction('finished', response); + this.get('wizard').finished(response); } else { this.sendAction('goNext', response); } @@ -64,8 +70,12 @@ export default { actions: { quit() { - this.set('finalStep', true); - this.send('nextStep'); + if ($(event.target).hasClass('quit')) { + this.get('wizard').skip(); + } else { + this.set('finalStep', true); + this.send('nextStep'); + }; }, showMessage(message) { diff --git a/assets/javascripts/wizard/models/custom.js.es6 b/assets/javascripts/wizard/models/custom.js.es6 index 021ad1ce..8e49d58c 100644 --- a/assets/javascripts/wizard/models/custom.js.es6 +++ b/assets/javascripts/wizard/models/custom.js.es6 @@ -1,11 +1,28 @@ import { default as computed } from 'ember-addons/ember-computed-decorators'; +import getUrl from 'discourse-common/lib/get-url'; import WizardField from 'wizard/models/wizard-field'; import { ajax } from 'wizard/lib/ajax'; import Step from 'wizard/models/step'; const CustomWizard = Ember.Object.extend({ @computed('steps.length') - totalSteps: length => length + totalSteps: length => length, + + skip() { + if (this.get('required')) return; + const id = this.get('id'); + ajax({ url: `/w/${id}/skip`, type: 'PUT' }).then((result) => { + this.finished(result); + }); + }, + + finished(result) { + let url = "/"; + if (result.redirect_to) { + url = result.redirect_to; + } + window.location.href = getUrl(url); + } }); export function findCustomWizard(wizardId) { diff --git a/assets/lib/jquery.timepicker.min.js b/assets/lib/jquery.timepicker.min.js new file mode 100644 index 00000000..6a7b5954 --- /dev/null +++ b/assets/lib/jquery.timepicker.min.js @@ -0,0 +1,7 @@ +/*! + * jquery-timepicker v1.11.11 - A jQuery timepicker plugin inspired by Google Calendar. It supports both mouse and keyboard navigation. + * Copyright (c) 2017 Jon Thornton - http://jonthornton.github.com/jquery-timepicker/ + * License: MIT + */ + +!function(a){"object"==typeof exports&&exports&&"object"==typeof module&&module&&module.exports===exports?a(require("jquery")):"function"==typeof define&&define.amd?define(["jquery"],a):a(jQuery)}(function(a){function b(a){var b=a[0];return b.offsetWidth>0&&b.offsetHeight>0}function c(b){if(b.minTime&&(b.minTime=t(b.minTime)),b.maxTime&&(b.maxTime=t(b.maxTime)),b.durationTime&&"function"!=typeof b.durationTime&&(b.durationTime=t(b.durationTime)),"now"==b.scrollDefault)b.scrollDefault=function(){return b.roundingFunction(t(new Date),b)};else if(b.scrollDefault&&"function"!=typeof b.scrollDefault){var c=b.scrollDefault;b.scrollDefault=function(){return b.roundingFunction(t(c),b)}}else b.minTime&&(b.scrollDefault=function(){return b.roundingFunction(b.minTime,b)});if("string"===a.type(b.timeFormat)&&b.timeFormat.match(/[gh]/)&&(b._twelveHourTime=!0),b.showOnFocus===!1&&-1!=b.showOn.indexOf("focus")&&b.showOn.splice(b.showOn.indexOf("focus"),1),b.disableTimeRanges.length>0){for(var d in b.disableTimeRanges)b.disableTimeRanges[d]=[t(b.disableTimeRanges[d][0]),t(b.disableTimeRanges[d][1])];b.disableTimeRanges=b.disableTimeRanges.sort(function(a,b){return a[0]-b[0]});for(var d=b.disableTimeRanges.length-1;d>0;d--)b.disableTimeRanges[d][0]<=b.disableTimeRanges[d-1][1]&&(b.disableTimeRanges[d-1]=[Math.min(b.disableTimeRanges[d][0],b.disableTimeRanges[d-1][0]),Math.max(b.disableTimeRanges[d][1],b.disableTimeRanges[d-1][1])],b.disableTimeRanges.splice(d,1))}return b}function d(b){var c=b.data("timepicker-settings"),d=b.data("timepicker-list");if(d&&d.length&&(d.remove(),b.data("timepicker-list",!1)),c.useSelect){d=a("