This is an automated email from the git hooks/post-receive script. New commit to branch feature/multi-ui in repository pollen. See https://gitlab.nuiton.org/chorem/pollen.git commit f19a682e7316a6b7b03cb4c3651ab2d70c228494 Author: jcouteau <couteau@codelutin.com> Date: Tue Jan 29 15:59:15 2019 +0100 refs #224 : Can now start multi-questions poll creation --- pollen-ui-riot-js/src/main/web/i18n/en.json | 3 + pollen-ui-riot-js/src/main/web/i18n/fr.json | 3 + pollen-ui-riot-js/src/main/web/js/PollForm.js | 19 +- .../src/main/web/tag/poll/Description.tag.html | 19 +- .../src/main/web/tag/poll/EditPoll.tag.html | 10 +- .../src/main/web/tag/poll/VoteSettings.tag.html | 470 +++++++++++++++++++++ 6 files changed, 517 insertions(+), 7 deletions(-) diff --git a/pollen-ui-riot-js/src/main/web/i18n/en.json b/pollen-ui-riot-js/src/main/web/i18n/en.json index 649ec254..8a233d84 100644 --- a/pollen-ui-riot-js/src/main/web/i18n/en.json +++ b/pollen-ui-riot-js/src/main/web/i18n/en.json @@ -298,6 +298,7 @@ "poll_description_nameNotBlank": "Name nust be not blank", "poll_description_email": "Your email", "poll_description_emailPlaceHolder": "Enter your email", + "poll_description_multiQuestion": "Multi-questions poll", "poll_settings_pollType": "Poll opening", "poll_settings_pollType_FREE": "Open to all", "poll_settings_pollType_FREE_help": "Anyone with the poll link can vote.", @@ -413,6 +414,8 @@ "poll_step_choices": "Choices", "poll_step_options": "Options", "poll_step_voters": "Voters", + "poll_step_questions": "Questions", + "poll_step_summary": "Summary", "poll_voters_previous": "Previous", "poll_voters_save": "Save", "poll_voters_freePoll": "Everybody can vote (Public poll)", diff --git a/pollen-ui-riot-js/src/main/web/i18n/fr.json b/pollen-ui-riot-js/src/main/web/i18n/fr.json index 52b60ade..b460506b 100644 --- a/pollen-ui-riot-js/src/main/web/i18n/fr.json +++ b/pollen-ui-riot-js/src/main/web/i18n/fr.json @@ -298,6 +298,7 @@ "poll_description_nameNotBlank": "Vote nom ne doit pas être blanc", "poll_description_email": "Votre adresse électronique", "poll_description_emailPlaceHolder": "Renseignez votre adresse électronique", + "poll_description_multiQuestion": "Sondage à plusieurs questions", "poll_settings_pollType": "Ouverture du sondage", "poll_settings_pollType_FREE": "Ouvert à tous", "poll_settings_pollType_FREE_help": "Toutes les personnes ayant le lien du sondage peuvent voter.", @@ -413,6 +414,8 @@ "poll_step_choices": "Choix", "poll_step_options": "Options", "poll_step_voters": "Participants", + "poll_step_questions": "Questions", + "poll_step_summary": "Résumé", "poll_voters_previous": "Précédent", "poll_voters_save": "Enregistrer", "poll_voters_freePoll": "Tout le monde peut voter (Sondage public)", diff --git a/pollen-ui-riot-js/src/main/web/js/PollForm.js b/pollen-ui-riot-js/src/main/web/js/PollForm.js index 08e37033..376318a2 100644 --- a/pollen-ui-riot-js/src/main/web/js/PollForm.js +++ b/pollen-ui-riot-js/src/main/web/js/PollForm.js @@ -31,12 +31,20 @@ import pageTracker from "./PageTracker"; class PollForm { constructor() { - this.steps = [ + this.stepsNoMulti = [ "general", "choices", "options", "voters" ]; + this.stepsMulti = [ + "general", + "options", + "questions", + "summary", + "voters" + ]; + this.types = ["FREE", "RESTRICTED", "REGISTERED"]; this.pollService = pollService; @@ -46,6 +54,7 @@ class PollForm { this.isInit = false; this.creation = false; this.showOptions = false; + this.multi = false; this.model = {}; this.mainVoterList = {}; this.currentVoterList = {}; @@ -58,6 +67,14 @@ class PollForm { return Promise.resolve(); } + getSteps(multi){ + if (multi) { + return this.stepsMulti; + } else { + return this.stepsNoMulti; + } + } + reloadVoteCountingTypes() { return voteCountingTypeService.getVoteCountingTypes().then(result => { this.voteCountingTypes = result; diff --git a/pollen-ui-riot-js/src/main/web/tag/poll/Description.tag.html b/pollen-ui-riot-js/src/main/web/tag/poll/Description.tag.html index 415d8a87..d658f085 100644 --- a/pollen-ui-riot-js/src/main/web/tag/poll/Description.tag.html +++ b/pollen-ui-riot-js/src/main/web/tag/poll/Description.tag.html @@ -27,7 +27,8 @@ <div class="o-form-element"> <label class="c-label" for="title">{_t.title}</label> - <input ref="title" + <input id="title" + ref="title" tabindex="1" type="text" class="c-field c-field--label" @@ -42,7 +43,8 @@ </div> <div class="o-form-element"> <label class="c-label" for="description">{_t.description}</label> - <textarea ref="description" + <textarea id="description" + ref="description" tabindex="1" class="c-field c-field--label" name="description" @@ -57,6 +59,7 @@ <div class="o-form-element"> <label class="c-label" for="name">{_t.name}</label> <input ref="name" + id="name" tabindex="1" type="text" class="c-field c-field--label" @@ -72,6 +75,7 @@ <div class="o-form-element"> <label class="c-label" for="email">{_t.email}</label> <input ref="email" + id="email" tabindex="1" type="email" class="c-field c-field--label" @@ -85,6 +89,16 @@ <option each={emailAddress in session.getUser().emailAddresses} value={emailAddress.emailAddress}> </datalist> </div> + <div class="o-form-element"> + <input type="checkbox" + ref="multi" + id="multi" + tabindex="1" + name="multi" + checked="{form.model.multi}" + disabled={opts.form.model.closed}/> + <label class="c-label" for="multi">{_t.multiQuestion}</label> + </div> </div> <script type="es6"> @@ -98,6 +112,7 @@ this.form.model.description = this.refs.description.value; this.form.model.creatorName = this.refs.name.value; this.form.model.creatorEmail = this.refs.email.value; + this.form.multi = this.refs.multi.checked; }; </script> diff --git a/pollen-ui-riot-js/src/main/web/tag/poll/EditPoll.tag.html b/pollen-ui-riot-js/src/main/web/tag/poll/EditPoll.tag.html index 93f67d4d..8115a75e 100644 --- a/pollen-ui-riot-js/src/main/web/tag/poll/EditPoll.tag.html +++ b/pollen-ui-riot-js/src/main/web/tag/poll/EditPoll.tag.html @@ -23,6 +23,7 @@ import "./Description.tag.html"; import "./Choices.tag.html"; import "./Settings.tag.html"; +import "./VoteSettings.tag.html"; import "../voterList/VoterList.tag.html"; import "./Summary.tag.html"; import "../components/HumanInput.tag.html"; @@ -40,7 +41,7 @@ import "./CheckEmails.tag.html"; <div class="tabs below-h1"> <div class="tab-container"> - <div each={step, index in form.steps} class="tab {selected : !showSummary && form.step === index}"> + <div each={step, index in form.getSteps(form.multi)} class="tab {selected : !showSummary && form.step === index}"> <button type="submit" if="{form.step > index || !form.creation}" class="cursor-pointer" onclick="{goto(index)}"> {getStepLabel(step)} </button> @@ -59,9 +60,10 @@ import "./CheckEmails.tag.html"; <div class="main-content"> <CheckEmails if={!form.creation} form={form}/> <Description if={!showSummary && form.step === 0} form={form} ref="description"/> - <Choices if={!showSummary && form.step === 1} form={form} ref="choices"/> - <Settings if={!showSummary && form.step === 2} form={form} ref="settings"/> - <VoterList if={!showSummary && form.step === 3 && form.model.pollType === "RESTRICTED"} form={form} ref="voters"/> + <Choices if={!showSummary && form.step === 1 && !form.multi} form={form} ref="choices"/> + <Settings if={!showSummary && form.step === 2 && !form.multi} form={form} ref="settings"/> + <VoterList if={!showSummary && form.step === 3 && ! form.multi && form.model.pollType === "RESTRICTED"} form={form} ref="voters"/> + <VoteSettings if={!showSummary && form.step === 1 && form.multi} form={form} ref="voteSettings"/> <Summary if={showSummary} form={form}/> <div class="form-footer"> diff --git a/pollen-ui-riot-js/src/main/web/tag/poll/VoteSettings.tag.html b/pollen-ui-riot-js/src/main/web/tag/poll/VoteSettings.tag.html new file mode 100644 index 00000000..3bc1d61d --- /dev/null +++ b/pollen-ui-riot-js/src/main/web/tag/poll/VoteSettings.tag.html @@ -0,0 +1,470 @@ +/*- +* #%L +* Pollen :: UI (Riot Js) +* +%% +Copyright (C) 2009 - 2017 CodeLutin +* +%% +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +#L% +*/ +import "../components/date-time-picker.tag.html"; +import "../components/Checkbox.tag.html"; + +<VoteSettings> + <div class="form-section"> + <h4><i class="fa fa-users" aria-hidden="true"></i> {_t.pollType}</h4> + <div class="o-form-element"> + <span class="c-input-group"> + <button each={type in form.types} + class="c-button c-button--brand {c-button--ghost-brand: form.model.pollType !== type, c-button--brand: form.model.pollType === type}" + onclick={pollTypeChange(type)} + title={_l("pollType_" + type)} + ref={"pollType_" + type} + disabled={form.hasVotes || form.model.closed} + type="button"> + {_l("pollType_" + type)} + </button> + </span> + <div class="help-selected"> + <i class="fa fa-info-circle" aria-hidden="true"></i> + {_l("pollType_" + form.model.pollType + "_help")} + </div> + </div> + <div class="settings-options {with-default: form.creation}" show={form.model.pollType === "REGISTERED"}> + <div class="o-form-element"> + <Checkbox label="{_t.pollType_REGISTERED_limitEmail}" + disabled={form.model.closed} + checkboxchecked={limitEmail} + ontogglecheckbox={toggleLimitEmail}/> + </div> + <div show={limitEmail}> + <div class="o-form-element" each={suffix, index in form.model.emailAddressSuffixes}> + <div class="c-input-group"> + <div class="o-field"> + <input ref="suffix" + type="text" + class="c-field" + required + title={_t.pollType_REGISTERED_suffix} + name="suffix" + value="{suffix}" + disabled={form.model.closed} + placeholder="{_t.pollType_REGISTERED_suffixPlaceHolder}"/> + </div> + <button class="c-button c-button--error" + onclick={removeSuffix(index)} + disabled={form.model.closed || form.model.emailAddressSuffixes.length === 1} + title={_t.pollType_REGISTERED_suffix_remove}> + <i class="fa fa-times" aria-hidden="true"></i> + </button> + </div> + </div> + <div class="actions-right"> + <button class="c-button c-button--success" + onclick={addSuffix} + title={_t.pollType_REGISTERED_suffix_add_title} + disabled={form.model.closed} + type="button"> + <i class="fa fa-plus" aria-hidden="true"></i> + {_t.pollType_REGISTERED_suffix_add} + </button> + </div> + </div> + </div> + </div> + + <div class="form-section" show={form.creation}> + <h4><i class="fa fa-cogs" aria-hidden="true"></i> {_t.basic_usage}</h4> + <div class="o-form-element"> + <Checkbox label="{_t.use_basic_usage}" + disabled={form.model.closed} + checkboxchecked={!showOptions} + ontogglecheckbox={toggleShowOptions}/> + </div> + <div class="c-alert c-alert--info" if={!showOptions}> + {_t.basic_usage_detail} + <ul> + <li>{_t.basic_usage_detail_opening}</li> + <li>{_t.basic_usage_detail_closing}</li> + <li>{_t.basic_usage_detail_comments}</li> + <li>{_t.basic_usage_detail_results}</li> + <li>{_t.basic_usage_detail_notifications}</li> + </ul> + </div> + </div> + <div class="settings-options {with-default: form.creation}" show={showOptions || !form.creation}> + + <div class="settings-options-row"> + <div class="form-section"> + <h4><i class="fa fa-envelope" aria-hidden="true"></i> {_t.nav_votes}</h4> + <div class="o-form-element"> + <Checkbox label="{_t.votePeriod}" + name="votePeriod" + id="votePeriod" + ref="votePeriod" + disabled={form.model.closed} + checkboxchecked={form.model.votePeriod} + ontogglecheckbox={toggleVotePeriod}/> + </div> + <div show="{form.model.votePeriod}" class="config-period"> + <div class="o-form-element"> + <label class="c-label" for="beginDate"> + {_t.beginDate} + </label> + <date-time-picker ref="beginDate" + tabindex="1" + name="beginDate" + id="beginDate" + disabled={form.hasVotes || form.model.closed} + datetime={form.model.beginDate}/> + </div> + <div class="o-form-element"> + <label class="c-label" for="endDate"> + {_t.endDate} + </label> + <date-time-picker ref="endDate" + tabindex="1" + name="endDate" + id="endDate" + datetime={form.model.endDate} + min={refs.beginDate.value} + disabled={form.model.closed} + onchange="{onEndDateChanged}"/> + </div> + </div> + <div class="o-form-element"> + <label class="c-label"> + {_t.voteVisibility} + </label> + <select class="c-field c-field--label" + tabindex="1" + ref="voteVisibility" + disabled={form.hasVotes || form.model.closed} + value={form.model.voteVisibility}> + <option value="ANONYMOUS">{_t.voteVisibility_anonymous}</option> + <option value="CREATOR">{_t.voteVisibility_creator}</option> + <option value="VOTER">{_t.voteVisibility_voter}</option> + <option value="EVERYBODY">{_t.voteVisibility_everybody}</option> + </select> + </div> + <div class="o-form-element"> + <Checkbox label="{_t.nav_anonymousVote}" + name="anonymousVote" + id="anonymousVote" + ref="anonymousVote" + disabled={form.hasVotes || form.model.closed} + checkboxchecked={form.model.anonymousVoteAllowed} + ontogglecheckbox={toggleAnonymousVote}/> + </div> + </div> + <div class="form-section"> + <h4><i class="fa fa-trophy" aria-hidden="true"></i> {_t.nav_result}</h4> + <div class="o-form-element"> + <label class="c-label"> + {_t.resultVisibility} + </label> + <select class="c-field c-field--label" + tabindex="1" + ref="resultVisibility" + disabled={form.hasVotes || form.model.closed} + value={form.model.resultVisibility}> + <option value="CREATOR">{_t.resultVisibility_creator}</option> + <option value="VOTER">{_t.resultVisibility_voter}</option> + <option value="EVERYBODY">{_t.resultVisibility_everybody}</option> + </select> + </div> + <div class="o-form-element"> + <Checkbox label="{_t.continuousResult}" + name="continuousResults" + id="continuousResults" + ref="continuousResults" + disabled={form.model.closed} + checkboxchecked={form.model.continuousResults} + ontogglecheckbox={toggleContinuousResults}/> + </div> + </div> + </div> + + <div class="settings-options-row"> + <div class="form-section"> + <h4><i class="fa fa-comments" aria-hidden="true"></i> {_t.nav_comments}</h4> + <div class="o-form-element"> + <label class="c-label"> + {_t.commentVisibility} + </label> + <select class="c-field c-field--label" + tabindex="1" + ref="commentVisibility" + disabled={form.hasVotes || form.model.closed} + value={form.model.commentVisibility}> + <option value="CREATOR">{_t.commentVisibility_creator}</option> + <option value="VOTER">{_t.commentVisibility_voter}</option> + <option value="EVERYBODY">{_t.commentVisibility_everybody}</option> + </select> + </div> + </div> + + <div class="form-section"> + <h4><i class="fa fa-paper-plane" aria-hidden="true"></i> {_t.nav_notification}</h4> + <fieldset class="o-fieldset"> + <legend class="o-fieldset__legend"> + {_t.notification} + <i class="fa fa-question-circle cursor-help warning" aria-hidden="true" if="{!form.model.creatorEmail}" title="{_t.notification_disabled}"></i> + </legend> + <Checkbox label="{_t.voteNotification}" + labelclass="c-field c-field--choice" + ref="voteNotification" + disabled={!form.model.creatorEmail || form.model.closed} + checkboxchecked={form.model.voteNotification}/> + <Checkbox label="{_t.commentNotification}" + labelclass="c-field c-field--choice" + ref="commentNotification" + disabled={!form.model.creatorEmail || form.model.closed} + checkboxchecked={form.model.commentNotification}/> + <Checkbox label="{_t.newChoiceNotification}" + labelclass="c-field c-field--choice" + ref="newChoiceNotification" + disabled={!form.model.creatorEmail || form.model.closed} + checkboxchecked={form.model.newChoiceNotification}/> + </fieldset> + <div class="o-form-element"> + <Checkbox label="{_t.notifyMeBeforePollEnds}" + labelclass="c-field c-field--choice" + i18nprefix="{i18nprefix}" + disabled={disableNotifyMeBeforePollEnds || form.model.closed} + checkboxchecked={notifyMeBeforePollEnds} + ontogglecheckbox={toggleNotifyMeBeforePollEnds}> + <i class="fa fa-question-circle cursor-help warning" aria-hidden="true" + if="{checked}" + title="{_t.notifyMeBeforePollEnds_disabled}"></i> + </Checkbox> + <div if={notifyMeBeforePollEnds}> + <label class="c-label"> + {_t.notifyMeHoursBeforePollEnds} + </label> + <input type="number" + tabindex="1" + class="c-field c-field--label" + ref="notifyMeHoursBeforePollEnds" + value={form.model.notifyMeHoursBeforePollEnds} + min="{notifyMeBeforePollEnds ? 1 : 0}" + disabled={form.model.closed}> + </div> + </div> + </div> + </div> + </div> + + <script type="es6"> + import session from "../../js/Session"; + + this.i18nprefix = "poll_settings"; + this.session = session; + this.installBundle(this.session, this.i18nprefix); + + this.form = this.opts.form; + + this.showOptions = this.form.showOptions; + this.showHelp = false; + this.form.model.votePeriod = this.form.model.beginDate || this.form.model.endDate; + this.notifyMeBeforePollEnds = this.form.model.creatorEmail && this.form.model.notifyMeHoursBeforePollEnds > 0; + this.disableNotifyMeBeforePollEnds = !this.form.model.creatorEmail || !this.form.model.votePeriod || !this.form.model.endDate; + this.limitEmail = this.form.model.emailAddressSuffixes && this.form.model.emailAddressSuffixes.length; + + this.on("mount", () => { + this.refs["pollType_" + this.form.model.pollType].focus(); + }); + + this.updateHelpHover = (helper) => () => { + this.refs.voteCountingTypeHelpHover.innerHTML = helper; + }; + + this.clearHelpHover = () => { + this.refs.voteCountingTypeHelpHover.innerHTML = ""; + }; + + this.updateDisableNotifyMeBeforePollEnds = () => { + this.disableNotifyMeBeforePollEnds = !this.form.model.creatorEmail || !this.form.model.votePeriod || !this.refs.endDate.getValue(); + this.notifyMeBeforePollEnds &= !this.disableNotifyMeBeforePollEnds; + this.update(); + }; + + this.toggleVotePeriod = () => { + this.form.model.votePeriod = !this.form.model.votePeriod; + this.updateDisableNotifyMeBeforePollEnds(); + this.update(); + }; + + this.onEndDateChanged = () => { + this.updateDisableNotifyMeBeforePollEnds(); + }; + + this.toggleAnonymousVote = () => { + this.form.model.anonymousVoteAllowed = !this.form.model.anonymousVoteAllowed; + this.update(); + }; + + this.toggleShowOptions = () => { + this.showOptions = this.form.showOptions = !this.showOptions; + this.update(); + }; + + this.toggleContinuousResults = () => { + this.form.model.continuousResults = !this.form.model.continuousResults; + this.update(); + }; + + this.toggleNotifyMeBeforePollEnds = () => { + if (!this.notifyMeBeforePollEnds) { + this.refs.notifyMeHoursBeforePollEnds = undefined; + } + this.notifyMeBeforePollEnds = !this.notifyMeBeforePollEnds; + this.update(); + }; + + this.pollTypeChange = newType => () => { + this.form.setPollType(newType); + this.update(); + }; + + this.toggleLimitEmail = () => { + this.limitEmail = !this.limitEmail; + if (this.limitEmail && !this.form.model.emailAddressSuffixes) { + this.form.model.emailAddressSuffixes = [""]; + } + this.update(); + }; + + this.removeSuffix = index => () => { + if (this.form.model.emailAddressSuffixes.length > 1) { + this.form.model.emailAddressSuffixes.splice(index, 1); + } + }; + + this.addSuffix = () => { + this.form.model.emailAddressSuffixes.push(""); + }; + + this.submit = () => { + + if (!this.showOptions && this.form.creation) { + this.form.setSettingsDefault(); + } else { + if (!this.form.hasVotes) { + this.form.model.beginDate = this.refs.votePeriod.checked ? this.refs.beginDate.getValue() : undefined; + this.form.model.voteVisibility = this.refs.voteVisibility.value; + this.form.model.anonymousVoteAllowed = this.refs.anonymousVote.checked; + this.form.model.resultVisibility = this.refs.resultVisibility.value; + this.form.model.commentVisibility = this.refs.commentVisibility.value; + } + + this.form.model.endDate = this.refs.votePeriod.checked ? this.refs.endDate.getValue() : undefined; + this.form.model.continuousResults = this.refs.continuousResults.checked; + + this.form.model.voteNotification = this.refs.voteNotification.checked; + this.form.model.commentNotification = this.refs.commentNotification.checked; + this.form.model.newChoiceNotification = this.refs.newChoiceNotification.checked; + this.form.model.notificationLocale = this.session.locale; + if (this.notifyMeBeforePollEnds) { + this.form.model.notifyMeHoursBeforePollEnds = this.refs.notifyMeHoursBeforePollEnds.value; + } else { + this.form.model.notifyMeHoursBeforePollEnds = 0; + } + } + if (this.form.model.pollType === "REGISTERED" && this.limitEmail) { + this.form.model.emailAddressSuffixes = + (Array.isArray(this.refs.suffix) ? this.refs.suffix : [this.refs.suffix]) + .map(suffixInput => suffixInput.value); + } else { + this.form.model.emailAddressSuffixes = null; + } + }; + </script> + <style> + + ul { + padding-left: 40px; + } + + h4 { + margin-bottom: 0.5em; + } + + .settings-options.with-default { + border-radius: 4px; + box-shadow: 0 0 1px hsla(0,0%,7%,.6); + padding: 0.1em 1.5em; + margin-bottom: 1em; + } + + .settings-options-row { + display: flex; + flex-direction: column; + } + + @media (min-width: 640px) { + .settings-options-row { + flex-direction: row; + } + + .settings-options-row .form-section { + flex-basis: 50%; + padding: 0 2em; + } + + .settings-options-row .form-section:first-child { + padding-left: 0; + } + + .settings-options-row .form-section:last-child { + padding-right: 0; + } + } + + .o-form-element { + margin: 0.5em 0; + padding: 0.5em 0; + } + + .o-form-element .c-input-group { + margin: 0.5em 0; + } + + .settings-options .form-section { + margin: 1.5em 0; + } + + .select-or-radio .choice-select { + display: none; + } + + .choice-radio label { + cursor: help; + } + + @media (max-width: 640px) { + .select-or-radio .choice-radio { + display: none; + } + + .select-or-radio .choice-select { + display: inline; + } + + } + + </style> +</VoteSettings> -- To stop receiving notification emails like this one, please contact chorem.org SCM administrator <admin+scm@chorem.org>.