diff options
Diffstat (limited to 'addons/survey/static/src')
| -rw-r--r-- | addons/survey/static/src/js/survey_form.js | 198 | ||||
| -rw-r--r-- | addons/survey/static/src/js/survey_quick_access.js | 66 | ||||
| -rw-r--r-- | addons/survey/static/src/js/survey_result.js | 16 | ||||
| -rw-r--r-- | addons/survey/static/src/js/survey_session_manage.js | 323 | ||||
| -rw-r--r-- | addons/survey/static/src/js/survey_timer.js | 3 | ||||
| -rw-r--r-- | addons/survey/static/src/scss/survey_form.scss | 189 |
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; + } } } |
