summaryrefslogtreecommitdiff
path: root/addons/survey/static/src
diff options
context:
space:
mode:
Diffstat (limited to 'addons/survey/static/src')
-rw-r--r--addons/survey/static/src/js/survey_form.js198
-rw-r--r--addons/survey/static/src/js/survey_quick_access.js66
-rw-r--r--addons/survey/static/src/js/survey_result.js16
-rw-r--r--addons/survey/static/src/js/survey_session_manage.js323
-rw-r--r--addons/survey/static/src/js/survey_timer.js3
-rw-r--r--addons/survey/static/src/scss/survey_form.scss189
6 files changed, 729 insertions, 66 deletions
diff --git a/addons/survey/static/src/js/survey_form.js b/addons/survey/static/src/js/survey_form.js
index 6c51b0e2d8c..f270949903d 100644
--- a/addons/survey/static/src/js/survey_form.js
+++ b/addons/survey/static/src/js/survey_form.js
@@ -33,8 +33,9 @@ publicWidget.registry.SurveyFormWidget = publicWidget.Widget.extend({
this.fadeInOutDelay = 400;
return this._super.apply(this, arguments).then(function () {
self.options = self.$target.find('form').data();
+ self.readonly = self.options.readonly;
// Init fields
- if (!self.options.isStartScreen) {
+ if (!self.options.isStartScreen && !self.readonly) {
self._initTimer();
self._initBreadcrumb();
}
@@ -45,9 +46,14 @@ publicWidget.registry.SurveyFormWidget = publicWidget.Widget.extend({
self._initTextArea();
self._focusOnFirstInput();
// Init event listener
- if (!self.options.readonly) {
+ if (!self.readonly) {
$(document).on('keypress', self._onKeyPress.bind(self));
}
+ if (self.options.sessionInProgress &&
+ (self.options.isStartScreen || self.options.hasAnswered)) {
+ self.preventEnterSubmit = true;
+ }
+ self._initSessionManagement();
});
},
@@ -71,7 +77,10 @@ publicWidget.registry.SurveyFormWidget = publicWidget.Widget.extend({
// Handle Start / Next / Submit
if (keyCode === 13) { // Enter : go Next
event.preventDefault();
- this._submitForm({});
+ if (!this.preventEnterSubmit) {
+ var isFinish = this.$('button[value="finish"]').length !== 0;
+ this._submitForm({isFinish: isFinish});
+ }
} else if (self.options.questionsLayout === 'page_per_question'
&& letter.match(/[a-z]/i)) {
var $choiceInput = this.$(`input[data-selection-key=${letter}]`);
@@ -131,14 +140,16 @@ publicWidget.registry.SurveyFormWidget = publicWidget.Widget.extend({
},
_onMatrixBtnClick: function (event) {
- if (!this.options.readonly) {
- var $target = $(event.currentTarget);
- var $input = $target.find('input');
- if ($input.attr('type') === 'radio') {
- $input.prop("checked", true).trigger('change');
- } else {
- $input.prop("checked", !$input.prop("checked")).trigger('change');
- }
+ if (this.readonly) {
+ return;
+ }
+
+ var $target = $(event.currentTarget);
+ var $input = $target.find('input');
+ if ($input.attr('type') === 'radio') {
+ $input.prop("checked", true).trigger('change');
+ } else {
+ $input.prop("checked", !$input.prop("checked")).trigger('change');
}
},
@@ -161,6 +172,75 @@ publicWidget.registry.SurveyFormWidget = publicWidget.Widget.extend({
this._submitForm({'previousPageId': event.data.previousPageId});
},
+ /**
+ * We listen to 'next_question' and 'end_session' events to load the next
+ * page of the survey automatically, based on the host pacing.
+ *
+ * If the trigger is 'next_question', we handle some extra computation to find
+ * a suitable "fadeInOutDelay" based on the delay between the time of the question
+ * change by the host and the time of reception of the event.
+ * This will allow us to account for a little bit of server lag (up to 2 seconds)
+ * while giving everyone a fair experience on the quiz.
+ *
+ * e.g 1:
+ * - The host switches the question
+ * - We receive the event 500 ms later due to server lag
+ * - -> The fadeInOutDelay will be 750 ms (500ms delay + 750ms * 2 fade in fade out)
+ *
+ * e.g 2:
+ * - The host switches the question
+ * - We receive the event 1500 ms later due to bigger server lag
+ * - -> The fadeInOutDelay will be 250ms (1500ms delay + 250ms * 2 fade in fade out)
+ *
+ * @private
+ * @param {Array[]} notifications structured as specified by the bus feature
+ */
+ _onNotification: function (notifications) {
+ var nextPageEvent = false;
+ if (notifications && notifications.length !== 0) {
+ notifications.forEach(function (notification) {
+ if (notification.length >= 2) {
+ var event = notification[1];
+ if (event.type === 'next_question' ||
+ event.type === 'end_session') {
+ nextPageEvent = event;
+ }
+ }
+ });
+ }
+
+ if (this.options.isStartScreen && nextPageEvent.type === 'end_session') {
+ // can happen when triggering the same survey session multiple times
+ // we received an "old" end_session event that needs to be ignored
+ return;
+ }
+
+ if (nextPageEvent) {
+ if (nextPageEvent.type === 'next_question') {
+ var serverDelayMS = moment.utc().valueOf() - moment.unix(nextPageEvent.question_start).utc().valueOf();
+ if (serverDelayMS < 0) {
+ serverDelayMS = 0;
+ } else if (serverDelayMS > 2000) {
+ serverDelayMS = 2000;
+ }
+ this.fadeInOutDelay = (2000 - serverDelayMS) / 2;
+ } else {
+ this.fadeInOutDelay = 400;
+ }
+
+ this.preventEnterSubmit = false;
+ this.readonly = false;
+ this._nextScreen(
+ this._rpc({
+ route: `/survey/next_question/${this.options.surveyToken}/${this.options.answerToken}`,
+ }), {
+ initTimer: true,
+ isFinish: nextPageEvent.type === 'end_session'
+ }
+ );
+ }
+ },
+
// SUBMIT
// -------------------------------------------------------------------------
@@ -174,6 +254,8 @@ publicWidget.registry.SurveyFormWidget = publicWidget.Widget.extend({
* @param {Array} [options]
* @param {Integer} [options.previousPageId] navigates to page id
* @param {Boolean} [options.skipValidation] skips JS validation
+ * @param {Boolean} [options.initTime] will force the re-init of the timer after next
+ * screen transition
* @param {Boolean} [options.isFinish] fades out breadcrumb and timer
* @private
*/
@@ -201,6 +283,32 @@ publicWidget.registry.SurveyFormWidget = publicWidget.Widget.extend({
this._prepareSubmitValues(formData, params);
}
+ // prevent user from submitting more times using enter key
+ this.preventEnterSubmit = true;
+
+ if (this.options.sessionInProgress) {
+ // reset the fadeInOutDelay when attendee is submitting form
+ this.fadeInOutDelay = 400;
+ // prevent user from clicking on matrix options when form is submitted
+ this.readonly = true;
+ }
+
+ var submitPromise = self._rpc({
+ route: _.str.sprintf('%s/%s/%s', route, self.options.surveyToken, self.options.answerToken),
+ params: params,
+ });
+ this._nextScreen(submitPromise, options);
+ },
+
+ /**
+ * Will fade out / fade in the next screen based on passed promise and options.
+ *
+ * @param {Promise} nextScreenPromise
+ * @param {Object} options see '_submitForm' for details
+ */
+ _nextScreen: function (nextScreenPromise, options) {
+ var self = this;
+
var resolveFadeOut;
var fadeOutPromise = new Promise(function (resolve, reject) {resolveFadeOut = resolve;});
@@ -211,33 +319,41 @@ publicWidget.registry.SurveyFormWidget = publicWidget.Widget.extend({
self.$(selectorsToFadeout.join(',')).fadeOut(this.fadeInOutDelay, function () {
resolveFadeOut();
});
- var submitPromise = self._rpc({
- route: _.str.sprintf('%s/%s/%s', route, self.options.surveyToken, self.options.answerToken),
- params: params,
- });
- Promise.all([fadeOutPromise, submitPromise]).then(function (results) {
- return self._onSubmitDone(results[1], options.isFinish);
+
+ Promise.all([fadeOutPromise, nextScreenPromise]).then(function (results) {
+ return self._onNextScreenDone(results[1], options);
});
},
/**
- * Follow the submit and handle the transition from one screen to another
- * Also handle server side validation and displays eventual error messages.
- */
- _onSubmitDone: function (result, isFinish) {
+ * Handle server side validation and display eventual error messages.
+ *
+ * @param {string} result the HTML result of the screen to display
+ * @param {Object} options see '_submitForm' for details
+ */
+ _onNextScreenDone: function (result, options) {
var self = this;
+ if (!(options && options.isFinish)
+ && !this.options.sessionInProgress) {
+ this.preventEnterSubmit = false;
+ }
+
if (result && !result.error) {
this.$(".o_survey_form_content").empty();
this.$(".o_survey_form_content").html(result);
this.$('div.o_survey_form_date').each(function () {
self._initDateTimePicker($(this));
});
- if (this.options.isStartScreen) {
+ if (this.options.isStartScreen || (options && options.initTimer)) {
this._initTimer();
this.options.isStartScreen = false;
+ } else {
+ if (this.options.sessionInProgress && this.surveyTimerWidget) {
+ this.surveyTimerWidget.destroy();
+ }
}
- if (isFinish) {
+ if (options && options.isFinish) {
this._initResultWidget();
if (this.surveyBreadcrumbWidget) {
this.$('.o_survey_breadcrumb_container').addClass('d-none');
@@ -588,12 +704,37 @@ publicWidget.registry.SurveyFormWidget = publicWidget.Widget.extend({
}
},
+ /**
+ * Will handle bus specific behavior for survey 'sessions'
+ *
+ * @private
+ */
+ _initSessionManagement: function () {
+ if (this.options.surveyToken && this.options.sessionInProgress) {
+ this.call('bus_service', 'addChannel', this.options.surveyToken);
+ this.call('bus_service', 'startPolling');
+
+ this.call('bus_service', 'onNotification', this, this._onNotification);
+ }
+ },
+
_initTimer: function () {
+ if (this.surveyTimerWidget) {
+ this.surveyTimerWidget.destroy();
+ }
+
var self = this;
- var $timer = this.$('.o_survey_timer');
- if ($timer.length) {
- var timeLimitMinutes = this.options.timeLimitMinutes;
- var timer = this.options.timer;
+ var $timerData = this.$('.o_survey_form_content_data');
+ var questionTimeLimitReached = $timerData.data('questionTimeLimitReached');
+ var timeLimitMinutes = $timerData.data('timeLimitMinutes');
+ var hasAnswered = $timerData.data('hasAnswered');
+
+ if (!questionTimeLimitReached && !hasAnswered && timeLimitMinutes) {
+ var timer = $timerData.data('timer');
+ var $timer = $('<span>', {
+ class: 'o_survey_timer'
+ });
+ this.$('.o_survey_timer_container').append($timer);
this.surveyTimerWidget = new publicWidget.registry.SurveyTimerWidget(this, {
'timer': timer,
'timeLimitMinutes': timeLimitMinutes
@@ -602,10 +743,9 @@ publicWidget.registry.SurveyFormWidget = publicWidget.Widget.extend({
this.surveyTimerWidget.on('time_up', this, function (ev) {
self._submitForm({
'skipValidation': true,
- 'isFinish': true
+ 'isFinish': !this.options.sessionInProgress
});
});
- $timer.removeClass('d-none');
}
},
diff --git a/addons/survey/static/src/js/survey_quick_access.js b/addons/survey/static/src/js/survey_quick_access.js
new file mode 100644
index 00000000000..0534aa41878
--- /dev/null
+++ b/addons/survey/static/src/js/survey_quick_access.js
@@ -0,0 +1,66 @@
+odoo.define('survey.quick.access', function (require) {
+'use strict';
+
+var publicWidget = require('web.public.widget');
+
+publicWidget.registry.SurveyQuickAccessWidget = publicWidget.Widget.extend({
+ selector: '.o_survey_quick_access',
+ events: {
+ 'click button[type="submit"]': '_onSubmit',
+ },
+
+ //--------------------------------------------------------------------------
+ // Widget
+ //--------------------------------------------------------------------------
+
+ /**
+ * @override
+ */
+ start: function () {
+ var self = this;
+ return this._super.apply(this, arguments).then(function () {
+ // Init event listener
+ if (!self.readonly) {
+ $(document).on('keypress', self._onKeyPress.bind(self));
+ }
+ });
+ },
+
+ // -------------------------------------------------------------------------
+ // Private
+ // -------------------------------------------------------------------------
+
+ // Handlers
+ // -------------------------------------------------------------------------
+
+ _onKeyPress: function (event) {
+ if (event.keyCode === 13) { // Enter
+ event.preventDefault();
+ this._submitCode();
+ }
+ },
+
+ _onSubmit: function (event) {
+ event.preventDefault();
+ this._submitCode();
+ },
+
+ _submitCode: function () {
+ var self = this;
+ this.$('.o_survey_error').addClass("d-none");
+ var $accessCodeInput = this.$('input#access_code');
+ this._rpc({
+ route: `/survey/check_access_code/${$accessCodeInput.val()}`,
+ }).then(function (response) {
+ if (response.survey_url) {
+ window.location = response.survey_url;
+ } else {
+ self.$('.o_survey_error').removeClass("d-none");
+ }
+ });
+ },
+});
+
+return publicWidget.registry.SurveyQuickAccessWidget;
+
+});
diff --git a/addons/survey/static/src/js/survey_result.js b/addons/survey/static/src/js/survey_result.js
index dd7e1d5773d..c27fc343a43 100644
--- a/addons/survey/static/src/js/survey_result.js
+++ b/addons/survey/static/src/js/survey_result.js
@@ -325,17 +325,25 @@ publicWidget.registry.SurveyResultWidget = publicWidget.Widget.extend({
start: function () {
var self = this;
return this._super.apply(this, arguments).then(function () {
+ var allPromises = [];
+
self.$('.pagination').each(function (){
var questionId = $(this).data("question_id");
- new publicWidget.registry.SurveyResultPagination(self, {
+ allPromises.push(new publicWidget.registry.SurveyResultPagination(self, {
'questionsEl': self.$('#survey_table_question_'+ questionId)
- }).attachTo($(this));
+ }).attachTo($(this)));
});
self.$('.survey_graph').each(function () {
- new publicWidget.registry.SurveyResultChart(self)
- .attachTo($(this));
+ allPromises.push(new publicWidget.registry.SurveyResultChart(self)
+ .attachTo($(this)));
});
+
+ if (allPromises.length !== 0) {
+ return Promise.all(allPromises);
+ } else {
+ return Promise.resolve();
+ }
});
},
diff --git a/addons/survey/static/src/js/survey_session_manage.js b/addons/survey/static/src/js/survey_session_manage.js
new file mode 100644
index 00000000000..daa6979b4ab
--- /dev/null
+++ b/addons/survey/static/src/js/survey_session_manage.js
@@ -0,0 +1,323 @@
+odoo.define('survey.session_manage', function (require) {
+'use strict';
+
+var publicWidget = require('web.public.widget');
+var core = require('web.core');
+var _t = core._t;
+
+publicWidget.registry.SurveySessionManage = publicWidget.Widget.extend({
+ selector: '.o_survey_session_manage',
+ events: {
+ 'click .o_survey_session_copy': '_onCopySessionLink',
+ 'click .o_survey_session_next, .o_survey_session_start': '_onNextQuestionClick',
+ 'click .o_survey_session_end': '_onEndSessionClick',
+ 'click .o_survey_session_results_href': '_onResultsTabClick',
+ 'click .o_survey_session_ranking_href': '_onRankingTabClick',
+ 'click .o_survey_session_refresh_results': '_onRefreshResultsClick',
+ 'click .o_survey_session_refresh_ranking': '_onRefreshRankingClick',
+ },
+
+ /**
+ * Overridden to set a few properties that come from the python template rendering.
+ *
+ * We also handle the timer IF we're not "transitioning", meaning a fade out of the previous
+ * $el to the next question (the fact that we're transitioning is in the isRpcCall data).
+ * If we're transitioning, the timer is handled manually at the end of the transition.
+ */
+ start: function () {
+ var self = this;
+ return this._super.apply(this, arguments).then(function () {
+ self.surveyId = self.$el.data('surveyId');
+ self.surveyAccessToken = self.$el.data('surveyAccessToken');
+ self.isStartScreen = self.$el.data('isStartScreen');
+
+ var isRpcCall = self.$el.data('isRpcCall');
+ if (!isRpcCall) {
+ self._startTimer();
+ }
+
+ if (self.isStartScreen) {
+ self._refreshAttendeesCount();
+ } else if (!self.refreshingAnswers) {
+ self.stopRefreshingAttendees = true;
+ self._refreshReceivedAnswers();
+ }
+ });
+ },
+
+ //--------------------------------------------------------------------------
+ // Handlers
+ //--------------------------------------------------------------------------
+
+ /**
+ * Copies the survey URL link to the clipboard.
+ * We use 'ClipboardJS' to avoid having to print the URL in a standard text input
+ *
+ * @param {MouseEvent} ev
+ */
+ _onCopySessionLink: function (ev) {
+ var self = this;
+ ev.preventDefault();
+
+ var $clipboardBtn = this.$('.o_survey_session_copy');
+
+ $clipboardBtn.popover({
+ placement: 'right',
+ container: 'body',
+ offset: '0, 3',
+ content: function () {
+ return _t("Copied !");
+ }
+ });
+
+ var clipboard = new ClipboardJS('.o_survey_session_copy', {
+ text: function () {
+ return self.$('.o_survey_session_copy_url').val();
+ },
+ container: this.el
+ });
+
+ clipboard.on('success', function () {
+ clipboard.destroy();
+ $clipboardBtn.popover('show');
+ _.delay(function () {
+ $clipboardBtn.popover('hide');
+ }, 800);
+ });
+
+ clipboard.on('error', function (e) {
+ clipboard.destroy();
+ });
+ },
+
+ /**
+ * We use a fade in/out mechanism to display the next question of the session.
+ *
+ * The fade out happens at the same moment as the _rpc to get the new question template.
+ * When they're both finished, we update the HTML of this widget with the new template and then
+ * fade in the updated question to the user.
+ *
+ * The timer (if configured) starts at the end of the fade in animation.
+ *
+ * @param {MouseEvent} ev
+ * @private
+ */
+ _onNextQuestionClick: function (ev) {
+ var self = this;
+ ev.preventDefault();
+ this.isStartScreen = false;
+
+ var resolveFadeOut;
+ var fadeOutPromise = new Promise(function (resolve, reject) { resolveFadeOut = resolve; });
+ this.$el.fadeOut(1000, function () {
+ resolveFadeOut();
+ });
+
+ var nextQuestionPromise = this._rpc({
+ route: _.str.sprintf('/survey/session/next_question/%s', self.surveyAccessToken)
+ });
+
+ Promise.all([fadeOutPromise, nextQuestionPromise]).then(function (results) {
+ var $renderedTemplate = $(results[1]);
+ self.$el.replaceWith($renderedTemplate);
+ self.attachTo($renderedTemplate);
+ self.$el.fadeIn(1000, function () {
+ self._startTimer();
+ });
+ });
+ },
+
+ /**
+ * Marks this session as 'done' and redirects the user to the results.
+ *
+ * @param {MouseEvent} ev
+ * @private
+ */
+ _onEndSessionClick: function (ev) {
+ var self = this;
+ ev.preventDefault();
+
+ this._rpc({
+ model: 'survey.survey',
+ method: 'action_end_session',
+ args: [[this.surveyId]],
+ }).then(function () {
+ document.location = _.str.sprintf(
+ '/survey/results/%s',
+ self.surveyId
+ );
+ });
+ },
+
+ _onResultsTabClick: function (ev) {
+ this._onRefreshResultsClick(ev, true);
+ },
+
+ _onRankingTabClick: function (ev) {
+ this._onRefreshRankingClick(ev, true);
+ },
+
+ _onRefreshResultsClick: function (ev, preventSpin) {
+ ev.preventDefault();
+
+ if (this.$('.o_survey_session_refresh_results').hasClass('fa-spin')) {
+ return;
+ }
+
+ var refreshPromise = this._refreshResults();
+ if (!preventSpin) {
+ this._spinIcon(this.$('.o_survey_session_refresh_results'), refreshPromise);
+ }
+ },
+
+ _onRefreshRankingClick: function (ev, preventSpin) {
+ ev.preventDefault();
+
+ if (this.$('.o_survey_session_refresh_ranking').hasClass('fa-spin')) {
+ return;
+ }
+
+ var refreshPromise = this._refreshRanking();
+
+ if (!preventSpin) {
+ this._spinIcon(this.$('.o_survey_session_refresh_ranking'), refreshPromise);
+ }
+ },
+
+ //--------------------------------------------------------------------------
+ // Private
+ //--------------------------------------------------------------------------
+
+ /**
+ * Will start the question timer so that the host may know when the question is done to display
+ * the results and the ranking.
+ */
+ _startTimer: function () {
+ var $timer = this.$('.o_survey_timer');
+
+ if ($timer.length) {
+ var timeLimitMinutes = this.$el.data('timeLimitMinutes');
+ var timer = this.$el.data('timer');
+ this.surveyTimerWidget = new publicWidget.registry.SurveyTimerWidget(this, {
+ 'timer': timer,
+ 'timeLimitMinutes': timeLimitMinutes
+ });
+ this.surveyTimerWidget.attachTo($timer);
+ }
+ },
+
+ /**
+ * Refreshes the question results on screen.
+ */
+ _refreshResults: function () {
+ var self = this;
+
+ return this._rpc({
+ route: _.str.sprintf('/survey/session/results/%s', this.surveyAccessToken)
+ }).then(function (renderedTemplate) {
+ var $renderedTemplate = $(renderedTemplate).addClass('py-3');
+ self.$('.o_survey_result').empty().append($renderedTemplate);
+ return new publicWidget.registry.SurveyResultWidget().attachTo($renderedTemplate);
+ });
+ },
+
+ /**
+ * Refreshes the question ranking on screen.
+ * We set the width of the progress bars after the rendering to enable a width css animation.
+ */
+ _refreshRanking: function () {
+ var self = this;
+
+ return this._rpc({
+ route: _.str.sprintf('/survey/session/ranking/%s', this.surveyAccessToken)
+ }).then(function (renderedTemplate) {
+ var $renderedTemplate = $(renderedTemplate).addClass('py-3');
+ self.$('.o_survey_session_ranking_container').empty().append($renderedTemplate);
+ // delay by 200ms to account for fade out / in
+ setTimeout(function () {
+ self.$('.o_survey_session_ranking_bar').each(function () {
+ $(this).css('width', `calc(calc(100% - 10rem) * ${$(this).data('widthRatio')})`);
+ });
+ }, 200);
+
+ return Promise.resolve();
+ });
+ },
+
+ /**
+ * We refresh the attendees count every 2 seconds while the user is on the start screen.
+ * When he leaves the start screen, the "stopRefreshingAttendees" becomes true and we stop
+ * querying the answer_count.
+ */
+ _refreshAttendeesCount: function () {
+ var self = this;
+
+ setTimeout(function () {
+ self._rpc({
+ model: 'survey.survey',
+ method: 'read',
+ args: [[self.surveyId], ['answer_count']],
+ }).then(function (result) {
+ if (result && result.length === 1){
+ self.$('.o_survey_session_attendees_count').text(
+ result[0].answer_count
+ );
+ }
+ if (!self.stopRefreshingAttendees) {
+ self._refreshAttendeesCount();
+ }
+ });
+ }, 2000);
+ },
+
+ /**
+ * We refresh the received answers count every 2 seconds for the current question.
+ * This allows the host to know what he can move on to the next question.
+ * (Without having to manually refresh the page).
+ */
+ _refreshReceivedAnswers: function () {
+ var self = this;
+
+ this.refreshingAnswers = true;
+ setTimeout(function () {
+ self._rpc({
+ model: 'survey.survey',
+ method: 'read',
+ args: [[self.surveyId], ['session_question_answer_count']],
+ }).then(function (result) {
+ if (result && result.length === 1){
+ self.$('.o_survey_session_answer_count').text(
+ result[0].session_question_answer_count
+ );
+ }
+ self._refreshReceivedAnswers();
+ });
+ }, 2000);
+ },
+
+ /**
+ * We need the refresh icon to spin for at least 1s to make the user feel like it's
+ * refreshing something.
+ * If we only wait for the 'refreshPromise', it can be so fast that the icon doesn't even move.
+ *
+ * @param {$.Element} $target
+ * @param {Promise} refreshPromise
+ */
+ _spinIcon: function ($target, refreshPromise) {
+ $target.addClass('fa-spin');
+
+ var minimumSpinResolve;
+ var minimumSpinPromise = new Promise(function (resolve) { minimumSpinResolve = resolve;});
+ setTimeout(function () {
+ minimumSpinResolve();
+ }, 1000);
+
+ Promise.all([minimumSpinPromise, refreshPromise]).then(function () {
+ $target.removeClass('fa-spin');
+ });
+ },
+});
+
+return publicWidget.registry.SurveySessionManage;
+
+});
diff --git a/addons/survey/static/src/js/survey_timer.js b/addons/survey/static/src/js/survey_timer.js
index 664871e4e7e..d0a38c88324 100644
--- a/addons/survey/static/src/js/survey_timer.js
+++ b/addons/survey/static/src/js/survey_timer.js
@@ -31,7 +31,6 @@ publicWidget.registry.SurveyTimerWidget = publicWidget.Widget.extend({
if (this.timeLimitMinutes <= 0 || this.countDownDate.diff(moment.utc(), 'seconds') < 0) {
this.trigger_up('time_up');
} else {
- this.$timer = this.$el.find('.timer');
this._updateTimer(this);
this.surveyTimerInterval = setInterval(this._updateTimer.bind(this), 1000);
}
@@ -57,7 +56,7 @@ publicWidget.registry.SurveyTimerWidget = publicWidget.Widget.extend({
if (timeLeft >= 0) {
var timeLeftMinutes = parseInt(timeLeft / 60);
var timeLeftSeconds = timeLeft - (timeLeftMinutes * 60);
- this.$timer.html(this._formatTime(timeLeftMinutes) + ':' + this._formatTime(timeLeftSeconds));
+ this.$el.text(this._formatTime(timeLeftMinutes) + ':' + this._formatTime(timeLeftSeconds));
} else {
clearInterval(this.surveyTimerInterval);
this.trigger_up('time_up');
diff --git a/addons/survey/static/src/scss/survey_form.scss b/addons/survey/static/src/scss/survey_form.scss
index 262a0532130..f9f98c9ea03 100644
--- a/addons/survey/static/src/scss/survey_form.scss
+++ b/addons/survey/static/src/scss/survey_form.scss
@@ -6,7 +6,7 @@
Common Style
**********************************************************/
-.o_survey_form, .o_survey_print {
+.o_survey_form, .o_survey_print, .o_survey_session_manage, .o_survey_quick_access {
.o_survey_question_error {
height: 0px;
transition: height .5s ease;
@@ -16,6 +16,15 @@
}
}
+ fieldset[disabled] {
+ .o_survey_question_text_box,
+ .o_survey_question_date,
+ .o_survey_question_datetime,
+ .o_survey_question_numerical_box {
+ padding-left: 0px;
+ }
+ }
+
.o_survey_question_text_box,
.o_survey_question_date,
.o_survey_question_datetime,
@@ -88,28 +97,12 @@
}
}
-/**********************************************************
- Form Specific Style
- **********************************************************/
-
-.o_survey_form {
+.o_survey_form, .o_survey_session_manage {
min-height: 100%;
.o_survey_choice_btn {
background-color: rgba($primary, 0.5);
border: 2px solid $primary;
- cursor: pointer;
-
- &.o_survey_selected {
- background-color: $primary;
- }
-
- &:hover {
- background-color: rgba($primary, 0.8);
- .o_survey_choice_key span.o_survey_key {
- opacity: 1;
- }
- }
}
.o_survey_choice_key {
@@ -120,14 +113,7 @@
font-size: smaller;
top: -2px;
&.o_survey_key {
- width: inherit;
- right: 10px;
- border: 2px solid $primary;
- border-right: 0px;
- height: 25px;
opacity: 0;
- transition: opacity 0.4s ease;
- white-space: nowrap;
}
}
}
@@ -138,19 +124,160 @@
}
td {
background-color: rgba($primary, 0.2);
+ }
+ }
+
+ .o_survey_progress {
+ height:0.5em;
+ }
+}
+
+/**********************************************************
+ Form Specific Style
+ **********************************************************/
+
+.o_survey_form {
+ .o_survey_choice_btn {
+ cursor: pointer;
+
+ &.o_survey_selected {
+ background-color: $primary;
+ }
+
+ &:hover {
+ background-color: rgba($primary, 0.8);
+ .o_survey_choice_key span.o_survey_key {
+ opacity: 1;
+ }
+ }
+ }
+
+ .o_survey_choice_key span.o_survey_key {
+ width: inherit;
+ right: 10px;
+ border: 2px solid $primary;
+ border-right: 0px;
+ height: 25px;
+ transition: opacity 0.4s ease;
+ white-space: nowrap;
+ }
+
+ .o_survey_question_matrix td:hover {
+ background-color: rgba($primary, 0.5);
+ cursor: pointer;
+ .o_survey_choice_key span.o_survey_key {
+ opacity: 1;
+ }
+ }
+}
+
+/**********************************************************
+ Survey Session Specific Style
+ **********************************************************/
+
+.o_survey_session_manage {
+ .o_survey_manage_fontsize_14 {
+ font-size: 1.4rem;
+ }
+
+ .o_survey_session_manage_container {
+ border: solid 1px #999999;
+ box-shadow: 0px 1px 1px 0px #999999;
+ min-height: 75vh;
+
+ .js_question-wrapper {
+ padding-bottom: 0px;
+ }
+ }
+
+ .tab-content > .o_survey_session_manage_question.tab-pane {
+ display: none;
+ }
+
+ .tab-content > .o_survey_session_manage_question.active {
+ display: flex;
+ }
+
+ .o_survey_session_copy {
+ cursor: pointer;
+ opacity: .75;
+ transition: opacity .3s ease;
+ &:hover {
+ opacity: 1;
+ }
+ }
+
+ .btn {
+ font-variant: small-caps;
+ &.o_survey_session_end {
+ right: 0px;
+ }
+ }
+ .o_survey_session_results, .o_survey_session_ranking {
+ .fa {
+ font-size: .9rem;
+ opacity: .75;
&:hover {
- background-color: rgba($primary, 0.5);
+ transition: opacity .4s ease;
+ opacity: 1;
cursor: pointer;
- .o_survey_choice_key span.o_survey_key {
- opacity: 1;
- }
}
}
+
+ .o_survey_result, .o_survey_session_ranking_container {
+ overflow: hidden;
+ }
+
+ .survey_filter {
+ display: none;
+ }
}
- .o_survey_progress {
- height:0.5em;
+ .o_survey_result {
+ .nav-tabs .nav-link.active {
+ background-color: transparent;
+ border-color: #dee2e6 #dee2e6 transparent;
+ }
+ }
+
+ .nav-tabs {
+ border-bottom: 0px;
+ .nav-item .nav-link {
+ border-radius: 0px;
+ background-color: transparent;
+ border: solid 1px transparent;
+
+ &.active {
+ border: solid 1px #999999;
+ border-bottom: 0px;
+ }
+ }
+ }
+}
+
+.o_survey_session_ranking {
+ .o_survey_session_ranking_item {
+ line-height: 1.4rem;
+
+ .o_survey_session_ranking_index {
+ width: 1.5rem;
+ }
+
+ .o_survey_session_ranking_bar {
+ min-width: 90px;
+ height: 1.4rem;
+ color: white;
+ background-color: #007A77;
+ transition: width ease 1.5s;
+ }
+
+ .o_survey_session_ranking_name {
+ width: 7.5rem;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
}
}